opencode-subagent-magazine 1.6.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tui.js CHANGED
@@ -6,7 +6,7 @@ import { createComponent as _$createComponent2 } from "@opentui/solid";
6
6
  import { createSignal as createSignal3 } from "solid-js";
7
7
 
8
8
  // src/_version.ts
9
- var PLUGIN_VERSION = "1.6.1";
9
+ var PLUGIN_VERSION = "1.7.0";
10
10
 
11
11
  // src/i18n.ts
12
12
  var ZH_T = {
@@ -50,6 +50,7 @@ var ZH_T = {
50
50
  "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",
51
51
  "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",
52
52
  "clear.done": "\u5DF2\u6E05\u9664 {n} \u6761\u5B50\u4EE3\u7406\u8BB0\u5F55",
53
+ "clear.failed": "\u6E05\u9664\u5931\u8D25",
53
54
  "clear.empty": "\u5F53\u524D\u4F1A\u8BDD\u65E0\u5B50\u4EE3\u7406\u8BB0\u5F55",
54
55
  "cancel.no_session": "\u5B50\u4F1A\u8BDD ID \u4E0D\u53EF\u7528",
55
56
  "cancel.not_child": "\u76EE\u6807\u4E0D\u662F\u5B50\u4F1A\u8BDD",
@@ -60,7 +61,25 @@ var ZH_T = {
60
61
  "cancel.sent": "\u5DF2\u53D1\u9001\u53D6\u6D88\u6307\u4EE4",
61
62
  "cancel.failed": "\u53D6\u6D88\u5931\u8D25",
62
63
  "borderShown": "\u9762\u677F\u8FB9\u6846 \u5DF2\u663E\u793A",
63
- "borderHidden": "\u9762\u677F\u8FB9\u6846 \u5DF2\u9690\u85CF"
64
+ "borderHidden": "\u9762\u677F\u8FB9\u6846 \u5DF2\u9690\u85CF",
65
+ "settings.title": "SubAgent Magazine \u2014 \u8BBE\u7F6E",
66
+ "settings.section.general": "\u901A\u7528",
67
+ "settings.lang": "\u8BED\u8A00",
68
+ "settings.max": "\u6700\u5927\u6761\u76EE\u6570",
69
+ "settings.order": "\u6392\u5E8F\u65B9\u5F0F",
70
+ "settings.scroll": "\u6EDA\u52A8\u6A21\u5F0F",
71
+ "settings.ttl": "\u4FDD\u7559\u671F\u9650",
72
+ "settings.border": "\u9762\u677F\u8FB9\u6846",
73
+ "settings.showEntryCost": "\u6BCF\u6761\u5B50\u4EE3\u7406\u663E\u793A\u8D39\u7528",
74
+ "settings.showEntryTime": "\u5217\u8868\u4E2D\u663E\u793A\u8017\u65F6",
75
+ "settings.showEntryTokens": "\u5217\u8868\u4E2D\u663E\u793A Token \u6570",
76
+ "settings.timeFormat": "\u65F6\u95F4\u683C\u5F0F",
77
+ "settings.dbSync": "\u672C\u5730\u6570\u636E\u5E93\u8865\u5168",
78
+ "settings.on": "\u5F00",
79
+ "settings.off": "\u5173",
80
+ "settings.saved": "\u5DF2\u4FDD\u5B58 {item}\uFF1A{value}",
81
+ "settings.invalid": "\u65E0\u6548\u7684\u6570\u503C",
82
+ "settings.close": "\u5173\u95ED"
64
83
  };
65
84
  var EN_T = {
66
85
  "panel.title": "SubAgent",
@@ -103,6 +122,7 @@ var EN_T = {
103
122
  "clear.prompt": "Clear all sub-agent records for this session? This cannot be undone.",
104
123
  "clear.prompt_running": "{n} sub-agent(s) are still running. Clearing will discard them permanently. Continue?",
105
124
  "clear.done": "Cleared {n} sub-agent record(s)",
125
+ "clear.failed": "Clear failed",
106
126
  "clear.empty": "No sub-agent records in this session",
107
127
  "cancel.no_session": "Child session ID is unavailable",
108
128
  "cancel.not_child": "Target is not a child session",
@@ -113,7 +133,25 @@ var EN_T = {
113
133
  "cancel.sent": "Cancel instruction sent",
114
134
  "cancel.failed": "Cancellation failed",
115
135
  "borderShown": "Panel border shown",
116
- "borderHidden": "Panel border hidden"
136
+ "borderHidden": "Panel border hidden",
137
+ "settings.title": "SubAgent Magazine \u2014 Settings",
138
+ "settings.section.general": "General",
139
+ "settings.lang": "Language",
140
+ "settings.max": "Max entries",
141
+ "settings.order": "Sort order",
142
+ "settings.scroll": "Scroll mode",
143
+ "settings.ttl": "Retention",
144
+ "settings.border": "Panel border",
145
+ "settings.showEntryCost": "Cost per sub-agent",
146
+ "settings.showEntryTime": "Elapsed time in list",
147
+ "settings.showEntryTokens": "Token count in list",
148
+ "settings.timeFormat": "Time format",
149
+ "settings.dbSync": "Local database enrichment",
150
+ "settings.on": "on",
151
+ "settings.off": "off",
152
+ "settings.saved": "Saved {item}: {value}",
153
+ "settings.invalid": "Invalid value",
154
+ "settings.close": "Close"
117
155
  };
118
156
  var JA_T = {
119
157
  "panel.title": "\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8",
@@ -156,6 +194,7 @@ var JA_T = {
156
194
  "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",
157
195
  "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",
158
196
  "clear.done": "{n} \u4EF6\u306E\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u8A18\u9332\u3092\u524A\u9664\u3057\u307E\u3057\u305F",
197
+ "clear.failed": "\u30AF\u30EA\u30A2\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
159
198
  "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",
160
199
  "cancel.no_session": "\u5B50\u30BB\u30C3\u30B7\u30E7\u30F3 ID \u304C\u5229\u7528\u3067\u304D\u307E\u305B\u3093",
161
200
  "cancel.not_child": "\u5BFE\u8C61\u306F\u5B50\u30BB\u30C3\u30B7\u30E7\u30F3\u3067\u306F\u3042\u308A\u307E\u305B\u3093",
@@ -166,7 +205,25 @@ var JA_T = {
166
205
  "cancel.sent": "\u30AD\u30E3\u30F3\u30BB\u30EB\u6307\u793A\u3092\u9001\u4FE1\u3057\u307E\u3057\u305F",
167
206
  "cancel.failed": "\u30AD\u30E3\u30F3\u30BB\u30EB\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
168
207
  "borderShown": "\u30D1\u30CD\u30EB\u67A0\u7DDA \u8868\u793A",
169
- "borderHidden": "\u30D1\u30CD\u30EB\u67A0\u7DDA \u975E\u8868\u793A"
208
+ "borderHidden": "\u30D1\u30CD\u30EB\u67A0\u7DDA \u975E\u8868\u793A",
209
+ "settings.title": "SubAgent Magazine \u2014 \u8A2D\u5B9A",
210
+ "settings.section.general": "\u4E00\u822C",
211
+ "settings.lang": "\u8A00\u8A9E",
212
+ "settings.max": "\u6700\u5927\u8868\u793A\u6570",
213
+ "settings.order": "\u4E26\u3073\u9806",
214
+ "settings.scroll": "\u30B9\u30AF\u30ED\u30FC\u30EB",
215
+ "settings.ttl": "\u4FDD\u6301\u671F\u9593",
216
+ "settings.border": "\u30D1\u30CD\u30EB\u67A0",
217
+ "settings.showEntryCost": "\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u6BCE\u306E\u8CBB\u7528",
218
+ "settings.showEntryTime": "\u4E00\u89A7\u306B\u6240\u8981\u6642\u9593\u3092\u8868\u793A",
219
+ "settings.showEntryTokens": "\u4E00\u89A7\u306B\u30C8\u30FC\u30AF\u30F3\u6570\u3092\u8868\u793A",
220
+ "settings.timeFormat": "\u6642\u9593\u5F62\u5F0F",
221
+ "settings.dbSync": "\u30ED\u30FC\u30AB\u30EBDB\u88DC\u5B8C",
222
+ "settings.on": "\u30AA\u30F3",
223
+ "settings.off": "\u30AA\u30D5",
224
+ "settings.saved": "\u4FDD\u5B58\u3057\u307E\u3057\u305F {item}\uFF1A{value}",
225
+ "settings.invalid": "\u7121\u52B9\u306A\u5024\u3067\u3059",
226
+ "settings.close": "\u9589\u3058\u308B"
170
227
  };
171
228
  var KO_T = {
172
229
  "panel.title": "\uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8",
@@ -209,6 +266,7 @@ var KO_T = {
209
266
  "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.",
210
267
  "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?",
211
268
  "clear.done": "\uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8 \uAE30\uB85D {n}\uAC1C\uB97C \uC0AD\uC81C\uD588\uC2B5\uB2C8\uB2E4",
269
+ "clear.failed": "\uC9C0\uC6B0\uAE30\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4",
212
270
  "clear.empty": "\uC774 \uC138\uC158\uC5D0 \uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8 \uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4",
213
271
  "cancel.no_session": "\uD558\uC704 \uC138\uC158 ID\uB97C \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4",
214
272
  "cancel.not_child": "\uB300\uC0C1\uC774 \uD558\uC704 \uC138\uC158\uC774 \uC544\uB2D9\uB2C8\uB2E4",
@@ -219,7 +277,25 @@ var KO_T = {
219
277
  "cancel.sent": "\uCDE8\uC18C \uC9C0\uC2DC\uB97C \uBCF4\uB0C8\uC2B5\uB2C8\uB2E4",
220
278
  "cancel.failed": "\uCDE8\uC18C\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4",
221
279
  "borderShown": "\uD328\uB110 \uD14C\uB450\uB9AC \uD45C\uC2DC",
222
- "borderHidden": "\uD328\uB110 \uD14C\uB450\uB9AC \uC228\uAE40"
280
+ "borderHidden": "\uD328\uB110 \uD14C\uB450\uB9AC \uC228\uAE40",
281
+ "settings.title": "SubAgent Magazine \u2014 \uC124\uC815",
282
+ "settings.section.general": "\uC77C\uBC18",
283
+ "settings.lang": "\uC5B8\uC5B4",
284
+ "settings.max": "\uCD5C\uB300 \uD56D\uBAA9 \uC218",
285
+ "settings.order": "\uC815\uB82C \uC21C\uC11C",
286
+ "settings.scroll": "\uC2A4\uD06C\uB864 \uBAA8\uB4DC",
287
+ "settings.ttl": "\uBCF4\uC874 \uAE30\uAC04",
288
+ "settings.border": "\uD328\uB110 \uD14C\uB450\uB9AC",
289
+ "settings.showEntryCost": "\uD558\uC704 \uC5D0\uC774\uC804\uD2B8\uBCC4 \uBE44\uC6A9",
290
+ "settings.showEntryTime": "\uBAA9\uB85D\uC5D0 \uC18C\uC694 \uC2DC\uAC04 \uD45C\uC2DC",
291
+ "settings.showEntryTokens": "\uBAA9\uB85D\uC5D0 \uD1A0\uD070 \uC218 \uD45C\uC2DC",
292
+ "settings.timeFormat": "\uC2DC\uAC04 \uD615\uC2DD",
293
+ "settings.dbSync": "\uB85C\uCEEC DB \uBCF4\uC644",
294
+ "settings.on": "\uCF2C",
295
+ "settings.off": "\uB054",
296
+ "settings.saved": "\uC800\uC7A5\uB428 {item}: {value}",
297
+ "settings.invalid": "\uC798\uBABB\uB41C \uAC12",
298
+ "settings.close": "\uB2EB\uAE30"
223
299
  };
224
300
  var LANGS = { zh: ZH_T, en: EN_T, ja: JA_T, ko: KO_T };
225
301
  var LANG_META = [
@@ -260,8 +336,125 @@ var SETTING_KEYS = {
260
336
  scrollMode: `${KV_PREFIX}.scroll_mode`,
261
337
  open: `${KV_PREFIX}.open`,
262
338
  ttlDays: `${KV_PREFIX}.ttl_days`,
263
- border: `${KV_PREFIX}.border`
339
+ border: `${KV_PREFIX}.border`,
340
+ showEntryCost: `${KV_PREFIX}.show_entry_cost`,
341
+ showEntryTime: `${KV_PREFIX}.show_entry_time`,
342
+ showEntryTokens: `${KV_PREFIX}.show_entry_tokens`,
343
+ timeFormat: `${KV_PREFIX}.time_format`,
344
+ dbSync: `${KV_PREFIX}.db_sync`
264
345
  };
346
+ function loadSessionData(kv) {
347
+ try {
348
+ const raw = kv.get(SESSION_DATA_KEY, "{}");
349
+ return JSON.parse(String(raw));
350
+ } catch {
351
+ return {};
352
+ }
353
+ }
354
+ function updateSessionData(kv, mutator) {
355
+ try {
356
+ if (typeof kv.update === "function") {
357
+ return kv.update(SESSION_DATA_KEY, (raw) => {
358
+ const data2 = parseSessionData(raw);
359
+ mutator(data2);
360
+ return JSON.stringify(data2);
361
+ });
362
+ }
363
+ const data = loadSessionData(kv);
364
+ mutator(data);
365
+ kv.set(SESSION_DATA_KEY, JSON.stringify(data));
366
+ } catch {
367
+ }
368
+ }
369
+ function parseSessionData(raw) {
370
+ try {
371
+ return JSON.parse(String(raw ?? "{}"));
372
+ } catch {
373
+ return {};
374
+ }
375
+ }
376
+
377
+ // src/core/format.ts
378
+ function charColumns(c) {
379
+ const code = c.codePointAt(0) ?? 0;
380
+ if (code < 32) return 0;
381
+ if (code < 127) return 1;
382
+ if (code < 160) return 0;
383
+ 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)
384
+ return 2;
385
+ return 1;
386
+ }
387
+ function visualWidth(s) {
388
+ let w = 0;
389
+ for (const c of s) w += charColumns(c);
390
+ return w;
391
+ }
392
+ function truncate(text, maxCols) {
393
+ if (visualWidth(text) <= maxCols) return text;
394
+ let cols = 0;
395
+ let i = 0;
396
+ for (const c of text) {
397
+ const w = charColumns(c);
398
+ if (cols + w > maxCols - 1) break;
399
+ cols += w;
400
+ i += c.length;
401
+ }
402
+ return text.slice(0, i) + "\u2026";
403
+ }
404
+ function fmtDurationShort(ms, running) {
405
+ if (running && ms < 2e3) return "";
406
+ if (ms < 1e3) return (ms / 1e3).toFixed(2) + "s";
407
+ if (ms < 6e4) return (ms / 1e3).toFixed(2) + "s";
408
+ const m = Math.floor(ms / 6e4);
409
+ const s = Math.round(ms % 6e4 / 1e3);
410
+ return `${m}m${s}s`;
411
+ }
412
+ var TIME_FORMATS = ["short", "decimal", "clock", "compact", "seconds"];
413
+ var TIME_FORMAT_SAMPLES = {
414
+ decimal: "45.3s \xB7 9.5m \xB7 1.9h",
415
+ short: "45.32s \xB7 112m35s",
416
+ clock: "0:45 \xB7 1:52:35",
417
+ compact: "45s \xB7 1h52m",
418
+ seconds: "45s \xB7 6755s"
419
+ };
420
+ function fmtDuration(ms, running, mode = "short") {
421
+ if (running && ms < 2e3) return "";
422
+ const t = Number.isFinite(ms) && ms > 0 ? ms : 0;
423
+ switch (mode) {
424
+ case "short":
425
+ return fmtDurationShort(t, false);
426
+ case "compact": {
427
+ if (t < 6e4) return `${Math.floor(t / 1e3)}s`;
428
+ if (t < 36e5) return `${Math.floor(t / 6e4)}m${Math.floor(t % 6e4 / 1e3)}s`;
429
+ return `${Math.floor(t / 36e5)}h${Math.floor(t % 36e5 / 6e4)}m`;
430
+ }
431
+ case "clock": {
432
+ const s = Math.floor(t % 6e4 / 1e3);
433
+ if (t < 36e5) return `${Math.floor(t / 6e4)}:${String(s).padStart(2, "0")}`;
434
+ const m = Math.floor(t % 36e5 / 6e4);
435
+ return `${Math.floor(t / 36e5)}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
436
+ }
437
+ case "seconds":
438
+ return `${Math.floor(t / 1e3)}s`;
439
+ case "decimal":
440
+ default: {
441
+ if (t < 59950) return `${(t / 1e3).toFixed(1)}s`;
442
+ if (t < 3597e3) return `${(t / 6e4).toFixed(1)}m`;
443
+ return `${(t / 36e5).toFixed(1)}h`;
444
+ }
445
+ }
446
+ }
447
+ function fmtTokens(n) {
448
+ if (n < 1e3) return `${n}`;
449
+ if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
450
+ return `${(n / 1e6).toFixed(1)}M`;
451
+ }
452
+ function safeErrorMsg(err) {
453
+ if (!err) return "";
454
+ if (typeof err === "string") return err;
455
+ if (typeof err === "object") return String(err.message || err.code || "");
456
+ return "";
457
+ }
265
458
 
266
459
  // src/panel/SubAgentPanel.tsx
267
460
  import { createTextNode as _$createTextNode } from "@opentui/solid";
@@ -376,53 +569,6 @@ async function copyText(text) {
376
569
  // src/core/types.ts
377
570
  var SUBAGENT_TOOLS = /* @__PURE__ */ new Set(["task", "subagent", "delegate", "call_omo_agent"]);
378
571
 
379
- // src/core/format.ts
380
- function charColumns(c) {
381
- const code = c.codePointAt(0) ?? 0;
382
- if (code < 32) return 0;
383
- if (code < 127) return 1;
384
- if (code < 160) return 0;
385
- 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)
386
- return 2;
387
- return 1;
388
- }
389
- function visualWidth(s) {
390
- let w = 0;
391
- for (const c of s) w += charColumns(c);
392
- return w;
393
- }
394
- function truncate(text, maxCols) {
395
- if (visualWidth(text) <= maxCols) return text;
396
- let cols = 0;
397
- let i = 0;
398
- for (const c of text) {
399
- const w = charColumns(c);
400
- if (cols + w > maxCols - 1) break;
401
- cols += w;
402
- i += c.length;
403
- }
404
- return text.slice(0, i) + "\u2026";
405
- }
406
- function fmtDurationShort(ms, running) {
407
- if (running && ms < 2e3) return "";
408
- if (ms < 1e3) return (ms / 1e3).toFixed(2) + "s";
409
- if (ms < 6e4) return (ms / 1e3).toFixed(2) + "s";
410
- const m = Math.floor(ms / 6e4);
411
- const s = Math.round(ms % 6e4 / 1e3);
412
- return `${m}m${s}s`;
413
- }
414
- function fmtTokens(n) {
415
- if (n < 1e3) return `${n}`;
416
- if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
417
- return `${(n / 1e6).toFixed(1)}M`;
418
- }
419
- function safeErrorMsg(err) {
420
- if (!err) return "";
421
- if (typeof err === "string") return err;
422
- if (typeof err === "object") return String(err.message || err.code || "");
423
- return "";
424
- }
425
-
426
572
  // src/core/color.ts
427
573
  function rgb(raw) {
428
574
  if (typeof raw === "string" && raw.startsWith("#")) {
@@ -525,17 +671,80 @@ function upsertSubEntry(entries, partial, now = Date.now()) {
525
671
  });
526
672
  return next;
527
673
  }
674
+ function statusRank(status) {
675
+ if (status === "running") return 0;
676
+ if (status === "cancel_requested") return 1;
677
+ return 2;
678
+ }
679
+ var isTerminalStatus = (status) => statusRank(status) === 2;
680
+ function minDefined(a, b) {
681
+ if (a === void 0) return b;
682
+ if (b === void 0) return a;
683
+ return Math.min(a, b);
684
+ }
685
+ function maxDefined(a, b) {
686
+ if (a === void 0) return b;
687
+ if (b === void 0) return a;
688
+ return Math.max(a, b);
689
+ }
690
+ function mergeSubEntry(base, incoming) {
691
+ const successor = statusRank(incoming.status) > statusRank(base.status) ? incoming : base;
692
+ const status = successor.status;
693
+ const ended = isTerminalStatus(status);
694
+ return {
695
+ ...base,
696
+ ...incoming,
697
+ id: base.id,
698
+ status,
699
+ sessionId: incoming.sessionId ?? base.sessionId,
700
+ startedAt: minDefined(base.startedAt, incoming.startedAt) ?? base.startedAt,
701
+ endedAt: ended ? base.endedAt ?? incoming.endedAt ?? Date.now() : void 0,
702
+ tokens: maxDefined(base.tokens, incoming.tokens),
703
+ cost: maxDefined(base.cost, incoming.cost),
704
+ model: incoming.model ?? base.model,
705
+ todoTotal: maxDefined(base.todoTotal, incoming.todoTotal),
706
+ todoDone: maxDefined(base.todoDone, incoming.todoDone),
707
+ error: incoming.error ?? base.error,
708
+ cancelRequestedAt: minDefined(base.cancelRequestedAt, incoming.cancelRequestedAt),
709
+ abortAccepted: base.abortAccepted || incoming.abortAccepted || void 0,
710
+ cancelReason: incoming.cancelReason ?? base.cancelReason
711
+ };
712
+ }
713
+ function mergeSubEntries(base, incoming) {
714
+ const out = /* @__PURE__ */ new Map();
715
+ for (const entry of base) out.set(entry.id, entry);
716
+ for (const entry of incoming) {
717
+ const key = findSubEntryKey(out, entry.id, entry.sessionId);
718
+ if (!key) {
719
+ out.set(entry.id, entry);
720
+ continue;
721
+ }
722
+ out.set(key, mergeSubEntry(out.get(key), entry));
723
+ }
724
+ return out;
725
+ }
528
726
 
529
727
  // src/panel/SubAgentPanel.tsx
530
728
  var LEFT_PAD = 4;
531
729
  var INDENT = 2;
730
+ function partMetadata(part, st) {
731
+ const partMeta = part?.metadata;
732
+ if (partMeta && Object.keys(partMeta).length > 0) return partMeta;
733
+ const stateMeta = st?.metadata;
734
+ return stateMeta && Object.keys(stateMeta).length > 0 ? stateMeta : void 0;
735
+ }
736
+ function validModel(model) {
737
+ if (typeof model !== "string") return false;
738
+ const s = model.trim();
739
+ return s.length > 0 && s !== "[object Object]" && !s.startsWith("{");
740
+ }
532
741
  function SubAgentPanel(props) {
533
742
  const t = createT(() => props.lang());
534
743
  const SESSION_DATA_KEY2 = `${KV_PREFIX}.session_data`;
535
744
  const ttlDaysRaw = parseInt(String(props.api.kv.get(`${KV_PREFIX}.ttl_days`, "3")), 10);
536
745
  const ttlDays = Number.isNaN(ttlDaysRaw) ? 3 : ttlDaysRaw;
537
746
  const TTL_MS = ttlDays * 24 * 60 * 60 * 1e3;
538
- const loadSessionData = () => {
747
+ const loadSessionData2 = () => {
539
748
  try {
540
749
  const raw = props.api.kv.get(SESSION_DATA_KEY2, "{}");
541
750
  return JSON.parse(String(raw));
@@ -543,11 +752,32 @@ function SubAgentPanel(props) {
543
752
  return {};
544
753
  }
545
754
  };
546
- const saveSessionData = (data) => {
547
- try {
548
- props.api.kv.set(SESSION_DATA_KEY2, JSON.stringify(data));
549
- } catch {
550
- }
755
+ const mutateSessionData = (mutator) => {
756
+ void Promise.resolve(updateSessionData(props.api.kv, mutator)).catch(() => {
757
+ });
758
+ };
759
+ const ensureRootRecord = (data, sid) => {
760
+ const rec = data[sid];
761
+ if (rec) return rec;
762
+ const created = {
763
+ ts: Date.now(),
764
+ entries: [],
765
+ scroll: 0,
766
+ expanded: "",
767
+ children: {}
768
+ };
769
+ data[sid] = created;
770
+ return created;
771
+ };
772
+ const ensureChildRecord = (data, parentSid, sid) => {
773
+ const parent = ensureRootRecord(data, parentSid);
774
+ if (!parent.children) parent.children = {};
775
+ if (!parent.children[sid]) parent.children[sid] = {
776
+ scroll: 0,
777
+ expanded: "",
778
+ entries: []
779
+ };
780
+ return parent.children[sid];
551
781
  };
552
782
  const resolveParent = (sid) => {
553
783
  try {
@@ -571,7 +801,7 @@ function SubAgentPanel(props) {
571
801
  parentSid,
572
802
  isChild
573
803
  } = resolveParent(sid);
574
- const rec = loadSessionData()[parentSid];
804
+ const rec = loadSessionData2()[parentSid];
575
805
  if (rec) {
576
806
  const source = isChild ? rec.children?.[sid]?.entries : rec.entries;
577
807
  if (source) {
@@ -589,129 +819,71 @@ function SubAgentPanel(props) {
589
819
  clearTimeout(persistTimer);
590
820
  persistTimer = setTimeout(() => {
591
821
  try {
592
- const data = loadSessionData();
822
+ mutateSessionData((data) => {
823
+ const {
824
+ parentSid,
825
+ isChild
826
+ } = resolveParent(sid);
827
+ if (isChild) {
828
+ const parent = ensureRootRecord(data, parentSid);
829
+ const child = ensureChildRecord(data, parentSid, sid);
830
+ child.entries = [...mergeSubEntries(child.entries ?? [], entries2.values()).values()];
831
+ parent.ts = Date.now();
832
+ } else {
833
+ const rec = ensureRootRecord(data, sid);
834
+ rec.ts = Date.now();
835
+ rec.entries = [...mergeSubEntries(rec.entries ?? [], entries2.values()).values()];
836
+ }
837
+ });
838
+ } catch {
839
+ }
840
+ }, 200);
841
+ };
842
+ const persistScroll = (sid, scroll) => {
843
+ try {
844
+ mutateSessionData((data) => {
593
845
  const {
594
846
  parentSid,
595
847
  isChild
596
848
  } = resolveParent(sid);
597
849
  if (isChild) {
598
- if (!data[parentSid]) data[parentSid] = {
599
- ts: Date.now(),
600
- entries: [],
601
- scroll: 0,
602
- expanded: "",
603
- children: {}
604
- };
605
- if (!data[parentSid].children) data[parentSid].children = {};
606
- if (!data[parentSid].children[sid]) data[parentSid].children[sid] = {
607
- scroll: 0,
608
- expanded: "",
609
- entries: []
610
- };
611
- data[parentSid].children[sid] = {
612
- ...data[parentSid].children[sid],
613
- entries: [...entries2.values()]
614
- };
850
+ ensureChildRecord(data, parentSid, sid).scroll = scroll;
615
851
  } else {
616
- data[sid] = {
617
- ...data[sid],
618
- ts: Date.now(),
619
- entries: [...entries2.values()],
620
- children: data[sid]?.children ?? {}
621
- };
852
+ const rec = ensureRootRecord(data, sid);
853
+ rec.ts = Date.now();
854
+ rec.scroll = scroll;
622
855
  }
623
- saveSessionData(data);
624
- } catch {
625
- }
626
- }, 200);
627
- };
628
- const persistScroll = (sid, scroll) => {
629
- try {
630
- const data = loadSessionData();
631
- const {
632
- parentSid,
633
- isChild
634
- } = resolveParent(sid);
635
- if (isChild) {
636
- if (!data[parentSid]) data[parentSid] = {
637
- ts: Date.now(),
638
- entries: [],
639
- scroll: 0,
640
- expanded: "",
641
- children: {}
642
- };
643
- if (!data[parentSid].children) data[parentSid].children = {};
644
- if (!data[parentSid].children[sid]) data[parentSid].children[sid] = {
645
- scroll: 0,
646
- expanded: "",
647
- entries: []
648
- };
649
- data[parentSid].children[sid] = {
650
- ...data[parentSid].children[sid],
651
- scroll
652
- };
653
- } else {
654
- data[sid] = {
655
- ...data[sid],
656
- ts: Date.now(),
657
- scroll,
658
- children: data[sid]?.children ?? {}
659
- };
660
- }
661
- saveSessionData(data);
856
+ });
662
857
  } catch {
663
858
  }
664
859
  };
665
860
  const persistExpanded = (sid, expanded2) => {
666
861
  try {
667
- const data = loadSessionData();
668
- const {
669
- parentSid,
670
- isChild
671
- } = resolveParent(sid);
672
- if (isChild) {
673
- if (!data[parentSid]) data[parentSid] = {
674
- ts: Date.now(),
675
- entries: [],
676
- scroll: 0,
677
- expanded: "",
678
- children: {}
679
- };
680
- if (!data[parentSid].children) data[parentSid].children = {};
681
- if (!data[parentSid].children[sid]) data[parentSid].children[sid] = {
682
- scroll: 0,
683
- expanded: "",
684
- entries: []
685
- };
686
- data[parentSid].children[sid] = {
687
- ...data[parentSid].children[sid],
688
- expanded: expanded2
689
- };
690
- } else {
691
- data[sid] = {
692
- ...data[sid],
693
- ts: Date.now(),
694
- expanded: expanded2,
695
- children: data[sid]?.children ?? {}
696
- };
697
- }
698
- saveSessionData(data);
862
+ mutateSessionData((data) => {
863
+ const {
864
+ parentSid,
865
+ isChild
866
+ } = resolveParent(sid);
867
+ if (isChild) {
868
+ ensureChildRecord(data, parentSid, sid).expanded = expanded2;
869
+ } else {
870
+ const rec = ensureRootRecord(data, sid);
871
+ rec.ts = Date.now();
872
+ rec.expanded = expanded2;
873
+ }
874
+ });
699
875
  } catch {
700
876
  }
701
877
  };
702
878
  const cleanupOldSessions = () => {
703
879
  if (ttlDays <= 0) return;
704
880
  try {
705
- const data = loadSessionData();
706
881
  const cutoff = Date.now() - TTL_MS;
707
- let changed = false;
708
- for (const sid of Object.keys(data)) {
709
- if (data[sid].ts < cutoff) {
710
- delete data[sid];
711
- changed = true;
882
+ mutateSessionData((data) => {
883
+ for (const sid of Object.keys(data)) {
884
+ if (data[sid].ts < cutoff) delete data[sid];
712
885
  }
713
- }
714
- if (changed) saveSessionData(data);
886
+ });
715
887
  } catch {
716
888
  }
717
889
  };
@@ -731,38 +903,22 @@ function SubAgentPanel(props) {
731
903
  if (needsImmediateFlush) {
732
904
  clearTimeout(persistTimer);
733
905
  try {
734
- const data = loadSessionData();
735
- const {
736
- parentSid,
737
- isChild
738
- } = resolveParent(props.sessionId);
739
- if (isChild) {
740
- if (!data[parentSid]) data[parentSid] = {
741
- ts: Date.now(),
742
- entries: [],
743
- scroll: 0,
744
- expanded: "",
745
- children: {}
746
- };
747
- if (!data[parentSid].children) data[parentSid].children = {};
748
- if (!data[parentSid].children[props.sessionId]) data[parentSid].children[props.sessionId] = {
749
- scroll: 0,
750
- expanded: "",
751
- entries: []
752
- };
753
- data[parentSid].children[props.sessionId] = {
754
- ...data[parentSid].children[props.sessionId],
755
- entries: [...next.values()]
756
- };
757
- } else {
758
- data[props.sessionId] = {
759
- ...data[props.sessionId],
760
- ts: Date.now(),
761
- entries: [...next.values()],
762
- children: data[props.sessionId]?.children ?? {}
763
- };
764
- }
765
- saveSessionData(data);
906
+ mutateSessionData((data) => {
907
+ const {
908
+ parentSid,
909
+ isChild
910
+ } = resolveParent(props.sessionId);
911
+ if (isChild) {
912
+ const parent = ensureRootRecord(data, parentSid);
913
+ const child = ensureChildRecord(data, parentSid, props.sessionId);
914
+ child.entries = [...mergeSubEntries(child.entries ?? [], next.values()).values()];
915
+ parent.ts = Date.now();
916
+ } else {
917
+ const rec = ensureRootRecord(data, props.sessionId);
918
+ rec.ts = Date.now();
919
+ rec.entries = [...mergeSubEntries(rec.entries ?? [], next.values()).values()];
920
+ }
921
+ });
766
922
  } catch {
767
923
  }
768
924
  } else {
@@ -786,7 +942,7 @@ function SubAgentPanel(props) {
786
942
  parentSid,
787
943
  isChild
788
944
  } = resolveParent(props.sessionId);
789
- const rec = loadSessionData()[parentSid];
945
+ const rec = loadSessionData2()[parentSid];
790
946
  if (rec) return isChild ? rec.children?.[props.sessionId]?.expanded || void 0 : rec.expanded || void 0;
791
947
  } catch {
792
948
  }
@@ -804,7 +960,7 @@ function SubAgentPanel(props) {
804
960
  parentSid,
805
961
  isChild
806
962
  } = resolveParent(props.sessionId);
807
- const rec = loadSessionData()[parentSid];
963
+ const rec = loadSessionData2()[parentSid];
808
964
  return isChild ? rec?.children?.[props.sessionId]?.scroll ?? 0 : rec?.scroll ?? 0;
809
965
  } catch {
810
966
  return 0;
@@ -930,7 +1086,9 @@ function SubAgentPanel(props) {
930
1086
  const part = event.payload?.part;
931
1087
  if (!part) return;
932
1088
  const eventSid = event.payload?.sessionID !== void 0 ? String(event.payload.sessionID) : void 0;
933
- if (event.scope === "global" && eventSid !== props.sessionId) return;
1089
+ if (event.scope === "global" && eventSid !== props.sessionId) {
1090
+ return;
1091
+ }
934
1092
  if (part.type === "subtask") {
935
1093
  const agent = String(part.agent ?? "?");
936
1094
  const prompt = String(part.prompt ?? "");
@@ -959,7 +1117,7 @@ function SubAgentPanel(props) {
959
1117
  if (rawStatus === "error") {
960
1118
  const id2 = `tool:${String(part.id ?? "")}`;
961
1119
  if (!part.id) return;
962
- const stMeta2 = st?.metadata;
1120
+ const stMeta2 = partMetadata(part, st);
963
1121
  const errorSid = stMeta2?.session_id !== void 0 ? String(stMeta2.session_id) : stMeta2?.sessionId !== void 0 ? String(stMeta2.sessionId) : void 0;
964
1122
  const key = findSubEntryKey(entryMap(), id2, errorSid);
965
1123
  const existing = key ? entryMap().get(key) : void 0;
@@ -979,7 +1137,7 @@ function SubAgentPanel(props) {
979
1137
  let status = "running";
980
1138
  if (rawStatus === "completed") status = "done";
981
1139
  if ((input?.run_in_background === true || input?.background === true) && status === "done") {
982
- const stMetaCheck = st?.metadata;
1140
+ const stMetaCheck = partMetadata(part, st);
983
1141
  const hasChild = stMetaCheck?.session_id !== void 0 || stMetaCheck?.sessionId !== void 0;
984
1142
  if (hasChild) status = "running";
985
1143
  }
@@ -988,7 +1146,7 @@ function SubAgentPanel(props) {
988
1146
  const desc = input?.description !== void 0 ? String(input.description) : "";
989
1147
  const title = desc || truncate(prompt.replace(/\n/g, " ").replace(/\s+/g, " ").trim(), 40);
990
1148
  const id = `tool:${String(part.id ?? crypto.randomUUID())}`;
991
- const stMeta = st?.metadata;
1149
+ const stMeta = partMetadata(part, st);
992
1150
  const subSid = stMeta?.session_id !== void 0 ? String(stMeta.session_id) : stMeta?.sessionId !== void 0 ? String(stMeta.sessionId) : void 0;
993
1151
  upsertEntry({
994
1152
  id,
@@ -1176,30 +1334,39 @@ function SubAgentPanel(props) {
1176
1334
  found = tryMatchAndUpdate(parentCache, sid, status, nowTs);
1177
1335
  }
1178
1336
  if (!found) {
1179
- const data = loadSessionData();
1337
+ const data = loadSessionData2();
1180
1338
  const rec = data[parentSid];
1181
- if (rec?.entries) {
1339
+ if (rec?.entries?.length) {
1182
1340
  const fallbackMap = new Map(rec.entries.map((e) => [e.id, e]));
1183
1341
  found = tryMatchAndUpdate(fallbackMap, sid, status, nowTs);
1184
1342
  if (found) {
1185
- data[parentSid] = {
1186
- ...rec,
1187
- ts: nowTs,
1188
- entries: [...fallbackMap.values()]
1189
- };
1190
- saveSessionData(data);
1191
1343
  globalEntryCache.set(parentSid, fallbackMap);
1344
+ mutateSessionData((latest) => {
1345
+ const latestRec = latest[parentSid];
1346
+ latest[parentSid] = {
1347
+ ...latestRec ?? rec,
1348
+ ts: nowTs,
1349
+ entries: [...mergeSubEntries(latestRec?.entries ?? [], fallbackMap.values()).values()]
1350
+ };
1351
+ });
1192
1352
  }
1193
1353
  }
1194
1354
  }
1195
1355
  if (found && parentCache) {
1196
- const data = loadSessionData();
1197
- data[parentSid] = {
1198
- ...data[parentSid],
1199
- ts: nowTs,
1200
- entries: [...parentCache.values()]
1201
- };
1202
- saveSessionData(data);
1356
+ mutateSessionData((data) => {
1357
+ const rec = data[parentSid];
1358
+ data[parentSid] = {
1359
+ ...rec ?? {
1360
+ entries: [],
1361
+ scroll: 0,
1362
+ expanded: "",
1363
+ children: {},
1364
+ ts: nowTs
1365
+ },
1366
+ ts: nowTs,
1367
+ entries: [...mergeSubEntries(rec?.entries ?? [], parentCache.values()).values()]
1368
+ };
1369
+ });
1203
1370
  }
1204
1371
  }
1205
1372
  } catch {
@@ -1238,52 +1405,357 @@ function SubAgentPanel(props) {
1238
1405
  }, 150);
1239
1406
  };
1240
1407
  const bump = () => setRenderTick((v) => v + 1);
1408
+ const settleIdleEntries = (prev) => {
1409
+ let changed = false;
1410
+ const next = new Map(prev);
1411
+ for (const [id, entry] of next) {
1412
+ if (entry.status !== "running" && entry.status !== "cancel_requested") continue;
1413
+ let sid = entry.sessionId;
1414
+ if (!sid) {
1415
+ try {
1416
+ const callId = String(entry.id).replace(/^tool:/, "");
1417
+ sid = props.api.session.resolveChild?.({
1418
+ parentId: props.sessionId,
1419
+ callId,
1420
+ agent: entry.agent,
1421
+ startedAt: entry.startedAt
1422
+ });
1423
+ } catch {
1424
+ }
1425
+ if (!sid) continue;
1426
+ next.set(id, {
1427
+ ...entry,
1428
+ sessionId: sid
1429
+ });
1430
+ changed = true;
1431
+ }
1432
+ try {
1433
+ const st = props.api.session.status(sid);
1434
+ if (!st || st.type !== "idle") continue;
1435
+ const tokens = props.api.usage.readSessionTokens(sid);
1436
+ const cost = props.api.usage.readSessionCost(sid);
1437
+ const finalStatus = entry.status === "cancel_requested" && entry.abortAccepted ? "cancelled" : "done";
1438
+ next.set(id, {
1439
+ ...entry,
1440
+ sessionId: sid,
1441
+ status: finalStatus,
1442
+ endedAt: Date.now(),
1443
+ tokens: tokens ?? entry.tokens,
1444
+ cost: cost ?? entry.cost
1445
+ });
1446
+ changed = true;
1447
+ } catch {
1448
+ }
1449
+ }
1450
+ return changed ? next : prev;
1451
+ };
1452
+ const reconcileIdleEntries = () => {
1453
+ try {
1454
+ const before = entryMap();
1455
+ const after = settleIdleEntries(before);
1456
+ if (after !== before) setEntryMap(after);
1457
+ } catch {
1458
+ }
1459
+ };
1460
+ const runSessionScan = (sid, replace) => {
1461
+ let msgs;
1462
+ try {
1463
+ msgs = props.api.session.messages(sid);
1464
+ } catch {
1465
+ }
1466
+ if (!msgs || msgs.length === 0) return false;
1467
+ setEntryMapRaw((prev) => {
1468
+ const next = replace ? new Map(globalEntryCache.get(sid) ?? loadEntries(sid)) : new Map(prev);
1469
+ const {
1470
+ parentSid: scanPSid,
1471
+ isChild: scanChild
1472
+ } = resolveParent(sid);
1473
+ const scanRec = loadSessionData2()[scanPSid];
1474
+ const clearedIds = new Set(scanChild ? scanRec?.children?.[sid]?.clearedIds : scanRec?.clearedIds);
1475
+ try {
1476
+ for (const msg of msgs) {
1477
+ const parts = props.api.session.part(msg.id) ?? [];
1478
+ for (const partRaw of parts) {
1479
+ const part = partRaw;
1480
+ if (part.type === "tool") {
1481
+ const tool = String(part.tool ?? "");
1482
+ if (!SUBAGENT_TOOLS.has(tool)) continue;
1483
+ const id = `tool:${String(part.id ?? "")}`;
1484
+ if (!part.id) continue;
1485
+ const st = part.state;
1486
+ const rawStatus = String(st?.status ?? "");
1487
+ const scanInput = st?.input;
1488
+ const scanMeta = partMetadata(part, st);
1489
+ let scanSubSid = scanMeta?.session_id !== void 0 ? String(scanMeta.session_id) : scanMeta?.sessionId !== void 0 ? String(scanMeta.sessionId) : scanMeta?.sessionID !== void 0 ? String(scanMeta.sessionID) : void 0;
1490
+ if (!scanSubSid) {
1491
+ try {
1492
+ scanSubSid = props.api.session.resolveChild?.({
1493
+ parentId: sid,
1494
+ callId: String(part.id)
1495
+ });
1496
+ } catch {
1497
+ }
1498
+ }
1499
+ const existingKey = findSubEntryKey(next, id, scanSubSid);
1500
+ const exists = existingKey ? next.get(existingKey) : void 0;
1501
+ if (!exists && clearedIds.has(id)) continue;
1502
+ if ((rawStatus === "pending" || rawStatus === "") && !exists) continue;
1503
+ if (rawStatus === "error") {
1504
+ if (exists && exists.status === "running") {
1505
+ next.set(existingKey ?? id, {
1506
+ ...exists,
1507
+ status: "error",
1508
+ endedAt: Date.now()
1509
+ });
1510
+ }
1511
+ continue;
1512
+ }
1513
+ const settled = exists !== void 0 && exists.status !== "running" && exists.status !== "cancel_requested";
1514
+ const needsRepair = settled && (exists.sessionId === void 0 || exists.tokens === void 0 || exists.cost === void 0 || !validModel(exists.model));
1515
+ if (settled && !needsRepair) continue;
1516
+ let status = "running";
1517
+ if (rawStatus === "completed") status = "done";
1518
+ if ((scanInput?.run_in_background === true || scanInput?.background === true) && status === "done") {
1519
+ if (scanSubSid !== void 0) status = "running";
1520
+ }
1521
+ if (status === "done" && scanSubSid) {
1522
+ try {
1523
+ const childStatus = props.api.session.status(scanSubSid);
1524
+ if (childStatus && childStatus.type !== "idle") status = "running";
1525
+ } catch {
1526
+ }
1527
+ }
1528
+ if (settled) {
1529
+ status = exists.status;
1530
+ } else if (exists && status === "running") {
1531
+ if (!rawStatus) {
1532
+ const msgTokens = msg?.tokens;
1533
+ if (msgTokens && (Number(msgTokens.input) > 0 || Number(msgTokens.output) > 0)) {
1534
+ status = "done";
1535
+ } else {
1536
+ continue;
1537
+ }
1538
+ } else {
1539
+ continue;
1540
+ }
1541
+ }
1542
+ const agent = String(part.subagent_type ?? scanInput?.agent ?? scanInput?.subagent_type ?? tool);
1543
+ const prompt = String(scanInput?.prompt ?? part.description ?? "");
1544
+ const desc = scanInput?.description !== void 0 ? String(scanInput.description) : "";
1545
+ const title = desc || truncate(prompt.replace(/\n/g, " ").trim(), 40);
1546
+ let tokens;
1547
+ if (scanSubSid) tokens = props.api.usage.readSessionTokens(scanSubSid);
1548
+ let cost;
1549
+ if (scanSubSid) cost = props.api.usage.readSessionCost(scanSubSid);
1550
+ let model;
1551
+ if (scanSubSid) model = props.api.usage.readSessionModel(scanSubSid);
1552
+ const entryKey = existingKey ?? id;
1553
+ next.set(entryKey, {
1554
+ ...exists ?? {
1555
+ id
1556
+ },
1557
+ title: title || exists?.title || "",
1558
+ agent: exists?.agent ?? agent,
1559
+ prompt: prompt || exists?.prompt || "",
1560
+ // Preserve existing values (from handleSessionEnd / KV) — scan must not overwrite
1561
+ tokens: exists?.tokens ?? tokens,
1562
+ cost: exists?.cost ?? cost,
1563
+ model: validModel(exists?.model) ? exists.model : model,
1564
+ sessionId: exists?.sessionId ?? scanSubSid,
1565
+ status,
1566
+ startedAt: exists?.startedAt || Date.now(),
1567
+ endedAt: exists?.endedAt ?? (status === "done" ? Date.now() : void 0)
1568
+ });
1569
+ if (entryKey !== id) next.delete(id);
1570
+ }
1571
+ }
1572
+ }
1573
+ } catch {
1574
+ }
1575
+ return next;
1576
+ });
1577
+ setEntryMapRaw((prev) => settleIdleEntries(prev));
1578
+ const snapshot = entryMap();
1579
+ if (snapshot.size > 0) {
1580
+ globalEntryCache.set(sid, new Map(snapshot));
1581
+ persistEntries(sid, snapshot);
1582
+ }
1583
+ return true;
1584
+ };
1585
+ let rescanTimer;
1586
+ const missingNewestToolPart = () => {
1587
+ try {
1588
+ const msgs = props.api.session.messages(props.sessionId);
1589
+ if (!msgs || msgs.length === 0) return false;
1590
+ const map = entryMap();
1591
+ const from = Math.max(0, msgs.length - 6);
1592
+ for (let i = msgs.length - 1; i >= from; i--) {
1593
+ const parts = props.api.session.part(msgs[i]?.id) ?? [];
1594
+ for (const part of parts) {
1595
+ if (!part || part.type !== "tool") continue;
1596
+ if (!SUBAGENT_TOOLS.has(String(part.tool ?? ""))) continue;
1597
+ const partId = String(part.id ?? "");
1598
+ if (!partId) continue;
1599
+ const st = part.state;
1600
+ const rawStatus = String(st?.status ?? "");
1601
+ if (rawStatus !== "running" && rawStatus !== "completed") continue;
1602
+ const id = `tool:${partId}`;
1603
+ const meta = partMetadata(part, st);
1604
+ const subSid = meta?.session_id !== void 0 ? String(meta.session_id) : meta?.sessionId !== void 0 ? String(meta.sessionId) : void 0;
1605
+ const key = findSubEntryKey(map, id, subSid);
1606
+ const entry = key ? map.get(key) : void 0;
1607
+ if (!entry) return true;
1608
+ const input = st?.input;
1609
+ const isBackground = input?.background === true || input?.run_in_background === true;
1610
+ const stillRunning = entry.status === "running" || entry.status === "cancel_requested";
1611
+ if (rawStatus === "completed" && !isBackground && stillRunning) return true;
1612
+ }
1613
+ }
1614
+ } catch {
1615
+ }
1616
+ return false;
1617
+ };
1618
+ const scheduleRescan = () => {
1619
+ if (disposed) return;
1620
+ clearTimeout(rescanTimer);
1621
+ rescanTimer = setTimeout(() => {
1622
+ if (disposed) return;
1623
+ untrack(() => {
1624
+ if (!missingNewestToolPart()) return;
1625
+ runSessionScan(props.sessionId, false);
1626
+ bump();
1627
+ });
1628
+ }, 400);
1629
+ };
1630
+ let enrichCursor = 0;
1241
1631
  onMount(() => {
1242
1632
  const clock = setInterval(() => {
1243
1633
  setNow(Date.now());
1244
1634
  bump();
1245
1635
  }, 100);
1636
+ let tick = 0;
1246
1637
  const tokenTimer = setInterval(() => {
1638
+ tick++;
1247
1639
  untrack(() => {
1640
+ let enriched;
1641
+ let attempted = 0;
1248
1642
  setEntryMapRaw((prev) => {
1249
1643
  let changed = false;
1250
1644
  const next = new Map(prev);
1645
+ const candidates = [];
1251
1646
  for (const [id, entry] of next) {
1252
- if (entry.status === "running" && entry.sessionId) {
1647
+ const running = entry.status === "running" || entry.status === "cancel_requested";
1648
+ const missing = entry.tokens === void 0 || entry.cost === void 0 || !validModel(entry.model);
1649
+ if (entry.sessionId && !running && !missing) continue;
1650
+ candidates.push(id);
1651
+ }
1652
+ if (candidates.length > 0) {
1653
+ const start = enrichCursor % candidates.length;
1654
+ let budget = 4;
1655
+ for (let k = 0; k < candidates.length && budget > 0; k++) {
1656
+ const id = candidates[(start + k) % candidates.length];
1657
+ let entry = next.get(id);
1658
+ if (!entry) continue;
1659
+ if (!entry.sessionId) {
1660
+ try {
1661
+ const callId = String(entry.id).replace(/^tool:/, "");
1662
+ const sid = props.api.session.resolveChild?.({
1663
+ parentId: props.sessionId,
1664
+ callId,
1665
+ agent: entry.agent,
1666
+ startedAt: entry.startedAt
1667
+ });
1668
+ if (sid) {
1669
+ entry = {
1670
+ ...entry,
1671
+ sessionId: sid
1672
+ };
1673
+ next.set(id, entry);
1674
+ changed = true;
1675
+ }
1676
+ } catch {
1677
+ }
1678
+ }
1679
+ if (!entry.sessionId) continue;
1680
+ const childSid = entry.sessionId;
1253
1681
  let isChild = false;
1254
1682
  try {
1255
- const s = props.api.session.get(entry.sessionId);
1256
- isChild = s?.parentID === props.sessionId;
1683
+ const s = props.api.session.get(childSid);
1684
+ isChild = !s || s.parentID === props.sessionId;
1257
1685
  } catch {
1686
+ isChild = true;
1258
1687
  }
1259
1688
  if (!isChild) continue;
1260
- const total = props.api.usage.readSessionTokens(entry.sessionId);
1261
- const todo = props.api.usage.readSessionTodo(entry.sessionId);
1262
- const model = entry.model ?? props.api.usage.readSessionModel(entry.sessionId);
1689
+ budget--;
1690
+ attempted++;
1691
+ const running = entry.status === "running" || entry.status === "cancel_requested";
1692
+ let entryChanged = false;
1263
1693
  const nextEntry = {
1264
1694
  ...entry
1265
1695
  };
1266
- if (total !== void 0 && total !== entry.tokens) {
1267
- nextEntry.tokens = total;
1268
- changed = true;
1696
+ if (running || entry.tokens === void 0) {
1697
+ const total = props.api.usage.readSessionTokens(childSid);
1698
+ if (total !== void 0 && total !== entry.tokens) {
1699
+ nextEntry.tokens = total;
1700
+ entryChanged = true;
1701
+ }
1702
+ }
1703
+ if (running || entry.cost === void 0) {
1704
+ const cost = props.api.usage.readSessionCost(childSid);
1705
+ if (cost !== void 0 && cost !== entry.cost) {
1706
+ nextEntry.cost = cost;
1707
+ entryChanged = true;
1708
+ }
1269
1709
  }
1270
- if (todo !== void 0) {
1271
- if (todo.total !== entry.todoTotal || todo.done !== entry.todoDone) {
1710
+ if (running || !validModel(entry.model)) {
1711
+ const model = props.api.usage.readSessionModel(childSid);
1712
+ if (model && model !== entry.model) {
1713
+ nextEntry.model = model;
1714
+ entryChanged = true;
1715
+ }
1716
+ }
1717
+ if (running) {
1718
+ const todo = props.api.usage.readSessionTodo(childSid);
1719
+ if (todo !== void 0 && (todo.total !== entry.todoTotal || todo.done !== entry.todoDone)) {
1272
1720
  nextEntry.todoTotal = todo.total;
1273
1721
  nextEntry.todoDone = todo.done;
1274
- changed = true;
1722
+ entryChanged = true;
1275
1723
  }
1276
1724
  }
1277
- if (model && !entry.model) {
1278
- nextEntry.model = model;
1725
+ if (entryChanged) {
1726
+ next.set(id, nextEntry);
1279
1727
  changed = true;
1280
1728
  }
1281
- if (changed) next.set(id, nextEntry);
1282
1729
  }
1730
+ enrichCursor = (start + Math.max(1, attempted)) % candidates.length;
1283
1731
  }
1284
- return changed ? next : prev;
1732
+ if (!changed) return prev;
1733
+ enriched = next;
1734
+ return next;
1285
1735
  });
1736
+ if (enriched) {
1737
+ globalEntryCache.set(props.sessionId, new Map(enriched));
1738
+ persistEntries(props.sessionId, enriched);
1739
+ }
1286
1740
  });
1741
+ reconcileIdleEntries();
1742
+ if (tick % 4 === 0) {
1743
+ untrack(() => {
1744
+ if (missingNewestToolPart()) {
1745
+ runSessionScan(props.sessionId, false);
1746
+ bump();
1747
+ }
1748
+ });
1749
+ }
1750
+ if (tick % 60 === 0) {
1751
+ untrack(() => {
1752
+ const hasRunning = entryList().some((e) => e.status === "running" || e.status === "cancel_requested");
1753
+ if (hasRunning) {
1754
+ runSessionScan(props.sessionId, false);
1755
+ bump();
1756
+ }
1757
+ });
1758
+ }
1287
1759
  bump();
1288
1760
  }, 500);
1289
1761
  bump();
@@ -1291,7 +1763,10 @@ function SubAgentPanel(props) {
1291
1763
  handlePartUpdated(e);
1292
1764
  bump();
1293
1765
  });
1294
- const unsubMsg = props.api.event.on("message.updated", () => bump());
1766
+ const unsubMsg = props.api.event.on("message.updated", () => {
1767
+ bump();
1768
+ scheduleRescan();
1769
+ });
1295
1770
  const unsubIdle = props.api.event.on("session.idle", (e) => {
1296
1771
  handleSessionEnd(e, "done");
1297
1772
  bump();
@@ -1302,6 +1777,7 @@ function SubAgentPanel(props) {
1302
1777
  });
1303
1778
  onCleanup(() => {
1304
1779
  disposed = true;
1780
+ clearTimeout(rescanTimer);
1305
1781
  clearInterval(clock);
1306
1782
  clearInterval(tokenTimer);
1307
1783
  unsubPart();
@@ -1319,139 +1795,38 @@ function SubAgentPanel(props) {
1319
1795
  const tick = clearTick();
1320
1796
  const forceReload = tick !== lastTick && !switched;
1321
1797
  lastTick = tick;
1322
- const t2 = setTimeout(() => {
1798
+ let attempts = 0;
1799
+ let retryTimer;
1800
+ const run = () => {
1323
1801
  untrack(() => {
1324
1802
  if (switched) {
1325
1803
  const {
1326
1804
  parentSid,
1327
1805
  isChild
1328
1806
  } = resolveParent(sid);
1329
- const data = loadSessionData();
1807
+ const data = loadSessionData2();
1330
1808
  const saved = isChild ? data[parentSid]?.children?.[sid]?.scroll ?? 0 : data[sid]?.scroll ?? 0;
1331
1809
  setScrollOffset(saved);
1332
1810
  if (!isChild && data[sid]?.entries?.length) {
1333
- data[sid].ts = Date.now();
1334
- saveSessionData(data);
1811
+ void Promise.resolve(updateSessionData(props.api.kv, (latest) => {
1812
+ if (latest[sid]?.entries?.length) latest[sid].ts = Date.now();
1813
+ })).catch(() => {
1814
+ });
1335
1815
  }
1336
1816
  }
1337
- setEntryMapRaw((prev) => {
1338
- const next = switched || forceReload ? new Map(globalEntryCache.get(sid) ?? loadEntries(sid)) : new Map(prev);
1339
- const {
1340
- parentSid: scanPSid,
1341
- isChild: scanChild
1342
- } = resolveParent(sid);
1343
- const scanRec = loadSessionData()[scanPSid];
1344
- const clearedIds = new Set(scanChild ? scanRec?.children?.[sid]?.clearedIds : scanRec?.clearedIds);
1345
- try {
1346
- const msgs = props.api.session.messages(sid);
1347
- if (msgs && msgs.length) {
1348
- for (const msg of msgs) {
1349
- const parts = props.api.session.part(msg.id) ?? [];
1350
- for (const partRaw of parts) {
1351
- const part = partRaw;
1352
- if (part.type === "tool") {
1353
- const tool = String(part.tool ?? "");
1354
- if (!SUBAGENT_TOOLS.has(tool)) continue;
1355
- const id = `tool:${String(part.id ?? "")}`;
1356
- if (!part.id) continue;
1357
- const st = part.state;
1358
- const rawStatus = String(st?.status ?? "");
1359
- const scanStMeta = st?.metadata;
1360
- const scanSubSid = scanStMeta?.session_id !== void 0 ? String(scanStMeta.session_id) : scanStMeta?.sessionId !== void 0 ? String(scanStMeta.sessionId) : void 0;
1361
- const existingKey = findSubEntryKey(next, id, scanSubSid);
1362
- const exists = existingKey ? next.get(existingKey) : void 0;
1363
- if (!exists && clearedIds.has(id)) continue;
1364
- if ((rawStatus === "pending" || rawStatus === "") && !exists) continue;
1365
- if (rawStatus === "error") {
1366
- if (exists && exists.status === "running") {
1367
- next.set(existingKey ?? id, {
1368
- ...exists,
1369
- status: "error",
1370
- endedAt: Date.now()
1371
- });
1372
- }
1373
- continue;
1374
- }
1375
- let status = "running";
1376
- if (rawStatus === "completed") status = "done";
1377
- if ((st?.input?.run_in_background === true || st?.input?.background === true) && status === "done") {
1378
- const scanStMeta2 = st?.metadata;
1379
- const scanHasChild = scanStMeta2?.session_id !== void 0 || scanStMeta2?.sessionId !== void 0;
1380
- if (scanHasChild) status = "running";
1381
- }
1382
- if (exists && existingKey === id && exists.status !== "running" && exists.status !== "cancel_requested") continue;
1383
- if (exists && status === "running") {
1384
- if (!rawStatus) {
1385
- const msgTokens = msg?.tokens;
1386
- if (msgTokens && (Number(msgTokens.input) > 0 || Number(msgTokens.output) > 0)) {
1387
- status = "done";
1388
- } else if (Date.now() - exists.startedAt > 30 * 60 * 1e3) {
1389
- status = "done";
1390
- } else {
1391
- continue;
1392
- }
1393
- } else {
1394
- continue;
1395
- }
1396
- }
1397
- const input = st?.input;
1398
- const agent = String(part.subagent_type ?? input?.agent ?? input?.subagent_type ?? tool);
1399
- const prompt = String(input?.prompt ?? part.description ?? "");
1400
- const desc = input?.description !== void 0 ? String(input.description) : "";
1401
- const title = desc || truncate(prompt.replace(/\n/g, " ").trim(), 40);
1402
- let tokens;
1403
- if (scanSubSid) tokens = props.api.usage.readSessionTokens(scanSubSid);
1404
- const ended = status === "done";
1405
- const entryKey = existingKey ?? id;
1406
- next.set(entryKey, {
1407
- id: exists?.id ?? id,
1408
- title,
1409
- agent,
1410
- prompt,
1411
- // Preserve existing values (from handleSessionEnd / KV) — scan must not overwrite
1412
- tokens: exists?.tokens ?? tokens,
1413
- sessionId: exists?.sessionId ?? scanSubSid,
1414
- status,
1415
- startedAt: exists?.startedAt || Date.now(),
1416
- endedAt: ended ? exists?.endedAt || Date.now() : void 0
1417
- });
1418
- if (entryKey !== id) next.delete(id);
1419
- }
1420
- }
1421
- }
1422
- }
1423
- } catch {
1424
- }
1425
- return next;
1426
- });
1427
- setEntryMapRaw((prev) => {
1428
- let changed = false;
1429
- const next = new Map(prev);
1430
- for (const [id, entry] of next) {
1431
- if (entry.status !== "running" && entry.status !== "cancel_requested" || !entry.sessionId) continue;
1432
- try {
1433
- const st = props.api.session.status(entry.sessionId);
1434
- if (!st || st.type !== "idle") continue;
1435
- const tokens = props.api.usage.readSessionTokens(entry.sessionId);
1436
- const cost = props.api.usage.readSessionCost(entry.sessionId);
1437
- const finalStatus = entry.status === "cancel_requested" && entry.abortAccepted ? "cancelled" : "done";
1438
- next.set(id, {
1439
- ...entry,
1440
- status: finalStatus,
1441
- endedAt: Date.now(),
1442
- tokens: tokens ?? entry.tokens,
1443
- cost: cost ?? entry.cost
1444
- });
1445
- changed = true;
1446
- } catch {
1447
- }
1448
- }
1449
- return changed ? next : prev;
1450
- });
1451
- bump();
1817
+ const ok = runSessionScan(sid, switched || forceReload);
1818
+ if (!ok && attempts < 4) {
1819
+ attempts++;
1820
+ retryTimer = setTimeout(run, 400);
1821
+ }
1452
1822
  });
1453
- }, 150);
1454
- onCleanup(() => clearTimeout(t2));
1823
+ bump();
1824
+ };
1825
+ const t2 = setTimeout(run, 150);
1826
+ onCleanup(() => {
1827
+ clearTimeout(t2);
1828
+ if (retryTimer) clearTimeout(retryTimer);
1829
+ });
1455
1830
  });
1456
1831
  const pal = createMemo(() => {
1457
1832
  const th = props.theme;
@@ -1830,14 +2205,17 @@ function SubAgentPanel(props) {
1830
2205
  return "#" + [r, g, bl].map((v) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, "0")).join("");
1831
2206
  };
1832
2207
  const timeColor = () => isActiveRunning ? pal().warning : isError ? pal().error : pal().muted;
1833
- const tokenText = () => !isExpanded() && entry.tokens !== void 0 && entry.tokens > 0 ? ` ${fmtTokens(entry.tokens)}` : "";
1834
- const timeText = () => !isExpanded() && (elapsed() >= 2e3 || entry.endedAt !== void 0) ? fmtDurationShort(elapsed(), isActiveRunning) : "";
2208
+ const tokenText = () => !isExpanded() && props.showEntryTokens() && entry.tokens !== void 0 && entry.tokens > 0 ? ` ${fmtTokens(entry.tokens)}` : "";
2209
+ const costText = () => !isExpanded() && props.showEntryCost() && entry.cost !== void 0 && entry.cost > 0 ? ` $${entry.cost.toFixed(4)}` : "";
2210
+ const timeText = () => !isExpanded() && props.showEntryTime() && (elapsed() >= 2e3 || entry.endedAt !== void 0) ? fmtDuration(elapsed(), isActiveRunning, props.timeFormat()) : "";
1835
2211
  const suffixW = () => {
1836
2212
  let w = 0;
1837
2213
  const t2 = timeText();
1838
2214
  if (t2) w += 1 + visualWidth(t2);
1839
2215
  const tk = tokenText();
1840
2216
  if (tk) w += visualWidth(tk);
2217
+ const c = costText();
2218
+ if (c) w += visualWidth(c);
1841
2219
  return w;
1842
2220
  };
1843
2221
  const labelAvail = () => Math.max(6, panelWidth() - gutter() - LEFT_PAD - suffixW());
@@ -1881,6 +2259,17 @@ function SubAgentPanel(props) {
1881
2259
  return _el$83;
1882
2260
  })() : null;
1883
2261
  })(), null);
2262
+ _$insert(_el$25, (() => {
2263
+ var _c$4 = _$memo(() => !!costText());
2264
+ return () => _c$4() ? (() => {
2265
+ var _el$84 = _$createElement("span");
2266
+ _$insert(_el$84, costText);
2267
+ _$effect((_$p) => _$setProp(_el$84, "style", {
2268
+ fg: pal().warning
2269
+ }, _$p));
2270
+ return _el$84;
2271
+ })() : null;
2272
+ })(), null);
1884
2273
  _$effect((_p$) => {
1885
2274
  var _v$3 = {
1886
2275
  fg: pal().muted
@@ -1973,7 +2362,7 @@ function SubAgentPanel(props) {
1973
2362
  _$insertNode(_el$45, _el$46);
1974
2363
  _$insert(_el$45, () => t("time.label"), _el$46);
1975
2364
  _$insert(_el$47, () => " ".repeat(expandedPad(t("time.label"))));
1976
- _$insert(_el$48, () => fmtDurationShort(elapsed(), isActiveRunning));
2365
+ _$insert(_el$48, () => fmtDuration(elapsed(), isActiveRunning, props.timeFormat()));
1977
2366
  _$effect((_p$) => {
1978
2367
  var _v$10 = {
1979
2368
  fg: pal().primary
@@ -2067,16 +2456,16 @@ function SubAgentPanel(props) {
2067
2456
  return (() => {
2068
2457
  const cost = entry.cost;
2069
2458
  return (() => {
2070
- 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(`$`);
2071
- _$insertNode(_el$84, _el$85);
2072
- _$insertNode(_el$84, _el$86);
2073
- _$insertNode(_el$84, _el$88);
2074
- _$insertNode(_el$84, _el$89);
2075
- _$insertNode(_el$86, _el$87);
2076
- _$insert(_el$86, () => t("cost.label"), _el$87);
2077
- _$insert(_el$88, () => " ".repeat(expandedPad(t("cost.label"))));
2078
- _$insertNode(_el$89, _el$90);
2079
- _$insert(_el$89, () => cost.toFixed(4), null);
2459
+ var _el$85 = _$createElement("text"), _el$86 = _$createTextNode(` `), _el$87 = _$createElement("span"), _el$88 = _$createTextNode(`: `), _el$89 = _$createElement("span"), _el$90 = _$createElement("span"), _el$91 = _$createTextNode(`$`);
2460
+ _$insertNode(_el$85, _el$86);
2461
+ _$insertNode(_el$85, _el$87);
2462
+ _$insertNode(_el$85, _el$89);
2463
+ _$insertNode(_el$85, _el$90);
2464
+ _$insertNode(_el$87, _el$88);
2465
+ _$insert(_el$87, () => t("cost.label"), _el$88);
2466
+ _$insert(_el$89, () => " ".repeat(expandedPad(t("cost.label"))));
2467
+ _$insertNode(_el$90, _el$91);
2468
+ _$insert(_el$90, () => cost.toFixed(4), null);
2080
2469
  _$effect((_p$) => {
2081
2470
  var _v$29 = {
2082
2471
  fg: pal().primary
@@ -2085,22 +2474,22 @@ function SubAgentPanel(props) {
2085
2474
  }, _v$31 = {
2086
2475
  fg: pal().muted
2087
2476
  };
2088
- _v$29 !== _p$.e && (_p$.e = _$setProp(_el$86, "style", _v$29, _p$.e));
2089
- _v$30 !== _p$.t && (_p$.t = _$setProp(_el$88, "style", _v$30, _p$.t));
2090
- _v$31 !== _p$.a && (_p$.a = _$setProp(_el$89, "style", _v$31, _p$.a));
2477
+ _v$29 !== _p$.e && (_p$.e = _$setProp(_el$87, "style", _v$29, _p$.e));
2478
+ _v$30 !== _p$.t && (_p$.t = _$setProp(_el$89, "style", _v$30, _p$.t));
2479
+ _v$31 !== _p$.a && (_p$.a = _$setProp(_el$90, "style", _v$31, _p$.a));
2091
2480
  return _p$;
2092
2481
  }, {
2093
2482
  e: void 0,
2094
2483
  t: void 0,
2095
2484
  a: void 0
2096
2485
  });
2097
- return _el$84;
2486
+ return _el$85;
2098
2487
  })();
2099
2488
  })();
2100
2489
  }
2101
2490
  }), _$createComponent(Show, {
2102
2491
  get when() {
2103
- return entry.model;
2492
+ return _$memo(() => !!validModel(entry.model))() ? entry.model : void 0;
2104
2493
  },
2105
2494
  get children() {
2106
2495
  var _el$61 = _$createElement("text"), _el$62 = _$createTextNode(` `), _el$63 = _$createElement("span"), _el$64 = _$createTextNode(`: `), _el$65 = _$createElement("span"), _el$66 = _$createElement("span");
@@ -2239,68 +2628,68 @@ ${t("session.toast.copy_failed")}`, {
2239
2628
  const rightW = (isRunning ? visualWidth(dismissLabel()) : 0) + (isRunning && entry.sessionId ? visualWidth(cancelLabel()) : 0);
2240
2629
  const spacerW = () => Math.max(1, panelWidth() - gutter() - openW() - rightW - 2);
2241
2630
  return (() => {
2242
- var _el$91 = _$createElement("box"), _el$95 = _$createElement("text");
2243
- _$insertNode(_el$91, _el$95);
2244
- _$setProp(_el$91, "flexDirection", "row");
2245
- _$insert(_el$91, _$createComponent(Show, {
2631
+ var _el$92 = _$createElement("box"), _el$96 = _$createElement("text");
2632
+ _$insertNode(_el$92, _el$96);
2633
+ _$setProp(_el$92, "flexDirection", "row");
2634
+ _$insert(_el$92, _$createComponent(Show, {
2246
2635
  get when() {
2247
2636
  return entry.sessionId;
2248
2637
  },
2249
2638
  get children() {
2250
- var _el$92 = _$createElement("text"), _el$93 = _$createElement("span"), _el$94 = _$createElement("span");
2251
- _$insertNode(_el$92, _el$93);
2252
- _$insertNode(_el$92, _el$94);
2253
- _$setProp(_el$92, "onMouseOver", () => setHoveredOpen(entry.id));
2254
- _$setProp(_el$92, "onMouseOut", () => setHoveredOpen(void 0));
2255
- _$setProp(_el$92, "onMouseUp", () => {
2639
+ var _el$93 = _$createElement("text"), _el$94 = _$createElement("span"), _el$95 = _$createElement("span");
2640
+ _$insertNode(_el$93, _el$94);
2641
+ _$insertNode(_el$93, _el$95);
2642
+ _$setProp(_el$93, "onMouseOver", () => setHoveredOpen(entry.id));
2643
+ _$setProp(_el$93, "onMouseOut", () => setHoveredOpen(void 0));
2644
+ _$setProp(_el$93, "onMouseUp", () => {
2256
2645
  if (entry.sessionId) {
2257
2646
  props.api.route.navigateSession(entry.sessionId);
2258
2647
  }
2259
2648
  });
2260
- _$insert(_el$93, openPrefix);
2261
- _$insert(_el$94, () => t("open.label"));
2649
+ _$insert(_el$94, openPrefix);
2650
+ _$insert(_el$95, () => t("open.label"));
2262
2651
  _$effect((_p$) => {
2263
2652
  var _v$32 = {
2264
2653
  fg: hoveredOpen() === entry.id ? pal().warning : pal().primary
2265
2654
  }, _v$33 = {
2266
2655
  fg: hoveredOpen() === entry.id ? pal().warning : pal().primary
2267
2656
  };
2268
- _v$32 !== _p$.e && (_p$.e = _$setProp(_el$93, "style", _v$32, _p$.e));
2269
- _v$33 !== _p$.t && (_p$.t = _$setProp(_el$94, "style", _v$33, _p$.t));
2657
+ _v$32 !== _p$.e && (_p$.e = _$setProp(_el$94, "style", _v$32, _p$.e));
2658
+ _v$33 !== _p$.t && (_p$.t = _$setProp(_el$95, "style", _v$33, _p$.t));
2270
2659
  return _p$;
2271
2660
  }, {
2272
2661
  e: void 0,
2273
2662
  t: void 0
2274
2663
  });
2275
- return _el$92;
2664
+ return _el$93;
2276
2665
  }
2277
- }), _el$95);
2278
- _$insert(_el$95, () => " ".repeat(spacerW()));
2279
- _$insert(_el$91, _$createComponent(Show, {
2666
+ }), _el$96);
2667
+ _$insert(_el$96, () => " ".repeat(spacerW()));
2668
+ _$insert(_el$92, _$createComponent(Show, {
2280
2669
  get when() {
2281
2670
  return isRunning && entry.sessionId;
2282
2671
  },
2283
2672
  get children() {
2284
- var _el$96 = _$createElement("text"), _el$97 = _$createElement("span");
2285
- _$insertNode(_el$96, _el$97);
2286
- _$setProp(_el$96, "onMouseOver", () => setHoveredCancel(entry.id));
2287
- _$setProp(_el$96, "onMouseOut", () => setHoveredCancel(void 0));
2288
- _$setProp(_el$96, "onMouseUp", () => cancelEntry(entry));
2289
- _$insert(_el$97, cancelLabel);
2290
- _$effect((_$p) => _$setProp(_el$97, "style", {
2673
+ var _el$97 = _$createElement("text"), _el$98 = _$createElement("span");
2674
+ _$insertNode(_el$97, _el$98);
2675
+ _$setProp(_el$97, "onMouseOver", () => setHoveredCancel(entry.id));
2676
+ _$setProp(_el$97, "onMouseOut", () => setHoveredCancel(void 0));
2677
+ _$setProp(_el$97, "onMouseUp", () => cancelEntry(entry));
2678
+ _$insert(_el$98, cancelLabel);
2679
+ _$effect((_$p) => _$setProp(_el$98, "style", {
2291
2680
  fg: hoveredCancel() === entry.id ? pal().warning : pal().error
2292
2681
  }, _$p));
2293
- return _el$96;
2682
+ return _el$97;
2294
2683
  }
2295
2684
  }), null);
2296
- _$insert(_el$91, _$createComponent(Show, {
2685
+ _$insert(_el$92, _$createComponent(Show, {
2297
2686
  when: isRunning,
2298
2687
  get children() {
2299
- var _el$98 = _$createElement("text"), _el$99 = _$createElement("span");
2300
- _$insertNode(_el$98, _el$99);
2301
- _$setProp(_el$98, "onMouseOver", () => setHoveredDismiss(entry.id));
2302
- _$setProp(_el$98, "onMouseOut", () => setHoveredDismiss(void 0));
2303
- _$setProp(_el$98, "onMouseUp", () => {
2688
+ var _el$99 = _$createElement("text"), _el$100 = _$createElement("span");
2689
+ _$insertNode(_el$99, _el$100);
2690
+ _$setProp(_el$99, "onMouseOver", () => setHoveredDismiss(entry.id));
2691
+ _$setProp(_el$99, "onMouseOut", () => setHoveredDismiss(void 0));
2692
+ _$setProp(_el$99, "onMouseUp", () => {
2304
2693
  upsertEntry({
2305
2694
  id: entry.id,
2306
2695
  title: entry.title,
@@ -2309,17 +2698,17 @@ ${t("session.toast.copy_failed")}`, {
2309
2698
  status: "done"
2310
2699
  });
2311
2700
  });
2312
- _$insert(_el$99, dismissLabel);
2313
- _$effect((_$p) => _$setProp(_el$99, "style", {
2701
+ _$insert(_el$100, dismissLabel);
2702
+ _$effect((_$p) => _$setProp(_el$100, "style", {
2314
2703
  fg: hoveredDismiss() === entry.id ? pal().warning : pal().muted
2315
2704
  }, _$p));
2316
- return _el$98;
2705
+ return _el$99;
2317
2706
  }
2318
2707
  }), null);
2319
- _$effect((_$p) => _$setProp(_el$95, "style", {
2708
+ _$effect((_$p) => _$setProp(_el$96, "style", {
2320
2709
  fg: pal().muted
2321
2710
  }, _$p));
2322
- return _el$91;
2711
+ return _el$92;
2323
2712
  })();
2324
2713
  })();
2325
2714
  }
@@ -2340,13 +2729,13 @@ ${t("session.toast.copy_failed")}`, {
2340
2729
  const right = props.sortOrder() === "desc" ? `\u2191 ${t("scroll.top")}` : `\u2193 ${t("scroll.bottom")}`;
2341
2730
  const pad = showTop ? Math.max(1, panelWidth() - gutter() - visualWidth(left) - visualWidth(right)) : 0;
2342
2731
  return (() => {
2343
- var _el$100 = _$createElement("box"), _el$101 = _$createElement("text"), _el$102 = _$createElement("span");
2344
- _$insertNode(_el$100, _el$101);
2345
- _$setProp(_el$100, "flexDirection", "row");
2732
+ var _el$101 = _$createElement("box"), _el$102 = _$createElement("text"), _el$103 = _$createElement("span");
2346
2733
  _$insertNode(_el$101, _el$102);
2347
- _$setProp(_el$101, "onMouseOver", () => showMore && setHoveredMoreBelow(true));
2348
- _$setProp(_el$101, "onMouseOut", () => setHoveredMoreBelow(false));
2349
- _$setProp(_el$101, "onMouseUp", () => {
2734
+ _$setProp(_el$101, "flexDirection", "row");
2735
+ _$insertNode(_el$102, _el$103);
2736
+ _$setProp(_el$102, "onMouseOver", () => showMore && setHoveredMoreBelow(true));
2737
+ _$setProp(_el$102, "onMouseOut", () => setHoveredMoreBelow(false));
2738
+ _$setProp(_el$102, "onMouseUp", () => {
2350
2739
  if (!showMore) return;
2351
2740
  const total = entryList().length;
2352
2741
  const m = max();
@@ -2358,20 +2747,20 @@ ${t("session.toast.copy_failed")}`, {
2358
2747
  }
2359
2748
  setHoveredMoreBelow(false);
2360
2749
  });
2361
- _$insert(_el$102, left);
2362
- _$insert(_el$100, showTop ? [(() => {
2363
- var _el$103 = _$createElement("text");
2364
- _$insert(_el$103, () => " ".repeat(pad));
2365
- _$effect((_$p) => _$setProp(_el$103, "style", {
2750
+ _$insert(_el$103, left);
2751
+ _$insert(_el$101, showTop ? [(() => {
2752
+ var _el$104 = _$createElement("text");
2753
+ _$insert(_el$104, () => " ".repeat(pad));
2754
+ _$effect((_$p) => _$setProp(_el$104, "style", {
2366
2755
  fg: pal().muted
2367
2756
  }, _$p));
2368
- return _el$103;
2757
+ return _el$104;
2369
2758
  })(), (() => {
2370
- var _el$104 = _$createElement("text"), _el$105 = _$createElement("span");
2371
- _$insertNode(_el$104, _el$105);
2372
- _$setProp(_el$104, "onMouseOver", () => setHoveredTop(true));
2373
- _$setProp(_el$104, "onMouseOut", () => setHoveredTop(false));
2374
- _$setProp(_el$104, "onMouseUp", () => {
2759
+ var _el$105 = _$createElement("text"), _el$106 = _$createElement("span");
2760
+ _$insertNode(_el$105, _el$106);
2761
+ _$setProp(_el$105, "onMouseOver", () => setHoveredTop(true));
2762
+ _$setProp(_el$105, "onMouseOut", () => setHoveredTop(false));
2763
+ _$setProp(_el$105, "onMouseUp", () => {
2375
2764
  const total = entryList().length;
2376
2765
  const m = max();
2377
2766
  if (props.sortOrder() === "desc") {
@@ -2381,16 +2770,16 @@ ${t("session.toast.copy_failed")}`, {
2381
2770
  }
2382
2771
  setHoveredTop(false);
2383
2772
  });
2384
- _$insert(_el$105, right);
2385
- _$effect((_$p) => _$setProp(_el$105, "style", {
2773
+ _$insert(_el$106, right);
2774
+ _$effect((_$p) => _$setProp(_el$106, "style", {
2386
2775
  fg: hoveredTop() ? pal().warning : pal().muted
2387
2776
  }, _$p));
2388
- return _el$104;
2777
+ return _el$105;
2389
2778
  })()] : null, null);
2390
- _$effect((_$p) => _$setProp(_el$102, "style", {
2779
+ _$effect((_$p) => _$setProp(_el$103, "style", {
2391
2780
  fg: showMore && hoveredMoreBelow() ? pal().warning : pal().muted
2392
2781
  }, _$p));
2393
- return _el$100;
2782
+ return _el$101;
2394
2783
  })();
2395
2784
  })();
2396
2785
  }
@@ -2444,6 +2833,18 @@ function createSidebarSlot(api, panelApi, sig) {
2444
2833
  get borderVisible() {
2445
2834
  return sig.borderVisible;
2446
2835
  },
2836
+ get showEntryCost() {
2837
+ return sig.showEntryCost;
2838
+ },
2839
+ get showEntryTime() {
2840
+ return sig.showEntryTime;
2841
+ },
2842
+ get showEntryTokens() {
2843
+ return sig.showEntryTokens;
2844
+ },
2845
+ get timeFormat() {
2846
+ return sig.timeFormat;
2847
+ },
2447
2848
  get sessionId() {
2448
2849
  return input.session_id;
2449
2850
  }
@@ -2460,6 +2861,12 @@ var tui = async (api) => {
2460
2861
  const [sortOrder, setSortOrder] = createSignal3(String(api.kv.get(`${KV_PREFIX}.order`, "desc")) === "asc" ? "asc" : "desc");
2461
2862
  const [scrollMode, setScrollMode] = createSignal3(String(api.kv.get(`${KV_PREFIX}.scroll_mode`, "wheel")) === "click" ? "click" : "wheel");
2462
2863
  const [borderVisible, setBorderVisible] = createSignal3(api.kv.get(`${KV_PREFIX}.border`, false) === true);
2864
+ const [showEntryCost, setShowEntryCost] = createSignal3(api.kv.get(`${KV_PREFIX}.show_entry_cost`, false) === true);
2865
+ const [showEntryTime, setShowEntryTime] = createSignal3(api.kv.get(`${KV_PREFIX}.show_entry_time`, true) !== false);
2866
+ const [showEntryTokens, setShowEntryTokens] = createSignal3(api.kv.get(`${KV_PREFIX}.show_entry_tokens`, true) !== false);
2867
+ const storedTimeFormat = String(api.kv.get(`${KV_PREFIX}.time_format`, "short"));
2868
+ const [timeFormat, setTimeFormat] = createSignal3(TIME_FORMATS.includes(storedTimeFormat) ? storedTimeFormat : "short");
2869
+ const [dbSync, setDbSync] = createSignal3(api.kv.get(`${KV_PREFIX}.db_sync`, true) !== false);
2463
2870
  const signals = {
2464
2871
  lang,
2465
2872
  setLang,
@@ -2471,6 +2878,16 @@ var tui = async (api) => {
2471
2878
  setScrollMode,
2472
2879
  borderVisible,
2473
2880
  setBorderVisible,
2881
+ showEntryCost,
2882
+ setShowEntryCost,
2883
+ showEntryTime,
2884
+ setShowEntryTime,
2885
+ showEntryTokens,
2886
+ setShowEntryTokens,
2887
+ timeFormat,
2888
+ setTimeFormat,
2889
+ dbSync,
2890
+ setDbSync,
2474
2891
  sessionId: ""
2475
2892
  };
2476
2893
  const v1Api = {
@@ -2971,6 +3388,85 @@ var tui = async (api) => {
2971
3388
  });
2972
3389
  dialog?.clear();
2973
3390
  }
3391
+ }, {
3392
+ title: "SubAgent Magazine: Entry Cost",
3393
+ value: "subagent-cost",
3394
+ description: "Show or hide the per-sub-agent cost in the sidebar",
3395
+ slash: {
3396
+ name: "subagent-cost"
3397
+ },
3398
+ onSelect: (dialog) => {
3399
+ const t = createT(() => signals.lang());
3400
+ const cur = Boolean(api.kv.get(`${KV_PREFIX}.show_entry_cost`, false));
3401
+ api.kv.set(`${KV_PREFIX}.show_entry_cost`, !cur);
3402
+ signals.setShowEntryCost(!cur);
3403
+ api.ui.toast({
3404
+ message: `${t("settings.showEntryCost")}: ${!cur ? t("settings.on") : t("settings.off")}`
3405
+ });
3406
+ dialog?.clear();
3407
+ }
3408
+ }, {
3409
+ title: "SubAgent Magazine: Entry Time",
3410
+ value: "subagent-time",
3411
+ description: "Show or hide the elapsed time in the sidebar list",
3412
+ slash: {
3413
+ name: "subagent-time"
3414
+ },
3415
+ onSelect: (dialog) => {
3416
+ const t = createT(() => signals.lang());
3417
+ const cur = Boolean(api.kv.get(`${KV_PREFIX}.show_entry_time`, true));
3418
+ api.kv.set(`${KV_PREFIX}.show_entry_time`, !cur);
3419
+ signals.setShowEntryTime(!cur);
3420
+ api.ui.toast({
3421
+ message: `${t("settings.showEntryTime")}: ${!cur ? t("settings.on") : t("settings.off")}`
3422
+ });
3423
+ dialog?.clear();
3424
+ }
3425
+ }, {
3426
+ title: "SubAgent Magazine: Entry Tokens",
3427
+ value: "subagent-tokens",
3428
+ description: "Show or hide the token count in the sidebar list",
3429
+ slash: {
3430
+ name: "subagent-tokens"
3431
+ },
3432
+ onSelect: (dialog) => {
3433
+ const t = createT(() => signals.lang());
3434
+ const cur = Boolean(api.kv.get(`${KV_PREFIX}.show_entry_tokens`, true));
3435
+ api.kv.set(`${KV_PREFIX}.show_entry_tokens`, !cur);
3436
+ signals.setShowEntryTokens(!cur);
3437
+ api.ui.toast({
3438
+ message: `${t("settings.showEntryTokens")}: ${!cur ? t("settings.on") : t("settings.off")}`
3439
+ });
3440
+ dialog?.clear();
3441
+ }
3442
+ }, {
3443
+ title: "SubAgent Magazine: Time Format",
3444
+ value: "subagent-time-format",
3445
+ description: "Set how elapsed time is displayed",
3446
+ slash: {
3447
+ name: "subagent-time-format"
3448
+ },
3449
+ onSelect: (dialog) => {
3450
+ dialog?.replace(() => _$createComponent2(api.ui.DialogSelect, {
3451
+ title: "Time Format / \u65F6\u95F4\u683C\u5F0F",
3452
+ get options() {
3453
+ return TIME_FORMATS.map((f) => ({
3454
+ title: `${f} \u2014 ${TIME_FORMAT_SAMPLES[f]}`,
3455
+ value: f
3456
+ }));
3457
+ },
3458
+ onSelect: (opt) => {
3459
+ const f = opt.value;
3460
+ setTimeFormat(f);
3461
+ api.kv.set(`${KV_PREFIX}.time_format`, f);
3462
+ const t = createT(() => lang());
3463
+ api.ui.toast({
3464
+ message: `${t("settings.timeFormat")}: ${TIME_FORMAT_SAMPLES[f]}`
3465
+ });
3466
+ dialog?.clear();
3467
+ }
3468
+ }));
3469
+ }
2974
3470
  }]);
2975
3471
  };
2976
3472
  var mod = {