opencode-subagent-magazine 1.5.2 → 1.6.0-beta.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.
Files changed (60) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +192 -192
  3. package/dist/_version.d.ts +1 -1
  4. package/dist/_version.js +1 -1
  5. package/dist/core/color.d.ts +20 -0
  6. package/dist/core/color.js +68 -0
  7. package/dist/core/format.d.ts +5 -0
  8. package/dist/core/format.js +68 -0
  9. package/dist/core/index.d.ts +6 -0
  10. package/dist/core/index.js +6 -0
  11. package/dist/core/kv.d.ts +20 -0
  12. package/dist/core/kv.js +30 -0
  13. package/dist/core/state-machine.d.ts +17 -0
  14. package/dist/core/state-machine.js +27 -0
  15. package/dist/core/types.d.ts +51 -0
  16. package/dist/core/types.js +2 -0
  17. package/dist/core/usage.d.ts +15 -0
  18. package/dist/core/usage.js +1 -0
  19. package/dist/index.js +148 -1484
  20. package/dist/panel/SubAgentPanel.d.ts +13 -0
  21. package/dist/panel/SubAgentPanel.js +1232 -0
  22. package/dist/panel/panel-api.d.ts +67 -0
  23. package/dist/panel/panel-api.js +1 -0
  24. package/dist/panel/store.d.ts +5 -0
  25. package/dist/panel/store.js +5 -0
  26. package/dist/tui.js +383 -289
  27. package/dist/v2/commands.d.ts +7 -0
  28. package/dist/v2/commands.js +263 -0
  29. package/dist/v2/index.d.ts +8 -0
  30. package/dist/v2/index.js +62 -0
  31. package/dist/v2/theme.d.ts +3 -0
  32. package/dist/v2/theme.js +12 -0
  33. package/dist/v2/types.d.ts +231 -0
  34. package/dist/v2/types.js +6 -0
  35. package/dist/v2/v2-panel-api.d.ts +12 -0
  36. package/dist/v2/v2-panel-api.js +345 -0
  37. package/dist/v2.js +3003 -0
  38. package/install.mjs +103 -103
  39. package/package.json +64 -63
  40. package/src/_version.ts +1 -1
  41. package/src/clipboard.ts +134 -134
  42. package/src/core/color.ts +70 -0
  43. package/src/core/format.ts +61 -0
  44. package/src/core/index.ts +6 -0
  45. package/src/core/kv.ts +36 -0
  46. package/src/core/state-machine.ts +46 -0
  47. package/src/core/types.ts +58 -0
  48. package/src/core/usage.ts +16 -0
  49. package/src/i18n.ts +261 -261
  50. package/src/index.tsx +124 -1750
  51. package/src/panel/SubAgentPanel.tsx +1460 -0
  52. package/src/panel/panel-api.ts +72 -0
  53. package/src/panel/store.ts +8 -0
  54. package/src/server.ts +10 -10
  55. package/src/v2/commands.ts +251 -0
  56. package/src/v2/index.tsx +98 -0
  57. package/src/v2/theme.ts +14 -0
  58. package/src/v2/types.ts +165 -0
  59. package/src/v2/v2-panel-api.ts +301 -0
  60. package/tui/index.js +11 -0
