opencode-subagent-magazine 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -2
- package/dist/_version.d.ts +1 -1
- package/dist/_version.js +1 -1
- package/dist/index.js +253 -31
- package/dist/tui.js +354 -41
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/index.tsx +242 -20
package/src/index.tsx
CHANGED
|
@@ -81,6 +81,19 @@ const I18N: Record<Lang, Record<string, string>> = {
|
|
|
81
81
|
"order.asc": "升序(最早在前)",
|
|
82
82
|
"scroll.wheel": "滚轮翻页",
|
|
83
83
|
"scroll.click": "点击翻页",
|
|
84
|
+
"ttl.label": "清理周期",
|
|
85
|
+
"ttl.3d": "3 天",
|
|
86
|
+
"ttl.7d": "7 天",
|
|
87
|
+
"ttl.14d": "14 天",
|
|
88
|
+
"ttl.30d": "30 天",
|
|
89
|
+
"ttl.unlimited": "无期限",
|
|
90
|
+
"ttl.toast": "清理周期已设为 {n} 天",
|
|
91
|
+
"ttl.toast_unlimited": "清理周期已设为无期限",
|
|
92
|
+
"clear.title": "确认清除",
|
|
93
|
+
"clear.prompt": "确定清除当前会话所有子代理记录?此操作不可撤销。",
|
|
94
|
+
"clear.prompt_running": "当前有 {n} 个运行中的子代理,清除后将不可恢复。确定继续?",
|
|
95
|
+
"clear.done": "已清除 {n} 条子代理记录",
|
|
96
|
+
"clear.empty": "当前会话无子代理记录",
|
|
84
97
|
},
|
|
85
98
|
en: {
|
|
86
99
|
"panel.title": "SubAgent",
|
|
@@ -107,6 +120,19 @@ const I18N: Record<Lang, Record<string, string>> = {
|
|
|
107
120
|
"order.asc": "Asc (oldest first)",
|
|
108
121
|
"scroll.wheel": "Wheel Scroll",
|
|
109
122
|
"scroll.click": "Click Scroll",
|
|
123
|
+
"ttl.label": "TTL (Time to Live)",
|
|
124
|
+
"ttl.3d": "3 days",
|
|
125
|
+
"ttl.7d": "7 days",
|
|
126
|
+
"ttl.14d": "14 days",
|
|
127
|
+
"ttl.30d": "30 days",
|
|
128
|
+
"ttl.unlimited": "Never",
|
|
129
|
+
"ttl.toast": "TTL set to {n} days",
|
|
130
|
+
"ttl.toast_unlimited": "TTL set to Never",
|
|
131
|
+
"clear.title": "Confirm",
|
|
132
|
+
"clear.prompt": "Clear all sub-agent records for this session? This cannot be undone.",
|
|
133
|
+
"clear.prompt_running": "{n} sub-agent(s) are still running. Clearing will discard them permanently. Continue?",
|
|
134
|
+
"clear.done": "Cleared {n} sub-agent record(s)",
|
|
135
|
+
"clear.empty": "No sub-agent records in this session",
|
|
110
136
|
},
|
|
111
137
|
}
|
|
112
138
|
|
|
@@ -266,6 +292,9 @@ function safeErrorMsg(err: unknown): string {
|
|
|
266
292
|
// 模块级缓存:各 session 的 entry 状态独立存储,不随当前视图切换而清除。
|
|
267
293
|
const globalEntryCache = new Map<string, Map<string, SubEntry>>()
|
|
268
294
|
|
|
295
|
+
// 模块级刷新信号:外部(如斜杠命令)触发清除后 +1,组件 scan 依赖它以重扫。
|
|
296
|
+
const [clearTick, setClearTick] = createSignal(0)
|
|
297
|
+
|
|
269
298
|
function SubAgentPanel(props: {
|
|
270
299
|
theme: TuiThemeCurrent
|
|
271
300
|
api: TuiPluginApi
|
|
@@ -279,13 +308,24 @@ function SubAgentPanel(props: {
|
|
|
279
308
|
|
|
280
309
|
// ── session data (single-key, true deletion on cleanup) ──
|
|
281
310
|
const SESSION_DATA_KEY = `${KV_PREFIX}.session_data`
|
|
282
|
-
const
|
|
311
|
+
const ttlDaysRaw = parseInt(String(props.api.kv.get(`${KV_PREFIX}.ttl_days`, "3")), 10)
|
|
312
|
+
const ttlDays = Number.isNaN(ttlDaysRaw) ? 3 : ttlDaysRaw
|
|
313
|
+
const TTL_MS = ttlDays * 24 * 60 * 60 * 1000
|
|
314
|
+
|
|
315
|
+
interface ChildRecord {
|
|
316
|
+
scroll: number
|
|
317
|
+
expanded: string
|
|
318
|
+
entries: SubEntry[]
|
|
319
|
+
clearedIds?: string[]
|
|
320
|
+
}
|
|
283
321
|
|
|
284
322
|
interface SessionRecord {
|
|
285
323
|
ts: number
|
|
286
324
|
entries: SubEntry[]
|
|
287
325
|
scroll: number
|
|
288
326
|
expanded: string
|
|
327
|
+
children: Record<string, ChildRecord>
|
|
328
|
+
clearedIds?: string[]
|
|
289
329
|
}
|
|
290
330
|
|
|
291
331
|
const loadSessionData = (): Record<string, SessionRecord> => {
|
|
@@ -299,12 +339,27 @@ function SubAgentPanel(props: {
|
|
|
299
339
|
try { props.api.kv.set(SESSION_DATA_KEY, JSON.stringify(data)) } catch {}
|
|
300
340
|
}
|
|
301
341
|
|
|
342
|
+
/** 将任意 session ID 解析为父会话 ID + 是否子会话。
|
|
343
|
+
* 通过 SDK session.get(sid).parentID 判断,无 parentID 即为主会话。 */
|
|
344
|
+
const resolveParent = (sid: string): { parentSid: string; isChild: boolean } => {
|
|
345
|
+
try {
|
|
346
|
+
const session = props.api.state.session.get(sid)
|
|
347
|
+
const parentID = (session as any)?.parentID as string | undefined
|
|
348
|
+
if (parentID) return { parentSid: parentID, isChild: true }
|
|
349
|
+
} catch {}
|
|
350
|
+
return { parentSid: sid, isChild: false }
|
|
351
|
+
}
|
|
352
|
+
|
|
302
353
|
const loadEntries = (sid: string): Map<string, SubEntry> => {
|
|
303
354
|
const m = new Map<string, SubEntry>()
|
|
304
355
|
try {
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
356
|
+
const { parentSid, isChild } = resolveParent(sid)
|
|
357
|
+
const rec = loadSessionData()[parentSid]
|
|
358
|
+
if (rec) {
|
|
359
|
+
const source = isChild ? rec.children?.[sid]?.entries : rec.entries
|
|
360
|
+
if (source) {
|
|
361
|
+
for (const e of source) m.set(e.id, e)
|
|
362
|
+
}
|
|
308
363
|
}
|
|
309
364
|
} catch {}
|
|
310
365
|
return m
|
|
@@ -316,7 +371,15 @@ function SubAgentPanel(props: {
|
|
|
316
371
|
persistTimer = setTimeout(() => {
|
|
317
372
|
try {
|
|
318
373
|
const data = loadSessionData()
|
|
319
|
-
|
|
374
|
+
const { parentSid, isChild } = resolveParent(sid)
|
|
375
|
+
if (isChild) {
|
|
376
|
+
if (!data[parentSid]) data[parentSid] = { ts: Date.now(), entries: [], scroll: 0, expanded: "", children: {} }
|
|
377
|
+
if (!data[parentSid].children) data[parentSid].children = {}
|
|
378
|
+
if (!data[parentSid].children[sid]) data[parentSid].children[sid] = { scroll: 0, expanded: "", entries: [] }
|
|
379
|
+
data[parentSid].children[sid] = { ...data[parentSid].children[sid], entries: [...entries.values()] }
|
|
380
|
+
} else {
|
|
381
|
+
data[sid] = { ...data[sid], ts: Date.now(), entries: [...entries.values()], children: data[sid]?.children ?? {} }
|
|
382
|
+
}
|
|
320
383
|
saveSessionData(data)
|
|
321
384
|
} catch {}
|
|
322
385
|
}, 200)
|
|
@@ -325,7 +388,15 @@ function SubAgentPanel(props: {
|
|
|
325
388
|
const persistScroll = (sid: string, scroll: number) => {
|
|
326
389
|
try {
|
|
327
390
|
const data = loadSessionData()
|
|
328
|
-
|
|
391
|
+
const { parentSid, isChild } = resolveParent(sid)
|
|
392
|
+
if (isChild) {
|
|
393
|
+
if (!data[parentSid]) data[parentSid] = { ts: Date.now(), entries: [], scroll: 0, expanded: "", children: {} }
|
|
394
|
+
if (!data[parentSid].children) data[parentSid].children = {}
|
|
395
|
+
if (!data[parentSid].children[sid]) data[parentSid].children[sid] = { scroll: 0, expanded: "", entries: [] }
|
|
396
|
+
data[parentSid].children[sid] = { ...data[parentSid].children[sid], scroll }
|
|
397
|
+
} else {
|
|
398
|
+
data[sid] = { ...data[sid], ts: Date.now(), scroll, children: data[sid]?.children ?? {} }
|
|
399
|
+
}
|
|
329
400
|
saveSessionData(data)
|
|
330
401
|
} catch {}
|
|
331
402
|
}
|
|
@@ -333,12 +404,21 @@ function SubAgentPanel(props: {
|
|
|
333
404
|
const persistExpanded = (sid: string, expanded: string) => {
|
|
334
405
|
try {
|
|
335
406
|
const data = loadSessionData()
|
|
336
|
-
|
|
407
|
+
const { parentSid, isChild } = resolveParent(sid)
|
|
408
|
+
if (isChild) {
|
|
409
|
+
if (!data[parentSid]) data[parentSid] = { ts: Date.now(), entries: [], scroll: 0, expanded: "", children: {} }
|
|
410
|
+
if (!data[parentSid].children) data[parentSid].children = {}
|
|
411
|
+
if (!data[parentSid].children[sid]) data[parentSid].children[sid] = { scroll: 0, expanded: "", entries: [] }
|
|
412
|
+
data[parentSid].children[sid] = { ...data[parentSid].children[sid], expanded }
|
|
413
|
+
} else {
|
|
414
|
+
data[sid] = { ...data[sid], ts: Date.now(), expanded, children: data[sid]?.children ?? {} }
|
|
415
|
+
}
|
|
337
416
|
saveSessionData(data)
|
|
338
417
|
} catch {}
|
|
339
418
|
}
|
|
340
419
|
|
|
341
420
|
const cleanupOldSessions = () => {
|
|
421
|
+
if (ttlDays <= 0) return // 无期限,跳过清理
|
|
342
422
|
try {
|
|
343
423
|
const data = loadSessionData()
|
|
344
424
|
const cutoff = Date.now() - TTL_MS
|
|
@@ -379,7 +459,15 @@ function SubAgentPanel(props: {
|
|
|
379
459
|
clearTimeout(persistTimer)
|
|
380
460
|
try {
|
|
381
461
|
const data = loadSessionData()
|
|
382
|
-
|
|
462
|
+
const { parentSid, isChild } = resolveParent(props.sessionId)
|
|
463
|
+
if (isChild) {
|
|
464
|
+
if (!data[parentSid]) data[parentSid] = { ts: Date.now(), entries: [], scroll: 0, expanded: "", children: {} }
|
|
465
|
+
if (!data[parentSid].children) data[parentSid].children = {}
|
|
466
|
+
if (!data[parentSid].children[props.sessionId]) data[parentSid].children[props.sessionId] = { scroll: 0, expanded: "", entries: [] }
|
|
467
|
+
data[parentSid].children[props.sessionId] = { ...data[parentSid].children[props.sessionId], entries: [...next.values()] }
|
|
468
|
+
} else {
|
|
469
|
+
data[props.sessionId] = { ...data[props.sessionId], ts: Date.now(), entries: [...next.values()], children: data[props.sessionId]?.children ?? {} }
|
|
470
|
+
}
|
|
383
471
|
saveSessionData(data)
|
|
384
472
|
} catch {}
|
|
385
473
|
} else {
|
|
@@ -398,7 +486,14 @@ function SubAgentPanel(props: {
|
|
|
398
486
|
(() => { try { return props.api.kv.get(`${KV_PREFIX}.open`, true) as boolean } catch { return true } })()
|
|
399
487
|
)
|
|
400
488
|
const [expanded, setExpanded] = createSignal<string | undefined>(
|
|
401
|
-
(() => {
|
|
489
|
+
(() => {
|
|
490
|
+
try {
|
|
491
|
+
const { parentSid, isChild } = resolveParent(props.sessionId)
|
|
492
|
+
const rec = loadSessionData()[parentSid]
|
|
493
|
+
if (rec) return isChild ? rec.children?.[props.sessionId]?.expanded || undefined : rec.expanded || undefined
|
|
494
|
+
} catch {}
|
|
495
|
+
return undefined
|
|
496
|
+
})(),
|
|
402
497
|
)
|
|
403
498
|
const [hoveredOpen, setHoveredOpen] = createSignal<string | undefined>(undefined)
|
|
404
499
|
const [hoveredDismiss, setHoveredDismiss] = createSignal<string | undefined>(undefined)
|
|
@@ -406,7 +501,13 @@ function SubAgentPanel(props: {
|
|
|
406
501
|
const [hoveredMoreAbove, setHoveredMoreAbove] = createSignal(false)
|
|
407
502
|
const [hoveredMoreBelow, setHoveredMoreBelow] = createSignal(false)
|
|
408
503
|
const [scrollOffset, setScrollOffset] = createSignal(
|
|
409
|
-
(() => {
|
|
504
|
+
(() => {
|
|
505
|
+
try {
|
|
506
|
+
const { parentSid, isChild } = resolveParent(props.sessionId)
|
|
507
|
+
const rec = loadSessionData()[parentSid]
|
|
508
|
+
return isChild ? rec?.children?.[props.sessionId]?.scroll ?? 0 : rec?.scroll ?? 0
|
|
509
|
+
} catch { return 0 }
|
|
510
|
+
})(),
|
|
410
511
|
)
|
|
411
512
|
const [now, setNow] = createSignal(Date.now())
|
|
412
513
|
const [renderTick, setRenderTick] = createSignal(0)
|
|
@@ -564,7 +665,7 @@ function SubAgentPanel(props: {
|
|
|
564
665
|
// Background tasks: tool completion ≠ agent completion — keep running until session.idle
|
|
565
666
|
// Only keep running if state metadata confirms a child session was spawned;
|
|
566
667
|
// otherwise (failed spawn, invalid agent) mark as done so the entry isn't stuck forever.
|
|
567
|
-
if (input?.run_in_background === true && status === "done") {
|
|
668
|
+
if ((input?.run_in_background === true || input?.background === true) && status === "done") {
|
|
568
669
|
const stMetaCheck = st?.metadata as Record<string, unknown> | undefined
|
|
569
670
|
const hasChild = stMetaCheck?.session_id !== undefined || stMetaCheck?.sessionId !== undefined
|
|
570
671
|
if (hasChild) status = "running"
|
|
@@ -883,23 +984,40 @@ function SubAgentPanel(props: {
|
|
|
883
984
|
// On session change: load from kv (entries survive component unmount), then scan+merge.
|
|
884
985
|
// On same session: only scan+merge (keep event‑driven running entries).
|
|
885
986
|
let lastSid = props.sessionId
|
|
987
|
+
let lastTick = 0
|
|
886
988
|
createEffect(() => {
|
|
887
989
|
const sid = props.sessionId
|
|
888
990
|
const switched = sid !== lastSid
|
|
889
991
|
lastSid = sid
|
|
992
|
+
const tick = clearTick() // 外部触发清除时 +1,effect 重跑
|
|
993
|
+
const forceReload = tick !== lastTick && !switched
|
|
994
|
+
lastTick = tick
|
|
890
995
|
const t = setTimeout(() => {
|
|
891
996
|
untrack(() => {
|
|
892
997
|
if (switched) {
|
|
893
|
-
const
|
|
998
|
+
const { parentSid, isChild } = resolveParent(sid)
|
|
999
|
+
const data = loadSessionData()
|
|
1000
|
+
const saved = isChild
|
|
1001
|
+
? data[parentSid]?.children?.[sid]?.scroll ?? 0
|
|
1002
|
+
: data[sid]?.scroll ?? 0
|
|
894
1003
|
setScrollOffset(saved)
|
|
1004
|
+
// 刷新父会话的访问时间 TTL,防止活跃会话的数据过期
|
|
1005
|
+
if (!isChild && data[sid]?.entries?.length) {
|
|
1006
|
+
data[sid].ts = Date.now()
|
|
1007
|
+
saveSessionData(data)
|
|
1008
|
+
}
|
|
895
1009
|
}
|
|
896
1010
|
// scan uses setEntryMapRaw — ephemeral data, not persisted to kv.
|
|
897
1011
|
// Only event-driven changes (handlePartUpdated, handleSessionEnd) persist.
|
|
898
1012
|
setEntryMapRaw((prev) => {
|
|
899
1013
|
// 优先从模块级缓存加载,KV 仅作缓存未命中时的回退
|
|
900
|
-
const next = switched
|
|
1014
|
+
const next = (switched || forceReload)
|
|
901
1015
|
? new Map(globalEntryCache.get(sid) ?? loadEntries(sid))
|
|
902
1016
|
: new Map(prev)
|
|
1017
|
+
// 从 KV 加载当前会话的清除名单,扫描时跳过被手动清除的历史条目
|
|
1018
|
+
const { parentSid: scanPSid, isChild: scanChild } = resolveParent(sid)
|
|
1019
|
+
const scanRec = loadSessionData()[scanPSid]
|
|
1020
|
+
const clearedIds = new Set(scanChild ? scanRec?.children?.[sid]?.clearedIds : scanRec?.clearedIds)
|
|
903
1021
|
try {
|
|
904
1022
|
const msgs = props.api.state.session.messages(sid)
|
|
905
1023
|
if (msgs && (msgs as any[]).length) {
|
|
@@ -920,6 +1038,9 @@ function SubAgentPanel(props: {
|
|
|
920
1038
|
const rawStatus = String(st?.status ?? "")
|
|
921
1039
|
const exists = next.get(id)
|
|
922
1040
|
|
|
1041
|
+
// 已手动清除的条目:scan 发现但不在内存 → 跳过重建
|
|
1042
|
+
if (!exists && clearedIds.has(id)) continue
|
|
1043
|
+
|
|
923
1044
|
// Only create entries for tool calls that entered execution.
|
|
924
1045
|
// "pending" / empty: skip new entries; allow heuristics for existing ones below.
|
|
925
1046
|
if ((rawStatus === "pending" || rawStatus === "") && !exists) continue
|
|
@@ -936,7 +1057,7 @@ function SubAgentPanel(props: {
|
|
|
936
1057
|
if (rawStatus === "completed") status = "done"
|
|
937
1058
|
// Background tasks: tool completion ≠ agent completion — keep running until session.idle
|
|
938
1059
|
// Only keep running if state metadata confirms a child session was spawned.
|
|
939
|
-
if ((st?.input as Record<string, unknown> | undefined)?.run_in_background === true && status === "done") {
|
|
1060
|
+
if (((st?.input as Record<string, unknown> | undefined)?.run_in_background === true || (st?.input as Record<string, unknown> | undefined)?.background === true) && status === "done") {
|
|
940
1061
|
const scanStMeta = st?.metadata as Record<string, unknown> | undefined
|
|
941
1062
|
const scanHasChild = scanStMeta?.session_id !== undefined || scanStMeta?.sessionId !== undefined
|
|
942
1063
|
if (scanHasChild) status = "running"
|
|
@@ -1754,7 +1875,8 @@ const tui: TuiPlugin = async (api: TuiPluginApi) => {
|
|
|
1754
1875
|
description: "Mark all running sub-agent entries as done (for stuck/zombie entries)",
|
|
1755
1876
|
slash: { name: "subagent-clear-running" },
|
|
1756
1877
|
onSelect: (dialog) => {
|
|
1757
|
-
const
|
|
1878
|
+
const sid = signals.sessionId
|
|
1879
|
+
const entries = globalEntryCache.get(sid)
|
|
1758
1880
|
if (!entries || entries.size === 0) {
|
|
1759
1881
|
const msg = signals.lang() === "zh" ? "暂无子代理条目" : "No sub-agent entries found"
|
|
1760
1882
|
api.ui.toast({ message: msg })
|
|
@@ -1773,11 +1895,21 @@ const tui: TuiPlugin = async (api: TuiPluginApi) => {
|
|
|
1773
1895
|
// 立即写 KV
|
|
1774
1896
|
try {
|
|
1775
1897
|
const data = JSON.parse(String(api.kv.get(`${KV_PREFIX}.session_data`, "{}")))
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1898
|
+
const sessionObj = api.state.session.get(sid)
|
|
1899
|
+
const parentID = (sessionObj as any)?.parentID as string | undefined
|
|
1900
|
+
if (parentID) {
|
|
1901
|
+
if (!data[parentID]) data[parentID] = { ts: Date.now(), entries: [], scroll: 0, expanded: "", children: {} }
|
|
1902
|
+
if (!data[parentID].children) data[parentID].children = {}
|
|
1903
|
+
if (!data[parentID].children[sid]) data[parentID].children[sid] = { scroll: 0, expanded: "", entries: [] }
|
|
1904
|
+
data[parentID].children[sid] = { ...data[parentID].children[sid], entries: [...entries.values()] }
|
|
1905
|
+
} else {
|
|
1906
|
+
data[sid] = {
|
|
1907
|
+
ts: Date.now(),
|
|
1908
|
+
entries: [...entries.values()],
|
|
1909
|
+
scroll: data[sid]?.scroll ?? 0,
|
|
1910
|
+
expanded: data[sid]?.expanded ?? "",
|
|
1911
|
+
children: data[sid]?.children ?? {},
|
|
1912
|
+
}
|
|
1781
1913
|
}
|
|
1782
1914
|
api.kv.set(`${KV_PREFIX}.session_data`, JSON.stringify(data))
|
|
1783
1915
|
} catch {}
|
|
@@ -1794,6 +1926,96 @@ const tui: TuiPlugin = async (api: TuiPluginApi) => {
|
|
|
1794
1926
|
dialog?.clear()
|
|
1795
1927
|
},
|
|
1796
1928
|
},
|
|
1929
|
+
{
|
|
1930
|
+
title: "SubAgent Magazine: TTL",
|
|
1931
|
+
value: "subagent-ttl",
|
|
1932
|
+
description: "Set session data retention period (days before auto-cleanup)",
|
|
1933
|
+
slash: { name: "subagent-ttl" },
|
|
1934
|
+
onSelect: (dialog) => {
|
|
1935
|
+
const t = (k: string) => I18N[signals.lang()][k] ?? k
|
|
1936
|
+
const curRaw = parseInt(String(api.kv.get(`${KV_PREFIX}.ttl_days`, "3")), 10)
|
|
1937
|
+
const curDays = Number.isNaN(curRaw) ? 3 : curRaw
|
|
1938
|
+
const curLabel = curDays === 0 ? t("ttl.unlimited") : `${curDays}d`
|
|
1939
|
+
dialog?.replace(() => (
|
|
1940
|
+
<api.ui.DialogSelect
|
|
1941
|
+
title={`${t("ttl.label")} (${curLabel})`}
|
|
1942
|
+
options={[
|
|
1943
|
+
{ title: t("ttl.3d"), value: "3" },
|
|
1944
|
+
{ title: t("ttl.7d"), value: "7" },
|
|
1945
|
+
{ title: t("ttl.14d"), value: "14" },
|
|
1946
|
+
{ title: t("ttl.30d"), value: "30" },
|
|
1947
|
+
{ title: t("ttl.unlimited"), value: "0" },
|
|
1948
|
+
]}
|
|
1949
|
+
onSelect={(opt) => {
|
|
1950
|
+
const days = parseInt(opt.value, 10)
|
|
1951
|
+
api.kv.set(`${KV_PREFIX}.ttl_days`, String(days))
|
|
1952
|
+
const msg = days === 0 ? t("ttl.toast_unlimited") : t("ttl.toast").replace("{n}", String(days))
|
|
1953
|
+
api.ui.toast({ message: msg })
|
|
1954
|
+
dialog?.clear()
|
|
1955
|
+
}}
|
|
1956
|
+
/>
|
|
1957
|
+
))
|
|
1958
|
+
},
|
|
1959
|
+
},
|
|
1960
|
+
{
|
|
1961
|
+
title: "SubAgent Magazine: Clear Entries",
|
|
1962
|
+
value: "subagent-clear-entries",
|
|
1963
|
+
description: "Delete all sub-agent records for the current session (cannot be undone)",
|
|
1964
|
+
slash: { name: "subagent-clear-entries" },
|
|
1965
|
+
onSelect: (dialog) => {
|
|
1966
|
+
const t = (k: string) => I18N[signals.lang()][k] ?? k
|
|
1967
|
+
const sid = signals.sessionId
|
|
1968
|
+
const sessionObj = api.state.session.get(sid)
|
|
1969
|
+
const parentID = (sessionObj as any)?.parentID as string | undefined
|
|
1970
|
+
// 检查是否存在运行中的条目
|
|
1971
|
+
const cached = globalEntryCache.get(sid)
|
|
1972
|
+
let runningCount = 0
|
|
1973
|
+
if (cached) {
|
|
1974
|
+
for (const [, e] of cached) { if (e.status === "running") runningCount++ }
|
|
1975
|
+
}
|
|
1976
|
+
const msg = runningCount > 0
|
|
1977
|
+
? t("clear.prompt_running").replace("{n}", String(runningCount))
|
|
1978
|
+
: t("clear.prompt")
|
|
1979
|
+
dialog?.replace(() => (
|
|
1980
|
+
<api.ui.DialogConfirm
|
|
1981
|
+
title={t("clear.title")}
|
|
1982
|
+
message={msg}
|
|
1983
|
+
onConfirm={() => {
|
|
1984
|
+
try {
|
|
1985
|
+
const data = JSON.parse(String(api.kv.get(`${KV_PREFIX}.session_data`, "{}")))
|
|
1986
|
+
let count = 0
|
|
1987
|
+
if (parentID) {
|
|
1988
|
+
if (data[parentID]?.children?.[sid]) {
|
|
1989
|
+
const child = data[parentID].children[sid]
|
|
1990
|
+
const ids = child.entries?.map((e: any) => e.id) ?? []
|
|
1991
|
+
count = ids.length
|
|
1992
|
+
child.entries = []
|
|
1993
|
+
child.scroll = 0
|
|
1994
|
+
child.expanded = ""
|
|
1995
|
+
child.clearedIds = [...new Set([...(child.clearedIds ?? []), ...ids])]
|
|
1996
|
+
}
|
|
1997
|
+
} else {
|
|
1998
|
+
count = data[sid]?.entries?.length ?? 0
|
|
1999
|
+
if (data[sid]) {
|
|
2000
|
+
const ids = data[sid].entries?.map((e: any) => e.id) ?? []
|
|
2001
|
+
data[sid].entries = []
|
|
2002
|
+
data[sid].scroll = 0
|
|
2003
|
+
data[sid].expanded = ""
|
|
2004
|
+
data[sid].clearedIds = [...new Set([...(data[sid].clearedIds ?? []), ...ids])]
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
api.kv.set(`${KV_PREFIX}.session_data`, JSON.stringify(data))
|
|
2008
|
+
globalEntryCache.delete(sid)
|
|
2009
|
+
setClearTick((v) => v + 1)
|
|
2010
|
+
const msg = t("clear.done").replace("{n}", String(count))
|
|
2011
|
+
api.ui.toast({ message: msg })
|
|
2012
|
+
} catch {}
|
|
2013
|
+
dialog?.clear()
|
|
2014
|
+
}}
|
|
2015
|
+
/>
|
|
2016
|
+
))
|
|
2017
|
+
},
|
|
2018
|
+
},
|
|
1797
2019
|
])
|
|
1798
2020
|
}
|
|
1799
2021
|
|