oh-my-agent-web 0.2.0 → 0.4.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 +86 -447
- package/dist/server/approval/read-only.js +107 -0
- package/dist/server/approval/rules.js +85 -0
- package/dist/server/approval/scope.js +150 -0
- package/dist/server/brand.js +2 -0
- package/dist/server/cli-config.js +52 -0
- package/dist/server/cli-info.js +101 -0
- package/dist/server/cli.js +304 -0
- package/dist/server/engine/builtin.js +61 -0
- package/dist/server/engine/catalog.js +8 -0
- package/dist/server/engine/codex-approvals.js +45 -0
- package/dist/server/engine/codex-map.js +298 -0
- package/dist/server/engine/codex-sessions.js +159 -0
- package/dist/server/engine/codex-transport.js +49 -0
- package/dist/server/engine/codex.js +314 -0
- package/dist/server/engine/dsh-history.js +195 -0
- package/dist/server/engine/dsh-log.js +245 -0
- package/dist/server/engine/dsh-map.js +221 -0
- package/dist/server/engine/dsh-sessions.js +207 -0
- package/dist/server/engine/dsh-transport.js +37 -0
- package/dist/server/engine/dsh.js +284 -0
- package/dist/server/engine/fake.js +231 -0
- package/dist/server/engine/jsonrpc-stdio.js +241 -0
- package/dist/server/engine/model-registry-reload.js +115 -0
- package/dist/server/engine/pi-events.js +148 -0
- package/dist/server/engine/pi-rename.js +14 -0
- package/dist/server/engine/pi-sdk.js +343 -0
- package/dist/server/engine/pi-session-model.js +23 -0
- package/dist/server/engine/pi-thinking.js +34 -0
- package/dist/server/engine/pi-transcript.js +225 -0
- package/dist/server/engine/pi-version.js +11 -0
- package/dist/server/engine/port.js +1 -0
- package/dist/server/engine/registry.js +47 -0
- package/dist/server/engine/scratch.js +13 -0
- package/dist/server/engine/suggestion-guidance.js +9 -0
- package/dist/server/engine/switchable.js +22 -0
- package/dist/server/engine/wiring.js +107 -0
- package/dist/server/files/dir-routes.js +40 -0
- package/dist/server/files/file-search.js +63 -0
- package/dist/server/files/file-service.js +232 -0
- package/dist/server/files/files-routes.js +182 -0
- package/dist/server/files/git-info.js +186 -0
- package/dist/server/http/auth-routes.js +99 -0
- package/dist/server/http/auth.js +191 -0
- package/dist/server/http/aux-routes.js +24 -0
- package/dist/server/http/compress.js +75 -0
- package/dist/server/http/health-and-static.js +33 -0
- package/dist/server/http/live-routes.js +113 -0
- package/dist/server/http/login-page.js +200 -0
- package/dist/server/index.js +342 -520
- package/dist/server/markers/actions.js +262 -0
- package/dist/server/protocol-parse.js +201 -0
- package/dist/server/protocol-settings.js +1 -0
- package/dist/server/protocol.js +6 -0
- package/dist/server/providers/catalog-store.js +85 -0
- package/dist/server/providers/catalog-writes.js +197 -0
- package/dist/server/providers/catalog.js +77 -0
- package/dist/server/providers/enabled-models.js +118 -0
- package/dist/server/providers/import-models.js +69 -0
- package/dist/server/providers/import-routes.js +136 -0
- package/dist/server/providers/model-test.js +52 -0
- package/dist/server/providers/probe.js +64 -0
- package/dist/server/providers/provider-handlers.js +203 -0
- package/dist/server/providers/provider-store.js +327 -0
- package/dist/server/providers/request-headers.js +7 -0
- package/dist/server/providers/validate.js +241 -0
- package/dist/server/push/push-routes.js +114 -0
- package/dist/server/push/push.js +120 -0
- package/dist/server/session/approval-bridge.js +103 -0
- package/dist/server/session/attachment-resolve.js +21 -0
- package/dist/server/session/client-view.js +82 -0
- package/dist/server/session/conversation-create.js +65 -0
- package/dist/server/session/delete-session.js +84 -0
- package/dist/server/session/engine-switch.js +108 -0
- package/dist/server/session/lock.js +82 -0
- package/dist/server/session/model-controller.js +111 -0
- package/dist/server/session/notices.js +31 -0
- package/dist/server/session/pi-opener.js +123 -0
- package/dist/server/session/pi-refresh.js +51 -0
- package/dist/server/session/pi-sessions-provider.js +46 -0
- package/dist/server/session/pi-sessions.js +272 -0
- package/dist/server/session/refresh-actions.js +42 -0
- package/dist/server/session/rename-session.js +23 -0
- package/dist/server/session/runtime-services.js +28 -0
- package/dist/server/session/runtime-types.js +1 -0
- package/dist/server/session/runtime.js +358 -0
- package/dist/server/session/session-stats.js +88 -0
- package/dist/server/session/settings.js +367 -0
- package/dist/server/session/snapshot-for.js +7 -0
- package/dist/server/session/stats-refresher.js +50 -0
- package/dist/server/session/switch-conversation.js +86 -0
- package/dist/server/session/throttle.js +14 -0
- package/dist/server/session/turn-events.js +126 -0
- package/dist/server/session/turn-finish.js +79 -0
- package/dist/server/session/turn-markers.js +26 -0
- package/dist/server/session/turn-queue.js +111 -0
- package/dist/server/session/turn-send.js +88 -0
- package/dist/server/session/turn-watchdog.js +77 -0
- package/dist/server/sessions/session-routes.js +17 -0
- package/dist/server/skills/skill-routes.js +28 -0
- package/dist/server/skills/skill-store.js +134 -0
- package/dist/server/snapshot/fan-out.js +10 -0
- package/dist/server/snapshot/messages.js +75 -0
- package/dist/server/snapshot/sender.js +62 -0
- package/dist/server/snapshot/state.js +193 -0
- package/dist/server/terminal/terminal-routes.js +176 -0
- package/dist/server/terminal/terminal-session.js +140 -0
- package/dist/server/uploads/upload-routes.js +64 -0
- package/dist/server/uploads/upload-store.js +115 -0
- package/dist/server/util/data-dir.js +36 -0
- package/dist/server/util/env.js +140 -0
- package/dist/server/util/ids.js +28 -0
- package/dist/server/util/log-file.js +67 -0
- package/dist/server/util/log.js +52 -0
- package/dist/server/util/origin.js +75 -0
- package/dist/server/util/paths.js +60 -0
- package/dist/server/util/pricing-index.js +121 -0
- package/dist/server/util/pricing.js +214 -0
- package/dist/server/util/startup.js +34 -0
- package/dist/server/util/upstream-version.js +140 -0
- package/dist/server/util/usage.js +19 -0
- package/dist/server/util/version.js +50 -0
- package/dist/server/ws/handle-message.js +95 -0
- package/package.json +49 -63
- package/web/dist/apple-touch-icon-precomposed.png +0 -0
- package/web/dist/apple-touch-icon.png +0 -0
- package/web/dist/assets/abnfDiagram-WEYOIA3C-NLp09iEC.js +1 -0
- package/web/dist/assets/arc-DviL9o3c.js +1 -0
- package/web/dist/assets/architecture-WOLXFQ4H-fjsM_Sy3.js +1 -0
- package/web/dist/assets/architectureDiagram-ZHVYKT53-Cn3B4TE8.js +36 -0
- package/web/dist/assets/array-BifhSqXX.js +1 -0
- package/web/dist/assets/blockDiagram-RJAE7QA3-CIuvxWvi.js +140 -0
- package/web/dist/assets/c4Diagram-XDPPPE54-DvdKmEpF.js +38 -0
- package/web/dist/assets/channel-C3AQ6nAs.js +1 -0
- package/web/dist/assets/chunk-2E7WHVMD-HNNASGCS.js +88 -0
- package/web/dist/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/web/dist/assets/chunk-4EMNKR2T-Cg-eRwBE.js +1 -0
- package/web/dist/assets/chunk-55XCU76K-BweiisZL.js +1 -0
- package/web/dist/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/web/dist/assets/chunk-7S6XSTFO-BCzIiP-6.js +2 -0
- package/web/dist/assets/chunk-AQDTSKZB-Dl0NRFuU.js +72 -0
- package/web/dist/assets/chunk-DSNLSHJZ-zhHCjjN9.js +129 -0
- package/web/dist/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
- package/web/dist/assets/chunk-HAGFOTED-BDQqU2EH.js +62 -0
- package/web/dist/assets/chunk-HNE4Q36P-B-qRzMjX.js +1 -0
- package/web/dist/assets/chunk-J5ZVWO5B-oDCd-gWO.js +1 -0
- package/web/dist/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- package/web/dist/assets/chunk-NGNAAXSQ-C2Wy9Xmz.js +136 -0
- package/web/dist/assets/chunk-NQOKRVIF-CNcvhMn_.js +213 -0
- package/web/dist/assets/chunk-UHW7PFBP-Dwr8LUzr.js +1 -0
- package/web/dist/assets/chunk-VQ5W62QI-O8sN6jJu.js +1 -0
- package/web/dist/assets/chunk-WQQL4CDK-B6-59Zg2.js +10 -0
- package/web/dist/assets/chunk-XXDRQBXY-DvSbeUlZ.js +1 -0
- package/web/dist/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/web/dist/assets/chunk-YZO67HFG-BXpKYKsw.js +1 -0
- package/web/dist/assets/chunk-ZAUX2LTX-B1Z5VSkz.js +1 -0
- package/web/dist/assets/chunk-ZCHMQSOR-h_5mBLIA.js +2 -0
- package/web/dist/assets/classDiagram-v2-4MYWR3UA-Dr6JnaaV.js +217 -0
- package/web/dist/assets/cose-bilkent-JH36ORCC-36ShkR7R.js +1 -0
- package/web/dist/assets/cynefin-EF2NZ3EQ-Dg6bI7pC.js +1 -0
- package/web/dist/assets/cynefinDiagram-WF24UDRP-B0YHpBlR.js +62 -0
- package/web/dist/assets/cytoscape.esm-BegBNzOV.js +321 -0
- package/web/dist/assets/dagre-NK4GIZVX-Cx3mn7YS.js +4 -0
- package/web/dist/assets/defaultLocale-BFoDCU3G.js +1 -0
- package/web/dist/assets/diagram-CBK3MWTC-l8aZG_Bm.js +24 -0
- package/web/dist/assets/diagram-I5XIIV2D-Bd5f14_h.js +24 -0
- package/web/dist/assets/diagram-ODIL2UCW-hBsxCYUj.js +30 -0
- package/web/dist/assets/diagram-PFBEJUXE-BgpIBUYl.js +3 -0
- package/web/dist/assets/diagram-RJA3I4VV-CDU1gUVD.js +41 -0
- package/web/dist/assets/diagram-TYUNFSNR-Iuxh7Tqf.js +200 -0
- package/web/dist/assets/dist-C86cKTuE.js +1 -0
- package/web/dist/assets/ebnfDiagram-V4P3TTPT-Cms0HFKh.js +1 -0
- package/web/dist/assets/elk-QONTBCTQ-B7yEy0PQ.js +27 -0
- package/web/dist/assets/erDiagram-KYQIGQAA-CL48j4aU.js +99 -0
- package/web/dist/assets/eventmodeling-K75KTNOO-CFvkaejh.js +1 -0
- package/web/dist/assets/flowDiagram-IUGLLWBU-6UyKXpqG.js +1 -0
- package/web/dist/assets/ganttDiagram-R7GPNTJY-vdMr-0ZV.js +292 -0
- package/web/dist/assets/gitGraph-VSP46ZUC-2z85Wuwo.js +1 -0
- package/web/dist/assets/gitGraphDiagram-PDYL7XHY-BY4GPiqC.js +106 -0
- package/web/dist/assets/graphlib-DS17s2tU.js +1 -0
- package/web/dist/assets/index-Bjn56a2i.js +76 -0
- package/web/dist/assets/index-Drp6e5iU.css +1 -0
- package/web/dist/assets/info-OHQRW6UA-ECzkYTx-.js +1 -0
- package/web/dist/assets/infoDiagram-MVDRKAX5-DDk5EAeb.js +2 -0
- package/web/dist/assets/init-C-OQMol4.js +1 -0
- package/web/dist/assets/ishikawaDiagram-HPEHV2GE-9yPH4J94.js +70 -0
- package/web/dist/assets/journeyDiagram-BOSHW7O7-BPTIap3l.js +139 -0
- package/web/dist/assets/kanban-definition-FNSA4HC5-BbADHrA8.js +89 -0
- package/web/dist/assets/katex-B_JcIwqj.js +257 -0
- package/web/dist/assets/line-CHV9_4lL.js +1 -0
- package/web/dist/assets/linear-C-eZsBTv.js +1 -0
- package/web/dist/assets/mermaid-parser.core-BiAhX1l1.js +7 -0
- package/web/dist/assets/mermaid.core-BxP_A_zj.js +44 -0
- package/web/dist/assets/mindmap-definition-6OY57UG3-BMWlit95.js +98 -0
- package/web/dist/assets/ordinal-BDEzSJ7C.js +1 -0
- package/web/dist/assets/packet-JDAUHWVQ-FVApuKOp.js +1 -0
- package/web/dist/assets/path-fybaL0A-.js +1 -0
- package/web/dist/assets/pegDiagram-ECIUJNBX-WjWJvDHM.js +1 -0
- package/web/dist/assets/pie-XZMESJXO-BGCZnvhp.js +1 -0
- package/web/dist/assets/pieDiagram-GNMPY74T-DJpAC_JC.js +39 -0
- package/web/dist/assets/quadrantDiagram-D57K4MC3-hxx5c7oq.js +7 -0
- package/web/dist/assets/radar-ABXABTNO-BU9Shc-O.js +1 -0
- package/web/dist/assets/railroad-I3PHUGI6-pWj8d-G4.js +1 -0
- package/web/dist/assets/railroad-abnf-LNEFI6M7-D7HZBTAj.js +1 -0
- package/web/dist/assets/railroad-ebnf-DWYD2UWJ-CKiXJSuM.js +1 -0
- package/web/dist/assets/railroad-peg-JR7BVNR7-DB3VJHgR.js +1 -0
- package/web/dist/assets/railroadDiagram-Y72NRBL7-Cev7Y65k.js +1 -0
- package/web/dist/assets/requirementDiagram-EQYSW5RA-CqbwMLD8.js +84 -0
- package/web/dist/assets/rough.esm-Dy-Kn_BL.js +1 -0
- package/web/dist/assets/sankeyDiagram-SY24JHO5-V2cyVi8q.js +40 -0
- package/web/dist/assets/sequenceDiagram-BLEHNHQU-By6NJOPM.js +169 -0
- package/web/dist/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
- package/web/dist/assets/src-Da91fG3g.js +1 -0
- package/web/dist/assets/stateDiagram-v2-A6IESKWZ-BnPGX5UI.js +291 -0
- package/web/dist/assets/swimlanes-B3ENRKFQ-C58BpIpm.js +1 -0
- package/web/dist/assets/swimlanesDiagram-AYQRV4AK-RaivxSsc.js +8 -0
- package/web/dist/assets/timeline-definition-JXYKGLGB-Hgo1DhnS.js +120 -0
- package/web/dist/assets/treeView-D4JQ5SDB-DF0dfjqB.js +1 -0
- package/web/dist/assets/treemap-SAJKECNS-CSSM5FqB.js +1 -0
- package/web/dist/assets/usecaseDiagram-WPOLBNNX-BkBBD1NY.js +328 -0
- package/web/dist/assets/vennDiagram-Q3Y3IDCW-Bm28keX_.js +34 -0
- package/web/dist/assets/wardley-7MLQ67FV-By7KgEQX.js +1 -0
- package/web/dist/assets/wardleyDiagram-IVAIDKWA-oVtIrnGa.js +78 -0
- package/web/dist/assets/xychartDiagram-4URNHDU4-aI8FH4g2.js +7 -0
- package/web/dist/favicon-32.png +0 -0
- package/web/dist/favicon.ico +0 -0
- package/web/dist/favicon.svg +20 -8
- package/web/dist/icons/apple-touch-icon-180.png +0 -0
- package/web/dist/icons/icon-192.png +0 -0
- package/web/dist/icons/icon-512.png +0 -0
- package/web/dist/icons/icon-maskable-512.png +0 -0
- package/web/dist/index.html +21 -27
- package/web/dist/manifest.webmanifest +17 -0
- package/web/dist/maskable.svg +25 -0
- package/web/dist/sw.js +40 -0
- package/bin/oh-my-agent-web-ps.mjs +0 -203
- package/bin/oh-my-agent-web-ws.mjs +0 -593
- package/bin/oh-my-agent-web.mjs +0 -321
- package/bin/update-check.mjs +0 -142
- package/dist/server/codex-client.js +0 -310
- package/dist/server/codex-supervisor.js +0 -446
- package/dist/server/fs-service.js +0 -144
- package/dist/server/request-inspector.js +0 -458
- package/dist/shared/protocol.js +0 -112
- package/scripts/manage-server.mjs +0 -155
- package/scripts/omaw.mjs +0 -74
- package/shared/protocol.ts +0 -228
- package/web/dist/assets/index-B8-VzDf2.css +0 -1
- package/web/dist/assets/index-DeC2eiiq.js +0 -42
- package/web/dist/assets/markdown-CYXe6JZx.js +0 -55
|
@@ -1,155 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
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
|
-
});
|
package/shared/protocol.ts
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
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;
|
|
@@ -1 +0,0 @@
|
|
|
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}}
|