package/dist/v2.js ADDED
@@ -0,0 +1,3003 @@
1
+ // src/v2/index.tsx
2
+ import { createComponent as _$createComponent2 } from "@opentui/solid";
3
+ import { createSignal as createSignal3 } from "solid-js";
4
+
5
+ // src/v2/v2-panel-api.ts
6
+ var SUBAGENT_TOOL_V2 = "subagent";
7
+ function normalizeMeta(meta) {
8
+ const m = meta && typeof meta === "object" ? { ...meta } : {};
9
+ if (m.sessionID !== void 0) {
10
+ if (m.session_id === void 0) m.session_id = m.sessionID;
11
+ if (m.sessionId === void 0) m.sessionId = m.sessionID;
12
+ }
13
+ return m;
14
+ }
15
+ function toV1Part(p) {
16
+ if (p.type === "tool") {
17
+ const st = { ...p.state ?? {} };
18
+ if (st.output === void 0 && Array.isArray(st.content)) {
19
+ st.output = st.content.map((c) => c.type === "text" ? c.text : c.type === "file" ? String(c.path ?? c.filename ?? "") : JSON.stringify(c)).filter((t) => typeof t === "string" && t.length > 0).join("\n");
20
+ }
21
+ return { type: "tool", tool: p.tool ?? p.name, state: st, subagent_type: p.subagent_type, metadata: normalizeMeta(p.metadata) };
22
+ }
23
+ if (p.type === "text") return { type: "text", text: String(p.text ?? "") };
24
+ if (p.type === "file") return { type: "file", source: p.source ?? {} };
25
+ if (p.type === "reasoning") return { type: "reasoning", text: String(p.text ?? "") };
26
+ return p;
27
+ }
28
+ function createPanelApi(context, settings) {
29
+ const kvStore = /* @__PURE__ */ new Map();
30
+ const messageIndex = /* @__PURE__ */ new Map();
31
+ const toolInfo = /* @__PURE__ */ new Map();
32
+ const kvGet = (key, fallback) => {
33
+ let entry = kvStore.get(key);
34
+ if (!entry) {
35
+ const created = context.storage.store(`subagent_magazine.${key}`, {
36
+ initial: { value: fallback }
37
+ });
38
+ entry = created;
39
+ kvStore.set(key, entry);
40
+ }
41
+ const v = entry[0].value;
42
+ return v === void 0 ? fallback : v;
43
+ };
44
+ const kvSet = (key, value) => {
45
+ let entry = kvStore.get(key);
46
+ if (!entry) {
47
+ const created = context.storage.store(`subagent_magazine.${key}`, {
48
+ initial: { value }
49
+ });
50
+ entry = created;
51
+ kvStore.set(key, entry);
52
+ }
53
+ const [, mutate] = entry;
54
+ return mutate((d) => {
55
+ d.value = value;
56
+ });
57
+ };
58
+ const isSubagentTool = (name) => name === SUBAGENT_TOOL_V2 || name === "task" || name === "delegate" || name === "call_omo_agent";
59
+ const toolEventToPart = (event) => {
60
+ const type = event.type;
61
+ const data = event.data;
62
+ if (!data) return void 0;
63
+ const key = data.assistantMessageID !== void 0 ? `${String(data.assistantMessageID)}\0${String(data.id)}` : void 0;
64
+ const info = key ? toolInfo.get(key) : void 0;
65
+ const normName = (n) => n === SUBAGENT_TOOL_V2 ? "task" : n;
66
+ const agentOf = (input) => {
67
+ const i = input && typeof input === "object" ? input : {};
68
+ return typeof i.agent === "string" ? i.agent : void 0;
69
+ };
70
+ if (type === "session.tool.input.started") {
71
+ if (!isSubagentTool(data.name)) return void 0;
72
+ if (key) toolInfo.set(key, { name: String(data.name), input: {} });
73
+ return { type: "tool", tool: normName(String(data.name)), id: String(data.id), state: { status: "pending", input: {} } };
74
+ }
75
+ if (type === "session.tool.called") {
76
+ const name = data.name ?? info?.name;
77
+ if (name && !isSubagentTool(name)) return void 0;
78
+ if (!name) return void 0;
79
+ if (key) toolInfo.set(key, { name: String(name), input: data.input ?? {} });
80
+ return {
81
+ type: "tool",
82
+ tool: normName(String(name)),
83
+ id: String(data.id),
84
+ subagent_type: agentOf(data.input),
85
+ state: { status: "running", input: data.input ?? {}, metadata: {} }
86
+ };
87
+ }
88
+ if (type === "session.tool.progress") {
89
+ const name = data.name ?? info?.name;
90
+ if (name && !isSubagentTool(name)) return void 0;
91
+ if (!name) return void 0;
92
+ return {
93
+ type: "tool",
94
+ tool: normName(String(name)),
95
+ id: String(data.id),
96
+ subagent_type: agentOf(info?.input),
97
+ state: { status: "running", input: info?.input ?? {}, metadata: normalizeMeta(data.metadata) }
98
+ };
99
+ }
100
+ if (type === "session.tool.success" || type === "session.tool.failed") {
101
+ const meta = normalizeMeta(data.metadata);
102
+ if (meta.sessionID === void 0) return void 0;
103
+ if (type === "session.tool.failed" && meta.status === "running") return void 0;
104
+ const name = data.name ?? info?.name;
105
+ if (name && !isSubagentTool(name)) return void 0;
106
+ const input = info?.input ?? data.input ?? {};
107
+ return {
108
+ type: "tool",
109
+ tool: normName(String(name ?? "task")),
110
+ id: String(data.id),
111
+ subagent_type: agentOf(input),
112
+ state: {
113
+ status: type === "session.tool.failed" ? "error" : "completed",
114
+ input,
115
+ metadata: meta
116
+ }
117
+ };
118
+ }
119
+ return void 0;
120
+ };
121
+ const normalizeMessages = (sid) => {
122
+ const raw = context.data.session.message.list(sid) ?? [];
123
+ const out = [];
124
+ for (const m of raw) {
125
+ const record = m;
126
+ messageIndex.set(String(m.id), record);
127
+ const model = m.model ?? (typeof m.model === "string" ? { id: m.model } : void 0);
128
+ out.push({
129
+ ...m,
130
+ role: m.type === "assistant" ? "assistant" : m.type === "user" ? "user" : m.type,
131
+ modelID: model?.id
132
+ });
133
+ }
134
+ return out;
135
+ };
136
+ return {
137
+ kv: {
138
+ get: kvGet,
139
+ set: kvSet
140
+ },
141
+ usage: {
142
+ readSessionTokens: (sid) => {
143
+ if (!sid) return void 0;
144
+ try {
145
+ const msgs = context.data.session.message.list(sid);
146
+ if (msgs) {
147
+ for (let i = msgs.length - 1; i >= 0; i--) {
148
+ const m = msgs[i];
149
+ if (m.type !== "assistant") continue;
150
+ const t = m.tokens;
151
+ if (!t || !(Number(t.output) > 0)) continue;
152
+ const ctx = (Number(t.input) || 0) + (Number(t.output) || 0) + (Number(t.reasoning) || 0) + (Number(t.cache?.read) || 0) + (Number(t.cache?.write) || 0);
153
+ if (ctx > 0) return ctx;
154
+ }
155
+ }
156
+ return void 0;
157
+ } catch {
158
+ return void 0;
159
+ }
160
+ },
161
+ readSessionCost: (sid) => {
162
+ if (!sid) return void 0;
163
+ try {
164
+ const session = context.data.session.get(sid);
165
+ if (session?.cost != null && session.cost > 0) return session.cost;
166
+ const msgs = context.data.session.message.list(sid);
167
+ if (!msgs) return void 0;
168
+ let total = 0;
169
+ for (const m of msgs) {
170
+ if (m.type === "assistant" && typeof m.cost === "number") total += m.cost;
171
+ }
172
+ return total > 0 ? total : void 0;
173
+ } catch {
174
+ return void 0;
175
+ }
176
+ },
177
+ readSessionModel: (sid) => {
178
+ if (!sid) return void 0;
179
+ try {
180
+ const msgs = context.data.session.message.list(sid);
181
+ if (msgs) {
182
+ for (let i = msgs.length - 1; i >= 0; i--) {
183
+ const m = msgs[i];
184
+ if (m.type !== "assistant") continue;
185
+ const model = m.model;
186
+ if (typeof model === "string") return model;
187
+ if (model?.id) return String(model.id);
188
+ }
189
+ }
190
+ return void 0;
191
+ } catch {
192
+ return void 0;
193
+ }
194
+ },
195
+ readSessionTodo: () => void 0
196
+ // V2 无 todo 数据源
197
+ },
198
+ session: {
199
+ get: (sid) => {
200
+ try {
201
+ return context.data.session.get(sid);
202
+ } catch {
203
+ return void 0;
204
+ }
205
+ },
206
+ status: (sid) => {
207
+ try {
208
+ const st = context.data.session.status(sid);
209
+ const type = st === "running" ? "busy" : st ?? "";
210
+ return { type };
211
+ } catch {
212
+ return void 0;
213
+ }
214
+ },
215
+ messages: (sid) => {
216
+ try {
217
+ return normalizeMessages(sid);
218
+ } catch {
219
+ return void 0;
220
+ }
221
+ },
222
+ part: (messageID) => {
223
+ const msg = messageIndex.get(String(messageID));
224
+ if (!msg) return void 0;
225
+ if (!Array.isArray(msg.content) || msg.content.length === 0) {
226
+ if (typeof msg.text === "string" && msg.text.length > 0) {
227
+ return [{ type: "text", text: msg.text }];
228
+ }
229
+ return [];
230
+ }
231
+ return msg.content.map((p) => toV1Part(p));
232
+ }
233
+ },
234
+ event: {
235
+ on: (type, cb) => {
236
+ switch (type) {
237
+ case "part.updated": {
238
+ const unsubs = [];
239
+ for (const evt of ["session.tool.input.started", "session.tool.called", "session.tool.progress", "session.tool.success", "session.tool.failed"]) {
240
+ unsubs.push(context.data.on(evt, (e) => {
241
+ const part = toolEventToPart(e);
242
+ if (part) cb({ type, payload: { part } });
243
+ }));
244
+ }
245
+ return () => {
246
+ for (const u of unsubs) u();
247
+ };
248
+ }
249
+ case "message.updated": {
250
+ const unsubs = [];
251
+ for (const evt of ["session.step.started", "session.step.ended", "session.step.failed"]) {
252
+ unsubs.push(context.data.on(evt, () => cb({ type })));
253
+ }
254
+ return () => {
255
+ for (const u of unsubs) u();
256
+ };
257
+ }
258
+ case "session.idle": {
259
+ const unsubs = [];
260
+ for (const evt of ["session.execution.succeeded", "session.execution.interrupted"]) {
261
+ unsubs.push(context.data.on(evt, (e) => {
262
+ const sid = String(e.data?.sessionID ?? "");
263
+ if (sid) cb({ type, payload: { sessionID: sid } });
264
+ }));
265
+ }
266
+ return () => {
267
+ for (const u of unsubs) u();
268
+ };
269
+ }
270
+ case "session.error":
271
+ return context.data.on("session.execution.failed", (e) => {
272
+ const evt = e.data;
273
+ const sid = String(evt?.sessionID ?? "");
274
+ if (sid) cb({ type, payload: { sessionID: sid, error: evt?.error } });
275
+ });
276
+ default:
277
+ return () => {
278
+ };
279
+ }
280
+ }
281
+ },
282
+ client: {
283
+ // V2 官方取消途径:context.client.session.interrupt(data.session 无 interrupt)
284
+ abort: ({ sessionID }) => context.client.session.interrupt({ sessionID }).then(() => {
285
+ })
286
+ },
287
+ route: {
288
+ navigateSession: (sessionID) => context.ui.router.navigate({ type: "session", sessionID })
289
+ },
290
+ ui: {
291
+ toast: (message, opts) => context.ui.toast.show({ message, title: opts?.title, variant: opts?.variant })
292
+ },
293
+ settings
294
+ };
295
+ }
296
+
297
+ // src/core/kv.ts
298
+ var KV_PREFIX = "subagent_magazine";
299
+ var SESSION_DATA_KEY = `${KV_PREFIX}.session_data`;
300
+ var SETTING_KEYS = {
301
+ lang: `${KV_PREFIX}.lang`,
302
+ maxEntries: `${KV_PREFIX}.max_entries`,
303
+ order: `${KV_PREFIX}.order`,
304
+ scrollMode: `${KV_PREFIX}.scroll_mode`,
305
+ open: `${KV_PREFIX}.open`,
306
+ ttlDays: `${KV_PREFIX}.ttl_days`
307
+ };
308
+ function loadSessionData(kv) {
309
+ try {
310
+ const raw = kv.get(SESSION_DATA_KEY, "{}");
311
+ return JSON.parse(String(raw));
312
+ } catch {
313
+ return {};
314
+ }
315
+ }
316
+ function saveSessionData(kv, data) {
317
+ try {
318
+ kv.set(SESSION_DATA_KEY, JSON.stringify(data));
319
+ } catch {
320
+ }
321
+ }
322
+ function readTTLDays(kv) {
323
+ const ttlDaysRaw = parseInt(String(kv.get(SETTING_KEYS.ttlDays, "3")), 10);
324
+ return Number.isNaN(ttlDaysRaw) ? 3 : ttlDaysRaw;
325
+ }
326
+
327
+ // src/_version.ts
328
+ var PLUGIN_VERSION = "1.6.0-beta.0";
329
+
330
+ // src/i18n.ts
331
+ var ZH_T = {
332
+ "panel.title": "\u5B50\u4EE3\u7406",
333
+ "status.none": "\u6682\u65E0\u5B50\u4EE3\u7406",
334
+ "agent.label": "\u4EE3\u7406",
335
+ "status.label": "\u72B6\u6001",
336
+ "time.label": "\u8017\u65F6",
337
+ "tokens.label": "\u4E0A\u4E0B\u6587",
338
+ "error.label": "\u9519\u8BEF",
339
+ "model.label": "\u6A21\u578B",
340
+ "todo.label": "\u8FDB\u5EA6",
341
+ "session.label": "\u4F1A\u8BDD ID",
342
+ "session.toast.copy": "\u53EF\u624B\u52A8\u590D\u5236\u4E0A\u65B9 ID",
343
+ "session.toast.copied": "\u4F1A\u8BDD ID \u5DF2\u590D\u5236",
344
+ "session.toast.copy_failed": "\u65E0\u6CD5\u8BBF\u95EE\u7CFB\u7EDF\u526A\u8D34\u677F\uFF0C\u8BF7\u624B\u52A8\u590D\u5236\u4E0A\u65B9 ID",
345
+ "open.label": "\u8FDB\u5165\u4F1A\u8BDD",
346
+ "cost.label": "\u8D39\u7528",
347
+ "scroll.more": "\u66F4\u591A",
348
+ "scroll.top": "\u56DE\u9876",
349
+ "scroll.bottom": "\u56DE\u5E95",
350
+ "dismiss.label": "\u6807\u8BB0\u5B8C\u6210",
351
+ "cancel.label": "\u53D6\u6D88",
352
+ "status.running": "\u8FD0\u884C\u4E2D",
353
+ "status.done": "\u5DF2\u5B8C\u6210",
354
+ "status.cancelled": "\u5DF2\u53D6\u6D88",
355
+ "status.error": "\u9519\u8BEF",
356
+ "order.desc": "\u964D\u5E8F\uFF08\u6700\u65B0\u5728\u524D\uFF09",
357
+ "order.asc": "\u5347\u5E8F\uFF08\u6700\u65E9\u5728\u524D\uFF09",
358
+ "scroll.wheel": "\u6EDA\u8F6E\u7FFB\u9875",
359
+ "scroll.click": "\u70B9\u51FB\u7FFB\u9875",
360
+ "ttl.label": "\u6E05\u7406\u5468\u671F",
361
+ "ttl.3d": "3 \u5929",
362
+ "ttl.7d": "7 \u5929",
363
+ "ttl.14d": "14 \u5929",
364
+ "ttl.30d": "30 \u5929",
365
+ "ttl.unlimited": "\u65E0\u671F\u9650",
366
+ "ttl.toast": "\u6E05\u7406\u5468\u671F\u5DF2\u8BBE\u4E3A {n} \u5929",
367
+ "ttl.toast_unlimited": "\u6E05\u7406\u5468\u671F\u5DF2\u8BBE\u4E3A\u65E0\u671F\u9650",
368
+ "clear.title": "\u786E\u8BA4\u6E05\u9664",
369
+ "clear.prompt": "\u786E\u5B9A\u6E05\u9664\u5F53\u524D\u4F1A\u8BDD\u6240\u6709\u5B50\u4EE3\u7406\u8BB0\u5F55\uFF1F\u6B64\u64CD\u4F5C\u4E0D\u53EF\u64A4\u9500\u3002",
370
+ "clear.prompt_running": "\u5F53\u524D\u6709 {n} \u4E2A\u8FD0\u884C\u4E2D\u7684\u5B50\u4EE3\u7406\uFF0C\u6E05\u9664\u540E\u5C06\u4E0D\u53EF\u6062\u590D\u3002\u786E\u5B9A\u7EE7\u7EED\uFF1F",
371
+ "clear.done": "\u5DF2\u6E05\u9664 {n} \u6761\u5B50\u4EE3\u7406\u8BB0\u5F55",
372
+ "clear.empty": "\u5F53\u524D\u4F1A\u8BDD\u65E0\u5B50\u4EE3\u7406\u8BB0\u5F55",
373
+ "cancel.no_session": "\u5B50\u4F1A\u8BDD ID \u4E0D\u53EF\u7528",
374
+ "cancel.not_child": "\u76EE\u6807\u4E0D\u662F\u5B50\u4F1A\u8BDD",
375
+ "cancel.read_error": "\u65E0\u6CD5\u8BFB\u53D6\u4F1A\u8BDD\u4FE1\u606F",
376
+ "cancel.outside_tree": "\u76EE\u6807\u4E0D\u5728\u5F53\u524D\u76D1\u63A7\u4F1A\u8BDD\u6811\u4E2D",
377
+ "cancel.already_ended": "\u4F1A\u8BDD\u5DF2\u7ED3\u675F\uFF0C\u65E0\u9700\u53D6\u6D88",
378
+ "cancel.status_error": "\u65E0\u6CD5\u67E5\u8BE2\u4F1A\u8BDD\u72B6\u6001",
379
+ "cancel.sent": "\u5DF2\u53D1\u9001\u53D6\u6D88\u6307\u4EE4",
380
+ "cancel.failed": "\u53D6\u6D88\u5931\u8D25"
381
+ };
382
+ var EN_T = {
383
+ "panel.title": "SubAgent",
384
+ "status.none": "No sub-agents yet",
385
+ "agent.label": "agent",
386
+ "status.label": "status",
387
+ "time.label": "time",
388
+ "tokens.label": "tokens",
389
+ "error.label": "error",
390
+ "model.label": "model",
391
+ "todo.label": "todo",
392
+ "session.label": "session ID",
393
+ "session.toast.copy": "Copy the ID above manually",
394
+ "session.toast.copied": "Session ID copied",
395
+ "session.toast.copy_failed": "Cannot access the system clipboard; copy the ID above manually",
396
+ "open.label": "Open session",
397
+ "cost.label": "cost",
398
+ "scroll.more": "more",
399
+ "scroll.top": "Top",
400
+ "scroll.bottom": "Bottom",
401
+ "dismiss.label": "dismiss",
402
+ "cancel.label": "Cancel",
403
+ "status.running": "running",
404
+ "status.done": "done",
405
+ "status.cancelled": "cancelled",
406
+ "status.error": "error",
407
+ "order.desc": "Desc (newest first)",
408
+ "order.asc": "Asc (oldest first)",
409
+ "scroll.wheel": "Wheel Scroll",
410
+ "scroll.click": "Click Scroll",
411
+ "ttl.label": "TTL (Time to Live)",
412
+ "ttl.3d": "3 days",
413
+ "ttl.7d": "7 days",
414
+ "ttl.14d": "14 days",
415
+ "ttl.30d": "30 days",
416
+ "ttl.unlimited": "Never",
417
+ "ttl.toast": "TTL set to {n} days",
418
+ "ttl.toast_unlimited": "TTL set to Never",
419
+ "clear.title": "Confirm",
420
+ "clear.prompt": "Clear all sub-agent records for this session? This cannot be undone.",
421
+ "clear.prompt_running": "{n} sub-agent(s) are still running. Clearing will discard them permanently. Continue?",
422
+ "clear.done": "Cleared {n} sub-agent record(s)",
423
+ "clear.empty": "No sub-agent records in this session",
424
+ "cancel.no_session": "Child session ID is unavailable",
425
+ "cancel.not_child": "Target is not a child session",
426
+ "cancel.read_error": "Cannot read session info",
427
+ "cancel.outside_tree": "Target is outside the monitored session tree",
428
+ "cancel.already_ended": "Session already ended, no need to cancel",
429
+ "cancel.status_error": "Cannot query session status",
430
+ "cancel.sent": "Cancel instruction sent",
431
+ "cancel.failed": "Cancellation failed"
432
+ };
433
+ var JA_T = {
434
+ "panel.title": "\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8",
435
+ "status.none": "\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u306A\u3057",
436
+ "agent.label": "\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8",
437
+ "status.label": "\u72B6\u614B",
438
+ "time.label": "\u6642\u9593",
439
+ "tokens.label": "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8",
440
+ "error.label": "\u30A8\u30E9\u30FC",
441
+ "model.label": "\u30E2\u30C7\u30EB",
442
+ "todo.label": "\u9032\u6357",
443
+ "session.label": "\u30BB\u30C3\u30B7\u30E7\u30F3 ID",
444
+ "session.toast.copy": "\u4E0A\u306E ID \u3092\u624B\u52D5\u3067\u30B3\u30D4\u30FC\u3057\u3066\u304F\u3060\u3055\u3044",
445
+ "session.toast.copied": "\u30BB\u30C3\u30B7\u30E7\u30F3 ID \u3092\u30B3\u30D4\u30FC\u3057\u307E\u3057\u305F",
446
+ "session.toast.copy_failed": "\u30B7\u30B9\u30C6\u30E0\u306E\u30AF\u30EA\u30C3\u30D7\u30DC\u30FC\u30C9\u306B\u30A2\u30AF\u30BB\u30B9\u3067\u304D\u306A\u3044\u305F\u3081\u3001\u4E0A\u306E ID \u3092\u624B\u52D5\u3067\u30B3\u30D4\u30FC\u3057\u3066\u304F\u3060\u3055\u3044",
447
+ "open.label": "\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u958B\u304F",
448
+ "cost.label": "\u8CBB\u7528",
449
+ "scroll.more": "\u3082\u3063\u3068",
450
+ "scroll.top": "\u5148\u982D\u3078",
451
+ "scroll.bottom": "\u672B\u5C3E\u3078",
452
+ "dismiss.label": "\u5B8C\u4E86\u306B\u3059\u308B",
453
+ "cancel.label": "\u30AD\u30E3\u30F3\u30BB\u30EB",
454
+ "status.running": "\u5B9F\u884C\u4E2D",
455
+ "status.done": "\u5B8C\u4E86",
456
+ "status.cancelled": "\u30AD\u30E3\u30F3\u30BB\u30EB\u6E08\u307F",
457
+ "status.error": "\u30A8\u30E9\u30FC",
458
+ "order.desc": "\u964D\u9806\uFF08\u65B0\u3057\u3044\u9806\uFF09",
459
+ "order.asc": "\u6607\u9806\uFF08\u53E4\u3044\u9806\uFF09",
460
+ "scroll.wheel": "\u30DB\u30A4\u30FC\u30EB\u30B9\u30AF\u30ED\u30FC\u30EB",
461
+ "scroll.click": "\u30AF\u30EA\u30C3\u30AF\u30B9\u30AF\u30ED\u30FC\u30EB",
462
+ "ttl.label": "\u4FDD\u6301\u671F\u9593",
463
+ "ttl.3d": "3 \u65E5",
464
+ "ttl.7d": "7 \u65E5",
465
+ "ttl.14d": "14 \u65E5",
466
+ "ttl.30d": "30 \u65E5",
467
+ "ttl.unlimited": "\u7121\u671F\u9650",
468
+ "ttl.toast": "\u4FDD\u6301\u671F\u9593\u3092 {n} \u65E5\u306B\u8A2D\u5B9A\u3057\u307E\u3057\u305F",
469
+ "ttl.toast_unlimited": "\u4FDD\u6301\u671F\u9593\u3092\u7121\u671F\u9650\u306B\u8A2D\u5B9A\u3057\u307E\u3057\u305F",
470
+ "clear.title": "\u524A\u9664\u306E\u78BA\u8A8D",
471
+ "clear.prompt": "\u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u5168\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u8A18\u9332\u3092\u524A\u9664\u3057\u307E\u3059\u304B\uFF1F\u3053\u306E\u64CD\u4F5C\u306F\u5143\u306B\u623B\u305B\u307E\u305B\u3093\u3002",
472
+ "clear.prompt_running": "\u5B9F\u884C\u4E2D\u306E\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u304C {n} \u500B\u3042\u308A\u307E\u3059\u3002\u524A\u9664\u3059\u308B\u3068\u5FA9\u5143\u3067\u304D\u307E\u305B\u3093\u3002\u7D9A\u884C\u3057\u307E\u3059\u304B\uFF1F",
473
+ "clear.done": "{n} \u4EF6\u306E\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u8A18\u9332\u3092\u524A\u9664\u3057\u307E\u3057\u305F",
474
+ "clear.empty": "\u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306B\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u8A18\u9332\u306F\u3042\u308A\u307E\u305B\u3093",
475
+ "cancel.no_session": "\u5B50\u30BB\u30C3\u30B7\u30E7\u30F3 ID \u304C\u5229\u7528\u3067\u304D\u307E\u305B\u3093",
476
+ "cancel.not_child": "\u5BFE\u8C61\u306F\u5B50\u30BB\u30C3\u30B7\u30E7\u30F3\u3067\u306F\u3042\u308A\u307E\u305B\u3093",
477
+ "cancel.read_error": "\u30BB\u30C3\u30B7\u30E7\u30F3\u60C5\u5831\u3092\u8AAD\u307F\u53D6\u308C\u307E\u305B\u3093",
478
+ "cancel.outside_tree": "\u5BFE\u8C61\u306F\u76E3\u8996\u5BFE\u8C61\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u30C4\u30EA\u30FC\u5916\u3067\u3059",
479
+ "cancel.already_ended": "\u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u65E2\u306B\u7D42\u4E86\u3057\u3066\u3044\u307E\u3059",
480
+ "cancel.status_error": "\u30BB\u30C3\u30B7\u30E7\u30F3\u72B6\u614B\u3092\u7167\u4F1A\u3067\u304D\u307E\u305B\u3093",
481
+ "cancel.sent": "\u30AD\u30E3\u30F3\u30BB\u30EB\u6307\u793A\u3092\u9001\u4FE1\u3057\u307E\u3057\u305F",
482
+ "cancel.failed": "\u30AD\u30E3\u30F3\u30BB\u30EB\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
483
+ };
484
+ var KO_T = {
485
+ "panel.title": "\uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8",
486
+ "status.none": "\uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8 \uC5C6\uC74C",
487
+ "agent.label": "\uC5D0\uC774\uC804\uD2B8",
488
+ "status.label": "\uC0C1\uD0DC",
489
+ "time.label": "\uC2DC\uAC04",
490
+ "tokens.label": "\uCEE8\uD14D\uC2A4\uD2B8",
491
+ "error.label": "\uC624\uB958",
492
+ "model.label": "\uBAA8\uB378",
493
+ "todo.label": "\uC9C4\uD589\uB3C4",
494
+ "session.label": "\uC138\uC158 ID",
495
+ "session.toast.copy": "\uC704 ID\uB97C \uC218\uB3D9\uC73C\uB85C \uBCF5\uC0AC\uD558\uC138\uC694",
496
+ "session.toast.copied": "\uC138\uC158 ID\uAC00 \uBCF5\uC0AC\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
497
+ "session.toast.copy_failed": "\uC2DC\uC2A4\uD15C \uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uC811\uADFC\uD560 \uC218 \uC5C6\uC5B4 \uC704 ID\uB97C \uC218\uB3D9\uC73C\uB85C \uBCF5\uC0AC\uD558\uC138\uC694",
498
+ "open.label": "\uC138\uC158 \uC5F4\uAE30",
499
+ "cost.label": "\uBE44\uC6A9",
500
+ "scroll.more": "\uB354 \uBCF4\uAE30",
501
+ "scroll.top": "\uB9E8 \uC704\uB85C",
502
+ "scroll.bottom": "\uB9E8 \uC544\uB798\uB85C",
503
+ "dismiss.label": "\uC644\uB8CC \uD45C\uC2DC",
504
+ "cancel.label": "\uCDE8\uC18C",
505
+ "status.running": "\uC2E4\uD589 \uC911",
506
+ "status.done": "\uC644\uB8CC",
507
+ "status.cancelled": "\uCDE8\uC18C\uB428",
508
+ "status.error": "\uC624\uB958",
509
+ "order.desc": "\uB0B4\uB9BC\uCC28\uC21C(\uCD5C\uC2E0\uC21C)",
510
+ "order.asc": "\uC624\uB984\uCC28\uC21C(\uC624\uB798\uB41C\uC21C)",
511
+ "scroll.wheel": "\uD720 \uC2A4\uD06C\uB864",
512
+ "scroll.click": "\uD074\uB9AD \uC2A4\uD06C\uB864",
513
+ "ttl.label": "\uBCF4\uC874 \uAE30\uAC04",
514
+ "ttl.3d": "3\uC77C",
515
+ "ttl.7d": "7\uC77C",
516
+ "ttl.14d": "14\uC77C",
517
+ "ttl.30d": "30\uC77C",
518
+ "ttl.unlimited": "\uBB34\uAE30\uD55C",
519
+ "ttl.toast": "\uBCF4\uC874 \uAE30\uAC04\uC744 {n}\uC77C\uB85C \uC124\uC815\uD588\uC2B5\uB2C8\uB2E4",
520
+ "ttl.toast_unlimited": "\uBCF4\uC874 \uAE30\uAC04\uC744 \uBB34\uAE30\uD55C\uC73C\uB85C \uC124\uC815\uD588\uC2B5\uB2C8\uB2E4",
521
+ "clear.title": "\uC0AD\uC81C \uD655\uC778",
522
+ "clear.prompt": "\uC774 \uC138\uC158\uC758 \uBAA8\uB4E0 \uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8 \uAE30\uB85D\uC744 \uC0AD\uC81C\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C? \uC774 \uC791\uC5C5\uC740 \uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
523
+ "clear.prompt_running": "\uC2E4\uD589 \uC911\uC778 \uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8\uAC00 {n}\uAC1C \uC788\uC2B5\uB2C8\uB2E4. \uC0AD\uC81C\uD558\uBA74 \uBCF5\uAD6C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uACC4\uC18D\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
524
+ "clear.done": "\uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8 \uAE30\uB85D {n}\uAC1C\uB97C \uC0AD\uC81C\uD588\uC2B5\uB2C8\uB2E4",
525
+ "clear.empty": "\uC774 \uC138\uC158\uC5D0 \uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8 \uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4",
526
+ "cancel.no_session": "\uD558\uC704 \uC138\uC158 ID\uB97C \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4",
527
+ "cancel.not_child": "\uB300\uC0C1\uC774 \uD558\uC704 \uC138\uC158\uC774 \uC544\uB2D9\uB2C8\uB2E4",
528
+ "cancel.read_error": "\uC138\uC158 \uC815\uBCF4\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4",
529
+ "cancel.outside_tree": "\uB300\uC0C1\uC774 \uBAA8\uB2C8\uD130\uB9C1 \uC138\uC158 \uD2B8\uB9AC \uBC16\uC5D0 \uC788\uC2B5\uB2C8\uB2E4",
530
+ "cancel.already_ended": "\uC138\uC158\uC774 \uC774\uBBF8 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
531
+ "cancel.status_error": "\uC138\uC158 \uC0C1\uD0DC\uB97C \uC870\uD68C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4",
532
+ "cancel.sent": "\uCDE8\uC18C \uC9C0\uC2DC\uB97C \uBCF4\uB0C8\uC2B5\uB2C8\uB2E4",
533
+ "cancel.failed": "\uCDE8\uC18C\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4"
534
+ };
535
+ var LANGS = { zh: ZH_T, en: EN_T, ja: JA_T, ko: KO_T };
536
+ var LANG_META = [
537
+ { code: "zh", label: "\u4E2D\u6587" },
538
+ { code: "en", label: "English" },
539
+ { code: "ja", label: "\u65E5\u672C\u8A9E" },
540
+ { code: "ko", label: "\uD55C\uAD6D\uC5B4" }
541
+ ];
542
+ function applyParams(tpl, params) {
543
+ if (!params) return tpl;
544
+ return tpl.replace(
545
+ /\{(\w+)\}/g,
546
+ (m, k) => k in params ? String(params[k]) : m
547
+ );
548
+ }
549
+ function createT(getCode) {
550
+ return (key, params) => applyParams(LANGS[getCode()][key] ?? key, params);
551
+ }
552
+ function detectLang() {
553
+ try {
554
+ const loc = Intl.DateTimeFormat().resolvedOptions().locale.toLowerCase();
555
+ if (loc.startsWith("zh")) return "zh";
556
+ if (loc.startsWith("ja")) return "ja";
557
+ if (loc.startsWith("ko")) return "ko";
558
+ return "en";
559
+ } catch {
560
+ return "en";
561
+ }
562
+ }
563
+
564
+ // src/panel/store.ts
565
+ import { createSignal } from "solid-js";
566
+ var globalEntryCache = /* @__PURE__ */ new Map();
567
+ var [clearTick, setClearTick] = createSignal(0);
568
+
569
+ // src/v2/commands.ts
570
+ function makeCommands(context, api, signals) {
571
+ const t = createT(() => signals.lang());
572
+ const kv = api.kv;
573
+ const clampMax = (n) => Math.max(1, Math.min(50, n));
574
+ const resolveParent = (sid) => {
575
+ try {
576
+ const session = api.session.get(sid);
577
+ const parentID = session?.parentID;
578
+ if (parentID) return { parentSid: parentID, isChild: true };
579
+ } catch {
580
+ }
581
+ return { parentSid: sid, isChild: false };
582
+ };
583
+ return [
584
+ {
585
+ id: "opencode-subagent-magazine.subagent.lang",
586
+ title: "SubAgent Magazine: Language",
587
+ description: "Switch display language (\u4E2D\u6587 / English / \u65E5\u672C\u8A9E / \uD55C\uAD6D\uC5B4)",
588
+ slash: { name: "subagent-lang" },
589
+ palette: true,
590
+ run: async () => {
591
+ const lang = await context.ui.dialog.select({
592
+ title: "Language / \u8BED\u8A00",
593
+ options: LANG_META.map((m) => ({ title: m.label, value: m.code }))
594
+ });
595
+ if (!lang) return;
596
+ signals.setLang(lang);
597
+ kv.set(SETTING_KEYS.lang, lang);
598
+ api.ui.toast("Language: " + (LANG_META.find((m) => m.code === lang)?.label ?? lang));
599
+ }
600
+ },
601
+ {
602
+ id: "opencode-subagent-magazine.subagent.order",
603
+ title: "SubAgent Magazine: Sort Order",
604
+ description: "Set sub-agent entry sort order (desc / asc)",
605
+ slash: { name: "subagent-order" },
606
+ palette: true,
607
+ run: async () => {
608
+ const order = await context.ui.dialog.select({
609
+ title: "Sort Order / \u6392\u5E8F\u65B9\u5F0F",
610
+ options: [
611
+ { title: t("order.desc"), value: "desc" },
612
+ { title: t("order.asc"), value: "asc" }
613
+ ]
614
+ });
615
+ if (!order) return;
616
+ signals.setSortOrder(order);
617
+ kv.set(SETTING_KEYS.order, order);
618
+ api.ui.toast(order === "desc" ? t("order.desc") : t("order.asc"));
619
+ }
620
+ },
621
+ {
622
+ id: "opencode-subagent-magazine.subagent.scroll",
623
+ title: "SubAgent Magazine: Scroll Mode",
624
+ description: "Set scroll mode (wheel / click)",
625
+ slash: { name: "subagent-scroll" },
626
+ palette: true,
627
+ run: async () => {
628
+ const mode = await context.ui.dialog.select({
629
+ title: "Scroll Mode / \u6EDA\u52A8\u6A21\u5F0F",
630
+ options: [
631
+ { title: t("scroll.wheel"), value: "wheel" },
632
+ { title: t("scroll.click"), value: "click" }
633
+ ]
634
+ });
635
+ if (!mode) return;
636
+ signals.setScrollMode(mode);
637
+ kv.set(SETTING_KEYS.scrollMode, mode);
638
+ api.ui.toast(mode === "wheel" ? t("scroll.wheel") : t("scroll.click"));
639
+ }
640
+ },
641
+ {
642
+ id: "opencode-subagent-magazine.subagent.max",
643
+ title: "SubAgent Magazine: Max Entries",
644
+ description: "Set max visible sub-agent entries in sidebar",
645
+ slash: { name: "subagent-max" },
646
+ palette: true,
647
+ run: async () => {
648
+ const val = await context.ui.dialog.prompt({
649
+ title: "Max Visible Entries",
650
+ message: "Number of entries to show in the sidebar (1\u201350)",
651
+ placeholder: String(signals.maxEntries())
652
+ });
653
+ if (val === void 0) return;
654
+ const n = clampMax(parseInt(val, 10) || 10);
655
+ signals.setMaxEntries(n);
656
+ kv.set(SETTING_KEYS.maxEntries, n);
657
+ api.ui.toast(`Max entries: ${n}`);
658
+ }
659
+ },
660
+ {
661
+ id: "opencode-subagent-magazine.subagent.version",
662
+ title: "SubAgent Magazine: Version",
663
+ description: "Show plugin version",
664
+ slash: { name: "subagent-version" },
665
+ palette: true,
666
+ run: () => {
667
+ api.ui.toast(`opencode-subagent-magazine v${PLUGIN_VERSION}`);
668
+ }
669
+ },
670
+ {
671
+ id: "opencode-subagent-magazine.subagent.session",
672
+ title: "SubAgent Magazine: Session",
673
+ description: "Show current session ID",
674
+ slash: { name: "subagent-session" },
675
+ palette: true,
676
+ run: () => {
677
+ api.ui.toast(`Session: ${signals.sessionId}`);
678
+ }
679
+ },
680
+ {
681
+ id: "opencode-subagent-magazine.subagent.clear-running",
682
+ title: "SubAgent Magazine: Clear Running",
683
+ description: "Mark all running sub-agent entries as done (for stuck/zombie entries)",
684
+ slash: { name: "subagent-clear-running" },
685
+ palette: true,
686
+ run: () => {
687
+ const sid = signals.sessionId;
688
+ const entries = globalEntryCache.get(sid);
689
+ if (!entries || entries.size === 0) {
690
+ api.ui.toast(signals.lang() === "zh" ? "\u6682\u65E0\u5B50\u4EE3\u7406\u6761\u76EE" : "No sub-agent entries found");
691
+ return;
692
+ }
693
+ let count = 0;
694
+ for (const [, entry] of entries) {
695
+ if (entry.status === "running" || entry.status === "cancel_requested") {
696
+ entry.status = "done";
697
+ entry.endedAt = Date.now();
698
+ count++;
699
+ }
700
+ }
701
+ if (count > 0) {
702
+ try {
703
+ const data = loadSessionData(kv);
704
+ const { parentSid, isChild } = resolveParent(sid);
705
+ if (isChild) {
706
+ if (!data[parentSid]) data[parentSid] = { ts: Date.now(), entries: [], scroll: 0, expanded: "", children: {} };
707
+ if (!data[parentSid].children) data[parentSid].children = {};
708
+ if (!data[parentSid].children[sid]) data[parentSid].children[sid] = { scroll: 0, expanded: "", entries: [] };
709
+ data[parentSid].children[sid] = { ...data[parentSid].children[sid], entries: [...entries.values()] };
710
+ } else {
711
+ data[sid] = {
712
+ ts: Date.now(),
713
+ entries: [...entries.values()],
714
+ scroll: data[sid]?.scroll ?? 0,
715
+ expanded: data[sid]?.expanded ?? "",
716
+ children: data[sid]?.children ?? {}
717
+ };
718
+ }
719
+ saveSessionData(kv, data);
720
+ } catch {
721
+ }
722
+ api.ui.toast(signals.lang() === "zh" ? `\u5DF2\u6807\u8BB0 ${count} \u4E2A\u8FD0\u884C\u4E2D\u7684\u6761\u76EE\u4E3A\u5B8C\u6210` : `Marked ${count} running entries as done`);
723
+ } else {
724
+ api.ui.toast(signals.lang() === "zh" ? "\u6CA1\u6709\u9700\u8981\u6E05\u7406\u7684\u8FD0\u884C\u4E2D\u6761\u76EE" : "No running entries to clear");
725
+ }
726
+ }
727
+ },
728
+ {
729
+ id: "opencode-subagent-magazine.subagent.ttl",
730
+ title: "SubAgent Magazine: TTL",
731
+ description: "Set session data retention period (days before auto-cleanup)",
732
+ slash: { name: "subagent-ttl" },
733
+ palette: true,
734
+ run: async () => {
735
+ const curDays = readTTLDays(kv);
736
+ const curLabel = curDays === 0 ? t("ttl.unlimited") : `${curDays}d`;
737
+ const days = await context.ui.dialog.select({
738
+ title: `${t("ttl.label")} (${curLabel})`,
739
+ options: [
740
+ { title: t("ttl.3d"), value: 3 },
741
+ { title: t("ttl.7d"), value: 7 },
742
+ { title: t("ttl.14d"), value: 14 },
743
+ { title: t("ttl.30d"), value: 30 },
744
+ { title: t("ttl.unlimited"), value: 0 }
745
+ ]
746
+ });
747
+ if (days === void 0) return;
748
+ kv.set(SETTING_KEYS.ttlDays, String(days));
749
+ api.ui.toast(days === 0 ? t("ttl.toast_unlimited") : t("ttl.toast", { n: days }));
750
+ }
751
+ },
752
+ {
753
+ id: "opencode-subagent-magazine.subagent.clear-entries",
754
+ title: "SubAgent Magazine: Clear Entries",
755
+ description: "Delete all sub-agent records for the current session (cannot be undone)",
756
+ slash: { name: "subagent-clear-entries" },
757
+ palette: true,
758
+ run: async () => {
759
+ const sid = signals.sessionId;
760
+ const sessionObj = api.session.get(sid);
761
+ const parentID = sessionObj?.parentID;
762
+ const cached = globalEntryCache.get(sid);
763
+ let runningCount = 0;
764
+ if (cached) {
765
+ for (const [, e] of cached) {
766
+ if (e.status === "running") runningCount++;
767
+ }
768
+ }
769
+ const msg = runningCount > 0 ? t("clear.prompt_running", { n: runningCount }) : t("clear.prompt");
770
+ const choice = await context.ui.dialog.select({
771
+ title: t("clear.title"),
772
+ options: [
773
+ { title: t("clear.title"), value: "yes" },
774
+ { title: t("cancel.label"), value: "no" }
775
+ ]
776
+ });
777
+ if (choice !== "yes") return;
778
+ try {
779
+ const data = loadSessionData(kv);
780
+ let count = 0;
781
+ if (parentID) {
782
+ if (data[parentID]?.children?.[sid]) {
783
+ const child = data[parentID].children[sid];
784
+ const ids = child.entries?.map((e) => e.id) ?? [];
785
+ count = ids.length;
786
+ child.entries = [];
787
+ child.scroll = 0;
788
+ child.expanded = "";
789
+ child.clearedIds = [.../* @__PURE__ */ new Set([...child.clearedIds ?? [], ...ids])];
790
+ }
791
+ } else {
792
+ count = data[sid]?.entries?.length ?? 0;
793
+ if (data[sid]) {
794
+ const ids = data[sid].entries?.map((e) => e.id) ?? [];
795
+ data[sid].entries = [];
796
+ data[sid].scroll = 0;
797
+ data[sid].expanded = "";
798
+ data[sid].clearedIds = [.../* @__PURE__ */ new Set([...data[sid].clearedIds ?? [], ...ids])];
799
+ }
800
+ }
801
+ saveSessionData(kv, data);
802
+ globalEntryCache.delete(sid);
803
+ setClearTick((v) => v + 1);
804
+ api.ui.toast(t("clear.done", { n: count }));
805
+ } catch {
806
+ }
807
+ }
808
+ }
809
+ ];
810
+ }
811
+
812
+ // src/v2/theme.ts
813
+ function mapTheme(theme) {
814
+ return {
815
+ primary: theme.hue.interactive[300],
816
+ text: theme.text.default,
817
+ textMuted: theme.text.subdued,
818
+ success: theme.text.feedback.success.default,
819
+ warning: theme.text.feedback.warning.default,
820
+ error: theme.text.feedback.error.default,
821
+ border: theme.text.subdued
822
+ };
823
+ }
824
+
825
+ // src/panel/SubAgentPanel.tsx
826
+ import { createTextNode as _$createTextNode } from "@opentui/solid";
827
+ import { insertNode as _$insertNode } from "@opentui/solid";
828
+ import { createComponent as _$createComponent } from "@opentui/solid";
829
+ import { effect as _$effect } from "@opentui/solid";
830
+ import { insert as _$insert } from "@opentui/solid";
831
+ import { memo as _$memo } from "@opentui/solid";
832
+ import { use as _$use } from "@opentui/solid";
833
+ import { setProp as _$setProp } from "@opentui/solid";
834
+ import { createElement as _$createElement } from "@opentui/solid";
835
+ import { createMemo, createSignal as createSignal2, createEffect, onMount, onCleanup, untrack, Show, For } from "solid-js";
836
+
837
+ // src/clipboard.ts
838
+ import { spawn } from "node:child_process";
839
+ import { platform, release } from "node:os";
840
+ function runWithInput(command, args, input) {
841
+ return new Promise((resolve, reject) => {
842
+ const child = spawn(command, args, {
843
+ stdio: ["pipe", "ignore", "ignore"],
844
+ windowsHide: true
845
+ });
846
+ child.once("error", reject);
847
+ child.once("close", (code) => {
848
+ if (code === 0) resolve();
849
+ else reject(new Error(`${command} exited with code ${code}`));
850
+ });
851
+ child.stdin?.end(input);
852
+ });
853
+ }
854
+ function writeOsc52(text) {
855
+ if (!process.stdout.isTTY) return false;
856
+ const payload = Buffer.from(text, "utf8").toString("base64");
857
+ const sequence = `\x1B]52;c;${payload}\x07`;
858
+ const wrapped = process.env.TMUX || process.env.STY ? `\x1BPtmux;\x1B${sequence}\x1B\\` : sequence;
859
+ process.stdout.write(wrapped);
860
+ return true;
861
+ }
862
+ async function tryCommand(method, command, args, text) {
863
+ try {
864
+ await runWithInput(command, args, text);
865
+ return { copied: true, method };
866
+ } catch {
867
+ return void 0;
868
+ }
869
+ }
870
+ async function copyText(text) {
871
+ if (!text) return { copied: false, method: "none", error: "empty_text" };
872
+ const os = platform();
873
+ const isWsl = release().toLowerCase().includes("microsoft");
874
+ const attempts = [];
875
+ if (os === "darwin") {
876
+ attempts.push(() => tryCommand("pbcopy", "pbcopy", [], text));
877
+ attempts.push(
878
+ () => tryCommand(
879
+ "osascript",
880
+ "osascript",
881
+ ["-e", 'set the clipboard to (read (POSIX file "/dev/stdin") as text)'],
882
+ text
883
+ )
884
+ );
885
+ }
886
+ if (os === "linux" && isWsl) {
887
+ attempts.push(
888
+ () => tryCommand(
889
+ "powershell",
890
+ "powershell.exe",
891
+ [
892
+ "-NonInteractive",
893
+ "-NoProfile",
894
+ "-Command",
895
+ "[Console]::InputEncoding=[Text.Encoding]::UTF8; Set-Clipboard ([Console]::In.ReadToEnd())"
896
+ ],
897
+ text
898
+ )
899
+ );
900
+ } else if (os === "linux") {
901
+ if (process.env.WAYLAND_DISPLAY) {
902
+ attempts.push(() => tryCommand("wl-copy", "wl-copy", [], text));
903
+ }
904
+ attempts.push(() => tryCommand("xclip", "xclip", ["-selection", "clipboard"], text));
905
+ attempts.push(() => tryCommand("xsel", "xsel", ["--clipboard", "--input"], text));
906
+ }
907
+ if (os === "win32") {
908
+ attempts.push(
909
+ () => tryCommand(
910
+ "powershell",
911
+ "powershell.exe",
912
+ [
913
+ "-NonInteractive",
914
+ "-NoProfile",
915
+ "-Command",
916
+ "[Console]::InputEncoding=[Text.Encoding]::UTF8; Set-Clipboard ([Console]::In.ReadToEnd())"
917
+ ],
918
+ text
919
+ )
920
+ );
921
+ }
922
+ for (const attempt of attempts) {
923
+ const result = await attempt();
924
+ if (result) return result;
925
+ }
926
+ try {
927
+ if (writeOsc52(text)) return { copied: true, method: "osc52" };
928
+ } catch {
929
+ }
930
+ return { copied: false, method: "none", error: "clipboard_unavailable" };
931
+ }
932
+
933
+ // src/core/types.ts
934
+ var SUBAGENT_TOOLS = /* @__PURE__ */ new Set(["task", "delegate", "call_omo_agent"]);
935
+
936
+ // src/core/format.ts
937
+ function charColumns(c) {
938
+ const code = c.codePointAt(0) ?? 0;
939
+ if (code < 32) return 0;
940
+ if (code < 127) return 1;
941
+ if (code < 160) return 0;
942
+ if (code >= 4352 && code <= 4447 || code >= 11904 && code <= 42191 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65040 && code <= 65135 || code >= 65281 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 127744 && code <= 128591 || code >= 131072 && code <= 262141)
943
+ return 2;
944
+ return 1;
945
+ }
946
+ function visualWidth(s) {
947
+ let w = 0;
948
+ for (const c of s) w += charColumns(c);
949
+ return w;
950
+ }
951
+ function truncate(text, maxCols) {
952
+ if (visualWidth(text) <= maxCols) return text;
953
+ let cols = 0;
954
+ let i = 0;
955
+ for (const c of text) {
956
+ const w = charColumns(c);
957
+ if (cols + w > maxCols - 1) break;
958
+ cols += w;
959
+ i += c.length;
960
+ }
961
+ return text.slice(0, i) + "\u2026";
962
+ }
963
+ function fmtDurationShort(ms, running) {
964
+ if (running && ms < 2e3) return "";
965
+ if (ms < 1e3) return (ms / 1e3).toFixed(2) + "s";
966
+ if (ms < 6e4) return (ms / 1e3).toFixed(2) + "s";
967
+ const m = Math.floor(ms / 6e4);
968
+ const s = Math.round(ms % 6e4 / 1e3);
969
+ return `${m}m${s}s`;
970
+ }
971
+ function fmtTokens(n) {
972
+ if (n < 1e3) return `${n}`;
973
+ if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
974
+ return `${(n / 1e6).toFixed(1)}M`;
975
+ }
976
+ function safeErrorMsg(err) {
977
+ if (!err) return "";
978
+ if (typeof err === "string") return err;
979
+ if (typeof err === "object") return String(err.message || err.code || "");
980
+ return "";
981
+ }
982
+
983
+ // src/core/color.ts
984
+ function rgb(raw) {
985
+ if (typeof raw === "string" && raw.startsWith("#")) {
986
+ const h = raw.slice(1);
987
+ return {
988
+ r: parseInt(h.slice(0, 2), 16),
989
+ g: parseInt(h.slice(2, 4), 16),
990
+ b: parseInt(h.slice(4, 6), 16)
991
+ };
992
+ }
993
+ if (raw && typeof raw === "object") {
994
+ const o = raw;
995
+ if (typeof o.r === "number" && typeof o.g === "number" && typeof o.b === "number") {
996
+ const scale = o.r > 1 || o.g > 1 || o.b > 1 ? 1 : 255;
997
+ return { r: Math.round(o.r * scale), g: Math.round(o.g * scale), b: Math.round(o.b * scale) };
998
+ }
999
+ }
1000
+ return null;
1001
+ }
1002
+ function saturation(r, g, b) {
1003
+ const max = Math.max(r, g, b) / 255;
1004
+ const min = Math.min(r, g, b) / 255;
1005
+ const delta = max - min;
1006
+ if (delta === 0) return 0;
1007
+ const L = (max + min) / 2;
1008
+ return L <= 0.5 ? delta / (max + min) : delta / (2 - max - min);
1009
+ }
1010
+ function desaturateTo(raw, maxSat, fallback) {
1011
+ const c = rgb(raw);
1012
+ if (!c) return fallback;
1013
+ const sat = saturation(c.r, c.g, c.b);
1014
+ if (sat <= maxSat) {
1015
+ return "#" + [c.r, c.g, c.b].map((v) => v.toString(16).padStart(2, "0")).join("");
1016
+ }
1017
+ const luma = c.r * 0.299 + c.g * 0.587 + c.b * 0.114;
1018
+ let lo = 0, hi = 1;
1019
+ for (let i = 0; i < 12; i++) {
1020
+ const mid = (lo + hi) / 2;
1021
+ const nr2 = Math.round(c.r + (luma - c.r) * mid);
1022
+ const ng2 = Math.round(c.g + (luma - c.g) * mid);
1023
+ const nb2 = Math.round(c.b + (luma - c.b) * mid);
1024
+ if (saturation(nr2, ng2, nb2) > maxSat) lo = mid;
1025
+ else hi = mid;
1026
+ }
1027
+ const nr = Math.round(c.r + (luma - c.r) * hi);
1028
+ const ng = Math.round(c.g + (luma - c.g) * hi);
1029
+ const nb = Math.round(c.b + (luma - c.b) * hi);
1030
+ return "#" + [nr, ng, nb].map((v) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, "0")).join("");
1031
+ }
1032
+ function dimColor(hex, factor = 0.5) {
1033
+ const c = rgb(hex);
1034
+ if (!c) return hex;
1035
+ const r = Math.round(c.r * factor);
1036
+ const g = Math.round(c.g * factor);
1037
+ const b = Math.round(c.b * factor);
1038
+ return "#" + [r, g, b].map((v) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, "0")).join("");
1039
+ }
1040
+ var FALLBACK = {
1041
+ primary: "#8B9DAF",
1042
+ text: "#C5C5BB",
1043
+ muted: "#7A7A72",
1044
+ success: "#9CAF8B",
1045
+ warning: "#C5B88D",
1046
+ error: "#B08A8A",
1047
+ border: "#6B6B63"
1048
+ };
1049
+ var MAX_SAT = 0.28;
1050
+
1051
+ // src/panel/SubAgentPanel.tsx
1052
+ var LEFT_PAD = 4;
1053
+ var INDENT = 2;
1054
+ function SubAgentPanel(props) {
1055
+ const t = createT(() => props.lang());
1056
+ const SESSION_DATA_KEY2 = `${KV_PREFIX}.session_data`;
1057
+ const ttlDaysRaw = parseInt(String(props.api.kv.get(`${KV_PREFIX}.ttl_days`, "3")), 10);
1058
+ const ttlDays = Number.isNaN(ttlDaysRaw) ? 3 : ttlDaysRaw;
1059
+ const TTL_MS = ttlDays * 24 * 60 * 60 * 1e3;
1060
+ const loadSessionData2 = () => {
1061
+ try {
1062
+ const raw = props.api.kv.get(SESSION_DATA_KEY2, "{}");
1063
+ return JSON.parse(String(raw));
1064
+ } catch {
1065
+ return {};
1066
+ }
1067
+ };
1068
+ const saveSessionData2 = (data) => {
1069
+ try {
1070
+ props.api.kv.set(SESSION_DATA_KEY2, JSON.stringify(data));
1071
+ } catch {
1072
+ }
1073
+ };
1074
+ const resolveParent = (sid) => {
1075
+ try {
1076
+ const session = props.api.session.get(sid);
1077
+ const parentID = session?.parentID;
1078
+ if (parentID) return {
1079
+ parentSid: parentID,
1080
+ isChild: true
1081
+ };
1082
+ } catch {
1083
+ }
1084
+ return {
1085
+ parentSid: sid,
1086
+ isChild: false
1087
+ };
1088
+ };
1089
+ const loadEntries = (sid) => {
1090
+ const m = /* @__PURE__ */ new Map();
1091
+ try {
1092
+ const {
1093
+ parentSid,
1094
+ isChild
1095
+ } = resolveParent(sid);
1096
+ const rec = loadSessionData2()[parentSid];
1097
+ if (rec) {
1098
+ const source = isChild ? rec.children?.[sid]?.entries : rec.entries;
1099
+ if (source) {
1100
+ for (const e of source) m.set(e.id, e);
1101
+ }
1102
+ }
1103
+ } catch {
1104
+ }
1105
+ return m;
1106
+ };
1107
+ let persistTimer;
1108
+ const persistEntries = (sid, entries2) => {
1109
+ clearTimeout(persistTimer);
1110
+ persistTimer = setTimeout(() => {
1111
+ try {
1112
+ const data = loadSessionData2();
1113
+ const {
1114
+ parentSid,
1115
+ isChild
1116
+ } = resolveParent(sid);
1117
+ if (isChild) {
1118
+ if (!data[parentSid]) data[parentSid] = {
1119
+ ts: Date.now(),
1120
+ entries: [],
1121
+ scroll: 0,
1122
+ expanded: "",
1123
+ children: {}
1124
+ };
1125
+ if (!data[parentSid].children) data[parentSid].children = {};
1126
+ if (!data[parentSid].children[sid]) data[parentSid].children[sid] = {
1127
+ scroll: 0,
1128
+ expanded: "",
1129
+ entries: []
1130
+ };
1131
+ data[parentSid].children[sid] = {
1132
+ ...data[parentSid].children[sid],
1133
+ entries: [...entries2.values()]
1134
+ };
1135
+ } else {
1136
+ data[sid] = {
1137
+ ...data[sid],
1138
+ ts: Date.now(),
1139
+ entries: [...entries2.values()],
1140
+ children: data[sid]?.children ?? {}
1141
+ };
1142
+ }
1143
+ saveSessionData2(data);
1144
+ } catch {
1145
+ }
1146
+ }, 200);
1147
+ };
1148
+ const persistScroll = (sid, scroll) => {
1149
+ try {
1150
+ const data = loadSessionData2();
1151
+ const {
1152
+ parentSid,
1153
+ isChild
1154
+ } = resolveParent(sid);
1155
+ if (isChild) {
1156
+ if (!data[parentSid]) data[parentSid] = {
1157
+ ts: Date.now(),
1158
+ entries: [],
1159
+ scroll: 0,
1160
+ expanded: "",
1161
+ children: {}
1162
+ };
1163
+ if (!data[parentSid].children) data[parentSid].children = {};
1164
+ if (!data[parentSid].children[sid]) data[parentSid].children[sid] = {
1165
+ scroll: 0,
1166
+ expanded: "",
1167
+ entries: []
1168
+ };
1169
+ data[parentSid].children[sid] = {
1170
+ ...data[parentSid].children[sid],
1171
+ scroll
1172
+ };
1173
+ } else {
1174
+ data[sid] = {
1175
+ ...data[sid],
1176
+ ts: Date.now(),
1177
+ scroll,
1178
+ children: data[sid]?.children ?? {}
1179
+ };
1180
+ }
1181
+ saveSessionData2(data);
1182
+ } catch {
1183
+ }
1184
+ };
1185
+ const persistExpanded = (sid, expanded2) => {
1186
+ try {
1187
+ const data = loadSessionData2();
1188
+ const {
1189
+ parentSid,
1190
+ isChild
1191
+ } = resolveParent(sid);
1192
+ if (isChild) {
1193
+ if (!data[parentSid]) data[parentSid] = {
1194
+ ts: Date.now(),
1195
+ entries: [],
1196
+ scroll: 0,
1197
+ expanded: "",
1198
+ children: {}
1199
+ };
1200
+ if (!data[parentSid].children) data[parentSid].children = {};
1201
+ if (!data[parentSid].children[sid]) data[parentSid].children[sid] = {
1202
+ scroll: 0,
1203
+ expanded: "",
1204
+ entries: []
1205
+ };
1206
+ data[parentSid].children[sid] = {
1207
+ ...data[parentSid].children[sid],
1208
+ expanded: expanded2
1209
+ };
1210
+ } else {
1211
+ data[sid] = {
1212
+ ...data[sid],
1213
+ ts: Date.now(),
1214
+ expanded: expanded2,
1215
+ children: data[sid]?.children ?? {}
1216
+ };
1217
+ }
1218
+ saveSessionData2(data);
1219
+ } catch {
1220
+ }
1221
+ };
1222
+ const cleanupOldSessions = () => {
1223
+ if (ttlDays <= 0) return;
1224
+ try {
1225
+ const data = loadSessionData2();
1226
+ const cutoff = Date.now() - TTL_MS;
1227
+ let changed = false;
1228
+ for (const sid of Object.keys(data)) {
1229
+ if (data[sid].ts < cutoff) {
1230
+ delete data[sid];
1231
+ changed = true;
1232
+ }
1233
+ }
1234
+ if (changed) saveSessionData2(data);
1235
+ } catch {
1236
+ }
1237
+ };
1238
+ cleanupOldSessions();
1239
+ const [entryMap, setEntryMapRaw] = createSignal2(loadEntries(props.sessionId));
1240
+ const setEntryMap = (arg) => {
1241
+ setEntryMapRaw((prev) => {
1242
+ const next = typeof arg === "function" ? arg(prev) : arg;
1243
+ let needsImmediateFlush = false;
1244
+ for (const [id, entry] of next) {
1245
+ const prevEntry = prev.get(id);
1246
+ if (prevEntry?.status === "running" && (entry.status === "done" || entry.status === "error" || entry.status === "cancelled")) {
1247
+ needsImmediateFlush = true;
1248
+ break;
1249
+ }
1250
+ }
1251
+ if (needsImmediateFlush) {
1252
+ clearTimeout(persistTimer);
1253
+ try {
1254
+ const data = loadSessionData2();
1255
+ const {
1256
+ parentSid,
1257
+ isChild
1258
+ } = resolveParent(props.sessionId);
1259
+ if (isChild) {
1260
+ if (!data[parentSid]) data[parentSid] = {
1261
+ ts: Date.now(),
1262
+ entries: [],
1263
+ scroll: 0,
1264
+ expanded: "",
1265
+ children: {}
1266
+ };
1267
+ if (!data[parentSid].children) data[parentSid].children = {};
1268
+ if (!data[parentSid].children[props.sessionId]) data[parentSid].children[props.sessionId] = {
1269
+ scroll: 0,
1270
+ expanded: "",
1271
+ entries: []
1272
+ };
1273
+ data[parentSid].children[props.sessionId] = {
1274
+ ...data[parentSid].children[props.sessionId],
1275
+ entries: [...next.values()]
1276
+ };
1277
+ } else {
1278
+ data[props.sessionId] = {
1279
+ ...data[props.sessionId],
1280
+ ts: Date.now(),
1281
+ entries: [...next.values()],
1282
+ children: data[props.sessionId]?.children ?? {}
1283
+ };
1284
+ }
1285
+ saveSessionData2(data);
1286
+ } catch {
1287
+ }
1288
+ } else {
1289
+ persistEntries(props.sessionId, next);
1290
+ }
1291
+ globalEntryCache.set(props.sessionId, new Map(next));
1292
+ return next;
1293
+ });
1294
+ };
1295
+ const [panelWidth, setPanelWidth] = createSignal2(28);
1296
+ const [open, setOpen] = createSignal2((() => {
1297
+ try {
1298
+ return props.api.kv.get(`${KV_PREFIX}.open`, true);
1299
+ } catch {
1300
+ return true;
1301
+ }
1302
+ })());
1303
+ const [expanded, setExpanded] = createSignal2((() => {
1304
+ try {
1305
+ const {
1306
+ parentSid,
1307
+ isChild
1308
+ } = resolveParent(props.sessionId);
1309
+ const rec = loadSessionData2()[parentSid];
1310
+ if (rec) return isChild ? rec.children?.[props.sessionId]?.expanded || void 0 : rec.expanded || void 0;
1311
+ } catch {
1312
+ }
1313
+ return void 0;
1314
+ })());
1315
+ const [hoveredOpen, setHoveredOpen] = createSignal2(void 0);
1316
+ const [hoveredDismiss, setHoveredDismiss] = createSignal2(void 0);
1317
+ const [hoveredCancel, setHoveredCancel] = createSignal2(void 0);
1318
+ const [hoveredTop, setHoveredTop] = createSignal2(false);
1319
+ const [hoveredMoreAbove, setHoveredMoreAbove] = createSignal2(false);
1320
+ const [hoveredMoreBelow, setHoveredMoreBelow] = createSignal2(false);
1321
+ const [scrollOffset, setScrollOffset] = createSignal2((() => {
1322
+ try {
1323
+ const {
1324
+ parentSid,
1325
+ isChild
1326
+ } = resolveParent(props.sessionId);
1327
+ const rec = loadSessionData2()[parentSid];
1328
+ return isChild ? rec?.children?.[props.sessionId]?.scroll ?? 0 : rec?.scroll ?? 0;
1329
+ } catch {
1330
+ return 0;
1331
+ }
1332
+ })());
1333
+ const [now, setNow] = createSignal2(Date.now());
1334
+ const [renderTick, setRenderTick] = createSignal2(0);
1335
+ let boxEl;
1336
+ let disposed = false;
1337
+ const upsertEntry = (partial) => {
1338
+ setEntryMap((prev) => {
1339
+ const existing = prev.get(partial.id);
1340
+ const next = new Map(prev);
1341
+ const nowTs = Date.now();
1342
+ const e = partial.status;
1343
+ const ended = e === "done" || e === "error" || e === "cancelled";
1344
+ next.set(partial.id, {
1345
+ ...existing ?? {
1346
+ startedAt: nowTs
1347
+ },
1348
+ ...partial,
1349
+ startedAt: existing?.startedAt || partial.startedAt || nowTs,
1350
+ endedAt: ended ? existing?.endedAt || nowTs : void 0
1351
+ });
1352
+ return next;
1353
+ });
1354
+ };
1355
+ const isDescendantOf = (childId, rootId) => {
1356
+ const visited = /* @__PURE__ */ new Set();
1357
+ try {
1358
+ let current = props.api.session.get(childId);
1359
+ while (current?.parentID) {
1360
+ if (visited.has(current.id)) return false;
1361
+ visited.add(current.id);
1362
+ if (current.parentID === rootId) return true;
1363
+ current = props.api.session.get(current.parentID);
1364
+ }
1365
+ } catch {
1366
+ }
1367
+ return false;
1368
+ };
1369
+ const settleOnIdle = (entry) => {
1370
+ if (entry.status === "cancel_requested" && entry.abortAccepted) return "cancelled";
1371
+ return "done";
1372
+ };
1373
+ const cancelEntry = async (entry) => {
1374
+ const childId = entry.sessionId;
1375
+ if (!childId) {
1376
+ props.api.ui.toast(t("cancel.label") + ": " + t("cancel.no_session"), {
1377
+ title: entry.title || entry.agent
1378
+ });
1379
+ return;
1380
+ }
1381
+ try {
1382
+ const child = props.api.session.get(childId);
1383
+ if (!child?.parentID) {
1384
+ props.api.ui.toast(t("cancel.label") + ": " + t("cancel.not_child"), {
1385
+ title: entry.title || entry.agent
1386
+ });
1387
+ return;
1388
+ }
1389
+ } catch {
1390
+ props.api.ui.toast(t("cancel.label") + ": " + t("cancel.read_error"), {
1391
+ title: entry.title || entry.agent
1392
+ });
1393
+ return;
1394
+ }
1395
+ if (!isDescendantOf(childId, props.sessionId)) {
1396
+ props.api.ui.toast(t("cancel.label") + ": " + t("cancel.outside_tree"), {
1397
+ title: entry.title || entry.agent
1398
+ });
1399
+ return;
1400
+ }
1401
+ try {
1402
+ const st = props.api.session.status(childId);
1403
+ if (st?.type !== "busy") {
1404
+ const tokens = props.api.usage.readSessionTokens(childId);
1405
+ const cost = props.api.usage.readSessionCost(childId);
1406
+ upsertEntry({
1407
+ id: entry.id,
1408
+ title: entry.title,
1409
+ agent: entry.agent,
1410
+ prompt: entry.prompt,
1411
+ status: "done",
1412
+ sessionId: entry.sessionId,
1413
+ tokens,
1414
+ cost
1415
+ });
1416
+ return;
1417
+ }
1418
+ } catch {
1419
+ props.api.ui.toast(t("cancel.label") + ": " + t("cancel.status_error"), {
1420
+ title: entry.title || entry.agent
1421
+ });
1422
+ return;
1423
+ }
1424
+ upsertEntry({
1425
+ id: entry.id,
1426
+ title: entry.title,
1427
+ agent: entry.agent,
1428
+ prompt: entry.prompt,
1429
+ status: "cancel_requested",
1430
+ sessionId: entry.sessionId,
1431
+ cancelRequestedAt: Date.now(),
1432
+ abortAccepted: false,
1433
+ cancelReason: "manual"
1434
+ });
1435
+ try {
1436
+ await props.api.client.abort({
1437
+ sessionID: childId
1438
+ });
1439
+ upsertEntry({
1440
+ id: entry.id,
1441
+ title: entry.title,
1442
+ agent: entry.agent,
1443
+ prompt: entry.prompt,
1444
+ status: "cancel_requested",
1445
+ sessionId: entry.sessionId,
1446
+ abortAccepted: true
1447
+ });
1448
+ props.api.ui.toast(t("cancel.label") + ": " + t("cancel.sent"));
1449
+ } catch (err) {
1450
+ upsertEntry({
1451
+ id: entry.id,
1452
+ title: entry.title,
1453
+ agent: entry.agent,
1454
+ prompt: entry.prompt,
1455
+ status: "error",
1456
+ sessionId: entry.sessionId,
1457
+ error: String(err)
1458
+ });
1459
+ props.api.ui.toast(t("cancel.label") + ": " + t("cancel.failed"), {
1460
+ title: entry.title || entry.agent
1461
+ });
1462
+ }
1463
+ };
1464
+ const handlePartUpdated = (event) => {
1465
+ const part = event.payload?.part;
1466
+ if (!part) return;
1467
+ if (part.type === "subtask") {
1468
+ const agent = String(part.agent ?? "?");
1469
+ const prompt = String(part.prompt ?? "");
1470
+ const desc = String(part.description ?? "");
1471
+ const title = desc || truncate(prompt.replace(/\n/g, " ").replace(/\s+/g, " ").trim(), 40);
1472
+ const id = `sub:${String(part.id ?? crypto.randomUUID())}`;
1473
+ const subSid = part.sessionID !== void 0 ? String(part.sessionID) : void 0;
1474
+ const partModel = part.model;
1475
+ const modelId = partModel?.modelID ? String(partModel.modelID) : void 0;
1476
+ upsertEntry({
1477
+ id,
1478
+ title,
1479
+ agent,
1480
+ prompt,
1481
+ sessionId: subSid,
1482
+ status: "running",
1483
+ model: modelId
1484
+ });
1485
+ }
1486
+ if (part.type === "tool") {
1487
+ const tool = String(part.tool ?? "");
1488
+ if (!SUBAGENT_TOOLS.has(tool)) return;
1489
+ const st = part.state;
1490
+ const rawStatus = String(st?.status ?? "");
1491
+ if (rawStatus === "pending" || rawStatus === "") return;
1492
+ if (rawStatus === "error") {
1493
+ const id2 = `tool:${String(part.id ?? "")}`;
1494
+ if (!part.id) return;
1495
+ const existing = entryMap().get(id2);
1496
+ if (existing) {
1497
+ upsertEntry({
1498
+ id: id2,
1499
+ title: existing.title,
1500
+ agent: existing.agent,
1501
+ prompt: existing.prompt,
1502
+ status: "error"
1503
+ });
1504
+ }
1505
+ return;
1506
+ }
1507
+ const input = st?.input;
1508
+ let status = "running";
1509
+ if (rawStatus === "completed") status = "done";
1510
+ if ((input?.run_in_background === true || input?.background === true) && status === "done") {
1511
+ const stMetaCheck = st?.metadata;
1512
+ const hasChild = stMetaCheck?.session_id !== void 0 || stMetaCheck?.sessionId !== void 0;
1513
+ if (hasChild) status = "running";
1514
+ }
1515
+ const agent = String(part.subagent_type ?? input?.subagent_type ?? input?.category ?? tool);
1516
+ const prompt = String(input?.prompt ?? part.description ?? "");
1517
+ const desc = input?.description !== void 0 ? String(input.description) : "";
1518
+ const title = desc || truncate(prompt.replace(/\n/g, " ").replace(/\s+/g, " ").trim(), 40);
1519
+ const id = `tool:${String(part.id ?? crypto.randomUUID())}`;
1520
+ const stMeta = st?.metadata;
1521
+ const subSid = stMeta?.session_id !== void 0 ? String(stMeta.session_id) : stMeta?.sessionId !== void 0 ? String(stMeta.sessionId) : void 0;
1522
+ upsertEntry({
1523
+ id,
1524
+ title,
1525
+ agent,
1526
+ prompt,
1527
+ sessionId: subSid,
1528
+ status
1529
+ });
1530
+ }
1531
+ };
1532
+ const handleSessionEnd = (event, status) => {
1533
+ const props_ = event.payload;
1534
+ const sid = String(props_?.sessionID ?? "");
1535
+ if (!sid) return;
1536
+ const sessionTokens = props.api.usage.readSessionTokens(sid);
1537
+ const sessionCost = props.api.usage.readSessionCost(sid);
1538
+ const sessionModel = props.api.usage.readSessionModel(sid);
1539
+ const sessionTodo = props.api.usage.readSessionTodo(sid);
1540
+ let sessionAgent;
1541
+ let errorMsg;
1542
+ try {
1543
+ const s = props.api.session.get(sid);
1544
+ sessionAgent = s?.agent;
1545
+ if (status === "error") {
1546
+ const evtErr = props_?.error;
1547
+ errorMsg = safeErrorMsg(evtErr) || safeErrorMsg(props_?.message);
1548
+ if (!errorMsg) {
1549
+ const msgs = props.api.session.messages(sid);
1550
+ if (msgs) {
1551
+ for (let i = msgs.length - 1; i >= 0; i--) {
1552
+ const m = msgs[i];
1553
+ if (m.role === "assistant" && m.error) {
1554
+ errorMsg = safeErrorMsg(m.error);
1555
+ break;
1556
+ }
1557
+ }
1558
+ }
1559
+ }
1560
+ }
1561
+ } catch {
1562
+ }
1563
+ const tryMatchAndUpdate = (entriesMap, targetSid, targetStatus, nowTs) => {
1564
+ for (const [, entry] of entriesMap) {
1565
+ if (entry.sessionId === targetSid && (entry.status === "running" || entry.status === "cancel_requested")) {
1566
+ const finalStatus = targetStatus === "error" ? "error" : settleOnIdle(entry);
1567
+ entry.status = finalStatus;
1568
+ entry.endedAt = nowTs;
1569
+ entry.tokens = entry.tokens ?? sessionTokens;
1570
+ entry.cost = entry.cost ?? sessionCost;
1571
+ entry.model = entry.model ?? sessionModel;
1572
+ entry.todoTotal = entry.todoTotal ?? sessionTodo?.total;
1573
+ entry.todoDone = entry.todoDone ?? sessionTodo?.done;
1574
+ entry.error = errorMsg || entry.error;
1575
+ return true;
1576
+ }
1577
+ }
1578
+ if (sessionAgent) {
1579
+ const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9-]/g, "");
1580
+ const saNorm = normalize(sessionAgent);
1581
+ let best = null;
1582
+ for (const [, entry] of entriesMap) {
1583
+ if (entry.status !== "running" && entry.status !== "cancel_requested") continue;
1584
+ const eaNorm = normalize(entry.agent);
1585
+ if (!eaNorm || !saNorm) continue;
1586
+ if (!eaNorm.includes(saNorm) && !saNorm.includes(eaNorm)) continue;
1587
+ const gap = nowTs - (entry.startedAt || 0);
1588
+ if (!best || gap > best.gap) best = {
1589
+ entry,
1590
+ gap
1591
+ };
1592
+ }
1593
+ if (!best) {
1594
+ for (const [, entry] of entriesMap) {
1595
+ if (entry.status !== "running" && entry.status !== "cancel_requested") continue;
1596
+ if (entry.sessionId) continue;
1597
+ const gap = nowTs - (entry.startedAt || 0);
1598
+ if (!best || gap > best.gap) best = {
1599
+ entry,
1600
+ gap
1601
+ };
1602
+ }
1603
+ }
1604
+ if (best) {
1605
+ const finalStatus = targetStatus === "error" ? "error" : settleOnIdle(best.entry);
1606
+ best.entry.status = finalStatus;
1607
+ best.entry.endedAt = nowTs;
1608
+ best.entry.tokens = best.entry.tokens ?? sessionTokens;
1609
+ best.entry.cost = best.entry.cost ?? sessionCost;
1610
+ best.entry.model = best.entry.model ?? sessionModel;
1611
+ best.entry.todoTotal = best.entry.todoTotal ?? sessionTodo?.total;
1612
+ best.entry.todoDone = best.entry.todoDone ?? sessionTodo?.done;
1613
+ best.entry.sessionId = targetSid;
1614
+ best.entry.error = errorMsg || best.entry.error;
1615
+ return true;
1616
+ }
1617
+ }
1618
+ return false;
1619
+ };
1620
+ setEntryMap((prev) => {
1621
+ let changed = false;
1622
+ const next = new Map(prev);
1623
+ for (const [id, entry] of next) {
1624
+ if (entry.sessionId !== sid) continue;
1625
+ if (entry.status !== "running" && entry.status !== "done" && entry.status !== "cancel_requested") continue;
1626
+ if (sid === props.sessionId) continue;
1627
+ const alreadySettled = entry.status !== "running" && entry.status !== "cancel_requested";
1628
+ const finalStatus = status === "error" ? "error" : settleOnIdle(entry);
1629
+ next.set(id, {
1630
+ ...entry,
1631
+ ...alreadySettled ? {} : {
1632
+ status: finalStatus,
1633
+ endedAt: Date.now()
1634
+ },
1635
+ tokens: entry.tokens ?? sessionTokens,
1636
+ cost: entry.cost ?? sessionCost,
1637
+ model: entry.model ?? sessionModel,
1638
+ todoTotal: entry.todoTotal ?? sessionTodo?.total,
1639
+ todoDone: entry.todoDone ?? sessionTodo?.done,
1640
+ error: errorMsg || entry.error
1641
+ });
1642
+ changed = true;
1643
+ }
1644
+ if (!changed && sessionAgent) {
1645
+ const nowTs = Date.now();
1646
+ const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9-]/g, "");
1647
+ const saNorm = normalize(sessionAgent);
1648
+ let best = null;
1649
+ for (const [id, entry] of next) {
1650
+ if (entry.status !== "running" && entry.status !== "cancel_requested") continue;
1651
+ const eaNorm = normalize(entry.agent);
1652
+ if (!eaNorm || !saNorm) continue;
1653
+ if (!eaNorm.includes(saNorm) && !saNorm.includes(eaNorm)) continue;
1654
+ const gap = nowTs - (entry.startedAt || 0);
1655
+ if (!best || gap > best.gap) best = {
1656
+ id,
1657
+ gap
1658
+ };
1659
+ }
1660
+ if (!best) {
1661
+ for (const [id, entry] of next) {
1662
+ if (entry.status !== "running" && entry.status !== "cancel_requested") continue;
1663
+ if (entry.sessionId) continue;
1664
+ const gap = nowTs - (entry.startedAt || 0);
1665
+ if (!best || gap > best.gap) best = {
1666
+ id,
1667
+ gap
1668
+ };
1669
+ }
1670
+ }
1671
+ if (best) {
1672
+ const entry = next.get(best.id);
1673
+ const finalStatus = status === "error" ? "error" : settleOnIdle(entry);
1674
+ next.set(best.id, {
1675
+ ...entry,
1676
+ status: finalStatus,
1677
+ endedAt: nowTs,
1678
+ tokens: sessionTokens || entry.tokens,
1679
+ cost: sessionCost || entry.cost,
1680
+ sessionId: sid,
1681
+ error: errorMsg || entry.error
1682
+ });
1683
+ changed = true;
1684
+ }
1685
+ }
1686
+ return changed ? next : prev;
1687
+ });
1688
+ try {
1689
+ const sessionObj = props.api.session.get(sid);
1690
+ const parentSid = sessionObj?.parentID;
1691
+ if (parentSid && parentSid !== props.sessionId) {
1692
+ const parentCache = globalEntryCache.get(parentSid);
1693
+ const nowTs = Date.now();
1694
+ let found = false;
1695
+ if (parentCache) {
1696
+ found = tryMatchAndUpdate(parentCache, sid, status, nowTs);
1697
+ }
1698
+ if (!found) {
1699
+ const data = loadSessionData2();
1700
+ const rec = data[parentSid];
1701
+ if (rec?.entries) {
1702
+ const fallbackMap = new Map(rec.entries.map((e) => [e.id, e]));
1703
+ found = tryMatchAndUpdate(fallbackMap, sid, status, nowTs);
1704
+ if (found) {
1705
+ data[parentSid] = {
1706
+ ...rec,
1707
+ ts: nowTs,
1708
+ entries: [...fallbackMap.values()]
1709
+ };
1710
+ saveSessionData2(data);
1711
+ globalEntryCache.set(parentSid, fallbackMap);
1712
+ }
1713
+ }
1714
+ }
1715
+ if (found && parentCache) {
1716
+ const data = loadSessionData2();
1717
+ data[parentSid] = {
1718
+ ...data[parentSid],
1719
+ ts: nowTs,
1720
+ entries: [...parentCache.values()]
1721
+ };
1722
+ saveSessionData2(data);
1723
+ }
1724
+ }
1725
+ } catch {
1726
+ }
1727
+ setTimeout(() => {
1728
+ if (disposed) return;
1729
+ const finalTokens = props.api.usage.readSessionTokens(sid);
1730
+ const finalCost = props.api.usage.readSessionCost(sid);
1731
+ const finalModel = props.api.usage.readSessionModel(sid);
1732
+ const finalTodo = props.api.usage.readSessionTodo(sid);
1733
+ setEntryMap((prev) => {
1734
+ let changed = false;
1735
+ const next = new Map(prev);
1736
+ for (const [id, entry] of next) {
1737
+ if (entry.sessionId !== sid) continue;
1738
+ const t2 = finalTokens ?? entry.tokens;
1739
+ const c = finalCost ?? entry.cost;
1740
+ const m = finalModel ?? entry.model;
1741
+ const tt = finalTodo?.total ?? entry.todoTotal;
1742
+ const td = finalTodo?.done ?? entry.todoDone;
1743
+ if (t2 !== entry.tokens || c !== entry.cost || m !== entry.model || tt !== entry.todoTotal || td !== entry.todoDone) {
1744
+ next.set(id, {
1745
+ ...entry,
1746
+ tokens: t2,
1747
+ cost: c,
1748
+ model: m,
1749
+ todoTotal: tt,
1750
+ todoDone: td
1751
+ });
1752
+ changed = true;
1753
+ }
1754
+ }
1755
+ return changed ? next : prev;
1756
+ });
1757
+ bump();
1758
+ }, 150);
1759
+ };
1760
+ const bump = () => setRenderTick((v) => v + 1);
1761
+ onMount(() => {
1762
+ const clock = setInterval(() => {
1763
+ setNow(Date.now());
1764
+ bump();
1765
+ }, 100);
1766
+ const tokenTimer = setInterval(() => {
1767
+ untrack(() => {
1768
+ setEntryMapRaw((prev) => {
1769
+ let changed = false;
1770
+ const next = new Map(prev);
1771
+ for (const [id, entry] of next) {
1772
+ if (entry.status === "running" && entry.sessionId) {
1773
+ let isChild = false;
1774
+ try {
1775
+ const s = props.api.session.get(entry.sessionId);
1776
+ isChild = s?.parentID === props.sessionId;
1777
+ } catch {
1778
+ }
1779
+ if (!isChild) continue;
1780
+ const total = props.api.usage.readSessionTokens(entry.sessionId);
1781
+ const todo = props.api.usage.readSessionTodo(entry.sessionId);
1782
+ const model = entry.model ?? props.api.usage.readSessionModel(entry.sessionId);
1783
+ const nextEntry = {
1784
+ ...entry
1785
+ };
1786
+ if (total !== void 0 && total !== entry.tokens) {
1787
+ nextEntry.tokens = total;
1788
+ changed = true;
1789
+ }
1790
+ if (todo !== void 0) {
1791
+ if (todo.total !== entry.todoTotal || todo.done !== entry.todoDone) {
1792
+ nextEntry.todoTotal = todo.total;
1793
+ nextEntry.todoDone = todo.done;
1794
+ changed = true;
1795
+ }
1796
+ }
1797
+ if (model && !entry.model) {
1798
+ nextEntry.model = model;
1799
+ changed = true;
1800
+ }
1801
+ if (changed) next.set(id, nextEntry);
1802
+ }
1803
+ }
1804
+ return changed ? next : prev;
1805
+ });
1806
+ });
1807
+ bump();
1808
+ }, 500);
1809
+ bump();
1810
+ const unsubPart = props.api.event.on("part.updated", (e) => {
1811
+ handlePartUpdated(e);
1812
+ bump();
1813
+ });
1814
+ const unsubMsg = props.api.event.on("message.updated", () => bump());
1815
+ const unsubIdle = props.api.event.on("session.idle", (e) => {
1816
+ handleSessionEnd(e, "done");
1817
+ bump();
1818
+ });
1819
+ const unsubError = props.api.event.on("session.error", (e) => {
1820
+ handleSessionEnd(e, "error");
1821
+ bump();
1822
+ });
1823
+ onCleanup(() => {
1824
+ disposed = true;
1825
+ clearInterval(clock);
1826
+ clearInterval(tokenTimer);
1827
+ unsubPart();
1828
+ unsubMsg();
1829
+ unsubIdle();
1830
+ unsubError();
1831
+ });
1832
+ });
1833
+ let lastSid = props.sessionId;
1834
+ let lastTick = 0;
1835
+ createEffect(() => {
1836
+ const sid = props.sessionId;
1837
+ const switched = sid !== lastSid;
1838
+ lastSid = sid;
1839
+ const tick = clearTick();
1840
+ const forceReload = tick !== lastTick && !switched;
1841
+ lastTick = tick;
1842
+ const t2 = setTimeout(() => {
1843
+ untrack(() => {
1844
+ if (switched) {
1845
+ const {
1846
+ parentSid,
1847
+ isChild
1848
+ } = resolveParent(sid);
1849
+ const data = loadSessionData2();
1850
+ const saved = isChild ? data[parentSid]?.children?.[sid]?.scroll ?? 0 : data[sid]?.scroll ?? 0;
1851
+ setScrollOffset(saved);
1852
+ if (!isChild && data[sid]?.entries?.length) {
1853
+ data[sid].ts = Date.now();
1854
+ saveSessionData2(data);
1855
+ }
1856
+ }
1857
+ setEntryMapRaw((prev) => {
1858
+ const next = switched || forceReload ? new Map(globalEntryCache.get(sid) ?? loadEntries(sid)) : new Map(prev);
1859
+ const {
1860
+ parentSid: scanPSid,
1861
+ isChild: scanChild
1862
+ } = resolveParent(sid);
1863
+ const scanRec = loadSessionData2()[scanPSid];
1864
+ const clearedIds = new Set(scanChild ? scanRec?.children?.[sid]?.clearedIds : scanRec?.clearedIds);
1865
+ try {
1866
+ const msgs = props.api.session.messages(sid);
1867
+ if (msgs && msgs.length) {
1868
+ for (const msg of msgs) {
1869
+ const parts = props.api.session.part(msg.id) ?? [];
1870
+ for (const partRaw of parts) {
1871
+ const part = partRaw;
1872
+ if (part.type === "tool") {
1873
+ const tool = String(part.tool ?? "");
1874
+ if (!SUBAGENT_TOOLS.has(tool)) continue;
1875
+ const id = `tool:${String(part.id ?? "")}`;
1876
+ if (!part.id) continue;
1877
+ const st = part.state;
1878
+ const rawStatus = String(st?.status ?? "");
1879
+ const exists = next.get(id);
1880
+ if (!exists && clearedIds.has(id)) continue;
1881
+ if ((rawStatus === "pending" || rawStatus === "") && !exists) continue;
1882
+ if (rawStatus === "error") {
1883
+ if (exists && exists.status === "running") {
1884
+ next.set(id, {
1885
+ ...exists,
1886
+ status: "error",
1887
+ endedAt: Date.now()
1888
+ });
1889
+ }
1890
+ continue;
1891
+ }
1892
+ let status = "running";
1893
+ if (rawStatus === "completed") status = "done";
1894
+ if ((st?.input?.run_in_background === true || st?.input?.background === true) && status === "done") {
1895
+ const scanStMeta = st?.metadata;
1896
+ const scanHasChild = scanStMeta?.session_id !== void 0 || scanStMeta?.sessionId !== void 0;
1897
+ if (scanHasChild) status = "running";
1898
+ }
1899
+ if (exists && exists.status !== "running" && exists.status !== "cancel_requested") continue;
1900
+ if (exists && status === "running") {
1901
+ if (!rawStatus) {
1902
+ const msgTokens = msg?.tokens;
1903
+ if (msgTokens && (Number(msgTokens.input) > 0 || Number(msgTokens.output) > 0)) {
1904
+ status = "done";
1905
+ } else if (Date.now() - exists.startedAt > 30 * 60 * 1e3) {
1906
+ status = "done";
1907
+ } else {
1908
+ continue;
1909
+ }
1910
+ } else {
1911
+ continue;
1912
+ }
1913
+ }
1914
+ const input = st?.input;
1915
+ const agent = String(part.subagent_type ?? input?.subagent_type ?? tool);
1916
+ const prompt = String(input?.prompt ?? part.description ?? "");
1917
+ const desc = input?.description !== void 0 ? String(input.description) : "";
1918
+ const title = desc || truncate(prompt.replace(/\n/g, " ").trim(), 40);
1919
+ let tokens;
1920
+ const scanStMeta2 = st?.metadata;
1921
+ const scanSubSid = scanStMeta2?.session_id !== void 0 ? String(scanStMeta2.session_id) : scanStMeta2?.sessionId !== void 0 ? String(scanStMeta2.sessionId) : void 0;
1922
+ if (scanSubSid) tokens = props.api.usage.readSessionTokens(scanSubSid);
1923
+ const ended = status === "done";
1924
+ next.set(id, {
1925
+ id,
1926
+ title,
1927
+ agent,
1928
+ prompt,
1929
+ // Preserve existing values (from handleSessionEnd / KV) — scan must not overwrite
1930
+ tokens: exists?.tokens ?? tokens,
1931
+ sessionId: exists?.sessionId ?? scanSubSid,
1932
+ status,
1933
+ startedAt: exists?.startedAt || Date.now(),
1934
+ endedAt: ended ? exists?.endedAt || Date.now() : void 0
1935
+ });
1936
+ }
1937
+ }
1938
+ }
1939
+ }
1940
+ } catch {
1941
+ }
1942
+ return next;
1943
+ });
1944
+ setEntryMapRaw((prev) => {
1945
+ let changed = false;
1946
+ const next = new Map(prev);
1947
+ for (const [id, entry] of next) {
1948
+ if (entry.status !== "running" && entry.status !== "cancel_requested" || !entry.sessionId) continue;
1949
+ try {
1950
+ const st = props.api.session.status(entry.sessionId);
1951
+ if (!st || st.type !== "idle") continue;
1952
+ const tokens = props.api.usage.readSessionTokens(entry.sessionId);
1953
+ const cost = props.api.usage.readSessionCost(entry.sessionId);
1954
+ const finalStatus = entry.status === "cancel_requested" && entry.abortAccepted ? "cancelled" : "done";
1955
+ next.set(id, {
1956
+ ...entry,
1957
+ status: finalStatus,
1958
+ endedAt: Date.now(),
1959
+ tokens: tokens ?? entry.tokens,
1960
+ cost: cost ?? entry.cost
1961
+ });
1962
+ changed = true;
1963
+ } catch {
1964
+ }
1965
+ }
1966
+ return changed ? next : prev;
1967
+ });
1968
+ bump();
1969
+ });
1970
+ }, 150);
1971
+ onCleanup(() => clearTimeout(t2));
1972
+ });
1973
+ const pal = createMemo(() => {
1974
+ const th = props.theme;
1975
+ const sat = (k, fb) => desaturateTo(th[k], MAX_SAT, fb);
1976
+ return {
1977
+ primary: sat("primary", FALLBACK.primary),
1978
+ text: sat("text", FALLBACK.text),
1979
+ muted: sat("textMuted", FALLBACK.muted),
1980
+ success: sat("success", FALLBACK.success),
1981
+ warning: sat("warning", FALLBACK.warning),
1982
+ error: sat("error", FALLBACK.error),
1983
+ border: sat("border", FALLBACK.border)
1984
+ };
1985
+ });
1986
+ const entryList = createMemo(() => {
1987
+ const entries2 = [...entryMap().values()];
1988
+ if (props.sortOrder() === "desc") {
1989
+ return entries2.sort((a, b) => b.startedAt - a.startedAt);
1990
+ }
1991
+ return entries2.sort((a, b) => a.startedAt - b.startedAt);
1992
+ });
1993
+ const max = props.maxEntries;
1994
+ const clampedOffset = createMemo(() => {
1995
+ const total = entryList().length;
1996
+ const m = max();
1997
+ if (total <= m) return 0;
1998
+ return Math.min(scrollOffset(), total - m);
1999
+ });
2000
+ const visibleList = createMemo(() => entryList().slice(clampedOffset(), clampedOffset() + max()));
2001
+ const hiddenAbove = createMemo(() => clampedOffset());
2002
+ const hiddenBelow = createMemo(() => Math.max(0, entryList().length - clampedOffset() - max()));
2003
+ createEffect(() => {
2004
+ if (hiddenAbove() === 0) setHoveredMoreAbove(false);
2005
+ });
2006
+ let sortInitialized = false;
2007
+ createEffect(() => {
2008
+ props.sortOrder();
2009
+ if (!sortInitialized) {
2010
+ sortInitialized = true;
2011
+ return;
2012
+ }
2013
+ const total = untrack(() => entryList().length);
2014
+ const m = untrack(() => max());
2015
+ const target = props.sortOrder() === "desc" ? 0 : Math.max(0, total - m);
2016
+ setScrollOffset(target);
2017
+ setTimeout(() => {
2018
+ try {
2019
+ persistScroll(props.sessionId, target);
2020
+ } catch {
2021
+ }
2022
+ }, 0);
2023
+ });
2024
+ let prevEntryCount = 0;
2025
+ createEffect(() => {
2026
+ const total = entryList().length;
2027
+ if (prevEntryCount === 0) {
2028
+ prevEntryCount = total;
2029
+ return;
2030
+ }
2031
+ if (total === prevEntryCount) return;
2032
+ const m = max();
2033
+ const wasAtNewest = props.sortOrder() === "desc" ? untrack(() => scrollOffset() === 0) : untrack(() => scrollOffset() >= prevEntryCount - m);
2034
+ prevEntryCount = total;
2035
+ if (wasAtNewest) {
2036
+ const target = props.sortOrder() === "desc" ? 0 : Math.max(0, total - m);
2037
+ setScrollOffset(target);
2038
+ setTimeout(() => {
2039
+ try {
2040
+ persistScroll(props.sessionId, target);
2041
+ } catch {
2042
+ }
2043
+ }, 0);
2044
+ }
2045
+ });
2046
+ const entries = createMemo(() => {
2047
+ const nowVal = now();
2048
+ return entryList().map((e) => ({
2049
+ ...e,
2050
+ elapsed: (e.endedAt ?? nowVal) - e.startedAt
2051
+ }));
2052
+ });
2053
+ const doneCount = createMemo(() => entryList().filter((e) => e.status === "done" || e.status === "cancelled").length);
2054
+ const runningCount = createMemo(() => entryList().filter((e) => e.status === "running" || e.status === "cancel_requested").length);
2055
+ const errCount = createMemo(() => entryList().filter((e) => e.status === "error").length);
2056
+ const anyEntry = () => entryList().length > 0;
2057
+ const totalTokens = createMemo(() => {
2058
+ let sum = 0;
2059
+ for (const e of entryList()) {
2060
+ if (e.tokens) sum += e.tokens;
2061
+ }
2062
+ return sum;
2063
+ });
2064
+ const totalCost = createMemo(() => {
2065
+ let sum = 0;
2066
+ for (const e of entryList()) {
2067
+ if (e.cost) sum += e.cost;
2068
+ }
2069
+ return sum;
2070
+ });
2071
+ const toggleExpand = (id) => {
2072
+ setExpanded((prev) => {
2073
+ const next = prev === id ? void 0 : id;
2074
+ try {
2075
+ persistExpanded(props.sessionId, next ?? "");
2076
+ } catch {
2077
+ }
2078
+ return next;
2079
+ });
2080
+ };
2081
+ const sep = () => "\u2500".repeat(Math.max(1, panelWidth()));
2082
+ const expandedMaxLabelW = createMemo(() => {
2083
+ const labels = [t("agent.label"), t("status.label"), t("time.label"), t("tokens.label"), t("error.label"), t("cost.label"), t("model.label"), t("todo.label"), t("session.label")];
2084
+ return Math.max(...labels.map((l) => visualWidth(l + ": ")));
2085
+ });
2086
+ const expandedPad = (label) => Math.max(0, expandedMaxLabelW() - visualWidth(label + ": "));
2087
+ const expandedValAvail = () => Math.max(6, panelWidth() - INDENT - expandedMaxLabelW());
2088
+ const summaryParts = createMemo(() => {
2089
+ if (!anyEntry()) return null;
2090
+ const dot = "\u25CF";
2091
+ const cost = totalCost();
2092
+ return {
2093
+ done: `${dot}${doneCount()}`,
2094
+ running: runningCount() > 0 ? `${dot}${runningCount()}` : null,
2095
+ err: errCount() > 0 ? `${dot}${errCount()}` : null,
2096
+ duration: totalTokens() > 0 ? fmtTokens(totalTokens()) : "",
2097
+ cost: cost > 0 ? `$${cost.toFixed(2)}` : ""
2098
+ };
2099
+ });
2100
+ const summaryCols = createMemo(() => {
2101
+ const p = summaryParts();
2102
+ if (!p) return 0;
2103
+ let w = visualWidth(p.done);
2104
+ if (p.running) w += 1 + visualWidth(p.running);
2105
+ if (p.err) w += 1 + visualWidth(p.err);
2106
+ w += p.duration ? 1 + visualWidth(p.duration) : 0;
2107
+ w += p.cost ? 1 + visualWidth(p.cost) : 0;
2108
+ return w;
2109
+ });
2110
+ const versionText = ` v${PLUGIN_VERSION}`;
2111
+ const versionW = visualWidth(versionText);
2112
+ const showVersion = createMemo(() => {
2113
+ if (!open()) return false;
2114
+ const icon = "\u25BC";
2115
+ const need = visualWidth(icon) + 1 + visualWidth(t("panel.title")) + versionW + summaryCols();
2116
+ return need <= panelWidth();
2117
+ });
2118
+ const leftCols = createMemo(() => {
2119
+ const icon = open() ? "\u25BC" : "\u25B6";
2120
+ let w = visualWidth(icon) + 1 + visualWidth(t("panel.title"));
2121
+ if (showVersion()) w += versionW;
2122
+ return w;
2123
+ });
2124
+ const spacerCols = createMemo(() => {
2125
+ if (!anyEntry()) return 0;
2126
+ return Math.max(0, panelWidth() - leftCols() - summaryCols());
2127
+ });
2128
+ const valueCols = (label) => Math.max(4, panelWidth() - INDENT - visualWidth(label + ": "));
2129
+ return (() => {
2130
+ var _el$ = _$createElement("box"), _el$2 = _$createElement("text"), _el$3 = _$createElement("span"), _el$4 = _$createElement("span");
2131
+ _$insertNode(_el$, _el$2);
2132
+ var _ref$ = boxEl;
2133
+ typeof _ref$ === "function" ? _$use(_ref$, _el$) : boxEl = _el$;
2134
+ _$setProp(_el$, "border", false);
2135
+ _$setProp(_el$, "paddingTop", 0);
2136
+ _$setProp(_el$, "paddingBottom", 0);
2137
+ _$setProp(_el$, "paddingLeft", 0);
2138
+ _$setProp(_el$, "paddingRight", 0);
2139
+ _$setProp(_el$, "flexDirection", "column");
2140
+ _$setProp(_el$, "gap", 0);
2141
+ _$setProp(_el$, "onSizeChange", () => {
2142
+ const w = boxEl ? Math.max(20, boxEl.width ?? 0) : 28;
2143
+ setPanelWidth((prev) => prev === w ? prev : w);
2144
+ });
2145
+ _$insertNode(_el$2, _el$3);
2146
+ _$insertNode(_el$2, _el$4);
2147
+ _$setProp(_el$2, "onMouseUp", () => {
2148
+ setOpen((o) => {
2149
+ const n = !o;
2150
+ try {
2151
+ props.api.kv.set(`${KV_PREFIX}.open`, n);
2152
+ } catch {
2153
+ }
2154
+ return n;
2155
+ });
2156
+ bump();
2157
+ });
2158
+ _$insert(_el$3, () => renderTick() >= 0 && open() ? "\u25BC " : "\u25B6 ");
2159
+ _$insert(_el$4, () => t("panel.title"));
2160
+ _$insert(_el$2, _$createComponent(Show, {
2161
+ get when() {
2162
+ return showVersion();
2163
+ },
2164
+ get children() {
2165
+ var _el$5 = _$createElement("span");
2166
+ _$insert(_el$5, versionText);
2167
+ _$effect((_$p) => _$setProp(_el$5, "style", {
2168
+ fg: dimColor(pal().muted, 0.75)
2169
+ }, _$p));
2170
+ return _el$5;
2171
+ }
2172
+ }), null);
2173
+ _$insert(_el$2, (() => {
2174
+ var _c$ = _$memo(() => !!anyEntry());
2175
+ return () => _c$() ? [(() => {
2176
+ var _el$11 = _$createElement("span");
2177
+ _$insert(_el$11, () => " ".repeat(spacerCols()));
2178
+ _$effect((_$p) => _$setProp(_el$11, "style", {
2179
+ fg: pal().muted
2180
+ }, _$p));
2181
+ return _el$11;
2182
+ })(), (() => {
2183
+ var _el$12 = _$createElement("span");
2184
+ _$insert(_el$12, () => summaryParts().done);
2185
+ _$effect((_$p) => _$setProp(_el$12, "style", {
2186
+ fg: pal().success
2187
+ }, _$p));
2188
+ return _el$12;
2189
+ })(), _$memo(() => _$memo(() => runningCount() > 0)() && (() => {
2190
+ var _el$13 = _$createElement("span"), _el$14 = _$createTextNode(` `);
2191
+ _$insertNode(_el$13, _el$14);
2192
+ _$insert(_el$13, () => summaryParts().running, null);
2193
+ _$effect((_$p) => _$setProp(_el$13, "style", {
2194
+ fg: pal().warning
2195
+ }, _$p));
2196
+ return _el$13;
2197
+ })()), _$memo(() => _$memo(() => errCount() > 0)() && (() => {
2198
+ var _el$15 = _$createElement("span"), _el$16 = _$createTextNode(` `);
2199
+ _$insertNode(_el$15, _el$16);
2200
+ _$insert(_el$15, () => summaryParts().err, null);
2201
+ _$effect((_$p) => _$setProp(_el$15, "style", {
2202
+ fg: pal().error
2203
+ }, _$p));
2204
+ return _el$15;
2205
+ })()), _$memo(() => _$memo(() => !!summaryParts().duration)() ? (() => {
2206
+ var _el$17 = _$createElement("span"), _el$18 = _$createTextNode(` `);
2207
+ _$insertNode(_el$17, _el$18);
2208
+ _$insert(_el$17, () => summaryParts().duration, null);
2209
+ _$effect((_$p) => _$setProp(_el$17, "style", {
2210
+ fg: pal().muted
2211
+ }, _$p));
2212
+ return _el$17;
2213
+ })() : null), _$memo(() => _$memo(() => !!summaryParts().cost)() ? (() => {
2214
+ var _el$19 = _$createElement("span"), _el$20 = _$createTextNode(` `);
2215
+ _$insertNode(_el$19, _el$20);
2216
+ _$insert(_el$19, () => summaryParts().cost, null);
2217
+ _$effect((_$p) => _$setProp(_el$19, "style", {
2218
+ fg: pal().warning
2219
+ }, _$p));
2220
+ return _el$19;
2221
+ })() : null)] : null;
2222
+ })(), null);
2223
+ _$insert(_el$, _$createComponent(Show, {
2224
+ get when() {
2225
+ return open();
2226
+ },
2227
+ get children() {
2228
+ return [(() => {
2229
+ var _el$6 = _$createElement("text");
2230
+ _$insert(_el$6, sep);
2231
+ _$effect((_$p) => _$setProp(_el$6, "fg", pal().muted, _$p));
2232
+ return _el$6;
2233
+ })(), _$createComponent(Show, {
2234
+ get when() {
2235
+ return anyEntry();
2236
+ },
2237
+ get fallback() {
2238
+ return (() => {
2239
+ var _el$21 = _$createElement("text"), _el$22 = _$createTextNode(` &gt; `), _el$24 = _$createTextNode(` `);
2240
+ _$insertNode(_el$21, _el$22);
2241
+ _$insertNode(_el$21, _el$24);
2242
+ _$insert(_el$21, () => t("status.none"), _el$24);
2243
+ _$effect((_$p) => _$setProp(_el$21, "style", {
2244
+ fg: pal().muted
2245
+ }, _$p));
2246
+ return _el$21;
2247
+ })();
2248
+ },
2249
+ get children() {
2250
+ var _el$7 = _$createElement("box");
2251
+ _$setProp(_el$7, "onMouseScroll", (e) => {
2252
+ if (props.scrollMode() === "click") return;
2253
+ const total = entryList().length;
2254
+ const m = max();
2255
+ if (total <= m) return;
2256
+ const dir = e.button === 0 ? 1 : -1;
2257
+ setScrollOffset((prev) => {
2258
+ const next = Math.max(0, Math.min(prev + dir, total - m));
2259
+ try {
2260
+ persistScroll(props.sessionId, next);
2261
+ } catch {
2262
+ }
2263
+ return next;
2264
+ });
2265
+ });
2266
+ _$insert(_el$7, _$createComponent(Show, {
2267
+ get when() {
2268
+ return hiddenAbove() > 0;
2269
+ },
2270
+ get children() {
2271
+ var _el$8 = _$createElement("text"), _el$9 = _$createElement("span"), _el$0 = _$createTextNode(` &uarr; `), _el$10 = _$createTextNode(` `);
2272
+ _$insertNode(_el$8, _el$9);
2273
+ _$setProp(_el$8, "onMouseOver", () => setHoveredMoreAbove(true));
2274
+ _$setProp(_el$8, "onMouseOut", () => setHoveredMoreAbove(false));
2275
+ _$setProp(_el$8, "onMouseUp", () => {
2276
+ const total = entryList().length;
2277
+ const m = max();
2278
+ if (total <= m) return;
2279
+ const next = Math.max(0, scrollOffset() - m);
2280
+ if (next === 0) {
2281
+ setTimeout(() => {
2282
+ setScrollOffset(next);
2283
+ try {
2284
+ persistScroll(props.sessionId, next);
2285
+ } catch {
2286
+ }
2287
+ }, 0);
2288
+ } else {
2289
+ setScrollOffset(next);
2290
+ try {
2291
+ persistScroll(props.sessionId, next);
2292
+ } catch {
2293
+ }
2294
+ }
2295
+ });
2296
+ _$insertNode(_el$9, _el$0);
2297
+ _$insertNode(_el$9, _el$10);
2298
+ _$insert(_el$9, hiddenAbove, _el$10);
2299
+ _$insert(_el$9, () => t("scroll.more"), null);
2300
+ _$effect((_$p) => _$setProp(_el$9, "style", {
2301
+ fg: hoveredMoreAbove() ? pal().warning : pal().muted
2302
+ }, _$p));
2303
+ return _el$8;
2304
+ }
2305
+ }), null);
2306
+ _$insert(_el$7, _$createComponent(For, {
2307
+ get each() {
2308
+ return visibleList();
2309
+ },
2310
+ children: (entry) => {
2311
+ const isExpanded = () => expanded() === entry.id;
2312
+ const isRunning = entry.status === "running";
2313
+ const isCancelRequested = entry.status === "cancel_requested";
2314
+ const isCancelled = entry.status === "cancelled";
2315
+ const isError = entry.status === "error";
2316
+ const isActiveRunning = isRunning || isCancelRequested;
2317
+ const elapsed = () => (entry.endedAt ?? now()) - entry.startedAt;
2318
+ const statusDot = () => "\u25CF";
2319
+ const statusColor = () => {
2320
+ if (isCancelled) return pal().muted;
2321
+ if (!isActiveRunning) return isError ? pal().error : pal().success;
2322
+ const t2 = (Math.sin(now() % 2e3 / 2e3 * Math.PI * 2 - Math.PI / 2) + 1) / 2;
2323
+ const a = rgb(pal().muted), b = rgb(pal().warning);
2324
+ if (!a || !b) return pal().warning;
2325
+ const r = Math.round(a.r + (b.r - a.r) * t2);
2326
+ const g = Math.round(a.g + (b.g - a.g) * t2);
2327
+ const bl = Math.round(a.b + (b.b - a.b) * t2);
2328
+ return "#" + [r, g, bl].map((v) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, "0")).join("");
2329
+ };
2330
+ const timeColor = () => isActiveRunning ? pal().warning : isError ? pal().error : pal().muted;
2331
+ const tokenText = () => !isExpanded() && entry.tokens !== void 0 && entry.tokens > 0 ? ` ${fmtTokens(entry.tokens)}` : "";
2332
+ const timeText = () => !isExpanded() && (elapsed() >= 2e3 || entry.endedAt !== void 0) ? fmtDurationShort(elapsed(), isActiveRunning) : "";
2333
+ const suffixW = () => {
2334
+ let w = 0;
2335
+ const t2 = timeText();
2336
+ if (t2) w += 1 + visualWidth(t2);
2337
+ const tk = tokenText();
2338
+ if (tk) w += visualWidth(tk);
2339
+ return w;
2340
+ };
2341
+ const labelAvail = () => Math.max(6, panelWidth() - LEFT_PAD - suffixW());
2342
+ const labelText = () => {
2343
+ const max2 = labelAvail();
2344
+ const text = entry.title || entry.agent;
2345
+ const truncated = truncate(text, max2);
2346
+ const pad = Math.max(0, max2 - visualWidth(truncated));
2347
+ return truncated + " ".repeat(pad);
2348
+ };
2349
+ return [(() => {
2350
+ var _el$25 = _$createElement("text"), _el$26 = _$createElement("span"), _el$27 = _$createTextNode(` `), _el$28 = _$createElement("span"), _el$29 = _$createTextNode(` `), _el$30 = _$createElement("span");
2351
+ _$insertNode(_el$25, _el$26);
2352
+ _$insertNode(_el$25, _el$27);
2353
+ _$insertNode(_el$25, _el$28);
2354
+ _$insertNode(_el$25, _el$29);
2355
+ _$insertNode(_el$25, _el$30);
2356
+ _$setProp(_el$25, "onMouseUp", () => toggleExpand(entry.id));
2357
+ _$insert(_el$26, () => isExpanded() ? "\u25BC" : "\u25B6");
2358
+ _$insert(_el$28, statusDot);
2359
+ _$insert(_el$30, labelText);
2360
+ _$insert(_el$25, (() => {
2361
+ var _c$2 = _$memo(() => !!timeText());
2362
+ return () => _c$2() ? [" ", (() => {
2363
+ var _el$82 = _$createElement("span");
2364
+ _$insert(_el$82, timeText);
2365
+ _$effect((_$p) => _$setProp(_el$82, "style", {
2366
+ fg: timeColor()
2367
+ }, _$p));
2368
+ return _el$82;
2369
+ })()] : null;
2370
+ })(), null);
2371
+ _$insert(_el$25, (() => {
2372
+ var _c$3 = _$memo(() => !!tokenText());
2373
+ return () => _c$3() ? (() => {
2374
+ var _el$83 = _$createElement("span");
2375
+ _$insert(_el$83, tokenText);
2376
+ _$effect((_$p) => _$setProp(_el$83, "style", {
2377
+ fg: pal().muted
2378
+ }, _$p));
2379
+ return _el$83;
2380
+ })() : null;
2381
+ })(), null);
2382
+ _$effect((_p$) => {
2383
+ var _v$3 = {
2384
+ fg: pal().muted
2385
+ }, _v$4 = {
2386
+ fg: statusColor()
2387
+ }, _v$5 = {
2388
+ fg: pal().text
2389
+ };
2390
+ _v$3 !== _p$.e && (_p$.e = _$setProp(_el$26, "style", _v$3, _p$.e));
2391
+ _v$4 !== _p$.t && (_p$.t = _$setProp(_el$28, "style", _v$4, _p$.t));
2392
+ _v$5 !== _p$.a && (_p$.a = _$setProp(_el$30, "style", _v$5, _p$.a));
2393
+ return _p$;
2394
+ }, {
2395
+ e: void 0,
2396
+ t: void 0,
2397
+ a: void 0
2398
+ });
2399
+ return _el$25;
2400
+ })(), _$createComponent(Show, {
2401
+ get when() {
2402
+ return isExpanded();
2403
+ },
2404
+ get children() {
2405
+ return [(() => {
2406
+ var _el$31 = _$createElement("text"), _el$32 = _$createTextNode(` `), _el$33 = _$createElement("span"), _el$34 = _$createTextNode(`: `), _el$35 = _$createElement("span"), _el$36 = _$createElement("span");
2407
+ _$insertNode(_el$31, _el$32);
2408
+ _$insertNode(_el$31, _el$33);
2409
+ _$insertNode(_el$31, _el$35);
2410
+ _$insertNode(_el$31, _el$36);
2411
+ _$insertNode(_el$33, _el$34);
2412
+ _$insert(_el$33, () => t("agent.label"), _el$34);
2413
+ _$insert(_el$35, () => " ".repeat(expandedPad(t("agent.label"))));
2414
+ _$insert(_el$36, () => entry.agent);
2415
+ _$effect((_p$) => {
2416
+ var _v$6 = {
2417
+ fg: pal().primary
2418
+ }, _v$7 = {
2419
+ fg: pal().muted
2420
+ }, _v$8 = {
2421
+ fg: pal().muted
2422
+ };
2423
+ _v$6 !== _p$.e && (_p$.e = _$setProp(_el$33, "style", _v$6, _p$.e));
2424
+ _v$7 !== _p$.t && (_p$.t = _$setProp(_el$35, "style", _v$7, _p$.t));
2425
+ _v$8 !== _p$.a && (_p$.a = _$setProp(_el$36, "style", _v$8, _p$.a));
2426
+ return _p$;
2427
+ }, {
2428
+ e: void 0,
2429
+ t: void 0,
2430
+ a: void 0
2431
+ });
2432
+ return _el$31;
2433
+ })(), (() => {
2434
+ var _el$37 = _$createElement("text"), _el$38 = _$createTextNode(` `), _el$39 = _$createElement("span"), _el$40 = _$createTextNode(`: `), _el$41 = _$createElement("span"), _el$42 = _$createElement("span");
2435
+ _$insertNode(_el$37, _el$38);
2436
+ _$insertNode(_el$37, _el$39);
2437
+ _$insertNode(_el$37, _el$41);
2438
+ _$insertNode(_el$37, _el$42);
2439
+ _$insertNode(_el$39, _el$40);
2440
+ _$insert(_el$39, () => t("status.label"), _el$40);
2441
+ _$insert(_el$41, () => " ".repeat(expandedPad(t("status.label"))));
2442
+ _$insert(_el$42, () => isActiveRunning ? t("status.running") : isCancelled ? t("status.cancelled") : isError ? t("status.error") : t("status.done"));
2443
+ _$effect((_p$) => {
2444
+ var _v$9 = {
2445
+ fg: pal().primary
2446
+ }, _v$0 = {
2447
+ fg: pal().muted
2448
+ }, _v$1 = {
2449
+ fg: isActiveRunning ? pal().warning : isCancelled ? pal().muted : isError ? pal().error : pal().success
2450
+ };
2451
+ _v$9 !== _p$.e && (_p$.e = _$setProp(_el$39, "style", _v$9, _p$.e));
2452
+ _v$0 !== _p$.t && (_p$.t = _$setProp(_el$41, "style", _v$0, _p$.t));
2453
+ _v$1 !== _p$.a && (_p$.a = _$setProp(_el$42, "style", _v$1, _p$.a));
2454
+ return _p$;
2455
+ }, {
2456
+ e: void 0,
2457
+ t: void 0,
2458
+ a: void 0
2459
+ });
2460
+ return _el$37;
2461
+ })(), _$createComponent(Show, {
2462
+ get when() {
2463
+ return elapsed() >= 2e3 || entry.endedAt !== void 0;
2464
+ },
2465
+ get children() {
2466
+ var _el$43 = _$createElement("text"), _el$44 = _$createTextNode(` `), _el$45 = _$createElement("span"), _el$46 = _$createTextNode(`: `), _el$47 = _$createElement("span"), _el$48 = _$createElement("span");
2467
+ _$insertNode(_el$43, _el$44);
2468
+ _$insertNode(_el$43, _el$45);
2469
+ _$insertNode(_el$43, _el$47);
2470
+ _$insertNode(_el$43, _el$48);
2471
+ _$insertNode(_el$45, _el$46);
2472
+ _$insert(_el$45, () => t("time.label"), _el$46);
2473
+ _$insert(_el$47, () => " ".repeat(expandedPad(t("time.label"))));
2474
+ _$insert(_el$48, () => fmtDurationShort(elapsed(), isActiveRunning));
2475
+ _$effect((_p$) => {
2476
+ var _v$10 = {
2477
+ fg: pal().primary
2478
+ }, _v$11 = {
2479
+ fg: pal().muted
2480
+ }, _v$12 = {
2481
+ fg: pal().muted
2482
+ };
2483
+ _v$10 !== _p$.e && (_p$.e = _$setProp(_el$45, "style", _v$10, _p$.e));
2484
+ _v$11 !== _p$.t && (_p$.t = _$setProp(_el$47, "style", _v$11, _p$.t));
2485
+ _v$12 !== _p$.a && (_p$.a = _$setProp(_el$48, "style", _v$12, _p$.a));
2486
+ return _p$;
2487
+ }, {
2488
+ e: void 0,
2489
+ t: void 0,
2490
+ a: void 0
2491
+ });
2492
+ return _el$43;
2493
+ }
2494
+ }), _$createComponent(Show, {
2495
+ get when() {
2496
+ return entry.tokens !== void 0;
2497
+ },
2498
+ get children() {
2499
+ var _el$49 = _$createElement("text"), _el$50 = _$createTextNode(` `), _el$51 = _$createElement("span"), _el$52 = _$createTextNode(`: `), _el$53 = _$createElement("span"), _el$54 = _$createElement("span");
2500
+ _$insertNode(_el$49, _el$50);
2501
+ _$insertNode(_el$49, _el$51);
2502
+ _$insertNode(_el$49, _el$53);
2503
+ _$insertNode(_el$49, _el$54);
2504
+ _$insertNode(_el$51, _el$52);
2505
+ _$insert(_el$51, () => t("tokens.label"), _el$52);
2506
+ _$insert(_el$53, () => " ".repeat(expandedPad(t("tokens.label"))));
2507
+ _$insert(_el$54, () => fmtTokens(entry.tokens));
2508
+ _$effect((_p$) => {
2509
+ var _v$13 = {
2510
+ fg: pal().primary
2511
+ }, _v$14 = {
2512
+ fg: pal().muted
2513
+ }, _v$15 = {
2514
+ fg: pal().muted
2515
+ };
2516
+ _v$13 !== _p$.e && (_p$.e = _$setProp(_el$51, "style", _v$13, _p$.e));
2517
+ _v$14 !== _p$.t && (_p$.t = _$setProp(_el$53, "style", _v$14, _p$.t));
2518
+ _v$15 !== _p$.a && (_p$.a = _$setProp(_el$54, "style", _v$15, _p$.a));
2519
+ return _p$;
2520
+ }, {
2521
+ e: void 0,
2522
+ t: void 0,
2523
+ a: void 0
2524
+ });
2525
+ return _el$49;
2526
+ }
2527
+ }), _$createComponent(Show, {
2528
+ get when() {
2529
+ return entry.error;
2530
+ },
2531
+ get children() {
2532
+ var _el$55 = _$createElement("text"), _el$56 = _$createTextNode(` `), _el$57 = _$createElement("span"), _el$58 = _$createTextNode(`: `), _el$59 = _$createElement("span"), _el$60 = _$createElement("span");
2533
+ _$insertNode(_el$55, _el$56);
2534
+ _$insertNode(_el$55, _el$57);
2535
+ _$insertNode(_el$55, _el$59);
2536
+ _$insertNode(_el$55, _el$60);
2537
+ _$insertNode(_el$57, _el$58);
2538
+ _$insert(_el$57, () => t("error.label"), _el$58);
2539
+ _$insert(_el$59, () => " ".repeat(expandedPad(t("error.label"))));
2540
+ _$insert(_el$60, () => truncate(String(entry.error), expandedValAvail()));
2541
+ _$effect((_p$) => {
2542
+ var _v$16 = {
2543
+ fg: pal().error
2544
+ }, _v$17 = {
2545
+ fg: pal().muted
2546
+ }, _v$18 = {
2547
+ fg: pal().error
2548
+ };
2549
+ _v$16 !== _p$.e && (_p$.e = _$setProp(_el$57, "style", _v$16, _p$.e));
2550
+ _v$17 !== _p$.t && (_p$.t = _$setProp(_el$59, "style", _v$17, _p$.t));
2551
+ _v$18 !== _p$.a && (_p$.a = _$setProp(_el$60, "style", _v$18, _p$.a));
2552
+ return _p$;
2553
+ }, {
2554
+ e: void 0,
2555
+ t: void 0,
2556
+ a: void 0
2557
+ });
2558
+ return _el$55;
2559
+ }
2560
+ }), _$createComponent(Show, {
2561
+ get when() {
2562
+ return entry.cost !== void 0;
2563
+ },
2564
+ get children() {
2565
+ return (() => {
2566
+ const cost = entry.cost;
2567
+ return (() => {
2568
+ var _el$84 = _$createElement("text"), _el$85 = _$createTextNode(` `), _el$86 = _$createElement("span"), _el$87 = _$createTextNode(`: `), _el$88 = _$createElement("span"), _el$89 = _$createElement("span"), _el$90 = _$createTextNode(`$`);
2569
+ _$insertNode(_el$84, _el$85);
2570
+ _$insertNode(_el$84, _el$86);
2571
+ _$insertNode(_el$84, _el$88);
2572
+ _$insertNode(_el$84, _el$89);
2573
+ _$insertNode(_el$86, _el$87);
2574
+ _$insert(_el$86, () => t("cost.label"), _el$87);
2575
+ _$insert(_el$88, () => " ".repeat(expandedPad(t("cost.label"))));
2576
+ _$insertNode(_el$89, _el$90);
2577
+ _$insert(_el$89, () => cost.toFixed(4), null);
2578
+ _$effect((_p$) => {
2579
+ var _v$29 = {
2580
+ fg: pal().primary
2581
+ }, _v$30 = {
2582
+ fg: pal().muted
2583
+ }, _v$31 = {
2584
+ fg: pal().muted
2585
+ };
2586
+ _v$29 !== _p$.e && (_p$.e = _$setProp(_el$86, "style", _v$29, _p$.e));
2587
+ _v$30 !== _p$.t && (_p$.t = _$setProp(_el$88, "style", _v$30, _p$.t));
2588
+ _v$31 !== _p$.a && (_p$.a = _$setProp(_el$89, "style", _v$31, _p$.a));
2589
+ return _p$;
2590
+ }, {
2591
+ e: void 0,
2592
+ t: void 0,
2593
+ a: void 0
2594
+ });
2595
+ return _el$84;
2596
+ })();
2597
+ })();
2598
+ }
2599
+ }), _$createComponent(Show, {
2600
+ get when() {
2601
+ return entry.model;
2602
+ },
2603
+ get children() {
2604
+ var _el$61 = _$createElement("text"), _el$62 = _$createTextNode(` `), _el$63 = _$createElement("span"), _el$64 = _$createTextNode(`: `), _el$65 = _$createElement("span"), _el$66 = _$createElement("span");
2605
+ _$insertNode(_el$61, _el$62);
2606
+ _$insertNode(_el$61, _el$63);
2607
+ _$insertNode(_el$61, _el$65);
2608
+ _$insertNode(_el$61, _el$66);
2609
+ _$insertNode(_el$63, _el$64);
2610
+ _$insert(_el$63, () => t("model.label"), _el$64);
2611
+ _$insert(_el$65, () => " ".repeat(expandedPad(t("model.label"))));
2612
+ _$insert(_el$66, () => truncate(entry.model, expandedValAvail()));
2613
+ _$effect((_p$) => {
2614
+ var _v$19 = {
2615
+ fg: pal().primary
2616
+ }, _v$20 = {
2617
+ fg: pal().muted
2618
+ }, _v$21 = {
2619
+ fg: pal().muted
2620
+ };
2621
+ _v$19 !== _p$.e && (_p$.e = _$setProp(_el$63, "style", _v$19, _p$.e));
2622
+ _v$20 !== _p$.t && (_p$.t = _$setProp(_el$65, "style", _v$20, _p$.t));
2623
+ _v$21 !== _p$.a && (_p$.a = _$setProp(_el$66, "style", _v$21, _p$.a));
2624
+ return _p$;
2625
+ }, {
2626
+ e: void 0,
2627
+ t: void 0,
2628
+ a: void 0
2629
+ });
2630
+ return _el$61;
2631
+ }
2632
+ }), _$createComponent(Show, {
2633
+ get when() {
2634
+ return entry.todoTotal !== void 0;
2635
+ },
2636
+ get children() {
2637
+ var _el$67 = _$createElement("text"), _el$68 = _$createTextNode(` `), _el$69 = _$createElement("span"), _el$70 = _$createTextNode(`: `), _el$71 = _$createElement("span"), _el$72 = _$createElement("span"), _el$73 = _$createTextNode(`/`);
2638
+ _$insertNode(_el$67, _el$68);
2639
+ _$insertNode(_el$67, _el$69);
2640
+ _$insertNode(_el$67, _el$71);
2641
+ _$insertNode(_el$67, _el$72);
2642
+ _$insertNode(_el$69, _el$70);
2643
+ _$insert(_el$69, () => t("todo.label"), _el$70);
2644
+ _$insert(_el$71, () => " ".repeat(expandedPad(t("todo.label"))));
2645
+ _$insertNode(_el$72, _el$73);
2646
+ _$insert(_el$72, () => entry.todoDone, _el$73);
2647
+ _$insert(_el$72, () => entry.todoTotal, null);
2648
+ _$effect((_p$) => {
2649
+ var _v$22 = {
2650
+ fg: pal().primary
2651
+ }, _v$23 = {
2652
+ fg: pal().muted
2653
+ }, _v$24 = {
2654
+ fg: pal().muted
2655
+ };
2656
+ _v$22 !== _p$.e && (_p$.e = _$setProp(_el$69, "style", _v$22, _p$.e));
2657
+ _v$23 !== _p$.t && (_p$.t = _$setProp(_el$71, "style", _v$23, _p$.t));
2658
+ _v$24 !== _p$.a && (_p$.a = _$setProp(_el$72, "style", _v$24, _p$.a));
2659
+ return _p$;
2660
+ }, {
2661
+ e: void 0,
2662
+ t: void 0,
2663
+ a: void 0
2664
+ });
2665
+ return _el$67;
2666
+ }
2667
+ }), _$createComponent(Show, {
2668
+ get when() {
2669
+ return entry.sessionId;
2670
+ },
2671
+ get children() {
2672
+ var _el$74 = _$createElement("text"), _el$75 = _$createTextNode(` `), _el$76 = _$createElement("span"), _el$77 = _$createTextNode(`: `), _el$78 = _$createElement("span"), _el$79 = _$createElement("span"), _el$80 = _$createElement("span");
2673
+ _$insertNode(_el$74, _el$75);
2674
+ _$insertNode(_el$74, _el$76);
2675
+ _$insertNode(_el$74, _el$78);
2676
+ _$insertNode(_el$74, _el$79);
2677
+ _$insertNode(_el$74, _el$80);
2678
+ _$setProp(_el$74, "onMouseUp", async () => {
2679
+ const sessionId = entry.sessionId;
2680
+ if (!sessionId) return;
2681
+ const result = await copyText(sessionId);
2682
+ if (result.copied) {
2683
+ props.api.ui.toast(t("session.toast.copied"), {
2684
+ variant: "success",
2685
+ title: entry.title || entry.agent,
2686
+ duration: 2500
2687
+ });
2688
+ return;
2689
+ }
2690
+ props.api.ui.toast(`${sessionId}
2691
+
2692
+ ${t("session.toast.copy_failed")}`, {
2693
+ variant: "warning",
2694
+ title: entry.title || entry.agent,
2695
+ duration: 8e3
2696
+ });
2697
+ });
2698
+ _$insertNode(_el$76, _el$77);
2699
+ _$insert(_el$76, () => t("session.label"), _el$77);
2700
+ _$insert(_el$78, () => " ".repeat(expandedPad(t("session.label"))));
2701
+ _$insert(_el$79, () => truncate(entry.sessionId, expandedValAvail() - visualWidth(" \u2398")));
2702
+ _$insertNode(_el$80, _$createTextNode(` \u2398`));
2703
+ _$effect((_p$) => {
2704
+ var _v$25 = {
2705
+ fg: pal().primary
2706
+ }, _v$26 = {
2707
+ fg: pal().muted
2708
+ }, _v$27 = {
2709
+ fg: pal().muted
2710
+ }, _v$28 = {
2711
+ fg: pal().warning
2712
+ };
2713
+ _v$25 !== _p$.e && (_p$.e = _$setProp(_el$76, "style", _v$25, _p$.e));
2714
+ _v$26 !== _p$.t && (_p$.t = _$setProp(_el$78, "style", _v$26, _p$.t));
2715
+ _v$27 !== _p$.a && (_p$.a = _$setProp(_el$79, "style", _v$27, _p$.a));
2716
+ _v$28 !== _p$.o && (_p$.o = _$setProp(_el$80, "style", _v$28, _p$.o));
2717
+ return _p$;
2718
+ }, {
2719
+ e: void 0,
2720
+ t: void 0,
2721
+ a: void 0,
2722
+ o: void 0
2723
+ });
2724
+ return _el$74;
2725
+ }
2726
+ }), _$createComponent(Show, {
2727
+ get when() {
2728
+ return entry.sessionId || isRunning;
2729
+ },
2730
+ get children() {
2731
+ return (() => {
2732
+ const openPrefix = () => " \u2192 ";
2733
+ const openFull = () => entry.sessionId ? openPrefix() + t("open.label") : "";
2734
+ const openW = () => entry.sessionId ? visualWidth(openFull()) : 0;
2735
+ const cancelLabel = () => ` ${t("cancel.label")}`;
2736
+ const dismissLabel = () => ` ${t("dismiss.label")}`;
2737
+ const rightW = (isRunning ? visualWidth(dismissLabel()) : 0) + (isRunning && entry.sessionId ? visualWidth(cancelLabel()) : 0);
2738
+ const spacerW = () => Math.max(1, panelWidth() - openW() - rightW - 2);
2739
+ return (() => {
2740
+ var _el$91 = _$createElement("box"), _el$95 = _$createElement("text");
2741
+ _$insertNode(_el$91, _el$95);
2742
+ _$setProp(_el$91, "flexDirection", "row");
2743
+ _$insert(_el$91, _$createComponent(Show, {
2744
+ get when() {
2745
+ return entry.sessionId;
2746
+ },
2747
+ get children() {
2748
+ var _el$92 = _$createElement("text"), _el$93 = _$createElement("span"), _el$94 = _$createElement("span");
2749
+ _$insertNode(_el$92, _el$93);
2750
+ _$insertNode(_el$92, _el$94);
2751
+ _$setProp(_el$92, "onMouseOver", () => setHoveredOpen(entry.id));
2752
+ _$setProp(_el$92, "onMouseOut", () => setHoveredOpen(void 0));
2753
+ _$setProp(_el$92, "onMouseUp", () => {
2754
+ if (entry.sessionId) {
2755
+ props.api.route.navigateSession(entry.sessionId);
2756
+ }
2757
+ });
2758
+ _$insert(_el$93, openPrefix);
2759
+ _$insert(_el$94, () => t("open.label"));
2760
+ _$effect((_p$) => {
2761
+ var _v$32 = {
2762
+ fg: hoveredOpen() === entry.id ? pal().warning : pal().primary
2763
+ }, _v$33 = {
2764
+ fg: hoveredOpen() === entry.id ? pal().warning : pal().primary
2765
+ };
2766
+ _v$32 !== _p$.e && (_p$.e = _$setProp(_el$93, "style", _v$32, _p$.e));
2767
+ _v$33 !== _p$.t && (_p$.t = _$setProp(_el$94, "style", _v$33, _p$.t));
2768
+ return _p$;
2769
+ }, {
2770
+ e: void 0,
2771
+ t: void 0
2772
+ });
2773
+ return _el$92;
2774
+ }
2775
+ }), _el$95);
2776
+ _$insert(_el$95, () => " ".repeat(spacerW()));
2777
+ _$insert(_el$91, _$createComponent(Show, {
2778
+ get when() {
2779
+ return isRunning && entry.sessionId;
2780
+ },
2781
+ get children() {
2782
+ var _el$96 = _$createElement("text"), _el$97 = _$createElement("span");
2783
+ _$insertNode(_el$96, _el$97);
2784
+ _$setProp(_el$96, "onMouseOver", () => setHoveredCancel(entry.id));
2785
+ _$setProp(_el$96, "onMouseOut", () => setHoveredCancel(void 0));
2786
+ _$setProp(_el$96, "onMouseUp", () => cancelEntry(entry));
2787
+ _$insert(_el$97, cancelLabel);
2788
+ _$effect((_$p) => _$setProp(_el$97, "style", {
2789
+ fg: hoveredCancel() === entry.id ? pal().warning : pal().error
2790
+ }, _$p));
2791
+ return _el$96;
2792
+ }
2793
+ }), null);
2794
+ _$insert(_el$91, _$createComponent(Show, {
2795
+ when: isRunning,
2796
+ get children() {
2797
+ var _el$98 = _$createElement("text"), _el$99 = _$createElement("span");
2798
+ _$insertNode(_el$98, _el$99);
2799
+ _$setProp(_el$98, "onMouseOver", () => setHoveredDismiss(entry.id));
2800
+ _$setProp(_el$98, "onMouseOut", () => setHoveredDismiss(void 0));
2801
+ _$setProp(_el$98, "onMouseUp", () => {
2802
+ upsertEntry({
2803
+ id: entry.id,
2804
+ title: entry.title,
2805
+ agent: entry.agent,
2806
+ prompt: entry.prompt,
2807
+ status: "done"
2808
+ });
2809
+ });
2810
+ _$insert(_el$99, dismissLabel);
2811
+ _$effect((_$p) => _$setProp(_el$99, "style", {
2812
+ fg: hoveredDismiss() === entry.id ? pal().warning : pal().muted
2813
+ }, _$p));
2814
+ return _el$98;
2815
+ }
2816
+ }), null);
2817
+ _$effect((_$p) => _$setProp(_el$95, "style", {
2818
+ fg: pal().muted
2819
+ }, _$p));
2820
+ return _el$91;
2821
+ })();
2822
+ })();
2823
+ }
2824
+ })];
2825
+ }
2826
+ })];
2827
+ }
2828
+ }), null);
2829
+ _$insert(_el$7, _$createComponent(Show, {
2830
+ get when() {
2831
+ return hiddenBelow() > 0 || (props.sortOrder() === "desc" ? scrollOffset() > 0 : entryList().length > max() && clampedOffset() < entryList().length - max());
2832
+ },
2833
+ get children() {
2834
+ return (() => {
2835
+ const showMore = hiddenBelow() > 0;
2836
+ const showTop = props.sortOrder() === "desc" ? scrollOffset() > 0 : entryList().length > max() && clampedOffset() < entryList().length - max();
2837
+ const left = showMore ? ` \u2193 ${hiddenBelow()} ${t("scroll.more")}` : " ";
2838
+ const right = props.sortOrder() === "desc" ? `\u2191 ${t("scroll.top")}` : `\u2193 ${t("scroll.bottom")}`;
2839
+ const pad = showTop ? Math.max(1, panelWidth() - visualWidth(left) - visualWidth(right)) : 0;
2840
+ return (() => {
2841
+ var _el$100 = _$createElement("box"), _el$101 = _$createElement("text"), _el$102 = _$createElement("span");
2842
+ _$insertNode(_el$100, _el$101);
2843
+ _$setProp(_el$100, "flexDirection", "row");
2844
+ _$insertNode(_el$101, _el$102);
2845
+ _$setProp(_el$101, "onMouseOver", () => showMore && setHoveredMoreBelow(true));
2846
+ _$setProp(_el$101, "onMouseOut", () => setHoveredMoreBelow(false));
2847
+ _$setProp(_el$101, "onMouseUp", () => {
2848
+ if (!showMore) return;
2849
+ const total = entryList().length;
2850
+ const m = max();
2851
+ if (total <= m) return;
2852
+ setScrollOffset((prev) => Math.min(total - m, prev + m));
2853
+ try {
2854
+ persistScroll(props.sessionId, scrollOffset());
2855
+ } catch {
2856
+ }
2857
+ setHoveredMoreBelow(false);
2858
+ });
2859
+ _$insert(_el$102, left);
2860
+ _$insert(_el$100, showTop ? [(() => {
2861
+ var _el$103 = _$createElement("text");
2862
+ _$insert(_el$103, () => " ".repeat(pad));
2863
+ _$effect((_$p) => _$setProp(_el$103, "style", {
2864
+ fg: pal().muted
2865
+ }, _$p));
2866
+ return _el$103;
2867
+ })(), (() => {
2868
+ var _el$104 = _$createElement("text"), _el$105 = _$createElement("span");
2869
+ _$insertNode(_el$104, _el$105);
2870
+ _$setProp(_el$104, "onMouseOver", () => setHoveredTop(true));
2871
+ _$setProp(_el$104, "onMouseOut", () => setHoveredTop(false));
2872
+ _$setProp(_el$104, "onMouseUp", () => {
2873
+ const total = entryList().length;
2874
+ const m = max();
2875
+ if (props.sortOrder() === "desc") {
2876
+ setScrollOffset(0);
2877
+ } else {
2878
+ setScrollOffset(Math.max(0, total - m));
2879
+ }
2880
+ setHoveredTop(false);
2881
+ });
2882
+ _$insert(_el$105, right);
2883
+ _$effect((_$p) => _$setProp(_el$105, "style", {
2884
+ fg: hoveredTop() ? pal().warning : pal().muted
2885
+ }, _$p));
2886
+ return _el$104;
2887
+ })()] : null, null);
2888
+ _$effect((_$p) => _$setProp(_el$102, "style", {
2889
+ fg: showMore && hoveredMoreBelow() ? pal().warning : pal().muted
2890
+ }, _$p));
2891
+ return _el$100;
2892
+ })();
2893
+ })();
2894
+ }
2895
+ }), null);
2896
+ return _el$7;
2897
+ }
2898
+ })];
2899
+ }
2900
+ }), null);
2901
+ _$effect((_p$) => {
2902
+ var _v$ = {
2903
+ fg: pal().muted
2904
+ }, _v$2 = {
2905
+ fg: pal().primary
2906
+ };
2907
+ _v$ !== _p$.e && (_p$.e = _$setProp(_el$3, "style", _v$, _p$.e));
2908
+ _v$2 !== _p$.t && (_p$.t = _$setProp(_el$4, "style", _v$2, _p$.t));
2909
+ return _p$;
2910
+ }, {
2911
+ e: void 0,
2912
+ t: void 0
2913
+ });
2914
+ return _el$;
2915
+ })();
2916
+ }
2917
+
2918
+ // src/v2/index.tsx
2919
+ function PluginRoot(props) {
2920
+ props.context.keymap.layer(() => ({
2921
+ mode: "global",
2922
+ commands: makeCommands(props.context, props.api, props.signals)
2923
+ }));
2924
+ return _$createComponent2(SubAgentPanel, {
2925
+ get api() {
2926
+ return props.api;
2927
+ },
2928
+ get theme() {
2929
+ return mapTheme(props.context.theme);
2930
+ },
2931
+ get lang() {
2932
+ return props.signals.lang;
2933
+ },
2934
+ get maxEntries() {
2935
+ return props.signals.maxEntries;
2936
+ },
2937
+ get sortOrder() {
2938
+ return props.signals.sortOrder;
2939
+ },
2940
+ get scrollMode() {
2941
+ return props.signals.scrollMode;
2942
+ },
2943
+ get sessionId() {
2944
+ return props.sessionID;
2945
+ }
2946
+ });
2947
+ }
2948
+ var mod = {
2949
+ id: "opencode-subagent-magazine",
2950
+ setup(context) {
2951
+ let lang;
2952
+ let maxEntries;
2953
+ let sortOrder;
2954
+ let scrollMode;
2955
+ const api = createPanelApi(context, {
2956
+ lang: () => lang(),
2957
+ maxEntries: () => maxEntries(),
2958
+ sortOrder: () => sortOrder(),
2959
+ scrollMode: () => scrollMode()
2960
+ });
2961
+ const storedLang = String(api.kv.get(SETTING_KEYS.lang, ""));
2962
+ const initialLang = LANG_META.some((m) => m.code === storedLang) ? storedLang : detectLang();
2963
+ const [langSignal, setLang] = createSignal3(initialLang);
2964
+ const [maxSignal, setMaxEntries] = createSignal3(Number(api.kv.get(SETTING_KEYS.maxEntries, "10")) || 10);
2965
+ const [orderSignal, setSortOrder] = createSignal3(String(api.kv.get(SETTING_KEYS.order, "desc")) === "asc" ? "asc" : "desc");
2966
+ const [scrollSignal, setScrollMode] = createSignal3(String(api.kv.get(SETTING_KEYS.scrollMode, "wheel")) === "click" ? "click" : "wheel");
2967
+ lang = langSignal;
2968
+ maxEntries = maxSignal;
2969
+ sortOrder = orderSignal;
2970
+ scrollMode = scrollSignal;
2971
+ const signals = {
2972
+ lang,
2973
+ setLang,
2974
+ maxEntries,
2975
+ setMaxEntries,
2976
+ sortOrder,
2977
+ setSortOrder,
2978
+ scrollMode,
2979
+ setScrollMode,
2980
+ sessionId: ""
2981
+ };
2982
+ context.ui.slot({
2983
+ prepend: "sidebar.content",
2984
+ render: (props) => {
2985
+ signals.sessionId = String(props.sessionID ?? "");
2986
+ return _$createComponent2(PluginRoot, {
2987
+ context,
2988
+ api,
2989
+ signals,
2990
+ get sessionID() {
2991
+ return String(props.sessionID ?? "");
2992
+ }
2993
+ });
2994
+ }
2995
+ });
2996
+ },
2997
+ // V1 server 空实现(兼容标记):v2 加载 setup,V1 检测需要 server 字段识别为插件
2998
+ server: async () => ({})
2999
+ };
3000
+ var index_default = mod;
3001
+ export {
3002
+ index_default as default
3003
+ };