opencode-subagent-magazine 1.6.0-beta.2 → 1.6.0-beta.3
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/_version.d.ts +1 -1
- package/dist/_version.js +1 -1
- package/dist/panel/SubAgentPanel.js +33 -28
- package/dist/panel/entry-map.d.ts +6 -0
- package/dist/panel/entry-map.js +25 -0
- package/dist/tui.js +49 -30
- package/dist/v2.js +49 -30
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/panel/SubAgentPanel.tsx +32 -27
- package/src/panel/entry-map.ts +39 -0
package/dist/_version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PLUGIN_VERSION = "1.6.0-beta.
|
|
1
|
+
export declare const PLUGIN_VERSION = "1.6.0-beta.3";
|
package/dist/_version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// auto-generated
|
|
2
|
-
export const PLUGIN_VERSION = "1.6.0-beta.
|
|
2
|
+
export const PLUGIN_VERSION = "1.6.0-beta.3";
|
|
@@ -9,6 +9,7 @@ import { rgb, desaturateTo, dimColor, FALLBACK, MAX_SAT } from "../core/color";
|
|
|
9
9
|
import { KV_PREFIX } from "../core/kv";
|
|
10
10
|
import { globalEntryCache, clearTick } from "./store";
|
|
11
11
|
import { isDirectChildSession } from "./session-routing";
|
|
12
|
+
import { findSubEntryKey, upsertSubEntry } from "./entry-map";
|
|
12
13
|
/** Entry line left prefix: icon + space + status dot + space */
|
|
13
14
|
const LEFT_PAD = 4;
|
|
14
15
|
/** Detail row indent: two spaces */
|
|
@@ -51,15 +52,16 @@ export function SubAgentPanel(props) {
|
|
|
51
52
|
return { parentSid: sid, isChild: false };
|
|
52
53
|
};
|
|
53
54
|
const loadEntries = (sid) => {
|
|
54
|
-
|
|
55
|
+
let m = new Map();
|
|
55
56
|
try {
|
|
56
57
|
const { parentSid, isChild } = resolveParent(sid);
|
|
57
58
|
const rec = loadSessionData()[parentSid];
|
|
58
59
|
if (rec) {
|
|
59
60
|
const source = isChild ? rec.children?.[sid]?.entries : rec.entries;
|
|
60
61
|
if (source) {
|
|
61
|
-
for (const e of source)
|
|
62
|
-
m
|
|
62
|
+
for (const e of source) {
|
|
63
|
+
m = upsertSubEntry(m, e);
|
|
64
|
+
}
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
67
|
}
|
|
@@ -232,20 +234,7 @@ export function SubAgentPanel(props) {
|
|
|
232
234
|
let disposed = false;
|
|
233
235
|
// ── upsert ──
|
|
234
236
|
const upsertEntry = (partial) => {
|
|
235
|
-
setEntryMap((prev) =>
|
|
236
|
-
const existing = prev.get(partial.id);
|
|
237
|
-
const next = new Map(prev);
|
|
238
|
-
const nowTs = Date.now();
|
|
239
|
-
const e = partial.status;
|
|
240
|
-
const ended = e === "done" || e === "error" || e === "cancelled";
|
|
241
|
-
next.set(partial.id, {
|
|
242
|
-
...(existing ?? { startedAt: nowTs }),
|
|
243
|
-
...partial,
|
|
244
|
-
startedAt: existing?.startedAt || partial.startedAt || nowTs,
|
|
245
|
-
endedAt: ended ? (existing?.endedAt || nowTs) : undefined,
|
|
246
|
-
});
|
|
247
|
-
return next;
|
|
248
|
-
});
|
|
237
|
+
setEntryMap((prev) => upsertSubEntry(prev, partial));
|
|
249
238
|
};
|
|
250
239
|
// ── cancel helpers ──
|
|
251
240
|
const isDescendantOf = (childId, rootId) => {
|
|
@@ -370,9 +359,21 @@ export function SubAgentPanel(props) {
|
|
|
370
359
|
const id = `tool:${String(part.id ?? "")}`;
|
|
371
360
|
if (!part.id)
|
|
372
361
|
return;
|
|
373
|
-
const
|
|
362
|
+
const stMeta = st?.metadata;
|
|
363
|
+
const errorSid = stMeta?.session_id !== undefined ? String(stMeta.session_id)
|
|
364
|
+
: stMeta?.sessionId !== undefined ? String(stMeta.sessionId)
|
|
365
|
+
: undefined;
|
|
366
|
+
const key = findSubEntryKey(entryMap(), id, errorSid);
|
|
367
|
+
const existing = key ? entryMap().get(key) : undefined;
|
|
374
368
|
if (existing) {
|
|
375
|
-
upsertEntry({
|
|
369
|
+
upsertEntry({
|
|
370
|
+
id: existing.id,
|
|
371
|
+
title: existing.title,
|
|
372
|
+
agent: existing.agent,
|
|
373
|
+
prompt: existing.prompt,
|
|
374
|
+
sessionId: existing.sessionId ?? errorSid,
|
|
375
|
+
status: "error",
|
|
376
|
+
});
|
|
376
377
|
}
|
|
377
378
|
return;
|
|
378
379
|
}
|
|
@@ -778,7 +779,12 @@ export function SubAgentPanel(props) {
|
|
|
778
779
|
continue;
|
|
779
780
|
const st = part.state;
|
|
780
781
|
const rawStatus = String(st?.status ?? "");
|
|
781
|
-
const
|
|
782
|
+
const scanStMeta = st?.metadata;
|
|
783
|
+
const scanSubSid = scanStMeta?.session_id !== undefined ? String(scanStMeta.session_id)
|
|
784
|
+
: scanStMeta?.sessionId !== undefined ? String(scanStMeta.sessionId)
|
|
785
|
+
: undefined;
|
|
786
|
+
const existingKey = findSubEntryKey(next, id, scanSubSid);
|
|
787
|
+
const exists = existingKey ? next.get(existingKey) : undefined;
|
|
782
788
|
// 已手动清除的条目:scan 发现但不在内存 → 跳过重建
|
|
783
789
|
if (!exists && clearedIds.has(id))
|
|
784
790
|
continue;
|
|
@@ -789,7 +795,7 @@ export function SubAgentPanel(props) {
|
|
|
789
795
|
// "error": only update existing, never create a new entry
|
|
790
796
|
if (rawStatus === "error") {
|
|
791
797
|
if (exists && exists.status === "running") {
|
|
792
|
-
next.set(id, { ...exists, status: "error", endedAt: Date.now() });
|
|
798
|
+
next.set(existingKey ?? id, { ...exists, status: "error", endedAt: Date.now() });
|
|
793
799
|
}
|
|
794
800
|
continue;
|
|
795
801
|
}
|
|
@@ -805,7 +811,7 @@ export function SubAgentPanel(props) {
|
|
|
805
811
|
status = "running";
|
|
806
812
|
}
|
|
807
813
|
// Already settled → skip
|
|
808
|
-
if (exists && exists.status !== "running" && exists.status !== "cancel_requested")
|
|
814
|
+
if (exists && existingKey === id && exists.status !== "running" && exists.status !== "cancel_requested")
|
|
809
815
|
continue;
|
|
810
816
|
// Running entry with no explicit status improvement from part:
|
|
811
817
|
// try message-level heuristics first, then time-based fallback.
|
|
@@ -834,15 +840,12 @@ export function SubAgentPanel(props) {
|
|
|
834
840
|
const desc = input?.description !== undefined ? String(input.description) : "";
|
|
835
841
|
const title = desc || truncate(prompt.replace(/\n/g, " ").trim(), 40);
|
|
836
842
|
let tokens;
|
|
837
|
-
const scanStMeta2 = st?.metadata;
|
|
838
|
-
const scanSubSid = scanStMeta2?.session_id !== undefined ? String(scanStMeta2.session_id)
|
|
839
|
-
: scanStMeta2?.sessionId !== undefined ? String(scanStMeta2.sessionId)
|
|
840
|
-
: undefined;
|
|
841
843
|
if (scanSubSid)
|
|
842
844
|
tokens = props.api.usage.readSessionTokens(scanSubSid);
|
|
843
845
|
const ended = status === "done"; // "error" handled above, never reaches here
|
|
844
|
-
|
|
845
|
-
|
|
846
|
+
const entryKey = existingKey ?? id;
|
|
847
|
+
next.set(entryKey, {
|
|
848
|
+
id: exists?.id ?? id, title, agent, prompt,
|
|
846
849
|
// Preserve existing values (from handleSessionEnd / KV) — scan must not overwrite
|
|
847
850
|
tokens: exists?.tokens ?? tokens,
|
|
848
851
|
sessionId: exists?.sessionId ?? scanSubSid,
|
|
@@ -850,6 +853,8 @@ export function SubAgentPanel(props) {
|
|
|
850
853
|
startedAt: exists?.startedAt || Date.now(),
|
|
851
854
|
endedAt: ended ? (exists?.endedAt || Date.now()) : undefined,
|
|
852
855
|
});
|
|
856
|
+
if (entryKey !== id)
|
|
857
|
+
next.delete(id);
|
|
853
858
|
}
|
|
854
859
|
}
|
|
855
860
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SubEntry } from "../core/types";
|
|
2
|
+
export type SubEntryPatch = Omit<SubEntry, "startedAt" | "endedAt"> & {
|
|
3
|
+
startedAt?: number;
|
|
4
|
+
};
|
|
5
|
+
export declare function findSubEntryKey(entries: ReadonlyMap<string, SubEntry>, id: string, sessionId?: string): string | undefined;
|
|
6
|
+
export declare function upsertSubEntry(entries: ReadonlyMap<string, SubEntry>, partial: SubEntryPatch, now?: number): Map<string, SubEntry>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function findSubEntryKey(entries, id, sessionId) {
|
|
2
|
+
if (sessionId) {
|
|
3
|
+
for (const [key, entry] of entries) {
|
|
4
|
+
if (entry.sessionId === sessionId)
|
|
5
|
+
return key;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
return entries.has(id) ? id : undefined;
|
|
9
|
+
}
|
|
10
|
+
export function upsertSubEntry(entries, partial, now = Date.now()) {
|
|
11
|
+
const key = findSubEntryKey(entries, partial.id, partial.sessionId) ?? partial.id;
|
|
12
|
+
const existing = entries.get(key);
|
|
13
|
+
const next = new Map(entries);
|
|
14
|
+
if (key !== partial.id)
|
|
15
|
+
next.delete(partial.id);
|
|
16
|
+
const ended = partial.status === "done" || partial.status === "error" || partial.status === "cancelled";
|
|
17
|
+
next.set(key, {
|
|
18
|
+
...(existing ?? { startedAt: now }),
|
|
19
|
+
...partial,
|
|
20
|
+
id: existing?.id ?? partial.id,
|
|
21
|
+
startedAt: existing?.startedAt || partial.startedAt || now,
|
|
22
|
+
endedAt: ended ? (existing?.endedAt || now) : undefined,
|
|
23
|
+
});
|
|
24
|
+
return next;
|
|
25
|
+
}
|
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.0-beta.
|
|
9
|
+
var PLUGIN_VERSION = "1.6.0-beta.3";
|
|
10
10
|
|
|
11
11
|
// src/i18n.ts
|
|
12
12
|
var ZH_T = {
|
|
@@ -490,6 +490,31 @@ function isDirectChildSession(currentSessionId, eventSessionId, eventSession) {
|
|
|
490
490
|
return eventSessionId !== currentSessionId && eventSession?.parentID === currentSessionId;
|
|
491
491
|
}
|
|
492
492
|
|
|
493
|
+
// src/panel/entry-map.ts
|
|
494
|
+
function findSubEntryKey(entries, id, sessionId) {
|
|
495
|
+
if (sessionId) {
|
|
496
|
+
for (const [key, entry] of entries) {
|
|
497
|
+
if (entry.sessionId === sessionId) return key;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return entries.has(id) ? id : void 0;
|
|
501
|
+
}
|
|
502
|
+
function upsertSubEntry(entries, partial, now = Date.now()) {
|
|
503
|
+
const key = findSubEntryKey(entries, partial.id, partial.sessionId) ?? partial.id;
|
|
504
|
+
const existing = entries.get(key);
|
|
505
|
+
const next = new Map(entries);
|
|
506
|
+
if (key !== partial.id) next.delete(partial.id);
|
|
507
|
+
const ended = partial.status === "done" || partial.status === "error" || partial.status === "cancelled";
|
|
508
|
+
next.set(key, {
|
|
509
|
+
...existing ?? { startedAt: now },
|
|
510
|
+
...partial,
|
|
511
|
+
id: existing?.id ?? partial.id,
|
|
512
|
+
startedAt: existing?.startedAt || partial.startedAt || now,
|
|
513
|
+
endedAt: ended ? existing?.endedAt || now : void 0
|
|
514
|
+
});
|
|
515
|
+
return next;
|
|
516
|
+
}
|
|
517
|
+
|
|
493
518
|
// src/panel/SubAgentPanel.tsx
|
|
494
519
|
var LEFT_PAD = 4;
|
|
495
520
|
var INDENT = 2;
|
|
@@ -529,7 +554,7 @@ function SubAgentPanel(props) {
|
|
|
529
554
|
};
|
|
530
555
|
};
|
|
531
556
|
const loadEntries = (sid) => {
|
|
532
|
-
|
|
557
|
+
let m = /* @__PURE__ */ new Map();
|
|
533
558
|
try {
|
|
534
559
|
const {
|
|
535
560
|
parentSid,
|
|
@@ -539,7 +564,9 @@ function SubAgentPanel(props) {
|
|
|
539
564
|
if (rec) {
|
|
540
565
|
const source = isChild ? rec.children?.[sid]?.entries : rec.entries;
|
|
541
566
|
if (source) {
|
|
542
|
-
for (const e of source)
|
|
567
|
+
for (const e of source) {
|
|
568
|
+
m = upsertSubEntry(m, e);
|
|
569
|
+
}
|
|
543
570
|
}
|
|
544
571
|
}
|
|
545
572
|
} catch {
|
|
@@ -777,22 +804,7 @@ function SubAgentPanel(props) {
|
|
|
777
804
|
let boxEl;
|
|
778
805
|
let disposed = false;
|
|
779
806
|
const upsertEntry = (partial) => {
|
|
780
|
-
setEntryMap((prev) =>
|
|
781
|
-
const existing = prev.get(partial.id);
|
|
782
|
-
const next = new Map(prev);
|
|
783
|
-
const nowTs = Date.now();
|
|
784
|
-
const e = partial.status;
|
|
785
|
-
const ended = e === "done" || e === "error" || e === "cancelled";
|
|
786
|
-
next.set(partial.id, {
|
|
787
|
-
...existing ?? {
|
|
788
|
-
startedAt: nowTs
|
|
789
|
-
},
|
|
790
|
-
...partial,
|
|
791
|
-
startedAt: existing?.startedAt || partial.startedAt || nowTs,
|
|
792
|
-
endedAt: ended ? existing?.endedAt || nowTs : void 0
|
|
793
|
-
});
|
|
794
|
-
return next;
|
|
795
|
-
});
|
|
807
|
+
setEntryMap((prev) => upsertSubEntry(prev, partial));
|
|
796
808
|
};
|
|
797
809
|
const isDescendantOf = (childId, rootId) => {
|
|
798
810
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -936,13 +948,17 @@ function SubAgentPanel(props) {
|
|
|
936
948
|
if (rawStatus === "error") {
|
|
937
949
|
const id2 = `tool:${String(part.id ?? "")}`;
|
|
938
950
|
if (!part.id) return;
|
|
939
|
-
const
|
|
951
|
+
const stMeta2 = st?.metadata;
|
|
952
|
+
const errorSid = stMeta2?.session_id !== void 0 ? String(stMeta2.session_id) : stMeta2?.sessionId !== void 0 ? String(stMeta2.sessionId) : void 0;
|
|
953
|
+
const key = findSubEntryKey(entryMap(), id2, errorSid);
|
|
954
|
+
const existing = key ? entryMap().get(key) : void 0;
|
|
940
955
|
if (existing) {
|
|
941
956
|
upsertEntry({
|
|
942
|
-
id:
|
|
957
|
+
id: existing.id,
|
|
943
958
|
title: existing.title,
|
|
944
959
|
agent: existing.agent,
|
|
945
960
|
prompt: existing.prompt,
|
|
961
|
+
sessionId: existing.sessionId ?? errorSid,
|
|
946
962
|
status: "error"
|
|
947
963
|
});
|
|
948
964
|
}
|
|
@@ -1329,12 +1345,15 @@ function SubAgentPanel(props) {
|
|
|
1329
1345
|
if (!part.id) continue;
|
|
1330
1346
|
const st = part.state;
|
|
1331
1347
|
const rawStatus = String(st?.status ?? "");
|
|
1332
|
-
const
|
|
1348
|
+
const scanStMeta = st?.metadata;
|
|
1349
|
+
const scanSubSid = scanStMeta?.session_id !== void 0 ? String(scanStMeta.session_id) : scanStMeta?.sessionId !== void 0 ? String(scanStMeta.sessionId) : void 0;
|
|
1350
|
+
const existingKey = findSubEntryKey(next, id, scanSubSid);
|
|
1351
|
+
const exists = existingKey ? next.get(existingKey) : void 0;
|
|
1333
1352
|
if (!exists && clearedIds.has(id)) continue;
|
|
1334
1353
|
if ((rawStatus === "pending" || rawStatus === "") && !exists) continue;
|
|
1335
1354
|
if (rawStatus === "error") {
|
|
1336
1355
|
if (exists && exists.status === "running") {
|
|
1337
|
-
next.set(id, {
|
|
1356
|
+
next.set(existingKey ?? id, {
|
|
1338
1357
|
...exists,
|
|
1339
1358
|
status: "error",
|
|
1340
1359
|
endedAt: Date.now()
|
|
@@ -1345,11 +1364,11 @@ function SubAgentPanel(props) {
|
|
|
1345
1364
|
let status = "running";
|
|
1346
1365
|
if (rawStatus === "completed") status = "done";
|
|
1347
1366
|
if ((st?.input?.run_in_background === true || st?.input?.background === true) && status === "done") {
|
|
1348
|
-
const
|
|
1349
|
-
const scanHasChild =
|
|
1367
|
+
const scanStMeta2 = st?.metadata;
|
|
1368
|
+
const scanHasChild = scanStMeta2?.session_id !== void 0 || scanStMeta2?.sessionId !== void 0;
|
|
1350
1369
|
if (scanHasChild) status = "running";
|
|
1351
1370
|
}
|
|
1352
|
-
if (exists && exists.status !== "running" && exists.status !== "cancel_requested") continue;
|
|
1371
|
+
if (exists && existingKey === id && exists.status !== "running" && exists.status !== "cancel_requested") continue;
|
|
1353
1372
|
if (exists && status === "running") {
|
|
1354
1373
|
if (!rawStatus) {
|
|
1355
1374
|
const msgTokens = msg?.tokens;
|
|
@@ -1370,12 +1389,11 @@ function SubAgentPanel(props) {
|
|
|
1370
1389
|
const desc = input?.description !== void 0 ? String(input.description) : "";
|
|
1371
1390
|
const title = desc || truncate(prompt.replace(/\n/g, " ").trim(), 40);
|
|
1372
1391
|
let tokens;
|
|
1373
|
-
const scanStMeta2 = st?.metadata;
|
|
1374
|
-
const scanSubSid = scanStMeta2?.session_id !== void 0 ? String(scanStMeta2.session_id) : scanStMeta2?.sessionId !== void 0 ? String(scanStMeta2.sessionId) : void 0;
|
|
1375
1392
|
if (scanSubSid) tokens = props.api.usage.readSessionTokens(scanSubSid);
|
|
1376
1393
|
const ended = status === "done";
|
|
1377
|
-
|
|
1378
|
-
|
|
1394
|
+
const entryKey = existingKey ?? id;
|
|
1395
|
+
next.set(entryKey, {
|
|
1396
|
+
id: exists?.id ?? id,
|
|
1379
1397
|
title,
|
|
1380
1398
|
agent,
|
|
1381
1399
|
prompt,
|
|
@@ -1386,6 +1404,7 @@ function SubAgentPanel(props) {
|
|
|
1386
1404
|
startedAt: exists?.startedAt || Date.now(),
|
|
1387
1405
|
endedAt: ended ? exists?.endedAt || Date.now() : void 0
|
|
1388
1406
|
});
|
|
1407
|
+
if (entryKey !== id) next.delete(id);
|
|
1389
1408
|
}
|
|
1390
1409
|
}
|
|
1391
1410
|
}
|
package/dist/v2.js
CHANGED
|
@@ -329,7 +329,7 @@ function readTTLDays(kv) {
|
|
|
329
329
|
}
|
|
330
330
|
|
|
331
331
|
// src/_version.ts
|
|
332
|
-
var PLUGIN_VERSION = "1.6.0-beta.
|
|
332
|
+
var PLUGIN_VERSION = "1.6.0-beta.3";
|
|
333
333
|
|
|
334
334
|
// src/i18n.ts
|
|
335
335
|
var ZH_T = {
|
|
@@ -1057,6 +1057,31 @@ function isDirectChildSession(currentSessionId, eventSessionId, eventSession) {
|
|
|
1057
1057
|
return eventSessionId !== currentSessionId && eventSession?.parentID === currentSessionId;
|
|
1058
1058
|
}
|
|
1059
1059
|
|
|
1060
|
+
// src/panel/entry-map.ts
|
|
1061
|
+
function findSubEntryKey(entries, id, sessionId) {
|
|
1062
|
+
if (sessionId) {
|
|
1063
|
+
for (const [key, entry] of entries) {
|
|
1064
|
+
if (entry.sessionId === sessionId) return key;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
return entries.has(id) ? id : void 0;
|
|
1068
|
+
}
|
|
1069
|
+
function upsertSubEntry(entries, partial, now = Date.now()) {
|
|
1070
|
+
const key = findSubEntryKey(entries, partial.id, partial.sessionId) ?? partial.id;
|
|
1071
|
+
const existing = entries.get(key);
|
|
1072
|
+
const next = new Map(entries);
|
|
1073
|
+
if (key !== partial.id) next.delete(partial.id);
|
|
1074
|
+
const ended = partial.status === "done" || partial.status === "error" || partial.status === "cancelled";
|
|
1075
|
+
next.set(key, {
|
|
1076
|
+
...existing ?? { startedAt: now },
|
|
1077
|
+
...partial,
|
|
1078
|
+
id: existing?.id ?? partial.id,
|
|
1079
|
+
startedAt: existing?.startedAt || partial.startedAt || now,
|
|
1080
|
+
endedAt: ended ? existing?.endedAt || now : void 0
|
|
1081
|
+
});
|
|
1082
|
+
return next;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1060
1085
|
// src/panel/SubAgentPanel.tsx
|
|
1061
1086
|
var LEFT_PAD = 4;
|
|
1062
1087
|
var INDENT = 2;
|
|
@@ -1096,7 +1121,7 @@ function SubAgentPanel(props) {
|
|
|
1096
1121
|
};
|
|
1097
1122
|
};
|
|
1098
1123
|
const loadEntries = (sid) => {
|
|
1099
|
-
|
|
1124
|
+
let m = /* @__PURE__ */ new Map();
|
|
1100
1125
|
try {
|
|
1101
1126
|
const {
|
|
1102
1127
|
parentSid,
|
|
@@ -1106,7 +1131,9 @@ function SubAgentPanel(props) {
|
|
|
1106
1131
|
if (rec) {
|
|
1107
1132
|
const source = isChild ? rec.children?.[sid]?.entries : rec.entries;
|
|
1108
1133
|
if (source) {
|
|
1109
|
-
for (const e of source)
|
|
1134
|
+
for (const e of source) {
|
|
1135
|
+
m = upsertSubEntry(m, e);
|
|
1136
|
+
}
|
|
1110
1137
|
}
|
|
1111
1138
|
}
|
|
1112
1139
|
} catch {
|
|
@@ -1344,22 +1371,7 @@ function SubAgentPanel(props) {
|
|
|
1344
1371
|
let boxEl;
|
|
1345
1372
|
let disposed = false;
|
|
1346
1373
|
const upsertEntry = (partial) => {
|
|
1347
|
-
setEntryMap((prev) =>
|
|
1348
|
-
const existing = prev.get(partial.id);
|
|
1349
|
-
const next = new Map(prev);
|
|
1350
|
-
const nowTs = Date.now();
|
|
1351
|
-
const e = partial.status;
|
|
1352
|
-
const ended = e === "done" || e === "error" || e === "cancelled";
|
|
1353
|
-
next.set(partial.id, {
|
|
1354
|
-
...existing ?? {
|
|
1355
|
-
startedAt: nowTs
|
|
1356
|
-
},
|
|
1357
|
-
...partial,
|
|
1358
|
-
startedAt: existing?.startedAt || partial.startedAt || nowTs,
|
|
1359
|
-
endedAt: ended ? existing?.endedAt || nowTs : void 0
|
|
1360
|
-
});
|
|
1361
|
-
return next;
|
|
1362
|
-
});
|
|
1374
|
+
setEntryMap((prev) => upsertSubEntry(prev, partial));
|
|
1363
1375
|
};
|
|
1364
1376
|
const isDescendantOf = (childId, rootId) => {
|
|
1365
1377
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -1503,13 +1515,17 @@ function SubAgentPanel(props) {
|
|
|
1503
1515
|
if (rawStatus === "error") {
|
|
1504
1516
|
const id2 = `tool:${String(part.id ?? "")}`;
|
|
1505
1517
|
if (!part.id) return;
|
|
1506
|
-
const
|
|
1518
|
+
const stMeta2 = st?.metadata;
|
|
1519
|
+
const errorSid = stMeta2?.session_id !== void 0 ? String(stMeta2.session_id) : stMeta2?.sessionId !== void 0 ? String(stMeta2.sessionId) : void 0;
|
|
1520
|
+
const key = findSubEntryKey(entryMap(), id2, errorSid);
|
|
1521
|
+
const existing = key ? entryMap().get(key) : void 0;
|
|
1507
1522
|
if (existing) {
|
|
1508
1523
|
upsertEntry({
|
|
1509
|
-
id:
|
|
1524
|
+
id: existing.id,
|
|
1510
1525
|
title: existing.title,
|
|
1511
1526
|
agent: existing.agent,
|
|
1512
1527
|
prompt: existing.prompt,
|
|
1528
|
+
sessionId: existing.sessionId ?? errorSid,
|
|
1513
1529
|
status: "error"
|
|
1514
1530
|
});
|
|
1515
1531
|
}
|
|
@@ -1896,12 +1912,15 @@ function SubAgentPanel(props) {
|
|
|
1896
1912
|
if (!part.id) continue;
|
|
1897
1913
|
const st = part.state;
|
|
1898
1914
|
const rawStatus = String(st?.status ?? "");
|
|
1899
|
-
const
|
|
1915
|
+
const scanStMeta = st?.metadata;
|
|
1916
|
+
const scanSubSid = scanStMeta?.session_id !== void 0 ? String(scanStMeta.session_id) : scanStMeta?.sessionId !== void 0 ? String(scanStMeta.sessionId) : void 0;
|
|
1917
|
+
const existingKey = findSubEntryKey(next, id, scanSubSid);
|
|
1918
|
+
const exists = existingKey ? next.get(existingKey) : void 0;
|
|
1900
1919
|
if (!exists && clearedIds.has(id)) continue;
|
|
1901
1920
|
if ((rawStatus === "pending" || rawStatus === "") && !exists) continue;
|
|
1902
1921
|
if (rawStatus === "error") {
|
|
1903
1922
|
if (exists && exists.status === "running") {
|
|
1904
|
-
next.set(id, {
|
|
1923
|
+
next.set(existingKey ?? id, {
|
|
1905
1924
|
...exists,
|
|
1906
1925
|
status: "error",
|
|
1907
1926
|
endedAt: Date.now()
|
|
@@ -1912,11 +1931,11 @@ function SubAgentPanel(props) {
|
|
|
1912
1931
|
let status = "running";
|
|
1913
1932
|
if (rawStatus === "completed") status = "done";
|
|
1914
1933
|
if ((st?.input?.run_in_background === true || st?.input?.background === true) && status === "done") {
|
|
1915
|
-
const
|
|
1916
|
-
const scanHasChild =
|
|
1934
|
+
const scanStMeta2 = st?.metadata;
|
|
1935
|
+
const scanHasChild = scanStMeta2?.session_id !== void 0 || scanStMeta2?.sessionId !== void 0;
|
|
1917
1936
|
if (scanHasChild) status = "running";
|
|
1918
1937
|
}
|
|
1919
|
-
if (exists && exists.status !== "running" && exists.status !== "cancel_requested") continue;
|
|
1938
|
+
if (exists && existingKey === id && exists.status !== "running" && exists.status !== "cancel_requested") continue;
|
|
1920
1939
|
if (exists && status === "running") {
|
|
1921
1940
|
if (!rawStatus) {
|
|
1922
1941
|
const msgTokens = msg?.tokens;
|
|
@@ -1937,12 +1956,11 @@ function SubAgentPanel(props) {
|
|
|
1937
1956
|
const desc = input?.description !== void 0 ? String(input.description) : "";
|
|
1938
1957
|
const title = desc || truncate(prompt.replace(/\n/g, " ").trim(), 40);
|
|
1939
1958
|
let tokens;
|
|
1940
|
-
const scanStMeta2 = st?.metadata;
|
|
1941
|
-
const scanSubSid = scanStMeta2?.session_id !== void 0 ? String(scanStMeta2.session_id) : scanStMeta2?.sessionId !== void 0 ? String(scanStMeta2.sessionId) : void 0;
|
|
1942
1959
|
if (scanSubSid) tokens = props.api.usage.readSessionTokens(scanSubSid);
|
|
1943
1960
|
const ended = status === "done";
|
|
1944
|
-
|
|
1945
|
-
|
|
1961
|
+
const entryKey = existingKey ?? id;
|
|
1962
|
+
next.set(entryKey, {
|
|
1963
|
+
id: exists?.id ?? id,
|
|
1946
1964
|
title,
|
|
1947
1965
|
agent,
|
|
1948
1966
|
prompt,
|
|
@@ -1953,6 +1971,7 @@ function SubAgentPanel(props) {
|
|
|
1953
1971
|
startedAt: exists?.startedAt || Date.now(),
|
|
1954
1972
|
endedAt: ended ? exists?.endedAt || Date.now() : void 0
|
|
1955
1973
|
});
|
|
1974
|
+
if (entryKey !== id) next.delete(id);
|
|
1956
1975
|
}
|
|
1957
1976
|
}
|
|
1958
1977
|
}
|
package/package.json
CHANGED
package/src/_version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// auto-generated
|
|
2
|
-
export const PLUGIN_VERSION="1.6.0-beta.
|
|
2
|
+
export const PLUGIN_VERSION="1.6.0-beta.3";
|
|
@@ -22,6 +22,7 @@ import { KV_PREFIX } from "../core/kv"
|
|
|
22
22
|
import type { PanelApi, PanelEvent } from "./panel-api"
|
|
23
23
|
import { globalEntryCache, clearTick } from "./store"
|
|
24
24
|
import { isDirectChildSession } from "./session-routing"
|
|
25
|
+
import { findSubEntryKey, upsertSubEntry } from "./entry-map"
|
|
25
26
|
|
|
26
27
|
/** Entry line left prefix: icon + space + status dot + space */
|
|
27
28
|
const LEFT_PAD = 4
|
|
@@ -87,14 +88,16 @@ export function SubAgentPanel(props: {
|
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
const loadEntries = (sid: string): Map<string, SubEntry> => {
|
|
90
|
-
|
|
91
|
+
let m = new Map<string, SubEntry>()
|
|
91
92
|
try {
|
|
92
93
|
const { parentSid, isChild } = resolveParent(sid)
|
|
93
94
|
const rec = loadSessionData()[parentSid]
|
|
94
95
|
if (rec) {
|
|
95
96
|
const source = isChild ? rec.children?.[sid]?.entries : rec.entries
|
|
96
97
|
if (source) {
|
|
97
|
-
for (const e of source)
|
|
98
|
+
for (const e of source) {
|
|
99
|
+
m = upsertSubEntry(m, e)
|
|
100
|
+
}
|
|
98
101
|
}
|
|
99
102
|
}
|
|
100
103
|
} catch {}
|
|
@@ -256,20 +259,7 @@ export function SubAgentPanel(props: {
|
|
|
256
259
|
const upsertEntry = (
|
|
257
260
|
partial: Omit<SubEntry, "startedAt" | "endedAt"> & { startedAt?: number }
|
|
258
261
|
) => {
|
|
259
|
-
setEntryMap((prev) =>
|
|
260
|
-
const existing = prev.get(partial.id)
|
|
261
|
-
const next = new Map(prev)
|
|
262
|
-
const nowTs = Date.now()
|
|
263
|
-
const e = partial.status
|
|
264
|
-
const ended = e === "done" || e === "error" || e === "cancelled"
|
|
265
|
-
next.set(partial.id, {
|
|
266
|
-
...(existing ?? { startedAt: nowTs }),
|
|
267
|
-
...partial,
|
|
268
|
-
startedAt: existing?.startedAt || partial.startedAt || nowTs,
|
|
269
|
-
endedAt: ended ? (existing?.endedAt || nowTs) : undefined,
|
|
270
|
-
})
|
|
271
|
-
return next
|
|
272
|
-
})
|
|
262
|
+
setEntryMap((prev) => upsertSubEntry(prev, partial))
|
|
273
263
|
}
|
|
274
264
|
|
|
275
265
|
// ── cancel helpers ──
|
|
@@ -397,9 +387,21 @@ export function SubAgentPanel(props: {
|
|
|
397
387
|
if (rawStatus === "error") {
|
|
398
388
|
const id = `tool:${String(part.id ?? "")}`
|
|
399
389
|
if (!part.id) return
|
|
400
|
-
const
|
|
390
|
+
const stMeta = st?.metadata as Record<string, unknown> | undefined
|
|
391
|
+
const errorSid = stMeta?.session_id !== undefined ? String(stMeta.session_id)
|
|
392
|
+
: stMeta?.sessionId !== undefined ? String(stMeta.sessionId)
|
|
393
|
+
: undefined
|
|
394
|
+
const key = findSubEntryKey(entryMap(), id, errorSid)
|
|
395
|
+
const existing = key ? entryMap().get(key) : undefined
|
|
401
396
|
if (existing) {
|
|
402
|
-
upsertEntry({
|
|
397
|
+
upsertEntry({
|
|
398
|
+
id: existing.id,
|
|
399
|
+
title: existing.title,
|
|
400
|
+
agent: existing.agent,
|
|
401
|
+
prompt: existing.prompt,
|
|
402
|
+
sessionId: existing.sessionId ?? errorSid,
|
|
403
|
+
status: "error",
|
|
404
|
+
})
|
|
403
405
|
}
|
|
404
406
|
return
|
|
405
407
|
}
|
|
@@ -791,7 +793,12 @@ export function SubAgentPanel(props: {
|
|
|
791
793
|
|
|
792
794
|
const st = (part as any).state as Record<string, unknown> | undefined
|
|
793
795
|
const rawStatus = String(st?.status ?? "")
|
|
794
|
-
const
|
|
796
|
+
const scanStMeta = st?.metadata as Record<string, unknown> | undefined
|
|
797
|
+
const scanSubSid = scanStMeta?.session_id !== undefined ? String(scanStMeta.session_id)
|
|
798
|
+
: scanStMeta?.sessionId !== undefined ? String(scanStMeta.sessionId)
|
|
799
|
+
: undefined
|
|
800
|
+
const existingKey = findSubEntryKey(next, id, scanSubSid)
|
|
801
|
+
const exists = existingKey ? next.get(existingKey) : undefined
|
|
795
802
|
|
|
796
803
|
// 已手动清除的条目:scan 发现但不在内存 → 跳过重建
|
|
797
804
|
if (!exists && clearedIds.has(id)) continue
|
|
@@ -803,7 +810,7 @@ export function SubAgentPanel(props: {
|
|
|
803
810
|
// "error": only update existing, never create a new entry
|
|
804
811
|
if (rawStatus === "error") {
|
|
805
812
|
if (exists && exists.status === "running") {
|
|
806
|
-
next.set(id, { ...exists, status: "error", endedAt: Date.now() })
|
|
813
|
+
next.set(existingKey ?? id, { ...exists, status: "error", endedAt: Date.now() })
|
|
807
814
|
}
|
|
808
815
|
continue
|
|
809
816
|
}
|
|
@@ -819,7 +826,7 @@ export function SubAgentPanel(props: {
|
|
|
819
826
|
}
|
|
820
827
|
|
|
821
828
|
// Already settled → skip
|
|
822
|
-
if (exists && exists.status !== "running" && exists.status !== "cancel_requested") continue
|
|
829
|
+
if (exists && existingKey === id && exists.status !== "running" && exists.status !== "cancel_requested") continue
|
|
823
830
|
// Running entry with no explicit status improvement from part:
|
|
824
831
|
// try message-level heuristics first, then time-based fallback.
|
|
825
832
|
if (exists && status === "running") {
|
|
@@ -847,15 +854,12 @@ export function SubAgentPanel(props: {
|
|
|
847
854
|
const title = desc || truncate(prompt.replace(/\n/g, " ").trim(), 40)
|
|
848
855
|
|
|
849
856
|
let tokens: number | undefined
|
|
850
|
-
const scanStMeta2 = st?.metadata as Record<string, unknown> | undefined
|
|
851
|
-
const scanSubSid = scanStMeta2?.session_id !== undefined ? String(scanStMeta2.session_id)
|
|
852
|
-
: scanStMeta2?.sessionId !== undefined ? String(scanStMeta2.sessionId)
|
|
853
|
-
: undefined
|
|
854
857
|
if (scanSubSid) tokens = props.api.usage.readSessionTokens(scanSubSid)
|
|
855
858
|
|
|
856
859
|
const ended = status === "done" // "error" handled above, never reaches here
|
|
857
|
-
|
|
858
|
-
|
|
860
|
+
const entryKey = existingKey ?? id
|
|
861
|
+
next.set(entryKey, {
|
|
862
|
+
id: exists?.id ?? id, title, agent, prompt,
|
|
859
863
|
// Preserve existing values (from handleSessionEnd / KV) — scan must not overwrite
|
|
860
864
|
tokens: exists?.tokens ?? tokens,
|
|
861
865
|
sessionId: exists?.sessionId ?? scanSubSid,
|
|
@@ -863,6 +867,7 @@ export function SubAgentPanel(props: {
|
|
|
863
867
|
startedAt: exists?.startedAt || Date.now(),
|
|
864
868
|
endedAt: ended ? (exists?.endedAt || Date.now()) : undefined,
|
|
865
869
|
})
|
|
870
|
+
if (entryKey !== id) next.delete(id)
|
|
866
871
|
}
|
|
867
872
|
}
|
|
868
873
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { SubEntry } from "../core/types"
|
|
2
|
+
|
|
3
|
+
export type SubEntryPatch = Omit<SubEntry, "startedAt" | "endedAt"> & {
|
|
4
|
+
startedAt?: number
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function findSubEntryKey(
|
|
8
|
+
entries: ReadonlyMap<string, SubEntry>,
|
|
9
|
+
id: string,
|
|
10
|
+
sessionId?: string,
|
|
11
|
+
) {
|
|
12
|
+
if (sessionId) {
|
|
13
|
+
for (const [key, entry] of entries) {
|
|
14
|
+
if (entry.sessionId === sessionId) return key
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return entries.has(id) ? id : undefined
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function upsertSubEntry(
|
|
21
|
+
entries: ReadonlyMap<string, SubEntry>,
|
|
22
|
+
partial: SubEntryPatch,
|
|
23
|
+
now = Date.now(),
|
|
24
|
+
) {
|
|
25
|
+
const key = findSubEntryKey(entries, partial.id, partial.sessionId) ?? partial.id
|
|
26
|
+
const existing = entries.get(key)
|
|
27
|
+
const next = new Map(entries)
|
|
28
|
+
if (key !== partial.id) next.delete(partial.id)
|
|
29
|
+
|
|
30
|
+
const ended = partial.status === "done" || partial.status === "error" || partial.status === "cancelled"
|
|
31
|
+
next.set(key, {
|
|
32
|
+
...(existing ?? { startedAt: now }),
|
|
33
|
+
...partial,
|
|
34
|
+
id: existing?.id ?? partial.id,
|
|
35
|
+
startedAt: existing?.startedAt || partial.startedAt || now,
|
|
36
|
+
endedAt: ended ? (existing?.endedAt || now) : undefined,
|
|
37
|
+
})
|
|
38
|
+
return next
|
|
39
|
+
}
|