oh-my-agent-web 0.2.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/README.md +484 -0
- package/bin/oh-my-agent-web-ps.mjs +203 -0
- package/bin/oh-my-agent-web-ws.mjs +593 -0
- package/bin/oh-my-agent-web.mjs +321 -0
- package/bin/update-check.mjs +142 -0
- package/dist/server/codex-client.js +310 -0
- package/dist/server/codex-supervisor.js +446 -0
- package/dist/server/fs-service.js +144 -0
- package/dist/server/index.js +543 -0
- package/dist/server/request-inspector.js +458 -0
- package/dist/shared/protocol.js +112 -0
- package/package.json +82 -0
- package/scripts/manage-server.mjs +155 -0
- package/scripts/omaw.mjs +74 -0
- package/shared/protocol.ts +228 -0
- package/web/dist/assets/index-B8-VzDf2.css +1 -0
- package/web/dist/assets/index-DeC2eiiq.js +42 -0
- package/web/dist/assets/markdown-CYXe6JZx.js +55 -0
- package/web/dist/favicon.svg +12 -0
- package/web/dist/index.html +35 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* npm run stop / npm run restart helper for the local oh-my-agent-web service.
|
|
4
|
+
* It only targets a oh-my-agent-web process listening on OMAW_PORT.
|
|
5
|
+
*/
|
|
6
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
7
|
+
|
|
8
|
+
const port = Number(process.env.OMAW_PORT || 25257);
|
|
9
|
+
const command = process.argv[2] || "stop";
|
|
10
|
+
const extraArgs = process.argv.slice(3);
|
|
11
|
+
|
|
12
|
+
function commandForPid(pid) {
|
|
13
|
+
if (process.platform === "win32") {
|
|
14
|
+
try {
|
|
15
|
+
return execFileSync("wmic", ["process", "where", `ProcessId=${pid}`, "get", "CommandLine", "/value"], {
|
|
16
|
+
encoding: "utf8",
|
|
17
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
18
|
+
}).trim();
|
|
19
|
+
} catch {
|
|
20
|
+
return "";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
return execFileSync("ps", ["-p", String(pid), "-o", "command="], {
|
|
25
|
+
encoding: "utf8",
|
|
26
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
27
|
+
}).trim();
|
|
28
|
+
} catch {
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function listeningPids() {
|
|
34
|
+
if (process.platform === "win32") {
|
|
35
|
+
try {
|
|
36
|
+
const output = execFileSync("netstat", ["-ano", "-p", "tcp"], { encoding: "utf8" });
|
|
37
|
+
const pids = new Set();
|
|
38
|
+
for (const line of output.split(/\r?\n/)) {
|
|
39
|
+
if (!line.includes(`:${port}`) || !/LISTENING/i.test(line)) continue;
|
|
40
|
+
const match = /\s+(\d+)\s*$/.exec(line);
|
|
41
|
+
if (match) pids.add(Number(match[1]));
|
|
42
|
+
}
|
|
43
|
+
return [...pids];
|
|
44
|
+
} catch {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const output = execFileSync("lsof", [`-tiTCP:${port}`, "-sTCP:LISTEN"], {
|
|
50
|
+
encoding: "utf8",
|
|
51
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
52
|
+
});
|
|
53
|
+
return [...new Set(output.split(/\s+/).filter(Boolean).map(Number).filter(Number.isInteger))];
|
|
54
|
+
} catch {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Is this pid one of ours?
|
|
61
|
+
*
|
|
62
|
+
* Matched against the **command line**, which differs by how the server was
|
|
63
|
+
* started:
|
|
64
|
+
* - global install: `…/bin/oh-my-agent-web.mjs` (or `…/bin/omaw` via the alias)
|
|
65
|
+
* - repo, dev: `tsx watch server/index.ts` → the listener child runs
|
|
66
|
+
* `node --require …/tsx … server/index.ts`
|
|
67
|
+
* - repo, built: `node …/dist/server/index.js`
|
|
68
|
+
*
|
|
69
|
+
* The checkout folder name is deliberately NOT used: it can be anything, and
|
|
70
|
+
* matching it only ever worked by accident (when the folder happened to be named
|
|
71
|
+
* after the package).
|
|
72
|
+
*/
|
|
73
|
+
function isProjectProcess(pid) {
|
|
74
|
+
const cmd = commandForPid(pid);
|
|
75
|
+
if (/oh-my-agent-web|bin[\\/](?:oh-my-agent-web|omaw)/.test(cmd)) return true;
|
|
76
|
+
// Dev / built entry point, however the runner was invoked. The path is
|
|
77
|
+
// relative in the child's argv (`… tsx/dist/loader.mjs server/index.ts`), so
|
|
78
|
+
// match on a word boundary rather than a preceding slash.
|
|
79
|
+
return /\bserver[\\/]index\.(?:ts|js)\b/.test(cmd) && /\b(?:node|tsx)\b/.test(cmd);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function isAlive(pid) {
|
|
83
|
+
try {
|
|
84
|
+
process.kill(pid, 0);
|
|
85
|
+
return true;
|
|
86
|
+
} catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function sleep(ms) {
|
|
92
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function stop() {
|
|
96
|
+
const pids = listeningPids();
|
|
97
|
+
if (pids.length === 0) {
|
|
98
|
+
console.log(`oh-my-agent-web 未运行(端口 ${port})。`);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const owned = pids.filter(isProjectProcess);
|
|
103
|
+
const foreign = pids.filter((pid) => !owned.includes(pid));
|
|
104
|
+
if (foreign.length > 0) {
|
|
105
|
+
console.error(`端口 ${port} 被其他进程占用,未执行停止:${foreign.join(", ")}`);
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
if (owned.length === 0) {
|
|
109
|
+
console.error(`端口 ${port} 被占用,但不是 oh-my-agent-web 进程,未执行停止。`);
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
for (const pid of owned) {
|
|
114
|
+
console.log(`正在停止 oh-my-agent-web (pid ${pid})…`);
|
|
115
|
+
try {
|
|
116
|
+
process.kill(pid, "SIGTERM");
|
|
117
|
+
} catch (error) {
|
|
118
|
+
if (error?.code !== "ESRCH") throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const deadline = Date.now() + 8_000;
|
|
123
|
+
while (Date.now() < deadline && owned.some(isAlive)) await sleep(200);
|
|
124
|
+
const stuck = owned.filter(isAlive);
|
|
125
|
+
for (const pid of stuck) {
|
|
126
|
+
console.warn(`pid ${pid} 未退出,发送 SIGKILL…`);
|
|
127
|
+
try {
|
|
128
|
+
process.kill(pid, "SIGKILL");
|
|
129
|
+
} catch {
|
|
130
|
+
/* already gone */
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
console.log("oh-my-agent-web 已停止。");
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (command === "stop") {
|
|
138
|
+
process.exitCode = (await stop()) ? 0 : 1;
|
|
139
|
+
} else if (command === "restart") {
|
|
140
|
+
if (!(await stop())) process.exit(1);
|
|
141
|
+
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
142
|
+
const args = extraArgs.length > 0 ? ["start", "--", ...extraArgs] : ["start"];
|
|
143
|
+
console.log("正在重新启动 oh-my-agent-web…");
|
|
144
|
+
const child = spawn(npm, args, { stdio: "inherit", env: process.env });
|
|
145
|
+
child.on("error", (error) => {
|
|
146
|
+
console.error(`启动失败:${error.message}`);
|
|
147
|
+
process.exitCode = 1;
|
|
148
|
+
});
|
|
149
|
+
child.on("exit", (code, signal) => {
|
|
150
|
+
process.exitCode = code ?? (signal ? 1 : 0);
|
|
151
|
+
});
|
|
152
|
+
} else {
|
|
153
|
+
console.error(`未知命令:${command}。可用命令:stop、restart`);
|
|
154
|
+
process.exitCode = 2;
|
|
155
|
+
}
|
package/scripts/omaw.mjs
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `npm run omaw` — 仓库内的一条总入口,行为取决于有没有参数:
|
|
4
|
+
*
|
|
5
|
+
* npm run omaw 起本地开发栈(源码直跑,2 个进程)
|
|
6
|
+
* = `npm run dev`:单进程、单端口 :25257,Vite 在进程内当中间件
|
|
7
|
+
* npm run omaw -- ps 转发给 CLI:进程 / 端口巡检
|
|
8
|
+
* npm run omaw -- stop 转发给 CLI:停掉监听 UI 端口的服务
|
|
9
|
+
* npm run omaw -- restart 转发给 CLI:停掉后重新起(前台)
|
|
10
|
+
* npm run omaw -- ws -p 25268 转发给 CLI:用 WS 客户端临时接入指定地址的 app-server
|
|
11
|
+
* npm run omaw -- --port 3000 转发给 CLI:直接起后端(源码直跑,不做 Vite)
|
|
12
|
+
* npm run omaw -- --help CLI 帮助
|
|
13
|
+
*
|
|
14
|
+
* ⚠️ 参数必须写在 `--` 之后。
|
|
15
|
+
*
|
|
16
|
+
* npm(≥7)会先把 `-p` / `--port` / `--url` 这类 flag 当成*自己的*配置吃掉,
|
|
17
|
+
* 剩下的位置参数才给脚本。实测 npm 11:
|
|
18
|
+
*
|
|
19
|
+
* npm run omaw ws -p 25268 → 收到 ["ws", "25268"] (-p 丢失,25268 被当成聊天消息)
|
|
20
|
+
* npm run omaw -- ws -p 25268 → 收到 ["ws", "-p", "25268"] ✅
|
|
21
|
+
*
|
|
22
|
+
* 这是 npm 的行为,脚本侧无法挽回(`npm_config_argv` 在 npm 7 已被移除),
|
|
23
|
+
* 所以这里不去猜、也不做兜底,只负责老老实实转发。
|
|
24
|
+
*/
|
|
25
|
+
import { spawn } from "node:child_process";
|
|
26
|
+
import { dirname, join, resolve } from "node:path";
|
|
27
|
+
import { fileURLToPath } from "node:url";
|
|
28
|
+
|
|
29
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
30
|
+
const repoRoot = resolve(here, "..");
|
|
31
|
+
const cliEntry = join(repoRoot, "bin", "oh-my-agent-web.mjs");
|
|
32
|
+
|
|
33
|
+
const args = process.argv.slice(2);
|
|
34
|
+
const isWindows = process.platform === "win32";
|
|
35
|
+
const npmBin = isWindows ? "npm.cmd" : "npm";
|
|
36
|
+
|
|
37
|
+
/** 无参数 → 复用 `npm run dev`,避免把 dev 的命令行在这里抄一遍。 */
|
|
38
|
+
function childFor() {
|
|
39
|
+
if (args.length === 0) {
|
|
40
|
+
// 端口与 dev 保持一致:OMAW_PORT 未设时默认 25257。
|
|
41
|
+
const port = Number(process.env.OMAW_PORT) || 25257;
|
|
42
|
+
const host = process.env.OMAW_HOST ?? "127.0.0.1";
|
|
43
|
+
return {
|
|
44
|
+
command: npmBin,
|
|
45
|
+
argv: ["run", "dev"],
|
|
46
|
+
note: `dev stack → http://${host}:${port} (源码模式,Vite 在进程内,无需 build)`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return { command: process.execPath, argv: [cliEntry, ...args], note: null };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const { command, argv, note } = childFor();
|
|
53
|
+
if (note) console.error(`[omaw] ${note}`);
|
|
54
|
+
|
|
55
|
+
const child = spawn(command, argv, {
|
|
56
|
+
cwd: repoRoot,
|
|
57
|
+
stdio: "inherit",
|
|
58
|
+
// Windows 上 npm.cmd 需要 shell 才能被 spawn。
|
|
59
|
+
shell: isWindows,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
child.on("error", (err) => {
|
|
63
|
+
console.error(`[omaw] failed to start: ${err.message}`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
child.on("exit", (code, signal) => {
|
|
68
|
+
// Ctrl-C 会同时发给整个前台进程组,这里只需要把退出状态如实传出去。
|
|
69
|
+
if (signal) {
|
|
70
|
+
process.kill(process.pid, signal);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
process.exit(code ?? 0);
|
|
74
|
+
});
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* oh-my-agent-web — browser <-> server wire protocol.
|
|
3
|
+
*
|
|
4
|
+
* The browser talks to OUR server (not directly to codex). Our server is a thin,
|
|
5
|
+
* transparent proxy in front of `codex app-server --listen ws://127.0.0.1:25258`:
|
|
6
|
+
*
|
|
7
|
+
* browser <--ws /ws--> oh-my-agent-web server <--ws--> codex app-server
|
|
8
|
+
*
|
|
9
|
+
* Because the proxy is transparent, the browser may invoke ANY codex app-server
|
|
10
|
+
* JSON-RPC method by name (`thread/start`, `turn/start`, `model/list`, ...) and
|
|
11
|
+
* receives every codex notification verbatim. The names/types below therefore
|
|
12
|
+
* mirror codex's own protocol; only the *envelope* is ours.
|
|
13
|
+
*
|
|
14
|
+
* Codex types can be regenerated with:
|
|
15
|
+
* codex app-server generate-ts --out shared/codex-ts
|
|
16
|
+
*
|
|
17
|
+
* Envelope rules
|
|
18
|
+
* --------------
|
|
19
|
+
* client -> server:
|
|
20
|
+
* { type: "rpc", requestId, method, params } invoke a codex method (or `omaw/*`)
|
|
21
|
+
* { type: "reply", id, result } answer a serverRequest (approval, user input)
|
|
22
|
+
* { type: "reply", id, error } reject a serverRequest
|
|
23
|
+
* { type: "ping" }
|
|
24
|
+
*
|
|
25
|
+
* server -> client:
|
|
26
|
+
* { type: "welcome", ... } handshake, sent once per connection
|
|
27
|
+
* { type: "status", codex } supervisor state, on change
|
|
28
|
+
* { type: "rpcResult", requestId, ok, ... } answer to our rpc
|
|
29
|
+
* { type: "event", method, params } codex notification (verbatim)
|
|
30
|
+
* { type: "serverRequest", id, method, params }codex -> client request (verbatim)
|
|
31
|
+
* { type: "pong" }
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
export const PROTOCOL_VERSION = 1;
|
|
35
|
+
|
|
36
|
+
/** JSON-RPC id as used by codex (`RequestId` in the generated bindings). */
|
|
37
|
+
export type RequestId = number | string;
|
|
38
|
+
|
|
39
|
+
export interface JsonRpcError {
|
|
40
|
+
code: number;
|
|
41
|
+
message: string;
|
|
42
|
+
data?: unknown;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* ------------------------------------------------------------------ */
|
|
46
|
+
/* client -> server */
|
|
47
|
+
/* ------------------------------------------------------------------ */
|
|
48
|
+
|
|
49
|
+
export type ClientMessage =
|
|
50
|
+
| { type: "rpc"; requestId: string; method: string; params?: unknown }
|
|
51
|
+
| { type: "reply"; id: RequestId; result: unknown }
|
|
52
|
+
| { type: "reply"; id: RequestId; error: JsonRpcError }
|
|
53
|
+
| { type: "ping" };
|
|
54
|
+
|
|
55
|
+
/* ------------------------------------------------------------------ */
|
|
56
|
+
/* server -> client */
|
|
57
|
+
/* ------------------------------------------------------------------ */
|
|
58
|
+
|
|
59
|
+
/** Lifecycle of the managed `codex app-server` child process. */
|
|
60
|
+
export type CodexPhase = "starting" | "ready" | "stopped" | "error";
|
|
61
|
+
|
|
62
|
+
export interface CodexStatus {
|
|
63
|
+
phase: CodexPhase;
|
|
64
|
+
/** ws:// endpoint the app-server listens on. */
|
|
65
|
+
url: string;
|
|
66
|
+
pid: number | null;
|
|
67
|
+
/** True once initialize + WS handshake completed. */
|
|
68
|
+
connected: boolean;
|
|
69
|
+
/** Last supervisor error, if any. */
|
|
70
|
+
error: string | null;
|
|
71
|
+
/** ms timestamp of the last successful (re)connect. */
|
|
72
|
+
since: number | null;
|
|
73
|
+
/** Restart counter — grows on every respawn. */
|
|
74
|
+
restarts: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface ServerInfo {
|
|
78
|
+
version: string;
|
|
79
|
+
protocolVersion: number;
|
|
80
|
+
cwd: string;
|
|
81
|
+
codexUrl: string;
|
|
82
|
+
codexVersion: string | null;
|
|
83
|
+
platform: NodeJS.Platform;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export type ServerMessage =
|
|
87
|
+
| { type: "welcome"; info: ServerInfo }
|
|
88
|
+
| { type: "status"; codex: CodexStatus }
|
|
89
|
+
| { type: "rpcResult"; requestId: string; ok: true; result: unknown }
|
|
90
|
+
| { type: "rpcResult"; requestId: string; ok: false; error: JsonRpcError }
|
|
91
|
+
| { type: "event"; method: string; params: unknown }
|
|
92
|
+
| { type: "serverRequest"; id: RequestId; method: string; params: unknown }
|
|
93
|
+
| { type: "pong" };
|
|
94
|
+
|
|
95
|
+
/* ------------------------------------------------------------------ */
|
|
96
|
+
/* local (`omaw/*`) methods — handled by OUR server, not proxied */
|
|
97
|
+
/* ------------------------------------------------------------------ */
|
|
98
|
+
|
|
99
|
+
export const LOCAL_METHODS = {
|
|
100
|
+
/** -> { status: CodexStatus } */
|
|
101
|
+
codexStatus: "omaw/codex/status",
|
|
102
|
+
/** -> { ok: true } — kill + respawn the app-server */
|
|
103
|
+
codexRestart: "omaw/codex/restart",
|
|
104
|
+
/** -> { lines: string[] } — tail of the app-server log */
|
|
105
|
+
codexLog: "omaw/codex/log",
|
|
106
|
+
/** { threadId?: string, limit?: number } -> request/response summaries */
|
|
107
|
+
requestLogsList: "omaw/request-logs/list",
|
|
108
|
+
/** { id: string } -> one full request/response exchange */
|
|
109
|
+
requestLogsDetail: "omaw/request-logs/detail",
|
|
110
|
+
/** { path?: string } -> { path, entries: [{name,path,kind,size,mtime}] } */
|
|
111
|
+
fsList: "omaw/fs/list",
|
|
112
|
+
/** { path: string, maxBytes?: number } -> { path, text, truncated } */
|
|
113
|
+
fsRead: "omaw/fs/read",
|
|
114
|
+
/** -> { cwd, home, codexHome } */
|
|
115
|
+
paths: "omaw/paths",
|
|
116
|
+
} as const;
|
|
117
|
+
|
|
118
|
+
export interface FsEntry {
|
|
119
|
+
name: string;
|
|
120
|
+
path: string;
|
|
121
|
+
kind: "file" | "directory" | "symlink" | "other";
|
|
122
|
+
size: number;
|
|
123
|
+
mtime: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface CwPaths {
|
|
127
|
+
cwd: string;
|
|
128
|
+
home: string;
|
|
129
|
+
codexHome: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface RequestLogSummary {
|
|
133
|
+
id: string;
|
|
134
|
+
startedAt: string;
|
|
135
|
+
status: number | null;
|
|
136
|
+
model: string | null;
|
|
137
|
+
durationMs: number | null;
|
|
138
|
+
requestBytes: number;
|
|
139
|
+
responseBytes: number;
|
|
140
|
+
error: string | null;
|
|
141
|
+
threadId: string;
|
|
142
|
+
turnId: string;
|
|
143
|
+
source: string;
|
|
144
|
+
trigger: string;
|
|
145
|
+
internal: boolean;
|
|
146
|
+
question: string;
|
|
147
|
+
answer: string;
|
|
148
|
+
reasoning: string;
|
|
149
|
+
tools: string[];
|
|
150
|
+
usage: Record<string, unknown> | null;
|
|
151
|
+
tokens: {
|
|
152
|
+
input: number;
|
|
153
|
+
cached: number;
|
|
154
|
+
cacheWrite: number;
|
|
155
|
+
output: number;
|
|
156
|
+
reasoning: number;
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface RequestLogDetail extends RequestLogSummary {
|
|
161
|
+
requestHeaders: Record<string, string>;
|
|
162
|
+
responseHeaders: Record<string, string>;
|
|
163
|
+
requestBody: string;
|
|
164
|
+
responseBody: string;
|
|
165
|
+
responseTruncated: boolean;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* ------------------------------------------------------------------ */
|
|
169
|
+
/* codex method names we rely on — kept loose on purpose */
|
|
170
|
+
/* ------------------------------------------------------------------ */
|
|
171
|
+
|
|
172
|
+
export const CODEX = {
|
|
173
|
+
initialize: "initialize",
|
|
174
|
+
threadStart: "thread/start",
|
|
175
|
+
threadResume: "thread/resume",
|
|
176
|
+
threadRead: "thread/read",
|
|
177
|
+
threadList: "thread/list",
|
|
178
|
+
threadDelete: "thread/delete",
|
|
179
|
+
threadArchive: "thread/archive",
|
|
180
|
+
threadUnarchive: "thread/unarchive",
|
|
181
|
+
threadFork: "thread/fork",
|
|
182
|
+
threadSetName: "thread/name/set",
|
|
183
|
+
threadTurnsList: "thread/turns/list",
|
|
184
|
+
threadItemsList: "thread/items/list",
|
|
185
|
+
turnStart: "turn/start",
|
|
186
|
+
turnSteer: "turn/steer",
|
|
187
|
+
turnInterrupt: "turn/interrupt",
|
|
188
|
+
modelList: "model/list",
|
|
189
|
+
accountRead: "account/read",
|
|
190
|
+
accountRateLimits: "account/rateLimits/read",
|
|
191
|
+
getAuthStatus: "getAuthStatus",
|
|
192
|
+
configRead: "config/read",
|
|
193
|
+
fsReadFile: "fs/readFile",
|
|
194
|
+
fsReadDirectory: "fs/readDirectory",
|
|
195
|
+
fuzzyFileSearch: "fuzzyFileSearch",
|
|
196
|
+
} as const;
|
|
197
|
+
|
|
198
|
+
/** codex notification methods that the UI cares about most. */
|
|
199
|
+
export const CODEX_EVENTS = {
|
|
200
|
+
threadStarted: "thread/started",
|
|
201
|
+
threadStatusChanged: "thread/status/changed",
|
|
202
|
+
threadNameUpdated: "thread/name/updated",
|
|
203
|
+
turnStarted: "turn/started",
|
|
204
|
+
turnCompleted: "turn/completed",
|
|
205
|
+
turnDiffUpdated: "turn/diff/updated",
|
|
206
|
+
turnPlanUpdated: "turn/plan/updated",
|
|
207
|
+
itemStarted: "item/started",
|
|
208
|
+
itemCompleted: "item/completed",
|
|
209
|
+
agentMessageDelta: "item/agentMessage/delta",
|
|
210
|
+
reasoningSummaryDelta: "item/reasoning/summaryTextDelta",
|
|
211
|
+
reasoningTextDelta: "item/reasoning/textDelta",
|
|
212
|
+
planDelta: "item/plan/delta",
|
|
213
|
+
commandOutputDelta: "item/commandExecution/outputDelta",
|
|
214
|
+
fileChangeDelta: "item/fileChange/outputDelta",
|
|
215
|
+
mcpProgress: "item/mcpToolCall/progress",
|
|
216
|
+
tokenUsage: "thread/tokenUsage/updated",
|
|
217
|
+
error: "error",
|
|
218
|
+
} as const;
|
|
219
|
+
|
|
220
|
+
/** codex server -> client request methods (need a `reply`). */
|
|
221
|
+
export const CODEX_REQUESTS = {
|
|
222
|
+
commandApproval: "item/commandExecution/requestApproval",
|
|
223
|
+
fileChangeApproval: "item/fileChange/requestApproval",
|
|
224
|
+
permissionsApproval: "item/permissions/requestApproval",
|
|
225
|
+
toolInput: "item/tool/requestUserInput",
|
|
226
|
+
elicitation: "mcpServer/elicitation/request",
|
|
227
|
+
dynamicToolCall: "item/tool/call",
|
|
228
|
+
} as const;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root,[data-theme=dark]{--bg: #0a0b0e;--bg-elev: #101218;--bg-elev2: #161923;--bg-inset: #0c0e13;--border: #232733;--border-soft: #1b1f29;--text: #e7e9ef;--text-dim: #9aa1b1;--text-faint: #666d7e;--accent: #8b7bff;--accent-soft: rgba(139, 123, 255, .14);--accent-strong: #a99cff;--green: #3ecf8e;--green-soft: rgba(62, 207, 142, .14);--red: #f16a6a;--red-soft: rgba(241, 106, 106, .14);--amber: #f0b429;--amber-soft: rgba(240, 180, 41, .14);--blue: #4c8dff;--blue-soft: rgba(76, 141, 255, .14);--code-bg: #0b0d12;--code-border: #1e2230;--user-bg: #1c1f2a;--shadow: 0 12px 40px rgba(0, 0, 0, .45);--shadow-soft: 0 4px 16px rgba(0, 0, 0, .3);--scroll-thumb: #2a2f3c;--scroll-thumb-hover: #3a4152;--sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Roboto, Helvetica, Arial, sans-serif;--mono: "SF Mono", "JetBrains Mono", ui-monospace, Menlo, Consolas, monospace}[data-theme=light]{color-scheme:light;--bg: #ffffff;--bg-elev: #f6f8fa;--bg-elev2: #eaeef2;--bg-inset: #f6f8fa;--border: #d0d7de;--border-soft: #e4e8ed;--text: #1f2328;--text-dim: #59636e;--text-faint: #8c959f;--accent: #0969da;--accent-soft: rgba(9, 105, 218, .1);--accent-strong: #0550ae;--green: #1a7f37;--green-soft: rgba(26, 127, 55, .1);--red: #cf222e;--red-soft: rgba(207, 34, 46, .09);--amber: #9a6700;--amber-soft: rgba(154, 103, 0, .14);--blue: #0969da;--blue-soft: rgba(9, 105, 218, .1);--code-bg: #f6f8fa;--code-border: #e4e8ed;--user-bg: #ddf4ff;--shadow: 0 12px 32px rgba(31, 35, 40, .13);--shadow-soft: 0 3px 12px rgba(31, 35, 40, .07);--scroll-thumb: #c9d1d9;--scroll-thumb-hover: #afb8c1}[data-theme=paper]{color-scheme:light;--bg: #f7f1e3;--bg-elev: #fffdf6;--bg-elev2: #efe7d3;--bg-inset: #fffdf6;--border: #ddcfae;--border-soft: #e7dcc3;--text: #3f372c;--text-dim: #6f6250;--text-faint: #9c8b70;--accent: #b45309;--accent-soft: rgba(180, 83, 9, .12);--accent-strong: #92400e;--green: #15803d;--green-soft: rgba(21, 128, 61, .12);--red: #b91c1c;--red-soft: rgba(185, 28, 28, .1);--amber: #a16207;--amber-soft: rgba(161, 98, 7, .14);--blue: #1d4ed8;--blue-soft: rgba(29, 78, 216, .1);--code-bg: #efe7d3;--code-border: #e0d3b8;--user-bg: #f3e6c9;--shadow: 0 12px 32px rgba(80, 60, 20, .16);--shadow-soft: 0 3px 12px rgba(80, 60, 20, .09);--scroll-thumb: #d3c4a3;--scroll-thumb-hover: #b8a67f}[data-theme=mist]{color-scheme:light;--bg: #f4f8f7;--bg-elev: #e9f0ee;--bg-elev2: #dde8e4;--bg-inset: #eef4f2;--border: #bfd0c8;--border-soft: #d5e2dc;--text: #202e2b;--text-dim: #4a6159;--text-faint: #7b918a;--accent: #0f766e;--accent-soft: rgba(15, 118, 110, .11);--accent-strong: #115e59;--green: #15803d;--green-soft: rgba(21, 128, 61, .12);--red: #b91c1c;--red-soft: rgba(185, 28, 28, .1);--amber: #a16207;--amber-soft: rgba(161, 98, 7, .14);--blue: #0e7490;--blue-soft: rgba(14, 116, 144, .11);--code-bg: #e9f0ee;--code-border: #d5e2dc;--user-bg: #d8ebe7;--shadow: 0 12px 32px rgba(20, 50, 45, .15);--shadow-soft: 0 3px 12px rgba(20, 50, 45, .08);--scroll-thumb: #b4c8c1;--scroll-thumb-hover: #9bb3ab}*{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{background:var(--bg);color:var(--text);font-family:var(--sans);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased}button,input,select,textarea{font:inherit;color:inherit}button{cursor:pointer}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{background:var(--scroll-thumb);border-radius:6px;border:2px solid transparent;background-clip:padding-box}::-webkit-scrollbar-thumb:hover{background:var(--scroll-thumb-hover);background-clip:padding-box}.muted{color:var(--text-dim)}.small{font-size:12px}.pad{padding:10px 12px}.spacer{flex:1}.error-text{color:var(--red)}.mono{font-family:var(--mono)}.btn{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--border);background:var(--bg-elev2);color:var(--text);border-radius:8px;padding:7px 12px;font-size:13px;font-weight:500;transition:background .12s ease,border-color .12s ease,transform .05s ease}.btn:hover:not(:disabled){background:var(--bg-elev);border-color:var(--text-faint)}.btn:active:not(:disabled){transform:translateY(1px)}.btn:disabled{opacity:.45;cursor:not-allowed}.btn.primary{background:var(--accent);border-color:var(--accent);color:#fff}.btn.primary:hover:not(:disabled){background:var(--accent-strong);border-color:var(--accent-strong)}.btn.danger{background:var(--red-soft);border-color:var(--red);color:var(--red)}.btn.ghost{background:transparent}.btn.small{padding:5px 9px;font-size:12px}.icon-btn{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:8px;border:1px solid transparent;background:transparent;color:var(--text-dim)}.icon-btn:hover{background:var(--bg-elev2);color:var(--text)}.icon-btn.tiny{width:24px;height:24px}.icon-btn.active{background:var(--accent-soft);color:var(--accent-strong)}.kbd{border:1px solid var(--border);border-radius:5px;padding:1px 5px;font-size:11px;color:var(--text-faint);background:var(--bg)}.app{display:flex;height:100%;overflow:hidden}.sidebar-container{width:var(--sidebar-w, 288px);flex:0 0 var(--sidebar-w, 288px);border-right:1px solid var(--border);background:var(--bg-elev);height:100%;z-index:30;position:relative}.sidebar-container.dragging{border-right-color:var(--accent)}.app.sidebar-collapsed .sidebar-container{display:none}.sidebar-rail{display:flex;align-items:flex-start;justify-content:center;padding-top:14px;width:30px;flex:0 0 30px;border:none;border-right:1px solid var(--border);background:var(--bg-elev);color:var(--text-faint);cursor:pointer}.sidebar-rail:hover{background:var(--bg-elev2);color:var(--text)}.resize-handle{position:absolute;z-index:45;touch-action:none}.resize-handle:after{content:"";position:absolute;background:transparent;transition:background .12s ease}.resize-handle.vertical{top:0;bottom:0;width:11px;cursor:col-resize}.resize-handle.vertical:after{top:0;bottom:0;left:5px;width:2px}.resize-handle.vertical.left{left:-6px}.resize-handle.vertical:not(.left){right:-6px}.resize-handle:hover:after,.dragging>.resize-handle:after{background:var(--accent)}.main{display:flex;flex-direction:column;flex:1;min-width:0;height:100%;background:var(--bg)}.sidebar{display:flex;flex-direction:column;height:100%;padding:12px;gap:10px}.sidebar-head{display:flex;align-items:center;justify-content:space-between;padding:2px 2px 4px}.brand{display:flex;align-items:center;gap:9px;font-weight:650;letter-spacing:-.01em}.brand-mark{display:inline-flex;width:26px;height:26px;align-items:center;justify-content:center;border-radius:8px;background:linear-gradient(135deg,var(--accent-strong),var(--accent));color:#fff}.brand-name{font-size:15px}.new-thread{width:100%;justify-content:center}.search-box{display:flex;align-items:center;gap:8px;background:var(--bg);border:1px solid var(--border);border-radius:9px;padding:6px 9px;color:var(--text-faint)}.search-box:focus-within{border-color:var(--accent)}.search-box input{flex:1;min-width:0;border:none;background:transparent;outline:none;color:var(--text);font-size:13px}.sidebar-section-head{display:flex;align-items:center;gap:8px;padding:2px 10px 4px}.sidebar-section-label{font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--text-faint)}.thread-count{margin-left:auto;font-size:11px}.sidebar-scroll{flex:1;overflow-y:auto;margin:0 -4px;padding:0 4px}.thread-list{display:flex;flex-direction:column;gap:2px}.list-empty,.list-error{padding:16px 8px;font-size:13px;text-align:center;display:flex;flex-direction:column;gap:8px;align-items:center}.thread-row{display:flex;align-items:center;border-radius:8px;position:relative}.thread-row:hover{background:var(--bg-elev2)}.thread-row.active{background:var(--accent-soft)}.thread-main{flex:1;min-width:0;display:flex;align-items:center;gap:9px;border:none;background:transparent;text-align:left;padding:7px 8px;border-radius:8px}.thread-texts{display:flex;flex-direction:column;min-width:0;gap:1px}.thread-title{font-size:13px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.thread-row.active .thread-title{color:var(--accent-strong)}.thread-sub{font-size:11.5px;color:var(--text-faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.status-dot{width:8px;height:8px;border-radius:50%;flex:0 0 8px;background:var(--text-faint)}.dot-idle{background:var(--text-faint)}.dot-active{background:var(--green);box-shadow:0 0 0 3px var(--green-soft);animation:pulse 1.6s infinite}.dot-error{background:var(--red)}.dot-faint{background:var(--border)}@keyframes pulse{0%,to{opacity:1}50%{opacity:.4}}.thread-menu-btn{opacity:0;margin-right:4px}.thread-row:hover .thread-menu-btn,.thread-row.active .thread-menu-btn{opacity:1}.thread-rename{flex:1;margin:4px;padding:5px 8px;border-radius:7px;border:1px solid var(--accent);background:var(--bg);outline:none;font-size:13px}.load-more{margin:8px 4px;justify-content:center}.menu{position:absolute;top:calc(100% + 2px);right:4px;z-index:40;min-width:168px;background:var(--bg-elev2);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow);padding:5px;display:flex;flex-direction:column}.menu button{display:flex;align-items:center;gap:9px;border:none;background:transparent;color:var(--text);text-align:left;padding:7px 9px;border-radius:7px;font-size:13px}.menu button:hover{background:var(--bg-elev)}.menu button.danger{color:var(--red)}.menu-sep{height:1px;background:var(--border-soft);margin:4px 6px}.header{display:flex;align-items:center;gap:12px;padding:9px 14px;border-bottom:1px solid var(--border);background:var(--bg-elev);min-height:52px}.header-left{display:flex;align-items:center;gap:8px;min-width:0;flex:1}.header-title{display:flex;flex-direction:column;min-width:0}.header-title-text{font-weight:600;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.header-title-btn{appearance:none;border:0;background:none;padding:0;margin:0;color:inherit;font:inherit;text-align:left;cursor:text;max-width:42vw}.header-title-btn:hover{text-decoration:underline dotted;text-decoration-color:var(--text-faint);text-underline-offset:3px}.header-title-input{font-weight:600;font-size:14px;font-family:inherit;color:var(--text);background:var(--bg);border:1px solid var(--accent);border-radius:7px;padding:1px 6px;outline:none;max-width:42vw;min-width:14ch}.header-cwd{font-size:11.5px;color:var(--text-faint);font-family:var(--mono);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:42vw}.header-right{display:flex;align-items:center;gap:8px}.inline-select select,.popover select,.popover .text-input{border:1px solid var(--border);background:var(--bg);border-radius:8px;padding:5px 8px;font-size:12.5px;outline:none;max-width:190px}.inline-select select:focus,.popover select:focus,.text-input:focus{border-color:var(--accent)}.popover-wrap{position:relative}.theme-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px}.theme-option{display:flex;align-items:center;gap:8px;padding:6px 7px;border:1px solid var(--border);border-radius:9px;background:var(--bg);color:var(--text-dim);font-size:12.5px;text-align:left}.theme-option:hover{border-color:var(--text-faint);color:var(--text)}.theme-option.active{border-color:var(--accent);background:var(--accent-soft);color:var(--accent-strong);font-weight:600}.theme-swatch{position:relative;width:20px;height:20px;flex:0 0 20px;border-radius:6px;border:1.5px solid;overflow:hidden}.theme-swatch i{position:absolute;left:0;bottom:0;width:100%;height:44%}.theme-option-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.project-picker{position:relative}.project-btn{display:flex;align-items:center;gap:8px;width:100%;padding:7px 10px;border:1px solid var(--border);border-radius:10px;background:var(--bg);color:var(--text);font-size:13px;text-align:left}.project-btn:hover{border-color:var(--text-faint)}.project-btn.open{border-color:var(--accent)}.project-btn-path{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.project-btn .project-dot{width:9px;height:9px;border-radius:50%;background:var(--accent)}.project-btn .project-dot.busy{animation:project-pulse 1s ease-in-out infinite}@keyframes project-pulse{0%,to{opacity:.35}50%{opacity:1}}.project-menu{position:absolute;top:calc(100% + 6px);left:0;right:0;z-index:60;background:var(--bg-elev);border:1px solid var(--border);border-radius:12px;box-shadow:var(--shadow);padding:6px;display:flex;flex-direction:column;gap:4px}.project-filter{display:flex;align-items:center;gap:7px;padding:6px 8px;border:1px solid var(--border);border-radius:8px;color:var(--text-faint)}.project-filter:focus-within{border-color:var(--accent)}.project-filter input{flex:1;min-width:0;border:none;background:transparent;outline:none;color:var(--text);font-size:12.5px}.project-list{max-height:47vh;overflow-y:auto;display:flex;flex-direction:column}.project-item{display:flex;align-items:center;gap:8px;width:100%;padding:7px 8px;border:none;border-radius:8px;background:transparent;color:var(--text-dim);font-size:13px;text-align:left}.project-item:hover:not(:disabled){background:var(--bg-elev2);color:var(--text)}.project-item.active{color:var(--accent-strong);font-weight:600}.project-item:disabled{opacity:.45}.project-check{display:flex;align-items:center;justify-content:center;width:14px;flex:0 0 14px;color:var(--accent)}.project-item-path{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.project-count{display:inline-flex;align-items:center;gap:5px;font-size:12px;color:var(--text-faint);flex:0 0 auto}.project-count.active{color:var(--accent-strong)}.project-item .project-dot{width:7px;height:7px;border-radius:50%;background:var(--text-faint)}.project-count.active .project-dot{background:var(--accent)}.project-empty{padding:10px 8px}.project-footer{border-top:1px solid var(--border-soft);padding-top:4px;display:flex;flex-direction:column}.project-custom{display:flex;flex-direction:column;gap:8px;padding:4px}.project-custom .text-input{width:100%;max-width:none}.project-custom-actions{display:flex;justify-content:flex-end;gap:6px}.popover{position:absolute;top:calc(100% + 8px);right:0;z-index:60;width:320px;background:var(--bg-elev2);border:1px solid var(--border);border-radius:12px;box-shadow:var(--shadow);padding:14px;display:flex;flex-direction:column;gap:14px}.popover-section{display:flex;flex-direction:column;gap:6px}.popover-title{font-size:12px;font-weight:600;color:var(--text-dim);text-transform:uppercase;letter-spacing:.04em}.text-input{width:100%;padding:7px 9px;border:1px solid var(--border);background:var(--bg);border-radius:8px;outline:none;font-size:12.5px;font-family:var(--mono)}.hint{font-size:11.5px;color:var(--text-faint)}.path-static{padding:5px 8px;border:1px solid var(--border);border-radius:8px;background:var(--bg);color:var(--text-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hint.block{display:block;margin-top:2px}.toggle-row{display:flex;gap:10px;align-items:flex-start;font-size:13px;cursor:pointer}.toggle-row input{margin-top:2px;accent-color:var(--accent)}.popover-actions{display:flex;gap:8px;border-top:1px solid var(--border-soft);padding-top:12px}.status-pill{display:inline-flex;align-items:center;gap:6px;padding:4px 9px;border-radius:999px;border:1px solid var(--border);background:var(--bg);font-size:12px;color:var(--text-dim);white-space:nowrap}.status-pill .status-dot{width:7px;height:7px;flex-basis:7px}.pill-ready .status-dot{background:var(--green)}.pill-ready{color:var(--green);border-color:var(--green-soft)}.pill-warn .status-dot{background:var(--amber)}.pill-warn{color:var(--amber)}.pill-error .status-dot,.pill-offline .status-dot{background:var(--red)}.pill-error,.pill-offline{color:var(--red)}.pill-unknown .status-dot{background:var(--text-faint)}.token-meter{display:flex;align-items:center;gap:7px;padding:3px 8px;border:1px solid var(--border);border-radius:999px;background:var(--bg)}.token-bar{width:56px;height:5px;border-radius:3px;background:var(--bg-elev2);overflow:hidden}.token-fill{height:100%;background:var(--blue);border-radius:3px;transition:width .3s ease}.token-meter.warm .token-fill{background:var(--amber)}.token-meter.hot .token-fill{background:var(--red)}.token-label{font-size:11px;color:var(--text-dim);font-variant-numeric:tabular-nums}.chat-wrap,.chat{flex:1;min-height:0;position:relative}.chat{overflow-y:auto;padding:20px 0 8px;scroll-behavior:auto}.chat-wrap{display:flex;flex-direction:column}.chat>.turn,.chat>.inline-error,.chat>.empty-state{max-width:none;margin:0;padding-left:10px;padding-right:10px}.chat-bottom-pad{height:20px}.jump-bottom{position:absolute;bottom:16px;left:50%;transform:translate(-50%);width:34px;height:34px;border-radius:50%;border:1px solid var(--border);background:var(--bg-elev2);color:var(--text);box-shadow:var(--shadow-soft);display:flex;align-items:center;justify-content:center;z-index:10}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;text-align:center;padding:80px 24px;color:var(--text-dim)}.empty-state.compact{padding:60px 24px}.empty-state h2{margin:0;color:var(--text);font-size:20px;letter-spacing:-.01em}.empty-mark{display:flex;width:58px;height:58px;align-items:center;justify-content:center;border-radius:16px;background:linear-gradient(135deg,var(--accent-strong),var(--accent));color:#fff;box-shadow:var(--shadow-soft)}.empty-state code{font-family:var(--mono);background:var(--bg-elev2);padding:1px 5px;border-radius:5px}.inline-error{display:flex;align-items:flex-start;gap:8px;margin-top:10px;padding:10px 12px;border-radius:10px;border:1px solid var(--red);background:var(--red-soft);color:var(--red);font-size:13px}.connection-banner{display:flex;align-items:center;gap:10px;padding:8px 14px;background:var(--amber-soft);border-bottom:1px solid var(--amber);color:var(--amber);font-size:13px}.connection-banner .btn{margin-left:auto;color:var(--text)}.turn{padding-top:10px}.turn-divider{display:flex;align-items:center;gap:8px;margin:10px 0 4px}.turn-status-dot{width:7px;height:7px;border-radius:50%;background:var(--text-faint)}.turn-status-dot.inProgress{background:var(--green);animation:pulse 1.6s infinite}.turn-status-dot.failed{background:var(--red)}.turn-status-dot.interrupted{background:var(--amber)}.turn-meta{font-size:11.5px;color:var(--text-faint);text-transform:uppercase;letter-spacing:.05em}.turn-items{display:flex;flex-direction:column;gap:10px}.turn-details{margin-top:2px}.turn-details-summary{display:flex;align-items:center;gap:7px;min-width:0;color:var(--text-dim);font-size:12.5px}.turn-details-label{font-weight:600;color:var(--text)}.turn-details-count{color:var(--text-faint);white-space:nowrap}.turn-details-items{display:flex;flex-direction:column;gap:10px}.item{position:relative}.item-user{display:flex;gap:10px;justify-content:flex-end}.user-avatar{order:2;display:flex;width:26px;height:26px;flex:0 0 26px;align-items:center;justify-content:center;border-radius:8px;background:var(--bg-elev2);color:var(--text-dim)}.user-bubble{order:1;max-width:82%;background:var(--user-bg);border:1px solid var(--border-soft);border-radius:12px 12px 4px;padding:9px 12px;display:flex;flex-direction:column;gap:8px}.user-text{white-space:pre-wrap;word-break:break-word}.user-image img,.generated-image{max-width:100%;border-radius:8px;border:1px solid var(--border)}.input-chip{display:inline-flex;align-items:center;gap:6px;font-size:12px;background:var(--bg);border:1px solid var(--border);border-radius:999px;padding:3px 9px;color:var(--text-dim);max-width:100%}.input-chip span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.input-chip.clickable:hover{border-color:var(--accent);color:var(--accent-strong)}.item-agent{display:flex;gap:10px}.agent-avatar{display:flex;width:26px;height:26px;flex:0 0 26px;align-items:center;justify-content:center;border-radius:8px;background:var(--accent-soft);color:var(--accent-strong)}.agent-body{flex:1;min-width:0}.agent-toolbar{display:flex;align-items:center;gap:8px;margin-bottom:2px;opacity:.9}.agent-toolbar .icon-btn{opacity:0;width:24px;height:24px}.item-agent:hover .agent-toolbar .icon-btn{opacity:1}.phase-badge{font-size:10.5px;text-transform:uppercase;letter-spacing:.05em;color:var(--text-faint);border:1px solid var(--border);border-radius:999px;padding:1px 7px}.typing{display:inline-flex;gap:4px;align-items:center;padding:4px 0}.typing .dot{width:6px;height:6px;border-radius:50%;background:var(--text-faint);animation:blink 1.2s infinite}.typing .dot:nth-child(2){animation-delay:.2s}.typing .dot:nth-child(3){animation-delay:.4s}@keyframes blink{0%,to{opacity:.3}50%{opacity:1}}.caret{display:inline-block;width:7px;height:15px;margin-left:2px;background:var(--accent);vertical-align:text-bottom;animation:blink 1s steps(2) infinite}.agent-questions{margin-top:8px;display:flex;flex-direction:column;gap:6px}.agent-question{border:1px solid var(--border);border-radius:9px;padding:8px 10px;background:var(--bg-elev)}.agent-question-title{display:flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600}.agent-question-options{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}.disclosure{border:1px solid var(--border-soft);border-radius:11px;background:var(--bg-elev);overflow:hidden}.disclosure-head{display:flex;align-items:center}.disclosure-toggle{flex:1;min-width:0;display:flex;align-items:center;gap:6px;border:none;background:transparent;color:var(--text);text-align:left;padding:9px 11px;font-size:13px}.disclosure-toggle:hover{background:var(--bg-elev2)}.disclosure-caret{display:inline-flex;color:var(--text-faint)}.disclosure-summary{flex:1;min-width:0;display:flex;align-items:center;gap:8px}.disclosure-actions{display:flex;align-items:center;gap:4px;padding-right:8px}.disclosure-body{padding:0 11px 11px}.item-reasoning .disclosure{background:transparent;border-style:dashed}.reasoning-summary{display:flex;align-items:center;gap:8px;color:var(--text-dim);font-size:12.5px;min-width:0}.reasoning-label{font-style:italic}.reasoning-preview{color:var(--text-faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-style:italic}.reasoning-content{display:flex;flex-direction:column;gap:10px;color:var(--text-dim);font-size:13px}.reasoning-block p{margin:0 0 8px;white-space:pre-wrap}.reasoning-raw{border-left:2px solid var(--border);padding-left:10px;font-size:12.5px}.command-summary{display:flex;align-items:center;gap:9px;min-width:0;flex:1}.command-text{font-family:var(--mono);font-size:12.5px;white-space:pre-wrap;overflow-wrap:anywhere;background:var(--code-bg);padding:2px 7px;border-radius:6px;border:1px solid var(--code-border)}.command-actions{display:flex;gap:6px;flex-wrap:wrap}.command-action{display:inline-flex;align-items:center;gap:4px;font-size:11px;color:var(--text-faint)}.command-meta{display:flex;align-items:center;gap:6px}.command-body{display:flex;flex-direction:column;gap:6px}.command-cwd{display:flex;align-items:center;gap:6px;font-size:11.5px;color:var(--text-faint);font-family:var(--mono);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.command-output{margin:0;max-height:340px;overflow:auto;background:var(--code-bg);border:1px solid var(--code-border);border-radius:8px;padding:10px 12px;font-family:var(--mono);font-size:12px;line-height:1.5;white-space:pre-wrap;word-break:break-word}.filechange-summary{display:flex;align-items:center;gap:9px}.filechange-title{font-weight:500}.filechange-body{display:flex;flex-direction:column;gap:12px}.file-change-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.file-change-path{font-family:var(--mono);font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}.file-change-move{font-size:11.5px;color:var(--text-faint)}.badge{display:inline-flex;align-items:center;gap:4px;font-size:10.5px;font-weight:600;padding:1px 7px;border-radius:999px;border:1px solid var(--border);color:var(--text-dim);white-space:nowrap}.badge-success,.kind-add{background:var(--green-soft);color:var(--green);border-color:var(--green)}.badge-error,.kind-del{background:var(--red-soft);color:var(--red);border-color:var(--red)}.badge-warn{background:var(--amber-soft);color:var(--amber);border-color:var(--amber)}.kind-update{background:var(--blue-soft);color:var(--blue);border-color:var(--blue)}.badge-running{background:var(--accent-soft);color:var(--accent-strong);border-color:var(--accent)}.chip{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;background:var(--bg);border:1px solid var(--border);border-radius:999px;padding:2px 8px;color:var(--text-dim)}.chip.tiny{font-size:10.5px;padding:1px 6px}.path-chip{font-family:var(--mono);font-size:11.5px;background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:1px 6px;color:var(--text-dim);max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tool-summary{display:flex;align-items:center;gap:9px;min-width:0;flex:1}.tool-name{display:inline-flex;align-items:center;gap:6px;font-family:var(--mono);font-size:12.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tool-sep{color:var(--text-faint)}.tool-body{display:flex;flex-direction:column;gap:12px}.tool-section{display:flex;flex-direction:column;gap:5px}.tool-section-title{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-faint)}.item-generic .disclosure,.item-tool .disclosure,.item-websearch .disclosure,.item-collab .disclosure{background:var(--bg-elev)}.item-head{display:flex;align-items:center;gap:8px;padding:2px 0}.item-icon{display:inline-flex;color:var(--text-dim)}.item-icon-error{color:var(--red)}.item-title{font-weight:500;font-size:13px}.item-subtitle{color:var(--text-faint);font-size:12px}.item-function-output{border:1px solid var(--border-soft);border-radius:10px;padding:9px 11px;background:var(--bg-elev);display:flex;flex-direction:column;gap:8px}.code-block{margin:0;background:var(--code-bg);border:1px solid var(--code-border);border-radius:8px;padding:10px 12px;font-family:var(--mono);font-size:12px;line-height:1.5;overflow:auto;max-height:300px;white-space:pre-wrap;word-break:break-word}.code-block.small{font-size:11.5px;padding:8px 10px}.code-block.max-h{max-height:360px}.item-error-card{border:1px solid var(--red);border-radius:10px;padding:10px 12px;background:var(--red-soft)}.item-plan{border:1px solid var(--border-soft);border-radius:11px;background:var(--bg-elev);padding:11px 13px}.plan-explanation{font-size:12.5px;margin:4px 0 8px}.plan-steps{list-style:none;margin:6px 0 0;padding:0;display:flex;flex-direction:column;gap:6px}.plan-step{display:flex;align-items:flex-start;gap:8px;font-size:13px;color:var(--text-dim)}.plan-step-icon{display:inline-flex;margin-top:2px}.plan-step.status-completed .plan-step-text{text-decoration:line-through;color:var(--text-faint)}.plan-step.status-completed .plan-step-icon{color:var(--green)}.plan-step.status-inProgress .plan-step-icon{color:var(--accent)}.plan-step.status-inProgress .plan-step-text{color:var(--text)}.item-compaction,.item-sleep,.item-subagent{display:flex;align-items:center;gap:8px;color:var(--text-faint);font-size:12.5px}.compaction-line{display:flex;align-items:center;gap:8px;width:100%}.compaction-line:before,.compaction-line:after{content:"";flex:1;height:1px;background:var(--border-soft)}.item-imageview{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--border);background:var(--bg-elev);border-radius:9px;padding:7px 10px;color:var(--text)}.item-imageview:hover{border-color:var(--accent)}.item-imagegen{border:1px solid var(--border-soft);border-radius:11px;background:var(--bg-elev);padding:11px;display:flex;flex-direction:column;gap:8px}.item-review{border:1px solid var(--amber);background:var(--amber-soft);border-radius:10px;padding:10px 12px}.review-line{display:flex;align-items:center;gap:8px;color:var(--amber);font-size:12.5px;font-weight:600}.markdown{overflow-wrap:anywhere}.markdown>*:first-child{margin-top:0}.markdown>*:last-child{margin-bottom:0}.markdown p{margin:0 0 10px}.markdown h1,.markdown h2,.markdown h3,.markdown h4{margin:16px 0 8px;line-height:1.3}.markdown h1{font-size:19px}.markdown h2{font-size:17px}.markdown h3{font-size:15px}.markdown ul,.markdown ol{margin:0 0 10px;padding-left:22px}.markdown li{margin:2px 0}.markdown a{color:var(--accent-strong);text-decoration:none}.markdown a:hover{text-decoration:underline}.md-file-link{border:none;background:var(--accent-soft);color:var(--accent-strong);border-radius:5px;padding:0 4px;font-family:var(--mono);font-size:12.5px}.md-file-link:hover{text-decoration:underline}.markdown code{font-family:var(--mono);font-size:12.5px;background:var(--code-bg);border:1px solid var(--code-border);border-radius:5px;padding:1px 5px}.markdown pre{margin:0;background:var(--code-bg);border:1px solid var(--code-border);border-radius:9px;padding:11px 13px;overflow:auto;max-height:420px}.markdown pre code{background:transparent;border:none;padding:0;font-size:12px;line-height:1.55}.md-pre-wrap{position:relative;margin:0 0 10px}.md-pre-bar{position:absolute;top:6px;right:8px;z-index:2;opacity:0;transition:opacity .12s ease}.md-pre-wrap:hover .md-pre-bar{opacity:1}.markdown blockquote{margin:0 0 10px;padding:2px 0 2px 12px;border-left:3px solid var(--border);color:var(--text-dim)}.markdown table{border-collapse:collapse;margin:0 0 10px;font-size:13px;display:block;overflow-x:auto}.markdown th,.markdown td{border:1px solid var(--border);padding:5px 9px;text-align:left}.markdown th{background:var(--bg-elev2)}.markdown img{max-width:100%;border-radius:8px}.markdown hr{border:none;border-top:1px solid var(--border);margin:16px 0}.hljs{color:var(--text)}.hljs-comment,.hljs-quote{color:var(--text-faint);font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-doctag,.hljs-formula{color:#c084fc}.hljs-string,.hljs-regexp,.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string{color:#3ecf8e}.hljs-number,.hljs-literal,.hljs-variable,.hljs-template-variable,.hljs-type,.hljs-selector-attr,.hljs-selector-pseudo{color:#f0b429}.hljs-title,.hljs-title.function_,.hljs-section,.hljs-name{color:#4c8dff}.hljs-attr,.hljs-selector-class,.hljs-selector-id,.hljs-property{color:#7db8ff}.hljs-built_in,.hljs-symbol,.hljs-bullet,.hljs-link{color:#f16a6a}.hljs-deletion{color:var(--red)}.hljs-addition{color:var(--green)}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}[data-scheme=light] .hljs-comment,[data-scheme=light] .hljs-quote{color:#6e7781}[data-scheme=light] .hljs-keyword,[data-scheme=light] .hljs-selector-tag,[data-scheme=light] .hljs-doctag,[data-scheme=light] .hljs-template-tag,[data-scheme=light] .hljs-template-variable,[data-scheme=light] .hljs-type{color:#cf222e}[data-scheme=light] .hljs-title,[data-scheme=light] .hljs-title.class_,[data-scheme=light] .hljs-title.function_,[data-scheme=light] .hljs-section{color:#8250df}[data-scheme=light] .hljs-attr,[data-scheme=light] .hljs-attribute,[data-scheme=light] .hljs-literal,[data-scheme=light] .hljs-meta,[data-scheme=light] .hljs-number,[data-scheme=light] .hljs-operator,[data-scheme=light] .hljs-variable,[data-scheme=light] .hljs-selector-attr,[data-scheme=light] .hljs-selector-class,[data-scheme=light] .hljs-selector-id,[data-scheme=light] .hljs-property{color:#0550ae}[data-scheme=light] .hljs-regexp,[data-scheme=light] .hljs-string,[data-scheme=light] .hljs-meta .hljs-string{color:#0a3069}[data-scheme=light] .hljs-built_in,[data-scheme=light] .hljs-symbol,[data-scheme=light] .hljs-bullet,[data-scheme=light] .hljs-link{color:#953800}[data-scheme=light] .hljs-name,[data-scheme=light] .hljs-selector-pseudo,[data-scheme=light] .hljs-title.class_.inherited__{color:#116329}[data-scheme=light] .hljs-subst{color:#1f2328}[data-scheme=light] .hljs-addition{color:#116329;background:#dafbe1}[data-scheme=light] .hljs-deletion{color:#82071e;background:#ffebe9}.diff-view{display:flex;flex-direction:column;gap:12px}.diff-file{border:1px solid var(--code-border);border-radius:9px;overflow:hidden}.diff-file-head{display:flex;align-items:center;gap:10px;padding:6px 10px;background:var(--bg-elev2);border-bottom:1px solid var(--code-border)}.diff-file-path{font-family:var(--mono);font-size:12px;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.diff-stats{display:flex;gap:8px;font-size:11.5px;font-family:var(--mono)}.diff-stats .add{color:var(--green)}.diff-stats .del{color:var(--red)}.diff-body{margin:0;overflow-x:auto;background:var(--code-bg);font-family:var(--mono);font-size:11.5px;line-height:1.5;padding:4px 0}.diff-line{display:flex;gap:8px;padding:0 10px;white-space:pre}.diff-line.add{background:var(--green-soft)}.diff-line.del{background:var(--red-soft)}.diff-line.hunk{color:var(--blue);background:var(--blue-soft)}.diff-line.meta{color:var(--text-faint)}.diff-no{width:38px;flex:0 0 38px;text-align:right;color:var(--text-faint);user-select:none}.diff-sign{width:10px;flex:0 0 10px}.diff-code{flex:1}.diff-view-actions{display:flex;justify-content:flex-end}.diff-empty{padding:8px;font-size:12.5px}.turn-diff{margin-top:8px}.turn-diff-summary{display:inline-flex;align-items:center;gap:7px;font-weight:500}.composer-wrap{border-top:1px solid var(--border);background:var(--bg-elev);padding:10px 10px 12px}.composer{margin:0;border:1px solid var(--border);border-radius:13px;background:var(--bg);padding:5px 9px;box-shadow:var(--shadow-soft)}.composer:focus-within{border-color:var(--accent)}.composer-row{display:flex;align-items:center;gap:8px}.composer-input{position:relative;flex:1;min-width:0}.composer textarea{display:block;width:100%;resize:none;border:none;background:transparent;outline:none;font-size:14px;line-height:21px;padding:5px 0;max-height:200px;font-family:var(--sans)}.composer-actions{display:flex;align-items:center;gap:8px;flex:0 0 auto}.composer-actions .btn{padding:6px 12px}.composer-hint{display:flex;gap:12px;margin:6px 0 0;font-size:11.5px}.running-hint{margin-left:auto;color:var(--accent-strong)}.attach-row{display:flex;flex-wrap:wrap;gap:6px;padding:2px 2px 6px}.attach-chip{display:inline-flex;align-items:center;gap:6px;font-size:12px;background:var(--bg-elev2);border:1px solid var(--border);border-radius:8px;padding:3px 6px;max-width:260px}.attach-chip.image img{width:26px;height:26px;object-fit:cover;border-radius:5px}.attach-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chip-x{display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;color:var(--text-faint);padding:1px;border-radius:4px}.chip-x:hover{color:var(--red)}.queue-bar{margin:0 0 8px;display:flex;align-items:center;gap:8px;font-size:12px;color:var(--accent-strong)}.mention-menu{position:absolute;bottom:calc(100% + 6px);left:0;right:0;z-index:50;background:var(--bg-elev2);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow);max-height:260px;overflow-y:auto;padding:4px}.mention-item{display:flex;flex-direction:column;width:100%;border:none;background:transparent;color:var(--text);text-align:left;padding:6px 9px;border-radius:7px}.mention-item.active,.mention-item:hover{background:var(--accent-soft)}.mention-name{font-size:13px;font-family:var(--mono)}.mention-path{font-size:11px;color:var(--text-faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.modal-backdrop{position:fixed;inset:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:100;padding:20px;backdrop-filter:blur(2px)}.modal{width:100%;background:var(--bg-elev);border:1px solid var(--border);border-radius:14px;box-shadow:var(--shadow);display:flex;flex-direction:column;max-height:86vh;overflow:hidden}.modal-head{display:flex;align-items:center;justify-content:space-between;padding:12px 14px;border-bottom:1px solid var(--border-soft)}.modal-title{font-weight:600}.modal-body{padding:14px;overflow-y:auto;display:flex;flex-direction:column;gap:10px}.modal-foot{display:flex;justify-content:flex-end;gap:8px;padding:12px 14px;border-top:1px solid var(--border-soft);flex-wrap:wrap}.modal-body .modal-foot{padding:12px 0 0}.approval-title{display:inline-flex;align-items:center;gap:8px;color:var(--amber)}.approval-method{font-family:var(--mono)}.approval-reason{margin:0}.approval-command{display:flex;align-items:flex-start;gap:8px;background:var(--code-bg);border:1px solid var(--code-border);border-radius:8px;padding:9px 11px}.approval-command code{font-family:var(--mono);font-size:12.5px;white-space:pre-wrap;word-break:break-word}.approval-cwd{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--text-faint);font-family:var(--mono)}.approval-actions{display:flex;flex-wrap:wrap;gap:6px}.question-form{display:flex;flex-direction:column;gap:14px}.question{display:flex;flex-direction:column;gap:7px}.question-head{font-weight:600;font-size:13px}.question-text{font-size:13px;color:var(--text-dim)}.question-options{display:flex;flex-direction:column;gap:6px}.question-option{display:flex;gap:9px;align-items:flex-start;border:1px solid var(--border);border-radius:8px;padding:7px 9px;cursor:pointer}.question-option:hover{background:var(--bg-elev2)}.question-option input{margin-top:2px;accent-color:var(--accent)}.option-label{display:block;font-size:13px}.option-desc{display:block;font-size:11.5px}.file-panel{position:relative;width:var(--file-panel-w, 560px);border-left:1px solid var(--border);background:var(--bg-elev);display:flex;flex-direction:column;height:100%;z-index:20}.file-panel.dragging{border-left-color:var(--accent)}.file-panel-head{display:flex;align-items:center;gap:8px;padding:9px 12px;border-bottom:1px solid var(--border);min-height:50px}.file-panel-title{display:flex;flex-direction:column;min-width:0;flex:1}.file-name{font-weight:600;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-dir{font-size:11px;color:var(--text-faint);font-family:var(--mono);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-panel-actions{display:flex;align-items:center;gap:2px}.file-tree{border-bottom:1px solid var(--border);max-height:40%;display:flex;flex-direction:column}.file-tree-head{display:flex;align-items:center;justify-content:space-between;padding:6px 10px;border-bottom:1px solid var(--border-soft)}.file-tree-body{overflow:auto;padding:4px 0 8px}.tree-row{display:flex;align-items:center;gap:6px;width:100%;border:none;background:transparent;color:var(--text);text-align:left;padding:4px 8px;font-size:12.5px}.tree-row:hover{background:var(--bg-elev2)}.tree-row.selected{background:var(--accent-soft);color:var(--accent-strong)}.tree-caret{display:inline-flex;width:12px;color:var(--text-faint)}.tree-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--mono)}.tree-loading{padding:2px 8px 2px 30px;font-size:11.5px}.file-content{flex:1;overflow:auto}.file-truncated{padding:6px 12px;background:var(--amber-soft);color:var(--amber)}.file-code{margin:0;padding:14px;font-family:var(--mono);font-size:12.5px;line-height:1.55;background:transparent;white-space:pre}.file-code code{display:block}.toasts{position:fixed;bottom:18px;right:18px;display:flex;flex-direction:column;gap:8px;z-index:200;max-width:380px}.toast{display:flex;align-items:flex-start;gap:9px;background:var(--bg-elev2);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow);padding:10px 12px;font-size:13px;animation:toast-in .2s ease}@keyframes toast-in{0%{opacity:0;transform:translateY(6px)}}.toast-error{border-color:var(--red)}.toast-error .toast-icon{color:var(--red)}.toast-success .toast-icon{color:var(--green)}.toast-msg{flex:1;word-break:break-word}.drawer-backdrop{position:fixed;inset:0;background:#00000073;z-index:120;display:flex;align-items:flex-end;justify-content:center}.drawer{width:100%;max-width:900px;height:60vh;background:var(--bg-elev);border:1px solid var(--border);border-bottom:none;border-radius:14px 14px 0 0;display:flex;flex-direction:column;box-shadow:var(--shadow)}.drawer-head{display:flex;align-items:center;gap:10px;padding:11px 14px;border-bottom:1px solid var(--border-soft);font-weight:600}.drawer-body{margin:0;padding:12px 14px;overflow:auto;font-family:var(--mono);font-size:11.5px;line-height:1.5;white-space:pre-wrap;flex:1}.session-log-drawer{width:min(1180px,calc(100vw - 28px));height:min(84vh,900px);background:var(--bg-elev);border:1px solid var(--border);border-radius:14px;display:flex;flex-direction:column;box-shadow:var(--shadow);overflow:hidden}.session-log-head{flex:0 0 auto}.session-log-layout{min-height:0;flex:1;display:grid;grid-template-columns:minmax(340px,.9fr) minmax(0,1.4fr)}.session-log-list{min-width:0;overflow:auto;border-right:1px solid var(--border-soft);padding:10px}.session-log-list-title{padding:2px 4px 8px;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--text-faint)}.session-log-group{border:1px solid var(--border-soft);border-radius:9px;overflow:hidden;margin-bottom:8px}.session-log-group.open{border-color:var(--border)}.session-log-group-head{width:100%;display:flex;align-items:flex-start;gap:7px;padding:9px;border:0;border-radius:0;background:var(--bg);text-align:left}.session-log-group-head:hover{background:var(--bg-elev2)}.session-log-caret{color:var(--text-faint);padding-top:1px}.session-log-group-main{min-width:0;flex:1;display:flex;flex-direction:column;gap:3px}.session-log-group-main strong,.session-log-group-main span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.session-log-group-main strong{font-size:12.5px}.session-log-group-main span{font-size:11.5px;color:var(--text-dim)}.session-log-group-stats{flex:0 0 auto;display:flex;align-items:flex-end;flex-direction:column;gap:2px;font-size:10.5px;color:var(--text-faint);white-space:nowrap}.session-log-group-rows{border-top:1px solid var(--border-soft);background:var(--bg-inset)}.session-log-row{width:100%;display:grid;grid-template-columns:30px 62px 54px minmax(70px,120px) auto;align-items:center;gap:6px;padding:7px 9px;border:0;border-bottom:1px solid var(--border-soft);border-radius:0;background:transparent;text-align:left;font-size:10.5px;color:var(--text-dim)}.session-log-row:last-child{border-bottom:0}.session-log-row:hover,.session-log-row.selected{background:var(--accent-soft);color:var(--text)}.session-log-row-index{color:var(--text-faint)}.session-log-row-model,.session-log-row-preview{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.session-log-detail{min-width:0;display:flex;flex-direction:column;background:var(--bg)}.session-log-detail-head{display:flex;align-items:center;gap:8px;padding:11px 14px 8px;border-bottom:1px solid var(--border-soft)}.session-log-tabs{display:flex;gap:5px;padding:8px 14px;border-bottom:1px solid var(--border-soft);overflow-x:auto}.session-log-tabs .btn.active{background:var(--accent-soft);border-color:var(--accent);color:var(--accent-strong)}.session-log-detail-body{min-height:0;flex:1;overflow:auto;padding:12px 14px}.session-log-section{margin-bottom:14px}.session-log-section h3{margin:0 0 6px;font-size:11px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--text-faint)}.session-log-box{background:var(--bg-inset);border:1px solid var(--border-soft);border-radius:8px}.session-log-text{padding:9px 10px;white-space:pre-wrap;word-break:break-word;font-size:12.5px}.session-log-code{margin:0 0 14px;padding:10px;max-height:58vh;overflow:auto;background:var(--code-bg);border:1px solid var(--code-border);border-radius:8px;font-family:var(--mono);font-size:11px;line-height:1.5;white-space:pre-wrap;word-break:break-word}.session-log-code.compact{max-height:30vh}.log-status{font-size:10px;line-height:1.2;padding:2px 6px;border:1px solid var(--border);border-radius:999px;white-space:nowrap}.log-status.ok{color:var(--green);border-color:var(--green)}.log-status.bad{color:var(--red);border-color:var(--red)}.log-status.live{color:var(--amber);border-color:var(--amber)}.session-log-empty{padding:24px 10px;color:var(--text-dim);font-size:12px;line-height:1.6}.drawer-scrim{display:none}@media(max-width:900px){.file-panel{position:fixed;right:0;top:0;bottom:0;width:min(92vw,640px)}}@media(max-width:768px){.sidebar-container{position:fixed;top:0;bottom:0;left:0;width:min(86vw,320px);flex:0 0 auto;transform:translate(-100%);transition:transform .2s ease;box-shadow:var(--shadow)}.sidebar-container.open{transform:translate(0)}.sidebar-rail,.sidebar-container>.resize-handle{display:none}.drawer-scrim{display:block;position:fixed;inset:0;background:#00000080;z-index:25}.session-log-drawer{width:calc(100vw - 12px);height:calc(100vh - 12px);border-radius:12px}.session-log-layout{display:flex;flex-direction:column}.session-log-list{flex:0 0 42%;border-right:0;border-bottom:1px solid var(--border-soft)}.session-log-detail{flex:1}.header-cwd{max-width:32vw}.inline-select{display:none}.composer-wrap{padding:10px 10px 12px}.chat>.turn,.chat>.inline-error,.chat>.empty-state{padding-left:12px;padding-right:12px}.token-meter .token-label{display:none}}
|