teapot-coding-agent 0.19.8 → 0.20.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 +1 -1
- package/dist/agent/agent.js +108 -10
- package/dist/agent/llm.js +9 -1
- package/dist/config-schema.js +4 -0
- package/dist/master.js +10 -0
- package/dist/model-meta.js +6 -0
- package/dist/server/api.js +18 -3
- package/package.json +1 -1
- package/public/assets/index-CBHrAQ6w.css +1 -0
- package/public/assets/index-bSU5CYaQ.js +30 -0
- package/public/assets/index-bSU5CYaQ.js.map +1 -0
- package/public/assets/{shiki-fBtMMlxc.js → shiki-BcL990K1.js} +2 -2
- package/public/assets/{shiki-fBtMMlxc.js.map → shiki-BcL990K1.js.map} +1 -1
- package/public/index.html +2 -2
- package/public/assets/index-BAosryif.js +0 -30
- package/public/assets/index-BAosryif.js.map +0 -1
- package/public/assets/index-BM9nVufQ.css +0 -1
package/README.md
CHANGED
|
@@ -238,7 +238,7 @@ GET /api/agents/:id/file?path= fetch text file
|
|
|
238
238
|
PUT /api/agents/:id/file?path= save { content, baseContent? } (409 on conflict)
|
|
239
239
|
GET /api/agents/:id/raw?path= raw media bytes
|
|
240
240
|
GET /api/agents/:id/tree file tree
|
|
241
|
-
POST /api/agents/:id/prompt send { text, start
|
|
241
|
+
POST /api/agents/:id/prompt send { text, start?, images?: [{url}] } → { promptId }
|
|
242
242
|
POST /api/agents/:id/prompt/cancel withdraw { promptId } (409 once delivered)
|
|
243
243
|
POST /api/agents/:id/goal { text?, status?, verify? }
|
|
244
244
|
POST /api/agents/:id/start|stop|fork|load
|
package/dist/agent/agent.js
CHANGED
|
@@ -120,6 +120,8 @@ export class Agent {
|
|
|
120
120
|
activityChars = 0;
|
|
121
121
|
turnsSinceProgress = 0;
|
|
122
122
|
consecutiveToolErrors = 0;
|
|
123
|
+
/** consecutive round-fatal API errors (drives the retry backoff) */
|
|
124
|
+
consecutiveApiErrors = 0;
|
|
123
125
|
constructor(opts) {
|
|
124
126
|
this.opts = {
|
|
125
127
|
progressIntervalMs: 10 * 60_000,
|
|
@@ -130,6 +132,8 @@ export class Agent {
|
|
|
130
132
|
autoCompact: true,
|
|
131
133
|
maxConsecutiveToolErrors: 5,
|
|
132
134
|
maxTurnsPerRound: 200,
|
|
135
|
+
onError: "stop",
|
|
136
|
+
retryDelayMs: 60_000,
|
|
133
137
|
contextTokenBudget: 96_000,
|
|
134
138
|
contextWindowTokens: 0,
|
|
135
139
|
restoreSession: true,
|
|
@@ -740,7 +744,7 @@ export class Agent {
|
|
|
740
744
|
* the running loop. The very first prompt on a fresh boot also triggers the
|
|
741
745
|
* lazy session restore (before the mailbox is filled, so no duplicates).
|
|
742
746
|
*/
|
|
743
|
-
enqueuePrompt(text, source = "user") {
|
|
747
|
+
enqueuePrompt(text, source = "user", images) {
|
|
744
748
|
// a prompt during a tool park (wait_children) takes over instantly:
|
|
745
749
|
// unpark fixes the DISPLAY, aborting the park signal actually ends the
|
|
746
750
|
// wait — the run chain resumes with this prompt drained at the boundary
|
|
@@ -766,10 +770,11 @@ export class Agent {
|
|
|
766
770
|
const promptId = source === "user" ? `u${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}` : "";
|
|
767
771
|
void this.ensureReady()
|
|
768
772
|
.then(() => {
|
|
769
|
-
this.pendingPrompts.push({ source, text, ...(promptId ? { id: promptId } : {}) });
|
|
773
|
+
this.pendingPrompts.push({ source, text, images, ...(promptId ? { id: promptId } : {}) });
|
|
770
774
|
return this.log.append("prompt", this.currentSession, this.currentBranch, {
|
|
771
775
|
source,
|
|
772
776
|
text,
|
|
777
|
+
...(images?.length ? { images: images.map((i) => ({ url: i.url, name: i.name })) } : {}),
|
|
773
778
|
...(promptId ? { promptId } : {}),
|
|
774
779
|
});
|
|
775
780
|
})
|
|
@@ -804,7 +809,21 @@ export class Agent {
|
|
|
804
809
|
*/
|
|
805
810
|
drainPendingPrompts() {
|
|
806
811
|
for (const p of this.pendingPrompts.splice(0)) {
|
|
807
|
-
|
|
812
|
+
// multimodal: images ride as OpenAI content parts (text first, then
|
|
813
|
+
// images); plain prompts keep the string form for cache friendliness
|
|
814
|
+
if (p.images?.length) {
|
|
815
|
+
this.messages.push({
|
|
816
|
+
role: "user",
|
|
817
|
+
content: p.text,
|
|
818
|
+
content_parts: [
|
|
819
|
+
{ type: "text", text: p.text },
|
|
820
|
+
...p.images.map((i) => ({ type: "image_url", image_url: { url: i.url } })),
|
|
821
|
+
],
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
else {
|
|
825
|
+
this.messages.push({ role: "user", content: p.text });
|
|
826
|
+
}
|
|
808
827
|
if (p.source === "user") {
|
|
809
828
|
const id = p.id ?? `u${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`;
|
|
810
829
|
this.deliveredPrompts.push(id);
|
|
@@ -895,6 +914,9 @@ export class Agent {
|
|
|
895
914
|
// counter only ever reset on success, so a tripped limit re-tripped
|
|
896
915
|
// on the first failure after restart ("6 consecutive" right after start)
|
|
897
916
|
this.consecutiveToolErrors = 0;
|
|
917
|
+
// a round that got as far as an LLM call means the API is reachable
|
|
918
|
+
// again — reset the error-retry backoff to its base delay
|
|
919
|
+
this.consecutiveApiErrors = 0;
|
|
898
920
|
const turnsAtStart = this.stats.turns;
|
|
899
921
|
let finished = await this.runTurnsUntilIdle();
|
|
900
922
|
// the round ended on the per-round turn cap (not finish/stop): tell
|
|
@@ -967,6 +989,27 @@ export class Agent {
|
|
|
967
989
|
break;
|
|
968
990
|
const msg = err.message ?? String(err);
|
|
969
991
|
await this.log.append("error", this.currentSession, this.currentBranch, { message: msg });
|
|
992
|
+
// onError:"retry" keeps an unattended agent alive across failures —
|
|
993
|
+
// BOTH provider outages (API errors) and runaway trips (a tool that
|
|
994
|
+
// keeps failing). Back off, then start a fresh round; the delay
|
|
995
|
+
// doubles per consecutive failure so a hard failure doesn't spin,
|
|
996
|
+
// and any stop/prompt still interrupts the sleep instantly.
|
|
997
|
+
const isRunaway = /runaway detection/.test(msg);
|
|
998
|
+
if (this.opts.onError === "retry" && !this.awaitingUser && !this.pendingPrompts.length) {
|
|
999
|
+
this.consecutiveApiErrors++;
|
|
1000
|
+
const wait = Math.min((this.opts.retryDelayMs || 60_000) * 2 ** Math.min(this.consecutiveApiErrors - 1, 6), 600_000);
|
|
1001
|
+
await this.log.append("system_note", this.currentSession, this.currentBranch, {
|
|
1002
|
+
event: "error-retry",
|
|
1003
|
+
attempt: this.consecutiveApiErrors,
|
|
1004
|
+
waitMs: wait,
|
|
1005
|
+
kind: isRunaway ? "runaway" : "api",
|
|
1006
|
+
});
|
|
1007
|
+
this.setStatus("error", `${msg.slice(0, 220)} — retrying in ${Math.round(wait / 1000)}s`);
|
|
1008
|
+
await this.sleepInterruptible(wait);
|
|
1009
|
+
if (this.stopRequested)
|
|
1010
|
+
break;
|
|
1011
|
+
continue; // fresh round; the loop re-arms the tool signal itself
|
|
1012
|
+
}
|
|
970
1013
|
this.setStatus("error", msg.slice(0, 300));
|
|
971
1014
|
return;
|
|
972
1015
|
}
|
|
@@ -1353,7 +1396,18 @@ export class Agent {
|
|
|
1353
1396
|
this.messages.push({ role: "tool", tool_call_id: call.id, content: result.result });
|
|
1354
1397
|
this.consecutiveToolErrors = result.ok ? 0 : this.consecutiveToolErrors + 1;
|
|
1355
1398
|
if (this.consecutiveToolErrors >= this.opts.maxConsecutiveToolErrors) {
|
|
1356
|
-
|
|
1399
|
+
// structured, greppable AND visible: the note carries what failed so
|
|
1400
|
+
// the operator (and the UI) can show "WHICH tool, with what error"
|
|
1401
|
+
// instead of a bare counter
|
|
1402
|
+
await this.log.append("system_note", this.currentSession, this.currentBranch, {
|
|
1403
|
+
event: "runaway-detected",
|
|
1404
|
+
failures: this.consecutiveToolErrors,
|
|
1405
|
+
limit: this.opts.maxConsecutiveToolErrors,
|
|
1406
|
+
lastTool: call.function.name,
|
|
1407
|
+
lastError: result.result.slice(0, 300),
|
|
1408
|
+
});
|
|
1409
|
+
throw new Error(`runaway detection: ${this.consecutiveToolErrors} consecutive tool failures ` +
|
|
1410
|
+
`(last: ${call.function.name})`);
|
|
1357
1411
|
}
|
|
1358
1412
|
}
|
|
1359
1413
|
if (finished)
|
|
@@ -2070,19 +2124,48 @@ function rebuildMessagesFrom(list) {
|
|
|
2070
2124
|
// synthesizer below must not duplicate them
|
|
2071
2125
|
const loggedResults = new Set(list.filter((e) => e.type === "tool_result").map((e) => String(e.data.callId ?? "")));
|
|
2072
2126
|
const bufferedUsers = [];
|
|
2127
|
+
// image attachments paired with buffered prompts (multimodal replay)
|
|
2128
|
+
const bufferedParts = [];
|
|
2073
2129
|
const flushUsers = () => {
|
|
2074
2130
|
if (openCalls.size === 0) {
|
|
2075
|
-
|
|
2076
|
-
|
|
2131
|
+
const texts = bufferedUsers.splice(0);
|
|
2132
|
+
const parts = bufferedParts.splice(0);
|
|
2133
|
+
texts.forEach((text, i) => {
|
|
2134
|
+
const imgs = parts[i] ?? [];
|
|
2135
|
+
msgs.push(imgs.length
|
|
2136
|
+
? {
|
|
2137
|
+
role: "user",
|
|
2138
|
+
content: text,
|
|
2139
|
+
content_parts: [
|
|
2140
|
+
{ type: "text", text },
|
|
2141
|
+
...imgs.map((im) => ({ type: "image_url", image_url: { url: im.url } })),
|
|
2142
|
+
],
|
|
2143
|
+
}
|
|
2144
|
+
: { role: "user", content: text });
|
|
2145
|
+
});
|
|
2077
2146
|
}
|
|
2078
2147
|
};
|
|
2079
2148
|
for (const e of list) {
|
|
2080
2149
|
const d = e.data;
|
|
2081
2150
|
if (e.type === "prompt" && typeof d.text === "string") {
|
|
2151
|
+
// restore image attachments so replays stay byte-identical with the
|
|
2152
|
+
// original request (prefix caches stay warm across restarts)
|
|
2153
|
+
const text = d.text;
|
|
2154
|
+
const imgs = Array.isArray(d.images) ? d.images : [];
|
|
2155
|
+
const mk = () => imgs.length
|
|
2156
|
+
? {
|
|
2157
|
+
role: "user",
|
|
2158
|
+
content: text,
|
|
2159
|
+
content_parts: [
|
|
2160
|
+
{ type: "text", text },
|
|
2161
|
+
...imgs.map((i) => ({ type: "image_url", image_url: { url: i.url } })),
|
|
2162
|
+
],
|
|
2163
|
+
}
|
|
2164
|
+
: { role: "user", content: text };
|
|
2082
2165
|
if (openCalls.size > 0)
|
|
2083
|
-
bufferedUsers.push(
|
|
2166
|
+
bufferedUsers.push(text), bufferedParts.push(imgs);
|
|
2084
2167
|
else
|
|
2085
|
-
msgs.push(
|
|
2168
|
+
msgs.push(mk());
|
|
2086
2169
|
}
|
|
2087
2170
|
else if (e.type === "message") {
|
|
2088
2171
|
// final summaries are operator-facing only — the live loop never puts
|
|
@@ -2190,7 +2273,22 @@ function rebuildMessagesFrom(list) {
|
|
|
2190
2273
|
}
|
|
2191
2274
|
}
|
|
2192
2275
|
// prompts that were still waiting on a hole-filled tail land here
|
|
2193
|
-
|
|
2194
|
-
|
|
2276
|
+
{
|
|
2277
|
+
const texts = bufferedUsers.splice(0);
|
|
2278
|
+
const parts = bufferedParts.splice(0);
|
|
2279
|
+
texts.forEach((text, i) => {
|
|
2280
|
+
const imgs = parts[i] ?? [];
|
|
2281
|
+
msgs.push(imgs.length
|
|
2282
|
+
? {
|
|
2283
|
+
role: "user",
|
|
2284
|
+
content: text,
|
|
2285
|
+
content_parts: [
|
|
2286
|
+
{ type: "text", text },
|
|
2287
|
+
...imgs.map((im) => ({ type: "image_url", image_url: { url: im.url } })),
|
|
2288
|
+
],
|
|
2289
|
+
}
|
|
2290
|
+
: { role: "user", content: text });
|
|
2291
|
+
});
|
|
2292
|
+
}
|
|
2195
2293
|
return msgs;
|
|
2196
2294
|
}
|
package/dist/agent/llm.js
CHANGED
|
@@ -80,13 +80,21 @@ function looksMangled(name) {
|
|
|
80
80
|
}
|
|
81
81
|
function sanitize(messages) {
|
|
82
82
|
return messages.map((m) => {
|
|
83
|
+
// multimodal user message → OpenAI content-parts array (text + images);
|
|
84
|
+
// providers without vision reject it, but those models were never a fit
|
|
85
|
+
if (m.content_parts?.length) {
|
|
86
|
+
const parts = m.content_parts.map((p) => p.type === "image_url"
|
|
87
|
+
? { type: "image_url", image_url: { url: p.image_url.url } }
|
|
88
|
+
: { type: "text", text: p.text ?? "" });
|
|
89
|
+
return { role: m.role, content: parts };
|
|
90
|
+
}
|
|
83
91
|
if ((m.role === "user" || m.role === "assistant") && !m.content) {
|
|
84
92
|
return { ...m, content: m.tool_calls?.length ? "(tool call)" : "(no content)" };
|
|
85
93
|
}
|
|
86
94
|
if (m.role === "tool" && typeof m.content !== "string") {
|
|
87
95
|
return { ...m, content: String(m.content ?? "(no output)") };
|
|
88
96
|
}
|
|
89
|
-
return m;
|
|
97
|
+
return { ...m };
|
|
90
98
|
});
|
|
91
99
|
}
|
|
92
100
|
export async function chat(cfg, messages, tools, signal, onDelta) {
|
package/dist/config-schema.js
CHANGED
|
@@ -44,6 +44,10 @@ export const ConfigPatchSchema = z.object({
|
|
|
44
44
|
// soft cap on LLM turns in one round; reaching it nudges the model to wrap
|
|
45
45
|
// up (not an error) — 0 disables the cap
|
|
46
46
|
maxTurnsPerRound: z.number().int().min(0).optional(),
|
|
47
|
+
// round-fatal API errors: "stop" (default) or "retry" (back off + fresh
|
|
48
|
+
// round — for unattended agents that should ride out outages)
|
|
49
|
+
onError: z.enum(["stop", "retry"]).optional(),
|
|
50
|
+
retryDelayMs: z.number().int().min(1_000).optional(),
|
|
47
51
|
tasks: z.array(TaskSchema).optional(),
|
|
48
52
|
});
|
|
49
53
|
/** Format a ZodError into a compact operator-readable message. */
|
package/dist/master.js
CHANGED
|
@@ -158,6 +158,10 @@ function printAgentEvent(e) {
|
|
|
158
158
|
line = `${c("31", "⚠ error")} ${clip1(d.message, 160)}`;
|
|
159
159
|
break;
|
|
160
160
|
case "system_note": {
|
|
161
|
+
if (d.event === "runaway-detected")
|
|
162
|
+
line = `${c("31", "🛑 runaway")} ${d.failures} consecutive tool failures (limit ${d.limit}) — last: ${clip1(String(d.lastTool), 24)}: ${clip1(String(d.lastError), 90)}`;
|
|
163
|
+
else if (d.event === "error-retry")
|
|
164
|
+
line = `${c("33", "↻ error-retry")} attempt ${d.attempt} — next round in ${Math.round(Number(d.waitMs) / 1000)}s`;
|
|
161
165
|
if (d.event === "llm-retry")
|
|
162
166
|
line = `${c("33", "↻ retry")} attempt ${d.attempt} in ${Math.round(Number(d.waitMs) / 1000)}s — ${clip1(d.error, 100)}`;
|
|
163
167
|
else if (d.event === "context-compacted")
|
|
@@ -230,6 +234,10 @@ export class Master {
|
|
|
230
234
|
this.config.maxSpawnDepth = patch.maxSpawnDepth;
|
|
231
235
|
if (patch.maxTurnsPerRound !== undefined)
|
|
232
236
|
this.config.maxTurnsPerRound = patch.maxTurnsPerRound;
|
|
237
|
+
if (patch.onError !== undefined)
|
|
238
|
+
this.config.onError = patch.onError;
|
|
239
|
+
if (patch.retryDelayMs !== undefined)
|
|
240
|
+
this.config.retryDelayMs = patch.retryDelayMs;
|
|
233
241
|
if (patch.tasks) {
|
|
234
242
|
this.config.tasks = patch.tasks;
|
|
235
243
|
// rebuild schedule table live
|
|
@@ -380,6 +388,8 @@ export class Master {
|
|
|
380
388
|
progressIntervalMs: this.config.progressIntervalMs,
|
|
381
389
|
...(this.config.progressMinChars ? { progressMinChars: this.config.progressMinChars } : {}),
|
|
382
390
|
...(this.config.maxTurnsPerRound !== undefined ? { maxTurnsPerRound: this.config.maxTurnsPerRound } : {}),
|
|
391
|
+
...(this.config.onError ? { onError: this.config.onError } : {}),
|
|
392
|
+
...(this.config.retryDelayMs ? { retryDelayMs: this.config.retryDelayMs } : {}),
|
|
383
393
|
autoContinue: ac.autoContinue ?? true,
|
|
384
394
|
...(this.config.contextTokenBudget ? { contextTokenBudget: this.config.contextTokenBudget } : {}),
|
|
385
395
|
...(ac.contextWindowTokens || this.config.contextWindowTokens || inferredWindow
|
package/dist/model-meta.js
CHANGED
|
@@ -38,6 +38,12 @@ export async function fetchModelList(baseUrl, apiKey) {
|
|
|
38
38
|
completion: Number(m.pricing.completion ?? 0),
|
|
39
39
|
}
|
|
40
40
|
: undefined,
|
|
41
|
+
modalities: Array.isArray(m.architecture?.input_modalities) && m.architecture.input_modalities.length
|
|
42
|
+
? {
|
|
43
|
+
input: m.architecture.input_modalities,
|
|
44
|
+
output: m.architecture.output_modalities ?? ["text"],
|
|
45
|
+
}
|
|
46
|
+
: undefined,
|
|
41
47
|
}))
|
|
42
48
|
.filter((m) => m.id);
|
|
43
49
|
cache.set(root, { at: Date.now(), list });
|
package/dist/server/api.js
CHANGED
|
@@ -38,6 +38,12 @@ async function listModels(baseUrl, apiKey) {
|
|
|
38
38
|
completion: Number(m.pricing.completion ?? 0),
|
|
39
39
|
}
|
|
40
40
|
: undefined,
|
|
41
|
+
modalities: Array.isArray(m.architecture?.input_modalities) && m.architecture.input_modalities.length
|
|
42
|
+
? {
|
|
43
|
+
input: m.architecture.input_modalities,
|
|
44
|
+
output: m.architecture.output_modalities ?? ["text"],
|
|
45
|
+
}
|
|
46
|
+
: undefined,
|
|
41
47
|
}))
|
|
42
48
|
.filter((m) => m.id)
|
|
43
49
|
.sort((a, b) => a.id.localeCompare(b.id));
|
|
@@ -607,12 +613,21 @@ export function buildApp(master) {
|
|
|
607
613
|
if (!a)
|
|
608
614
|
return c.json({ error: "not found" }, 404);
|
|
609
615
|
const body = await c.req.json();
|
|
610
|
-
if (!body.text?.trim())
|
|
611
|
-
return c.json({ error: "text required" }, 400);
|
|
616
|
+
if (!body.text?.trim() && !body.images?.length)
|
|
617
|
+
return c.json({ error: "text or images required" }, 400);
|
|
618
|
+
// validate images: data URLs or http(s), size-capped (a 20MB base64 blob
|
|
619
|
+
// in the log would hurt every future replay of this session)
|
|
620
|
+
const images = (body.images ?? []).filter((im) => {
|
|
621
|
+
const u = String(im?.url ?? "");
|
|
622
|
+
if (u.startsWith("data:image/"))
|
|
623
|
+
return u.length < 12_000_000; // ~9MB binary
|
|
624
|
+
return /^https?:\/\//.test(u);
|
|
625
|
+
});
|
|
626
|
+
const text = body.text ?? "";
|
|
612
627
|
// returns immediately: the prompt is logged + broadcast now, delivered to
|
|
613
628
|
// the model at the next turn boundary (never blocks on a running agent).
|
|
614
629
|
// promptId ties the UI's pending echo to the later prompt-delivered note.
|
|
615
|
-
const promptId = a.enqueuePrompt(
|
|
630
|
+
const promptId = a.enqueuePrompt(text || "(see attached image)", "user", images.length ? images : undefined);
|
|
616
631
|
if (body.start !== false && a.status !== "running")
|
|
617
632
|
a.start("prompt");
|
|
618
633
|
return c.json({ ok: true, promptId, queued: a.snapshot().pendingPrompts });
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--bg-darkest:#1a1c22;--bg-dark:#232630;--bg-mid:#2b2e39;--bg-light:#343845;--fg:#dcdee4;--dim:#9298a5;--line:#1d2027;--acc:#5865f2;--ok:#3ba55d;--warn:#faa81a;--err:#ed4245;--tool:#3ba0c9;--term-bg:#0d0e12;--term-fg:#dcdee4;font-family:gg sans,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif}[data-theme=midnight]{--bg-darkest:#0d0f18;--bg-dark:#12141f;--bg-mid:#1b1f31;--bg-light:#252a42;--fg:#dfe2f2;--dim:#8a90ad;--line:#0a0c14;--acc:#8b7cf7;--ok:#54c08a;--warn:#e5b567;--err:#ef6a6a;--tool:#6aa9e9;--term-bg:#0a0c13;--term-fg:#dfe2f2}[data-theme=tea]{--bg-darkest:#241c16;--bg-dark:#2e241c;--bg-mid:#3b2f24;--bg-light:#493b2d;--fg:#f0e4d2;--dim:#b39d82;--line:#1b140f;--acc:#d98e32;--ok:#7fa653;--warn:#e0b058;--err:#cf5b47;--tool:#c9975f;--term-bg:#1d1712;--term-fg:#ecdcc3}[data-theme=coffee]{--bg-darkest:#191410;--bg-dark:#211a14;--bg-mid:#30271e;--bg-light:#403527;--fg:#ede3d0;--dim:#a89478;--line:#120d07;--acc:#c68a4b;--ok:#8aa15c;--warn:#d9a441;--err:#c25450;--tool:#a67c52;--term-bg:#15100c;--term-fg:#e8dcc5}[data-theme=matcha]{--bg-darkest:#1b2217;--bg-dark:#232b1e;--bg-mid:#2f3a28;--bg-light:#3c4a33;--fg:#e8eeda;--dim:#9aab84;--line:#131a10;--acc:#8db64e;--ok:#6fae4e;--warn:#d8b13c;--err:#cd5c50;--tool:#5da88f;--term-bg:#151b11;--term-fg:#dfe8cb}[data-theme=matrix]{--bg-darkest:#060a06;--bg-dark:#0a0f0a;--bg-mid:#122116;--bg-light:#18301d;--fg:#c9f2cf;--dim:#6fa075;--line:#030603;--acc:#2fd558;--ok:#39c95f;--warn:#b8d977;--err:#ff6b6b;--tool:#57c99b;--term-bg:#030503;--term-fg:#3f7}[data-theme=sunset]{--bg-darkest:#1c1422;--bg-dark:#241b2b;--bg-mid:#33253e;--bg-light:#452f52;--fg:#f2e6ea;--dim:#ab93a8;--line:#150e1b;--acc:#f2784b;--ok:#63b46f;--warn:#eda63d;--err:#e25563;--tool:#b06ac9;--term-bg:#170f1d;--term-fg:#f0dfe4}[data-theme=light]{--bg-darkest:#eceef4;--bg-dark:#f6f7fa;--bg-mid:#e2e5ee;--bg-light:#fff;--fg:#24272f;--dim:#5d6470;--line:#d8dbe4;--acc:#4757d8;--ok:#2b8a4b;--warn:#b97a0f;--err:#d83c3f;--tool:#2278a8;--term-bg:#fff;--term-fg:#24272f}[data-theme=strawberry]{--bg-darkest:#fbe4ea;--bg-dark:#fdf0f3;--bg-mid:#f7d4de;--bg-light:#fffafb;--fg:#4a2733;--dim:#96707e;--line:#f0ccd6;--acc:#e05575;--ok:#4d9e63;--warn:#c98a2d;--err:#d43d51;--tool:#b05687;--term-bg:#fff5f8;--term-fg:#4a2733}[data-theme=ramune]{--bg-darkest:#ddeff7;--bg-dark:#ecf7fb;--bg-mid:#c9e7f4;--bg-light:#fbfeff;--fg:#173342;--dim:#5b7d90;--line:#c2e2ef;--acc:#1d86ae;--ok:#3a9e6e;--warn:#cf9a2a;--err:#d64f5c;--tool:#3a9db8;--term-bg:#f4fbfd;--term-fg:#173342}*{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{background:var(--bg-dark);color:var(--fg);font-size:15px}.layout{grid-template-columns:250px 1fr 370px;height:100dvh;display:grid;overflow:hidden}.layout.right-hidden{grid-template-columns:250px 1fr}.layout.right-hidden .rightbar{display:none}@media (width<=1100px){.layout,.layout.right-hidden{grid-template-columns:220px 1fr}.layout.right-hidden .rightbar{display:block}.rightbar{z-index:5;width:min(420px,92vw);transition:transform .18s;position:fixed;top:0;bottom:0;right:0;transform:translate(100%);box-shadow:-8px 0 24px #0007}.rightbar.open{transform:none}}.sidebar{background:var(--bg-darkest);flex-direction:column;padding:10px 8px;display:flex;overflow-y:auto}.themepop{background:var(--bg-mid);border:1px solid var(--bg-light);border-radius:8px;flex-direction:column;gap:8px;padding:10px;display:flex;position:static}.trow{color:var(--fg);cursor:pointer;align-items:center;gap:6px;font-size:12.5px;display:flex}.trow select{background:var(--bg-darkest);color:var(--fg);border:none;border-radius:5px;outline:none;padding:3px 6px}.themegrid{grid-template-columns:1fr 1fr;gap:4px;display:grid}.themebtn{background:var(--bg-light);color:var(--fg);cursor:pointer;text-align:left;border:1px solid #0000;border-radius:6px;align-items:center;gap:6px;min-width:0;padding:5px 8px;font-size:12px;display:flex;overflow:hidden}.themebtn>:last-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.themebtn:hover{filter:brightness(1.15)}.themebtn.on{border-color:var(--acc)}.swdots{flex-shrink:0;gap:2px;display:inline-flex}.swdots i{border-radius:50%;width:9px;height:9px;display:inline-block;box-shadow:inset 0 0 0 1px #0004}.agent-list{flex:1;min-height:0;overflow-y:auto}.agent-item{cursor:pointer;color:var(--dim);border-radius:6px;align-items:center;gap:8px;margin-bottom:2px;padding:7px 10px;display:flex}.agent-item .subtag{color:var(--dim);font-size:10px}.caret{cursor:pointer;width:12px;color:var(--dim);text-align:center;-webkit-user-select:none;user-select:none;flex-shrink:0;font-size:9px}.caret:hover{color:var(--fg)}.caret-spacer{flex-shrink:0;width:12px}.agent-item:hover{background:var(--bg-mid)}.agent-item.sel{background:var(--bg-mid);color:var(--fg)}.dot{border-radius:50%;flex-shrink:0;width:9px;height:9px}.dot.running{background:var(--ok);box-shadow:0 0 6px var(--ok)}.dot.waiting{background:var(--acc);box-shadow:0 0 6px var(--acc);animation:1.6s ease-in-out infinite pulse}.badge.waiting{color:var(--acc)}.dot.idle{background:var(--warn)}.dot.stopped{background:var(--dim)}.dot.error{background:var(--err);box-shadow:0 0 6px var(--err)}.sidebar .metrics{color:var(--dim);border-top:1px solid var(--line);margin-top:12px;padding:8px 10px;font-size:11px;line-height:1.7}.sidebar-footer{border-top:1px solid var(--line);flex-direction:column;gap:2px;margin-top:auto;padding-top:8px;display:flex}.footer-row{align-items:center;gap:8px;display:flex}.footer-row .conn{margin-left:0}.sidebar-footer .metrics{border-top:none;margin-top:4px;padding:0}.brand{color:var(--fg);align-items:center;gap:8px;font-size:14px;display:flex}.version{color:var(--dim);font-family:ui-monospace,Menlo,monospace;font-size:11px}.channel{flex-direction:column;min-width:0;min-height:0;display:flex;position:relative;overflow:hidden}.chan-head{border-bottom:2px solid var(--line);background:var(--bg-dark);flex-shrink:0;align-items:center;gap:10px;padding:10px 16px;display:flex}.chan-head .hash{color:var(--dim);font-size:20px}.chan-head .title{font-weight:700}.chan-head .sub{color:var(--dim);text-overflow:ellipsis;white-space:nowrap;margin-left:8px;font-size:12px;overflow:hidden}.badge{background:var(--bg-light);vertical-align:middle;border-radius:10px;padding:1px 8px;font-size:11px}.badge.running{color:var(--ok)}.badge.error{color:var(--err)}.badge.idle{color:var(--warn)}.badge.done{color:var(--acc)}.badge.queued{color:var(--warn);white-space:nowrap;background:#faa81a1a;border:1px solid #faa81a44}.feed{overscroll-behavior:contain;flex:1;min-height:0;padding:14px 0 8px;overflow-y:auto}.msg{gap:14px;padding:3px 18px;display:flex}.msg:hover{background:#ffffff08}.msg.pending{opacity:.75}.msg.pending .ts{color:var(--warn);font-weight:600}.msg.pending[data-sent="1"] .ts{color:var(--ok)}.msg.pending .editbtn.cancel{color:var(--err)}.msg.grouped{padding-top:0}.avatar{border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;width:38px;height:38px;margin-top:2px;font-size:17px;display:flex}.msg-body{flex:1;min-width:0}.msg-head{align-items:baseline;gap:8px;display:flex}.actor{color:var(--warn);white-space:nowrap;font-size:11px}.runcard{flex-direction:column;gap:8px;display:flex}.statgrid{grid-template-columns:repeat(3,1fr);gap:6px;display:grid}.stat{background:var(--bg-mid);border-radius:6px;flex-direction:column;gap:1px;min-width:0;padding:5px 8px;display:flex}.stat span{color:var(--dim);text-transform:uppercase;letter-spacing:.05em;font-size:10px}.stat b{text-overflow:ellipsis;font-size:15px;overflow:hidden}.statrow{align-items:center;gap:8px;min-width:0;font-size:12.5px;display:flex}.statrow .k{color:var(--dim);text-transform:uppercase;letter-spacing:.05em;white-space:nowrap;flex-shrink:0;width:auto;min-width:58px;font-size:10px}.pill{background:var(--bg-mid);white-space:nowrap;border-radius:999px;margin-left:auto;padding:1px 8px;font-size:11px}.pill.ok{color:var(--ok)}.pill.warn{color:var(--warn)}.pill.crit{color:var(--err);background:#ed42451a}.bartrack{background:var(--bg-light);border-radius:3px;height:6px;margin-top:2px;overflow:hidden}.barfill{background:var(--ok);border-radius:3px;min-width:2px;height:100%;transition:width .3s}.barfill.warn{background:var(--warn)}.barfill.crit{background:var(--err)}.author{font-size:14.5px;font-weight:600}.ts{color:var(--dim);font-size:11px}.content{white-space:pre-wrap;word-break:break-word;line-height:1.45}.content p{margin:2px 0}.content pre{background:var(--bg-darkest);border-radius:6px;padding:8px;overflow-x:auto}.content code{background:var(--bg-darkest);border-radius:4px;padding:1px 4px;font-size:13px}.content pre code{background:0 0;padding:0}.content h1,.content h2,.content h3,.content h4,.content h5,.content h6{margin:8px 0 2px;font-size:15px}.content h1{font-size:16.5px}.content h2{font-size:15.5px}.content blockquote{border-left:3px solid var(--bg-light);color:var(--dim);background:#ffffff05;border-radius:0 4px 4px 0;margin:4px 0;padding:2px 10px}.content blockquote p{margin:2px 0}.content hr{border:none;border-top:1px solid var(--bg-light);margin:8px 0}.content del{color:var(--dim)}.content img{border-radius:6px;max-width:100%}.content li.task{margin-left:-18px;list-style:none}.content li:has(>input[type=checkbox]){margin-left:-18px;list-style:none}.content li input[type=checkbox]{accent-color:var(--ok);margin-right:6px}input[type=checkbox]{accent-color:var(--acc)}.content a{color:var(--tool);text-decoration:underline;-webkit-text-decoration-color:color-mix(in srgb, var(--tool) 45%, transparent);text-decoration-color:color-mix(in srgb, var(--tool) 45%, transparent);text-underline-offset:2px}.content a:hover{filter:brightness(1.15)}.themebtn.on{background:var(--acc);color:#fff}.trow select:focus{outline:1px solid var(--acc)}.tbl{margin:6px 0;overflow-x:auto}.tbl table{border-collapse:collapse;font-size:13px}.tbl th,.tbl td{border:1px solid var(--bg-light);text-align:left;padding:3px 9px}.tbl th{background:var(--bg-darkest);font-weight:600}.embed{border-left:3px solid var(--tool);background:var(--bg-darkest);border-radius:4px;margin-top:3px;padding:6px 10px;font-size:13.5px}.embed.fail{border-color:var(--err)}.embed.warn-embed{border-color:var(--warn)}.embed.warn-embed .mono{color:var(--warn)}.embed.done{border-color:var(--ok)}.embed.running{border-color:var(--warn)}.embed.running summary .meta:after{content:"⋯";color:var(--warn);margin-left:6px;animation:1s step-end infinite blink;display:inline-block}.embed summary{gap:8px}.embed summary .mono{font-family:ui-monospace,Menlo,monospace}.toolbody{background:var(--bg-darkest);white-space:pre-wrap;word-break:break-word;border-radius:4px;max-height:340px;margin:4px 0;padding:6px 8px;font-size:12.5px;overflow-y:auto}.toolbody.del{border-left:2px solid var(--err);color:#f0a5a6;background:#ed424514}.toolbody.add{border-left:2px solid var(--ok);color:#9fd8ae;background:#3ba55d14}.toolbody.patch{padding:4px 8px}.toolbody.patch .pline{white-space:pre-wrap;word-break:break-word}.toolbody.patch .pline.add{color:#9fd8ae;border-left:2px solid var(--ok);background:#3ba55d14;padding-left:4px}.toolbody.patch .pline.del{color:#f0a5a6;border-left:2px solid var(--err);background:#ed424514;padding-left:4px}.toolbody.patch .pline.meta{color:var(--dim)}.embed.question{border-color:var(--acc);background:#5865f20f}.qopts{flex-wrap:wrap;gap:6px;margin-top:6px;display:flex}.qopt{background:var(--acc);color:#fff;cursor:pointer;border:none;border-radius:999px;padding:3px 12px;font-size:12.5px}.qopt:hover{filter:brightness(1.15)}.qopt:disabled{opacity:.45;cursor:default;filter:none}.embed.question.answered{opacity:.75}.qfree{gap:6px;margin-top:8px;display:flex}.qfree input{background:var(--bg-mid);border:1px solid var(--bg-light);color:var(--fg);border-radius:6px;outline:none;flex:1;padding:4px 10px;font-size:13px}.qfree input:focus{border-color:var(--acc)}.qfree button{background:var(--bg-light);color:var(--fg);cursor:pointer;border:none;border-radius:6px;padding:4px 12px;font-size:12.5px}.qfree button:disabled{opacity:.45;cursor:default}.bashelapsed{color:var(--warn);margin-left:8px;font-size:11px}.embed.question .meta{margin-top:6px}.embed.decision{border-color:var(--acc);background:#5865f20f}.embed.decision summary .meta{margin-left:6px}.embed.decision .content{margin-top:3px;font-size:13px}.embed summary{cursor:pointer;-webkit-user-select:none;user-select:none;list-style-position:outside}.embed summary::marker{color:var(--dim)}.embed[open] summary{margin-bottom:4px}.embed .mono{white-space:pre-wrap;word-break:break-word;max-height:340px;font-family:ui-monospace,Menlo,monospace;font-size:12.5px;overflow-y:auto}.embed .meta{color:var(--dim);margin-top:3px;font-size:11px}.reasoning{border-left:3px dashed var(--bg-light);color:var(--dim);background:#ffffff05;border-radius:4px;margin:2px 0;padding:2px 10px;font-size:12.5px}.reasoning summary{cursor:pointer;-webkit-user-select:none;user-select:none;opacity:.75}.reasoning summary:hover{opacity:1;color:var(--fg)}.reasoning[open] summary{margin-bottom:4px}.reasoning .mono{white-space:pre-wrap;word-break:break-word;max-height:260px;overflow-y:auto}.msg.live .avatar{animation:1.6s ease-in-out infinite pulse}.cursor{color:var(--acc);animation:1s step-end infinite blink}.interrupted{color:var(--warn);margin-top:3px;font-size:11.5px}@keyframes blink{50%{opacity:0}}@keyframes pulse{50%{opacity:.55}}.conn{background:var(--err);vertical-align:middle;width:8px;height:8px;box-shadow:0 0 6px var(--err);border-radius:50%;display:inline-block}.conn.ok{background:var(--ok);box-shadow:0 0 6px var(--ok)}.copybtn{border:1px solid var(--bg-light);color:var(--dim);cursor:pointer;background:0 0;border-radius:4px;flex-shrink:0;padding:0 5px;font-size:11px;line-height:16px}.copybtn:hover{color:var(--fg);filter:brightness(1.3)}.embed summary{align-items:center;gap:6px;display:flex}.toolcopy{flex-shrink:0;gap:4px;margin-left:auto;display:flex}.msgfoot{color:var(--dim);align-items:center;gap:6px;margin-top:4px;font-size:11px;display:flex}.imgstage{flex-wrap:wrap;gap:6px;margin-bottom:6px;display:flex}.imgchip{display:inline-flex;position:relative}.imgchip img{object-fit:cover;border:1px solid var(--line);border-radius:6px;width:64px;height:64px}.imgchip .imgx{background:var(--err);color:#fff;cursor:pointer;border:none;border-radius:50%;width:16px;height:16px;font-size:9px;line-height:1;position:absolute;top:-5px;right:-5px}.promptimgs{flex-wrap:wrap;gap:6px;margin-top:4px;display:flex}img.promptimg{border:1px solid var(--line);border-radius:6px;max-width:180px;max-height:120px}.termdrawer{background:var(--term-bg);border-top:2px solid var(--line);flex-direction:column;display:flex}.termgrip{cursor:ns-resize;background:var(--bg-light);opacity:.6;border-bottom:1px solid var(--line);height:6px}.termgrip:hover{opacity:1}.termbar{color:var(--dim);border-bottom:1px solid var(--line);background:var(--bg-darkest);justify-content:space-between;align-items:center;padding:4px 10px;font-size:11.5px;display:flex}.termtabs{flex:1;align-items:center;gap:2px;min-width:0;display:flex;overflow-x:auto}.termtab{cursor:pointer;color:var(--dim);white-space:nowrap;background:var(--bg-mid);border-radius:5px;align-items:center;gap:6px;padding:2px 8px;font-size:11.5px;display:inline-flex}.termtab.active{color:var(--fg);outline:1px solid var(--acc)}.termtab:hover{filter:brightness(1.15)}.tabx{color:inherit;opacity:.5;cursor:pointer;background:0 0;border:none;padding:0;font-size:10px}.tabx:hover{opacity:1;color:var(--err)}.termbody{background:var(--term-bg);flex:1;gap:4px;min-height:0;padding:4px;display:flex}.termbody.split .termpane{width:50%}.termpane{background:var(--term-bg);border-radius:6px;flex:1;min-width:0;position:relative;overflow:hidden}.termpane:not(.focused){opacity:.9}.termpane.focused{box-shadow:inset 0 0 0 2px var(--acc)}.termhost{position:absolute;inset:0}.termsession{padding:4px 6px;position:absolute;inset:0}.termsplit{background:var(--line);cursor:col-resize;border-radius:2px;flex-shrink:0;width:3px}.termempty{color:var(--dim);place-items:center;font-size:12px;display:grid;position:absolute;inset:0}.jump{background:var(--acc);color:#fff;cursor:pointer;z-index:2;border:none;border-radius:999px;padding:6px 14px;font-size:12.5px;font-weight:600;position:absolute;top:-34px;left:50%;transform:translate(-50%);box-shadow:0 4px 14px #0008}body:has(.termdrawer) .jump{top:auto;bottom:calc(var(--termh,300px) + 44px);left:50%}.divider-msg{color:var(--dim);align-items:center;gap:10px;padding:4px 18px;font-size:11.5px;display:flex}.divider-msg:before,.divider-msg:after{content:"";background:var(--line);flex:1;height:1px}.divider-msg.err{color:var(--err)}.divider-msg.compacting{color:var(--acc);animation:1.6s ease-in-out infinite pulse}.day-divider{align-items:center;gap:10px;padding:14px 18px 6px;display:flex}.day-divider:before,.day-divider:after{content:"";background:var(--bg-light);flex:1;height:1px}.day-divider span{color:var(--dim);font-size:11px}.composer{padding:0 16px 18px;position:relative}.composer form{background:var(--bg-light);border-radius:10px;align-items:flex-end;gap:8px;padding:10px 12px;display:flex}.composer textarea{color:var(--fg);font:inherit;resize:none;background:0 0;border:none;outline:none;flex:1;max-height:160px;line-height:1.4;overflow-y:auto}.cmds{background:var(--bg-mid);border:1px solid var(--bg-light);z-index:3;border-radius:8px;position:absolute;bottom:calc(100% - 8px);left:16px;right:16px;overflow:hidden;box-shadow:0 -6px 20px #0007}.cmdrow{cursor:pointer;align-items:baseline;gap:10px;padding:7px 12px;font-size:13.5px;display:flex}.cmdrow:hover{background:var(--bg-light)}.cmdrow.active{background:var(--acc);color:#fff}.cmdrow.active .muted{color:#e6e9ff}.cmdrow .muted{font-size:12px}.composer button{background:var(--acc);color:#fff;cursor:pointer;border:none;border-radius:8px;padding:7px 14px;font-weight:600}.composer button:hover{opacity:.9}.composer label{color:var(--dim);white-space:nowrap;align-items:center;gap:4px;font-size:12px;display:flex}.hint{color:var(--dim);margin-top:5px;font-size:11px}.rightbar{background:var(--bg-dark);border-left:2px solid var(--line);scrollbar-gutter:stable;flex-direction:column;min-height:0;padding:14px;font-size:13px;display:flex;overflow-y:auto}.rightbar>*{flex-shrink:0}.rightbar h3{text-transform:uppercase;letter-spacing:.04em;color:var(--dim);margin:14px 0 6px;font-size:11px}.rightbar h3:first-child{margin-top:0}.card{background:var(--bg-darkest);white-space:pre-wrap;word-break:break-word;border-radius:8px;max-height:200px;padding:10px;overflow-y:auto}.card.runcard{max-height:none;overflow-y:visible}.muted{color:var(--dim)}.prog .progrow{gap:8px;margin-bottom:4px;display:flex}.prog .progrow b{color:var(--dim);text-transform:uppercase;letter-spacing:.05em;flex-shrink:0;min-width:62px;padding-top:2px;font-size:10px}.embed .progrow{gap:8px;margin-bottom:4px;display:flex}.embed .progrow b{color:var(--dim);text-transform:uppercase;letter-spacing:.05em;flex-shrink:0;min-width:62px;padding-top:2px;font-size:10px}.embed .progrow span{min-width:0}.inline-md{display:inline}.inline-md>:first-child{margin-top:0}.inline-md>:last-child{margin-bottom:0}.prog .progrow.warn span{color:var(--err)}.msg-head .editbtn{color:var(--dim);cursor:pointer;opacity:0;background:0 0;border:none;padding:0 2px;font-size:11px;transition:opacity .12s}.msg:hover .msg-head .editbtn{opacity:1}.msg-head .editbtn:hover{color:var(--acc)}.branch-row{color:var(--dim);cursor:pointer;justify-content:space-between;padding:3px 0;font-size:12px;display:flex}.branch-row:hover,.branch-row.cur{color:var(--fg)}.mini-cron{opacity:.75;margin-left:auto;font-size:10px}.sched-row{cursor:pointer;border-radius:6px;margin-bottom:2px;padding:6px 8px}.sched-row:hover,.sched-row.cur{background:var(--bg-mid)}.sched-top{align-items:baseline;gap:6px;font-size:12.5px;display:flex}.sched-row .sched-meta{color:var(--dim);margin-top:2px;font-size:11px}.sched-row .sched-prompt{color:var(--dim);text-overflow:ellipsis;white-space:nowrap;margin-top:2px;font-size:11.5px;overflow:hidden}.badge.cron{color:var(--tool);white-space:nowrap;background:#3ba0c91a;border:1px solid #3ba0c944}.sesscard{flex-direction:column;gap:6px;font-size:12.5px;display:flex}.sessrow{align-items:center;gap:8px;display:flex}.filebox{background:var(--bg-darkest);border-radius:8px;height:260px;padding:4px;overflow-y:auto}.filerow{cursor:pointer;color:var(--fg);border-radius:4px;align-items:center;gap:6px;padding:3px 8px;font-size:12.5px;display:flex}.filerow:hover{background:var(--bg-mid)}.filerow .ficon{width:10px;color:var(--dim);text-align:center;flex-shrink:0;font-size:9px}.filerow.isdir .fname{font-weight:600}.fname{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.fsize{color:var(--dim);flex-shrink:0;margin-left:auto;font-size:10px}.sessrow .k{color:var(--dim);text-transform:uppercase;letter-spacing:.05em;flex-shrink:0;width:74px;font-size:10px}.ellip{text-overflow:ellipsis;white-space:nowrap;text-align:left;direction:rtl;overflow:hidden}.modelbox{background:var(--bg-darkest);border-radius:8px;flex-direction:column;gap:6px;padding:10px;display:flex}.modelbox select,.modelbox input[type=text]{background:var(--bg-mid);color:var(--fg);font:inherit;border:none;border-radius:6px;outline:none;min-width:0;padding:6px 8px}.modelbox button{background:var(--acc);color:#fff;cursor:pointer;white-space:nowrap;border:none;border-radius:6px;padding:6px 10px;font-size:12.5px;font-weight:600}.modelbox button:hover{opacity:.9}.modelbox .meta{color:var(--dim);font-size:11px}.btnrow{gap:6px;margin:8px 0;display:flex}.btnrow button{background:var(--bg-light);color:var(--fg);cursor:pointer;border:none;border-radius:6px;padding:6px 10px;font-size:13px}.btnrow button:hover{filter:brightness(1.2)}.btnrow .runbtn{background:var(--ok);color:#fff;font-weight:600}.btnrow .runbtn,.btnrow .danger{min-width:74px}.btnrow .danger{background:var(--err);color:#fff;font-weight:600}.ctrlrow{align-items:center;gap:8px;margin-top:6px;font-size:12.5px;display:flex}.ctrlrow label{cursor:pointer;color:var(--fg);white-space:nowrap;align-items:center;gap:5px;display:flex}.ctrlrow .muted{font-size:11.5px}.goalstate{background:var(--bg-light);color:var(--fg);cursor:pointer;border:none;border-radius:6px;padding:3px 10px;font-size:12px}.goalstate.on.active{background:var(--ok);color:#fff;font-weight:600}.goalstate.on.done{background:var(--acc);color:#fff;font-weight:600}.goalstate.on.paused{background:var(--warn);color:#fff;font-weight:600}.goalstate:disabled{cursor:default;filter:none}.verifycard{border-left:3px solid var(--tool);margin-top:6px}.auditcard{border-left:3px solid var(--warn);margin-top:6px}.auditcard.ok{border-left-color:var(--ok)}.vtitle{color:var(--dim);text-transform:uppercase;letter-spacing:.04em;align-items:center;gap:6px;margin-bottom:4px;font-size:12px;font-weight:600;display:flex}.iconbtn{background:var(--bg-light);color:var(--fg);cursor:pointer;border:none;border-radius:6px;width:26px;height:24px;font-size:13px}.iconbtn:hover{filter:brightness(1.3)}.iconbtn.active{background:var(--acc);color:#fff}.overlay{z-index:10;background:#0009;place-items:center;display:grid;position:fixed;inset:0}.modal{background:var(--bg-mid);border-radius:10px;width:min(620px,92vw);max-height:88vh;padding:16px 18px;overflow-y:auto}.modal-head{justify-content:space-between;align-items:center;margin-bottom:12px;font-size:16px;display:flex}.modal label{color:var(--dim);flex-direction:column;gap:4px;font-size:12.5px;display:flex}.modal input[type=text],.modal input[type=number],.modal input[type=password],.modal input[type=url],.modal input[type=email],.modal select,.modal textarea{background:var(--bg-darkest);color:var(--fg);font:inherit;border:1px solid #0000;border-radius:6px;outline:none;padding:7px 9px}.modal input:focus,.modal select:focus,.modal textarea:focus{border-color:var(--acc)}.modal ::placeholder{color:#7c818f;opacity:1}.modal select option{background:var(--bg-darkest);color:var(--fg)}.modal input.compact{padding:5px 8px;font-size:12.5px}.modal textarea{resize:vertical}.w100{width:100%}.mono{font-family:ui-monospace,Menlo,monospace;font-size:13px}.modal fieldset{border:1px solid var(--bg-light);border-radius:8px;margin:0 0 4px;padding:10px}.modal fieldset legend{color:var(--dim);text-transform:uppercase;letter-spacing:.05em;padding:0 6px;font-size:11px}.cfgrow{flex-wrap:wrap;align-items:center;gap:6px;margin-bottom:6px;display:flex}.cfgrow input{background:var(--bg-darkest);border:1px solid var(--bg-light);color:var(--fg);border-radius:5px;flex:1;min-width:0;padding:5px 8px;font-size:12.5px}.cfgrow select{background:var(--bg-darkest);border:1px solid var(--bg-light);color:var(--fg);border-radius:5px;padding:5px 8px;font-size:12.5px}.cfgrow .cfgname{width:90px;color:var(--warn);flex:none;font-weight:600}.cfgrow button{background:var(--bg-mid);border:1px solid var(--bg-light);color:var(--fg);cursor:pointer;border-radius:5px;padding:4px 8px;font-size:12px}.cfgrow button:hover{background:var(--bg-light)}.cfgrow button.danger{background:var(--err);border-color:var(--err);color:#fff}.cfggrid{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:8px;display:grid}.cfggrid label,.cfgcol label{color:var(--dim);flex-direction:column;gap:3px;font-size:11px;display:flex}.cfggrid input{background:var(--bg-darkest);border:1px solid var(--bg-light);color:var(--fg);border-radius:5px;width:100%;padding:5px 8px}.cfgcol{margin-bottom:8px}.cfgcol textarea{background:var(--bg-darkest);border:1px solid var(--bg-light);color:var(--fg);font:inherit;border-radius:5px;width:100%;margin-top:4px;padding:5px 8px;font-size:12.5px}.presetbtn{background:var(--bg-light);color:var(--fg);cursor:pointer;border:1px solid #0000;border-radius:6px;flex:1;padding:7px 10px;font-size:13px}.presetbtn.active{background:var(--acc);color:#fff}.forkchip{white-space:nowrap;color:var(--dim);cursor:pointer;align-items:center;gap:4px;font-size:11.5px;display:flex}.dirlist{background:var(--bg-darkest);border-radius:6px;max-height:160px;padding:4px;overflow-y:auto}.direntry{cursor:pointer;border-radius:4px;padding:4px 8px;font-size:14px}.direntry:hover{background:var(--bg-light)}.filepreview{border-radius:8px;max-height:62vh;overflow:auto}.filetoolbar{gap:6px;margin-bottom:8px;display:flex}.filetoolbar button{background:var(--bg-light);color:var(--dim);cursor:pointer;border:none;border-radius:6px;padding:4px 12px;font-size:12.5px}.filetoolbar button:hover{color:var(--fg);filter:brightness(1.15)}.filetoolbar button.on{background:var(--acc);color:#fff;font-weight:600}.fileeditor{flex-direction:column;gap:8px;display:flex}.fileeditor textarea{background:var(--bg-darkest);min-height:55vh;max-height:65vh;color:var(--fg);border:1px solid var(--bg-light);white-space:pre;border-radius:8px;padding:10px;font-size:12.5px;line-height:1.45;overflow:auto}.filerow-actions{justify-content:flex-end;align-items:center;gap:10px;display:flex}.savebtn{background:var(--ok);color:#fff;cursor:pointer;border:none;border-radius:6px;padding:5px 14px;font-size:13px;font-weight:600}.savebtn:disabled{opacity:.4;cursor:default}.mdpreview{background:var(--bg-darkest);border-radius:8px;max-height:62vh;padding:14px 16px;overflow:auto}.filepreview .shiki,.filepreview .shiki span{color:var(--shiki-light);font-family:ui-monospace,Menlo,monospace;font-size:12.5px}html[data-appearance=dark] .filepreview .shiki,html[data-appearance=dark] .filepreview .shiki span{color:var(--shiki-dark)}.filepreview .shiki{background-color:var(--shiki-dark-bg);white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere;margin:0;padding:10px 12px;font-size:12.5px}html[data-appearance=light] .filepreview .shiki{background-color:var(--shiki-light-bg)}.notifbtn{position:relative}.notifbtn.has-unread{color:var(--acc)}.notifdot{background:var(--err);color:#fff;border-radius:999px;padding:2px 4px;font-size:9px;font-style:normal;font-weight:700;line-height:1;position:absolute;top:-4px;right:-4px}.brand-row{align-items:center;margin-top:2px;display:flex}.notifbackdrop{z-index:7;position:fixed;inset:0}.notifpanel{z-index:8;background:var(--bg-mid);border:1px solid var(--bg-light);border-radius:10px;width:min(360px,88vw);position:absolute;bottom:8px;left:8px;overflow:hidden;box-shadow:0 8px 30px #000a}.notifhead{border-bottom:1px solid var(--line);text-transform:uppercase;letter-spacing:.04em;color:var(--dim);justify-content:space-between;align-items:center;padding:8px 12px;font-size:11px;display:flex}.notiflist{max-height:46vh;overflow-y:auto}.notifitem{border-bottom:1px solid var(--line);cursor:pointer;padding:8px 12px}.notifitem:last-child{border-bottom:none}.notifitem:hover{background:var(--bg-light)}.notifitem .nhead{align-items:baseline;gap:6px;min-width:0;display:flex}.notifitem .ntitle{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-size:13px;font-weight:600;overflow:hidden}.notifitem.error .ntitle{color:var(--err)}.notifitem.finish .ntitle{color:var(--ok)}.notifitem.question .ntitle{color:var(--acc)}.notifitem .nbody{color:var(--dim);word-break:break-word;margin-top:3px;font-size:12px}.subcount{color:var(--dim);background:var(--bg-light);white-space:nowrap;border-radius:999px;flex-shrink:0;padding:1px 7px;font-size:10px}.subcount.live{color:var(--ok)}.mediapreview{flex-direction:column;align-items:center;gap:8px;display:flex}.mediapreview img{background:var(--bg-darkest);border-radius:8px;max-width:100%;max-height:65vh}.mediapreview video{background:#000;border-radius:8px;max-width:100%;max-height:65vh}.mediapreview audio{width:100%}.composer{flex-direction:column;display:flex}.composer .hint{flex-shrink:0}.composer.maximized{z-index:5;background:var(--bg-dark);padding:16px 16px 10px;position:absolute;inset:0}.composer.maximized .hint{white-space:nowrap;text-overflow:ellipsis;flex-shrink:0;margin-top:8px;line-height:1.4;overflow:hidden}.composer.maximized:before{content:"";background:var(--bg-dark);z-index:-1;position:absolute;inset:0}.composer.maximized form{flex:1;align-items:stretch;min-height:0}.composer.maximized textarea{resize:none;flex:1;max-height:none;height:auto!important}.composer.maximized .maxbtn,.composer.maximized form>button[type=submit]{align-self:flex-end}.maxbtn{justify-content:center;align-items:center;font-size:12px;line-height:1;display:inline-flex;padding:0!important}.filerow.gitignored .fname,.filerow.gitignored .ficon,.filerow.gitignored .fsize{opacity:.38}.embed.compaction{border-left-color:var(--tool)}.embed.compaction summary .meta{color:var(--tool)}.avatar.ghosted{opacity:.35;transition:opacity .15s}.msg:hover .avatar.ghosted{opacity:1}.grouptime{display:none}.notifitem{opacity:1}.notifitem.read{opacity:.55}.notifitem.read .ntitle{font-weight:400}.nreadbtn{flex-shrink:0;width:auto;padding:0 6px;font-size:11px}.flash-target{outline:2px solid var(--acc);border-radius:8px;transition:outline-color .6s}.msg.flash-target{background:color-mix(in srgb, var(--acc) 12%, transparent)}.agent-item .ghost{opacity:.55}.ghosttag{flex-shrink:0;font-size:10px}.filetoggle{text-align:center;flex-shrink:0;width:26px;padding:0}h3 .filetoggle{margin-left:0}.notifbadge{color:var(--acc);background:var(--bg-light);white-space:nowrap;border-radius:999px;flex-shrink:0;margin-left:auto;padding:1px 6px;font-size:10px}.agent-item.sel .notifbadge{background:var(--bg-darkest)}.xterm{cursor:text;-webkit-user-select:none;user-select:none;position:relative}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{z-index:5;position:absolute;top:0}.xterm .xterm-helper-textarea{opacity:0;z-index:-5;white-space:nowrap;resize:none;border:0;width:0;height:0;margin:0;padding:0;position:absolute;top:0;left:-9999em;overflow:hidden}.xterm .composition-view{color:#fff;white-space:nowrap;z-index:1;background:#000;display:none;position:absolute}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{cursor:default;background-color:#000;position:absolute;inset:0;overflow-y:scroll}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;top:0;left:0}.xterm-char-measure-element{visibility:hidden;line-height:normal;display:inline-block;position:absolute;top:0;left:-9999em}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{z-index:10;color:#0000;pointer-events:none;position:absolute;inset:0}.xterm .xterm-accessibility-tree:not(.debug) ::selection{color:#0000}.xterm .xterm-accessibility-tree{-webkit-user-select:text;user-select:text;white-space:pre;font-family:monospace}.xterm .xterm-accessibility-tree>div{transform-origin:0;width:fit-content}.xterm .live-region{width:1px;height:1px;position:absolute;left:-9999px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{-webkit-text-decoration:underline double;text-decoration:underline double}.xterm-underline-3{-webkit-text-decoration:underline wavy;text-decoration:underline wavy}.xterm-underline-4{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.xterm-underline-5{-webkit-text-decoration:underline dashed;text-decoration:underline dashed}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:underline overline}.xterm-overline.xterm-underline-2{-webkit-text-decoration:overline double underline;text-decoration:overline double underline}.xterm-overline.xterm-underline-3{-webkit-text-decoration:overline wavy underline;text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{-webkit-text-decoration:overline dotted underline;text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{-webkit-text-decoration:overline dashed underline;text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;pointer-events:none;position:absolute;top:0;right:0}.xterm-decoration-top{z-index:2;position:relative}.xterm .xterm-scrollable-element>.scrollbar{cursor:default}.xterm .xterm-scrollable-element>.scrollbar>.scra{cursor:pointer;font-size:11px!important}.xterm .xterm-scrollable-element>.visible{opacity:1;z-index:11;background:0 0;transition:opacity .1s linear}.xterm .xterm-scrollable-element>.invisible{opacity:0;pointer-events:none}.xterm .xterm-scrollable-element>.invisible.fade{transition:opacity .8s linear}.xterm .xterm-scrollable-element>.shadow{display:none;position:absolute}.xterm .xterm-scrollable-element>.shadow.top{width:100%;height:3px;box-shadow:var(--vscode-scrollbar-shadow,#000) 0 6px 6px -6px inset;display:block;top:0;left:3px}.xterm .xterm-scrollable-element>.shadow.left{width:3px;height:100%;box-shadow:var(--vscode-scrollbar-shadow,#000) 6px 0 6px -6px inset;display:block;top:3px;left:0}.xterm .xterm-scrollable-element>.shadow.top-left-corner{width:3px;height:3px;display:block;top:0;left:0}.xterm .xterm-scrollable-element>.shadow.top.left{box-shadow:var(--vscode-scrollbar-shadow,#000) 6px 0 6px -6px inset}
|