dsh-sessions-manager 3.7.6 → 3.7.8
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.en.md +8 -7
- package/README.md +8 -7
- package/lib/client.js +795 -330
- package/lib/client.js.map +3 -3
- package/lib/index.js +192 -15
- package/lib/index.js.map +2 -2
- package/package.json +1 -1
- package/src/client/index.jsx +598 -73
- package/src/client/logic.js +133 -0
- package/src/index.js +156 -14
- package/src/lineage.js +86 -6
package/lib/index.js
CHANGED
|
@@ -2019,13 +2019,61 @@ var LIFECYCLE_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
2019
2019
|
"sandbox/mode",
|
|
2020
2020
|
"approval/policy"
|
|
2021
2021
|
]);
|
|
2022
|
-
function
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2022
|
+
function isInjectedMessage(msg) {
|
|
2023
|
+
const src = msg && typeof msg === "object" ? msg.source : null;
|
|
2024
|
+
if (!src || typeof src !== "object") return false;
|
|
2025
|
+
if (src.form === "instructions") return true;
|
|
2026
|
+
return typeof src.kind === "string" && src.kind.startsWith("plugin:");
|
|
2027
|
+
}
|
|
2028
|
+
function isNoiseEvent(event) {
|
|
2029
|
+
const type = typeof event === "string" ? event : event && typeof event === "object" ? event.type : null;
|
|
2030
|
+
if (typeof type !== "string") return false;
|
|
2031
|
+
if (LIFECYCLE_EVENT_TYPES.has(type)) return true;
|
|
2032
|
+
if (type !== "agent/inbox/spliced") return false;
|
|
2033
|
+
const data = event && typeof event === "object" ? event.data : null;
|
|
2034
|
+
if (!data || typeof data !== "object") return false;
|
|
2035
|
+
const removed = Number(data.removedCount);
|
|
2036
|
+
if (Number.isFinite(removed) && removed > 0) return false;
|
|
2037
|
+
if (!Array.isArray(data.inserted)) return false;
|
|
2038
|
+
if (data.inserted.length === 0) return true;
|
|
2039
|
+
return data.inserted.every(isInjectedMessage);
|
|
2040
|
+
}
|
|
2041
|
+
function isEmptyEventTypes(events) {
|
|
2042
|
+
if (!Array.isArray(events)) return false;
|
|
2043
|
+
for (const ev of events) {
|
|
2044
|
+
if (!isNoiseEvent(ev)) return false;
|
|
2026
2045
|
}
|
|
2027
2046
|
return true;
|
|
2028
2047
|
}
|
|
2048
|
+
var SUBAGENT_CASCADE_LIMIT = 500;
|
|
2049
|
+
function subagentDescendantsOf(headers, rootId, limit = SUBAGENT_CASCADE_LIMIT) {
|
|
2050
|
+
const root = rootId == null ? "" : String(rootId);
|
|
2051
|
+
if (!root) return [];
|
|
2052
|
+
const kidsOf = /* @__PURE__ */ new Map();
|
|
2053
|
+
for (const h of Array.isArray(headers) ? headers : []) {
|
|
2054
|
+
if (!h || typeof h !== "object" || h.id == null || h.origin !== "subagent") continue;
|
|
2055
|
+
const id = String(h.id);
|
|
2056
|
+
const parent = h.parentSession != null ? String(h.parentSession) : "";
|
|
2057
|
+
if (!parent || parent === id) continue;
|
|
2058
|
+
if (!kidsOf.has(parent)) kidsOf.set(parent, []);
|
|
2059
|
+
kidsOf.get(parent).push(id);
|
|
2060
|
+
}
|
|
2061
|
+
const max = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : SUBAGENT_CASCADE_LIMIT;
|
|
2062
|
+
const out = [];
|
|
2063
|
+
const visited = /* @__PURE__ */ new Set([root]);
|
|
2064
|
+
const queue = [root];
|
|
2065
|
+
while (queue.length && out.length < max) {
|
|
2066
|
+
const cur = queue.shift();
|
|
2067
|
+
for (const kid of kidsOf.get(cur) || []) {
|
|
2068
|
+
if (visited.has(kid)) continue;
|
|
2069
|
+
visited.add(kid);
|
|
2070
|
+
out.push(kid);
|
|
2071
|
+
queue.push(kid);
|
|
2072
|
+
if (out.length >= max) break;
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
return out;
|
|
2076
|
+
}
|
|
2029
2077
|
function classifyLineage(header) {
|
|
2030
2078
|
if (!header || typeof header !== "object") return null;
|
|
2031
2079
|
const origin = header.origin === "subagent" ? "subagent" : null;
|
|
@@ -2039,7 +2087,7 @@ function emptyScanCandidate(sizeBytes) {
|
|
|
2039
2087
|
}
|
|
2040
2088
|
|
|
2041
2089
|
// src/index.js
|
|
2042
|
-
var BUILD_STAMP = true ? "3.7.
|
|
2090
|
+
var BUILD_STAMP = true ? "3.7.8+846b91ab" : "dev";
|
|
2043
2091
|
var name = "dsh-sessions-manager";
|
|
2044
2092
|
var inject = ["webServer", "workspaceRegistry", "sessionPersistence", "sessionQuery", "storageDomain"];
|
|
2045
2093
|
var MAX_TITLE = 80;
|
|
@@ -2523,7 +2571,7 @@ function apply(ctx) {
|
|
|
2523
2571
|
} catch (e) {
|
|
2524
2572
|
}
|
|
2525
2573
|
}
|
|
2526
|
-
async function deleteOne(sid) {
|
|
2574
|
+
async function deleteOne(sid, opts = {}) {
|
|
2527
2575
|
requireSessionId(sid);
|
|
2528
2576
|
let header = null;
|
|
2529
2577
|
let cwd = null;
|
|
@@ -2531,7 +2579,7 @@ function apply(ctx) {
|
|
|
2531
2579
|
let removedPath = null;
|
|
2532
2580
|
let persistenceEntry = null;
|
|
2533
2581
|
try {
|
|
2534
|
-
const entries = await persistence.listEntries();
|
|
2582
|
+
const entries = Array.isArray(opts.entries) ? opts.entries : await persistence.listEntries();
|
|
2535
2583
|
const found = entries.find((entry) => entry.id === sid) || null;
|
|
2536
2584
|
persistenceEntry = found;
|
|
2537
2585
|
header = found ? found.header : null;
|
|
@@ -2576,6 +2624,7 @@ function apply(ctx) {
|
|
|
2576
2624
|
wasArchived: archived,
|
|
2577
2625
|
deletedAt: Date.now()
|
|
2578
2626
|
};
|
|
2627
|
+
if (opts.cascadeFrom) entry.cascadeFrom = String(opts.cascadeFrom);
|
|
2579
2628
|
const at = store.items.findIndex((t) => String(t.sessionId) === sid);
|
|
2580
2629
|
if (at >= 0) store.items[at] = entry;
|
|
2581
2630
|
else store.items.push(entry);
|
|
@@ -2583,6 +2632,64 @@ function apply(ctx) {
|
|
|
2583
2632
|
});
|
|
2584
2633
|
return { ok: true, trashed: true };
|
|
2585
2634
|
}
|
|
2635
|
+
function subagentDescendantIdsFrom(entries, rootId) {
|
|
2636
|
+
const headers = [];
|
|
2637
|
+
for (const entry of Array.isArray(entries) ? entries : []) {
|
|
2638
|
+
if (entry && entry.header) headers.push(entry.header);
|
|
2639
|
+
}
|
|
2640
|
+
return subagentDescendantsOf(headers, rootId);
|
|
2641
|
+
}
|
|
2642
|
+
async function deleteWithSubagents(sid, opts = {}) {
|
|
2643
|
+
let entries = Array.isArray(opts.entries) ? opts.entries : null;
|
|
2644
|
+
if (!entries) {
|
|
2645
|
+
try {
|
|
2646
|
+
entries = await persistence.listEntries();
|
|
2647
|
+
} catch (e) {
|
|
2648
|
+
entries = null;
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
const out = await deleteOne(sid, { entries });
|
|
2652
|
+
const cascade = opts.cascade !== false;
|
|
2653
|
+
if (!cascade || !entries) return { ...out, cascaded: 0, cascadedIds: [] };
|
|
2654
|
+
const kids = subagentDescendantIdsFrom(entries, sid);
|
|
2655
|
+
let alreadyTrashed = null;
|
|
2656
|
+
try {
|
|
2657
|
+
const store = await readTrashStore();
|
|
2658
|
+
alreadyTrashed = new Set(store.items.map((t) => String(t.sessionId)));
|
|
2659
|
+
} catch (e) {
|
|
2660
|
+
alreadyTrashed = null;
|
|
2661
|
+
}
|
|
2662
|
+
const cascadedIds = [];
|
|
2663
|
+
for (const kid of kids) {
|
|
2664
|
+
if (alreadyTrashed && alreadyTrashed.has(String(kid))) continue;
|
|
2665
|
+
try {
|
|
2666
|
+
await deleteOne(kid, { entries, cascadeFrom: sid });
|
|
2667
|
+
metaCache.invalidate(kid);
|
|
2668
|
+
cascadedIds.push(kid);
|
|
2669
|
+
} catch (e) {
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
return { ...out, cascaded: cascadedIds.length, cascadedIds };
|
|
2673
|
+
}
|
|
2674
|
+
async function cascadeTrashChildren(sid, op) {
|
|
2675
|
+
let kids = [];
|
|
2676
|
+
try {
|
|
2677
|
+
const store = await readTrashStore();
|
|
2678
|
+
kids = store.items.filter((t) => String(t.cascadeFrom || "") === String(sid)).map((t) => String(t.sessionId));
|
|
2679
|
+
} catch (e) {
|
|
2680
|
+
return [];
|
|
2681
|
+
}
|
|
2682
|
+
const results = [];
|
|
2683
|
+
for (const kid of kids) {
|
|
2684
|
+
try {
|
|
2685
|
+
results.push({ sessionId: kid, ok: true, ...await op(kid) });
|
|
2686
|
+
metaCache.invalidate(kid);
|
|
2687
|
+
} catch (e) {
|
|
2688
|
+
results.push({ sessionId: kid, ok: false, error: String(e && e.message || e) });
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
return results;
|
|
2692
|
+
}
|
|
2586
2693
|
async function verifyTrashRestore(sid) {
|
|
2587
2694
|
const sessions = ctx.get("sessions");
|
|
2588
2695
|
if (sessions && sessions.get && sessions.get(sid)) {
|
|
@@ -3496,6 +3603,18 @@ function apply(ctx) {
|
|
|
3496
3603
|
const payload = {
|
|
3497
3604
|
titles: Object.fromEntries(authorityTitleCache),
|
|
3498
3605
|
trashedSessionIds: store.items.map((item) => String(item.sessionId)),
|
|
3606
|
+
// issue #16:回收站按 cwd 的条数。折叠分组下上游把 group.sessions 收回成
|
|
3607
|
+
// 空数组,client 拿不到成员 id,只能用「组内会话总数 − 本组回收站条数」
|
|
3608
|
+
// 还原展开按钮该显示的数字;有了这份统计,那次扣减才是准确的而非估算。
|
|
3609
|
+
trashedCountByCwd: (() => {
|
|
3610
|
+
const acc = {};
|
|
3611
|
+
for (const item of store.items) {
|
|
3612
|
+
const cwd = typeof item.cwd === "string" ? item.cwd : "";
|
|
3613
|
+
if (!cwd) continue;
|
|
3614
|
+
acc[cwd] = (acc[cwd] || 0) + 1;
|
|
3615
|
+
}
|
|
3616
|
+
return acc;
|
|
3617
|
+
})(),
|
|
3499
3618
|
purgedSessionIds: activeTombstones,
|
|
3500
3619
|
lineage,
|
|
3501
3620
|
// v3.6.2:还有预热/退避重试在途 → client 缩短轮询节拍,预热一完成就把
|
|
@@ -3549,11 +3668,29 @@ function apply(ctx) {
|
|
|
3549
3668
|
const persistBatch = {};
|
|
3550
3669
|
for (const [id, desc] of batch) {
|
|
3551
3670
|
try {
|
|
3552
|
-
const
|
|
3671
|
+
const events = [];
|
|
3553
3672
|
await persistence.inspectSession(id, { onEvents: (b) => {
|
|
3554
|
-
for (const ev of b || [])
|
|
3673
|
+
for (const ev of b || []) {
|
|
3674
|
+
if (events.length >= 64) break;
|
|
3675
|
+
if (!ev) {
|
|
3676
|
+
events.push(void 0);
|
|
3677
|
+
continue;
|
|
3678
|
+
}
|
|
3679
|
+
if (ev.type !== "agent/inbox/spliced") {
|
|
3680
|
+
events.push({ type: ev.type });
|
|
3681
|
+
continue;
|
|
3682
|
+
}
|
|
3683
|
+
const data = ev.data && typeof ev.data === "object" ? ev.data : null;
|
|
3684
|
+
events.push({
|
|
3685
|
+
type: ev.type,
|
|
3686
|
+
data: data ? {
|
|
3687
|
+
removedCount: data.removedCount,
|
|
3688
|
+
inserted: Array.isArray(data.inserted) ? data.inserted.map((m) => ({ source: m && typeof m === "object" ? m.source : null })) : null
|
|
3689
|
+
} : null
|
|
3690
|
+
});
|
|
3691
|
+
}
|
|
3555
3692
|
} });
|
|
3556
|
-
const isEmpty = isEmptyEventTypes(
|
|
3693
|
+
const isEmpty = isEmptyEventTypes(events);
|
|
3557
3694
|
emptyScanCache.set(id, { sizeBytes: desc.sizeBytes, empty: isEmpty });
|
|
3558
3695
|
persistBatch[id] = { empty: isEmpty ? 1 : 0, fingerprint: `sz:${desc.sizeBytes}`, updatedAt: Date.now() };
|
|
3559
3696
|
} catch (e) {
|
|
@@ -3826,7 +3963,7 @@ function apply(ctx) {
|
|
|
3826
3963
|
const body = await readJsonBody(req);
|
|
3827
3964
|
const sid = body && typeof body.sessionId === "string" ? body.sessionId : null;
|
|
3828
3965
|
if (!sid) return json(res, { ok: false, error: "missing sessionId" }, 400);
|
|
3829
|
-
const out = await
|
|
3966
|
+
const out = await deleteWithSubagents(sid, { cascade: body.cascadeSubagents !== false });
|
|
3830
3967
|
metaCache.invalidate(sid);
|
|
3831
3968
|
json(res, out);
|
|
3832
3969
|
} catch (e) {
|
|
@@ -3843,15 +3980,30 @@ function apply(ctx) {
|
|
|
3843
3980
|
const ids = parseIds(body);
|
|
3844
3981
|
if (!ids || ids.length === 0) return json(res, { ok: false, error: "missing sessionIds" }, 400);
|
|
3845
3982
|
const results = [];
|
|
3983
|
+
let cascadeEntries = null;
|
|
3984
|
+
try {
|
|
3985
|
+
cascadeEntries = await persistence.listEntries();
|
|
3986
|
+
} catch (e) {
|
|
3987
|
+
cascadeEntries = null;
|
|
3988
|
+
}
|
|
3989
|
+
const cascade = body.cascadeSubagents !== false;
|
|
3990
|
+
const done = /* @__PURE__ */ new Set();
|
|
3846
3991
|
for (const sid of ids) {
|
|
3847
3992
|
try {
|
|
3848
|
-
|
|
3993
|
+
if (done.has(sid)) {
|
|
3994
|
+
results.push({ sessionId: sid, ok: true, trashed: true, cascaded: 0 });
|
|
3995
|
+
continue;
|
|
3996
|
+
}
|
|
3997
|
+
const out = cascade && cascadeEntries ? await deleteWithSubagents(sid, { cascade: true, entries: cascadeEntries }) : await deleteOne(sid, { entries: cascadeEntries });
|
|
3998
|
+
done.add(sid);
|
|
3999
|
+
if (Array.isArray(out.cascadedIds)) for (const k of out.cascadedIds) done.add(k);
|
|
4000
|
+
results.push({ sessionId: sid, ok: true, ...out });
|
|
3849
4001
|
metaCache.invalidate(sid);
|
|
3850
4002
|
} catch (e) {
|
|
3851
4003
|
results.push({ sessionId: sid, ok: false, error: String(e && e.message || e) });
|
|
3852
4004
|
}
|
|
3853
4005
|
}
|
|
3854
|
-
json(res, { ok: true, deleted: results.filter((r) => r.ok).length, results });
|
|
4006
|
+
json(res, { ok: true, deleted: results.filter((r) => r.ok).length, cascaded: results.reduce((n, r) => n + (Number(r.cascaded) || 0), 0), results });
|
|
3855
4007
|
} catch (e) {
|
|
3856
4008
|
json(res, { ok: false, code: e && e.code, error: String(e && e.message || e) }, errorStatus(e));
|
|
3857
4009
|
}
|
|
@@ -3920,7 +4072,8 @@ function apply(ctx) {
|
|
|
3920
4072
|
if (!sid) return json(res, { ok: false, error: "missing sessionId" }, 400);
|
|
3921
4073
|
const out = await restoreFromTrash(sid);
|
|
3922
4074
|
metaCache.invalidate(sid);
|
|
3923
|
-
|
|
4075
|
+
const cascaded = await cascadeTrashChildren(sid, (kid) => restoreFromTrash(kid));
|
|
4076
|
+
json(res, { ...out, cascaded: cascaded.filter((r) => r.ok).length, cascadedResults: cascaded.length ? cascaded : void 0 });
|
|
3924
4077
|
} catch (e) {
|
|
3925
4078
|
json(res, { ok: false, code: e && e.code, error: String(e && e.message || e) }, errorStatus(e));
|
|
3926
4079
|
}
|
|
@@ -3944,7 +4097,19 @@ function apply(ctx) {
|
|
|
3944
4097
|
await emptyIndex.remove([sid]);
|
|
3945
4098
|
} catch (e) {
|
|
3946
4099
|
}
|
|
3947
|
-
|
|
4100
|
+
const cascaded = await cascadeTrashChildren(sid, async (kid) => {
|
|
4101
|
+
const r = await purgeFromTrash(kid);
|
|
4102
|
+
try {
|
|
4103
|
+
await titleIndex.remove([kid]);
|
|
4104
|
+
} catch (e) {
|
|
4105
|
+
}
|
|
4106
|
+
try {
|
|
4107
|
+
await emptyIndex.remove([kid]);
|
|
4108
|
+
} catch (e) {
|
|
4109
|
+
}
|
|
4110
|
+
return r;
|
|
4111
|
+
});
|
|
4112
|
+
json(res, { ...out, cascaded: cascaded.filter((r) => r.ok).length });
|
|
3948
4113
|
} catch (e) {
|
|
3949
4114
|
json(res, { ok: false, code: e && e.code, error: String(e && e.message || e) }, errorStatus(e));
|
|
3950
4115
|
}
|
|
@@ -3967,6 +4132,18 @@ function apply(ctx) {
|
|
|
3967
4132
|
});
|
|
3968
4133
|
await emptyIndex.remove([sid]).catch(() => {
|
|
3969
4134
|
});
|
|
4135
|
+
await cascadeTrashChildren(sid, async (kid) => {
|
|
4136
|
+
const r = await purgeFromTrash(kid);
|
|
4137
|
+
try {
|
|
4138
|
+
await titleIndex.remove([kid]);
|
|
4139
|
+
} catch (e) {
|
|
4140
|
+
}
|
|
4141
|
+
try {
|
|
4142
|
+
await emptyIndex.remove([kid]);
|
|
4143
|
+
} catch (e) {
|
|
4144
|
+
}
|
|
4145
|
+
return r;
|
|
4146
|
+
});
|
|
3970
4147
|
} catch (e) {
|
|
3971
4148
|
results.push({ sessionId: sid, ok: false, error: String(e && e.message || e) });
|
|
3972
4149
|
}
|