@mirrowel/opencode-subagent-explorer 0.1.0 → 0.2.0-dev.2

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/wizard.js CHANGED
@@ -13,6 +13,7 @@ import { useTerminalDimensions } from "@opentui/solid";
13
13
  import { createEffect, createMemo, createSignal, For, onCleanup, Show } from "solid-js";
14
14
 
15
15
  // src/sessions.ts
16
+ import { existsSync, readFileSync } from "fs";
16
17
  var CALL_TIMEOUT_MS = 5000;
17
18
  var BUILTIN_AGENTS = new Set(["build", "plan"]);
18
19
  function withTimeout(fn, ms = CALL_TIMEOUT_MS) {
@@ -54,16 +55,17 @@ function normalizeSession(raw) {
54
55
  updated: toMillis(time.updated ?? record.time_updated)
55
56
  };
56
57
  }
58
+ var SESSION_LIST_LIMIT = 5000;
57
59
  async function fetchSessions(client, directory) {
58
60
  const namespace = client?.session;
59
61
  if (!namespace?.list)
60
62
  return;
61
63
  const calls = [
62
- () => namespace.list({ directory }),
63
- () => namespace.list({ query: { directory } })
64
+ () => namespace.list({ directory, limit: SESSION_LIST_LIMIT }),
65
+ () => namespace.list({ query: { directory, limit: SESSION_LIST_LIMIT } })
64
66
  ];
65
67
  for (const call of calls) {
66
- const result = unwrap(await withTimeout(call));
68
+ const result = unwrap(await withTimeout(call, 8000));
67
69
  if (Array.isArray(result))
68
70
  return result.map(normalizeSession).filter((s) => !!s);
69
71
  }
@@ -74,23 +76,20 @@ async function fetchActiveSessionIDs(client) {
74
76
  if (!namespace)
75
77
  return;
76
78
  if (typeof namespace.active === "function") {
77
- const active = unwrap(await withTimeout(() => namespace.active()));
78
- if (active && typeof active === "object") {
79
- const ids = new Set;
80
- for (const [id, value] of Object.entries(active)) {
81
- if (value)
82
- ids.add(id);
83
- }
84
- return ids;
79
+ const result = await withTimeout(() => namespace.active());
80
+ const data = result && typeof result === "object" ? result.data : undefined;
81
+ if (data && typeof data === "object" && !Array.isArray(data)) {
82
+ return new Set(Object.keys(data));
85
83
  }
86
84
  }
87
85
  if (typeof namespace.status === "function") {
88
- const status = unwrap(await withTimeout(() => namespace.status()));
89
- if (status && typeof status === "object") {
86
+ const result = await withTimeout(() => namespace.status());
87
+ const data = result && typeof result === "object" ? result.data : undefined;
88
+ if (data && typeof data === "object" && !Array.isArray(data)) {
90
89
  const ids = new Set;
91
- for (const [id, value] of Object.entries(status)) {
92
- const state = value?.status ?? value;
93
- if (state === "busy" || state === "retry")
90
+ for (const [id, value] of Object.entries(data)) {
91
+ const type = value && typeof value === "object" ? value.type ?? value.status : value;
92
+ if (type === undefined || type === "busy" || type === "retry")
94
93
  ids.add(id);
95
94
  }
96
95
  return ids;
@@ -98,6 +97,88 @@ async function fetchActiveSessionIDs(client) {
98
97
  }
99
98
  return;
100
99
  }
100
+ function loadAvSidecar(globalConfigDir) {
101
+ const parents = new Map;
102
+ if (!globalConfigDir)
103
+ return { parents };
104
+ try {
105
+ const path = `${globalConfigDir.replace(/[\\/]+$/, "")}/agent-variants.jsonc`;
106
+ if (!existsSync(path))
107
+ return { parents };
108
+ const raw = readFileSync(path, "utf8");
109
+ const json = JSON.parse(stripJsoncComments(raw));
110
+ for (const [parent, entry] of Object.entries(json.agents ?? {})) {
111
+ if (!entry || typeof entry !== "object")
112
+ continue;
113
+ parents.set(parent, { disableBase: entry.disable_base === true && entry.disable !== true });
114
+ }
115
+ } catch {}
116
+ return { parents };
117
+ }
118
+ function stripJsoncComments(text) {
119
+ return text.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:"'\\])\/\/.*$/gm, "$1").replace(/,(\s*[}\]])/g, "$1");
120
+ }
121
+ var ATTRIBUTION_PAGE_SIZE = 100;
122
+ var ATTRIBUTION_PAGES = 5;
123
+ async function fetchVariantAttribution(client, parentSessionID, directory, avParents) {
124
+ const map = new Map;
125
+ if (avParents.size === 0 || !parentSessionID)
126
+ return map;
127
+ const namespace = client?.session;
128
+ if (!namespace || typeof namespace.messages !== "function")
129
+ return map;
130
+ const fetchPage = (before) => {
131
+ const query = { directory, limit: ATTRIBUTION_PAGE_SIZE };
132
+ if (before)
133
+ query.before = before;
134
+ const flat = namespace.messages({ sessionID: parentSessionID, ...query });
135
+ return flat instanceof Promise ? flat : namespace.messages({ path: { id: parentSessionID }, query });
136
+ };
137
+ const asList = (value) => {
138
+ if (Array.isArray(value))
139
+ return value;
140
+ if (value && typeof value === "object") {
141
+ const unwrapped = value.data;
142
+ if (Array.isArray(unwrapped))
143
+ return unwrapped;
144
+ }
145
+ return;
146
+ };
147
+ let before;
148
+ const seenCursors = new Set;
149
+ for (let page = 0;page < ATTRIBUTION_PAGES; page++) {
150
+ const messages = await withTimeout(() => fetchPage(before), 8000);
151
+ const list = asList(messages);
152
+ if (!list || list.length === 0)
153
+ break;
154
+ for (const message of list) {
155
+ for (const part of message.parts ?? []) {
156
+ if (part?.type !== "tool" || part.tool !== "task")
157
+ continue;
158
+ const metadata = part.state?.metadata;
159
+ const child = metadata?.sessionId;
160
+ const variantMeta = metadata?.agentVariants;
161
+ const alias = variantMeta?.alias;
162
+ const routedAgent = variantMeta?.routedAgent;
163
+ if (typeof child === "string" && typeof alias === "string" && !map.has(child)) {
164
+ map.set(child, {
165
+ alias,
166
+ parent: typeof routedAgent === "string" ? routedAgent : alias.replace(/-[a-z0-9-]+$/, "")
167
+ });
168
+ }
169
+ }
170
+ }
171
+ if (list.length < ATTRIBUTION_PAGE_SIZE)
172
+ break;
173
+ const last = list[list.length - 1];
174
+ const next = last?.info?.id ?? last?.id;
175
+ if (!next || seenCursors.has(next))
176
+ break;
177
+ seenCursors.add(next);
178
+ before = next;
179
+ }
180
+ return map;
181
+ }
101
182
  function buildSessionTree(sessions, rootID) {
102
183
  const byParent = new Map;
103
184
  for (const session of sessions) {
@@ -170,10 +251,18 @@ function isHiddenAgent(agent, hidden, configAgents) {
170
251
  return true;
171
252
  return !configAgents.has(agent) && !BUILTIN_AGENTS.has(agent) && !agent.startsWith("av:");
172
253
  }
254
+ function rowHidden(variant, agent, avParents, hidden, configAgents) {
255
+ if (variant)
256
+ return false;
257
+ if (agent && avParents.has(agent))
258
+ return false;
259
+ return isHiddenAgent(agent, hidden, configAgents);
260
+ }
173
261
  async function deleteSession(client, sessionID) {
174
262
  const namespace = client?.session;
175
263
  if (!namespace)
176
264
  return false;
265
+ const isErrorEnvelope = (value) => !!value && typeof value === "object" && ("error" in value) && !("data" in value);
177
266
  const shapes = [];
178
267
  if (typeof namespace.delete === "function") {
179
268
  shapes.push(() => namespace.delete({ sessionID }), () => namespace.delete({ path: { sessionID } }), () => namespace.delete({ path: { id: sessionID } }), () => namespace.delete({ id: sessionID }));
@@ -183,8 +272,11 @@ async function deleteSession(client, sessionID) {
183
272
  }
184
273
  for (const call of shapes) {
185
274
  const result = await withTimeout(call);
186
- if (result !== undefined)
187
- return true;
275
+ if (result === undefined)
276
+ continue;
277
+ if (isErrorEnvelope(result))
278
+ continue;
279
+ return true;
188
280
  }
189
281
  return false;
190
282
  }
@@ -260,28 +352,42 @@ function parentCandidates(sessions) {
260
352
  }
261
353
 
262
354
  // src/wizard.tsx
263
- async function loadExplorerData(api, directory) {
355
+ function globalConfigDirOf(api) {
356
+ const config = api.state.path?.config;
357
+ if (!config)
358
+ return;
359
+ const index = Math.max(config.lastIndexOf("/"), config.lastIndexOf("\\"));
360
+ return index > 0 ? config.slice(0, index) : undefined;
361
+ }
362
+ async function loadExplorerData(api, directory, rootOverride) {
264
363
  const sessions = await fetchSessions(api.client, directory);
265
364
  if (!sessions)
266
365
  return;
267
- const activeIDs = await fetchActiveSessionIDs(api.client);
366
+ const [activeIDs, avSidecar] = await Promise.all([fetchActiveSessionIDs(api.client), Promise.resolve(loadAvSidecar(globalConfigDirOf(api)))]);
367
+ const rootID = rootOverride ?? currentSessionID(api.currentSessionID?.(), sessions);
368
+ const variantOf = await fetchVariantAttribution(api.client, rootID, directory, new Set(avSidecar.parents.keys()));
268
369
  return {
269
370
  sessions,
270
371
  activeIDs,
271
372
  activeKnown: activeIDs !== undefined,
272
373
  hidden: hiddenAgentIDs(api.state.config),
273
- configAgents: configAgentIDs(api.state.config)
374
+ configAgents: configAgentIDs(api.state.config),
375
+ variantOf,
376
+ avParents: avSidecar.parents
274
377
  };
275
378
  }
276
379
  function explorerRows(tree, data) {
277
380
  const rows = [];
278
381
  const walk = (nodes, depth) => {
279
382
  for (const node of nodes) {
383
+ const variant = data.variantOf.get(node.session.id);
384
+ const agent = node.session.agent;
280
385
  rows.push({
281
386
  session: node.session,
282
387
  depth,
283
388
  running: data.activeIDs?.has(node.session.id) ?? false,
284
- hidden: isHiddenAgent(node.session.agent, data.hidden, data.configAgents)
389
+ hidden: rowHidden(variant, agent, data.avParents, data.hidden, data.configAgents),
390
+ variant
285
391
  });
286
392
  walk(node.children, depth + 1);
287
393
  }
@@ -289,6 +395,9 @@ function explorerRows(tree, data) {
289
395
  walk(tree, 0);
290
396
  return rows;
291
397
  }
398
+ function agentLabelOf(row) {
399
+ return row.variant?.alias ?? row.session.agent ?? "unknown";
400
+ }
292
401
  function themeOf(api) {
293
402
  return api.theme.current;
294
403
  }
@@ -661,7 +770,7 @@ function SubagentTreeDialog(props) {
661
770
  _$setProp(_el$30, "flexShrink", 0);
662
771
  _$setProp(_el$30, "wrapMode", "none");
663
772
  _$setProp(_el$30, "overflow", "hidden");
664
- _$insert(_el$31, () => truncate(row.session.agent ?? "agent", agentWidth));
773
+ _$insert(_el$31, () => truncate(agentLabelOf(row), agentWidth));
665
774
  _$insert(_el$28, _$createComponent(Show, {
666
775
  get when() {
667
776
  return row.hidden;
@@ -986,7 +1095,7 @@ function showConfirm(api, props) {
986
1095
  async function deleteOne(api, row) {
987
1096
  const label = `${rowTitle(row)}${row.hidden ? " [hidden agent]" : ""}`;
988
1097
  const summary = `${label}
989
- agent: ${row.session.agent ?? "unknown"}
1098
+ ${row.variant ? `variant: ${row.variant.alias} (of ${row.variant.parent})` : `agent: ${row.session.agent ?? "unknown"}`}
990
1099
  last activity: ${formatAge(row.session.updated ?? row.session.created)}
991
1100
 
992
1101
  Deleting a session permanently removes it, its messages, and its history. The parent session keeps the task result text; only the jump target is lost.`;
@@ -1010,7 +1119,50 @@ Deleting a session permanently removes it, its messages, and its history. The pa
1010
1119
  });
1011
1120
  return ok;
1012
1121
  }
1013
- async function runCleanup(api, plan, rootTitle) {
1122
+ function CleanupProgressDialog(props) {
1123
+ const theme = () => themeOf(props.api);
1124
+ const popMode = props.api.mode.push("subagent-explorer.cleanup");
1125
+ onCleanup(() => popMode());
1126
+ const done = props.done;
1127
+ const total = props.total;
1128
+ return (() => {
1129
+ var _el$56 = _$createElement("box"), _el$57 = _$createElement("text"), _el$58 = _$createElement("b"), _el$60 = _$createElement("text"), _el$61 = _$createElement("text");
1130
+ _$insertNode(_el$56, _el$57);
1131
+ _$insertNode(_el$56, _el$60);
1132
+ _$insertNode(_el$56, _el$61);
1133
+ _$setProp(_el$56, "flexDirection", "column");
1134
+ _$setProp(_el$56, "width", "100%");
1135
+ _$setProp(_el$56, "paddingLeft", 2);
1136
+ _$setProp(_el$56, "paddingRight", 2);
1137
+ _$setProp(_el$56, "paddingTop", 2);
1138
+ _$setProp(_el$56, "paddingBottom", 2);
1139
+ _$setProp(_el$56, "gap", 1);
1140
+ _$insertNode(_el$57, _el$58);
1141
+ _$insertNode(_el$58, _$createTextNode(`Cleaning up idle subagent sessions\u2026`));
1142
+ _$insert(_el$60, () => `${done()} / ${total} deleted`);
1143
+ _$insertNode(_el$61, _$createTextNode(`This dialog closes when the batch finishes.`));
1144
+ _$effect((_p$) => {
1145
+ var _v$24 = theme().text, _v$25 = theme().textMuted, _v$26 = theme().textMuted;
1146
+ _v$24 !== _p$.e && (_p$.e = _$setProp(_el$57, "fg", _v$24, _p$.e));
1147
+ _v$25 !== _p$.t && (_p$.t = _$setProp(_el$60, "fg", _v$25, _p$.t));
1148
+ _v$26 !== _p$.a && (_p$.a = _$setProp(_el$61, "fg", _v$26, _p$.a));
1149
+ return _p$;
1150
+ }, {
1151
+ e: undefined,
1152
+ t: undefined,
1153
+ a: undefined
1154
+ });
1155
+ return _el$56;
1156
+ })();
1157
+ }
1158
+ function showCleanupProgress(api, done, total) {
1159
+ api.ui.dialog.replace(() => _$createComponent(CleanupProgressDialog, {
1160
+ api,
1161
+ done,
1162
+ total
1163
+ }), () => {});
1164
+ }
1165
+ async function runCleanup(api, plan, rootTitle, directory, rootID) {
1014
1166
  if (plan.deletable.length === 0) {
1015
1167
  await showInfo(api, {
1016
1168
  title: "Nothing to clean up",
@@ -1029,18 +1181,40 @@ ${plan.sparedActive.length} running session(s) will be spared.` : "";
1029
1181
  if (!confirmed)
1030
1182
  return 0;
1031
1183
  let deleted = 0;
1184
+ const [progressDone, setProgressDone] = createSignal(0);
1185
+ showCleanupProgress(api, progressDone, plan.deletable.length);
1032
1186
  for (const session of plan.deletable) {
1033
- if (await deleteSession(api.client, session.id))
1187
+ if (await deleteSession(api.client, session.id)) {
1034
1188
  deleted += 1;
1189
+ setProgressDone(deleted);
1190
+ }
1035
1191
  }
1192
+ const remainingIdle = await remainingIdleCount(api, directory, rootID);
1193
+ api.ui.dialog.clear();
1036
1194
  const failed = plan.deletable.length - deleted;
1037
1195
  api.ui.toast({
1038
- variant: failed > 0 ? "warning" : "default",
1196
+ variant: failed > 0 || remainingIdle > 0 ? "warning" : "default",
1039
1197
  title: "Cleanup finished",
1040
- message: failed > 0 ? `Deleted ${deleted}, failed ${failed}.` : `Deleted ${deleted} session(s).`
1198
+ message: remainingIdle > 0 ? `Deleted ${deleted}. ${remainingIdle} idle session(s) still remain${failed > 0 ? ` (${failed} delete call(s) failed - re-run cleanup or delete rows individually)` : " - re-run cleanup for the newly-idle"}.` : failed > 0 ? `Deleted ${deleted}, failed ${failed}.` : `Deleted ${deleted} session(s).`
1041
1199
  });
1042
1200
  return deleted;
1043
1201
  }
1202
+ async function remainingIdleCount(api, directory, rootID) {
1203
+ const sessions = await fetchSessions(api.client, directory);
1204
+ if (!sessions)
1205
+ return 0;
1206
+ const active = await fetchActiveSessionIDs(api.client);
1207
+ const rows = explorerRows(buildSessionTree(sessions, rootID), {
1208
+ sessions,
1209
+ activeIDs: active,
1210
+ activeKnown: active !== undefined,
1211
+ hidden: new Set,
1212
+ configAgents: new Set,
1213
+ variantOf: new Map,
1214
+ avParents: new Map
1215
+ });
1216
+ return rows.filter((row) => !row.running).length;
1217
+ }
1044
1218
  function showRootPicker(api, sessions, currentRoot) {
1045
1219
  return new Promise((resolve) => {
1046
1220
  let settled = false;
@@ -1065,9 +1239,19 @@ function showRootPicker(api, sessions, currentRoot) {
1065
1239
  }), () => done(undefined));
1066
1240
  });
1067
1241
  }
1068
- async function showDetails(api, row) {
1242
+ async function showDetails(api, row, data) {
1069
1243
  const details = await fetchSessionDetails(api.client, row.session.id);
1070
- const lines = [`title: ${rowTitle(row)}`, `agent: ${row.session.agent ?? "unknown"}${row.hidden ? " (hidden/background)" : ""}`, `session: ${row.session.id}`, `status: ${row.running ? "running" : "idle"}`, `created: ${formatAge(row.session.created)}`, `last activity: ${formatAge(row.session.updated ?? row.session.created)}`];
1244
+ const parentAgent = row.session.agent ?? "unknown";
1245
+ const lines = [`title: ${rowTitle(row)}`, row.variant ? `variant: ${row.variant.alias} (of ${row.variant.parent})` : `agent: ${parentAgent}${row.hidden ? " (hidden/background)" : ""}`];
1246
+ if (row.variant) {
1247
+ const parentEntry = data.avParents.get(row.variant.parent);
1248
+ if (parentEntry) {
1249
+ lines.push(`base ${row.variant.parent}: ${parentEntry.disableBase ? "disabled (variants only)" : "enabled"}`);
1250
+ }
1251
+ } else if (data.avParents.has(parentAgent)) {
1252
+ lines.push(`base ${parentAgent}: ${data.avParents.get(parentAgent)?.disableBase ? "disabled (variants only)" : "enabled"}`);
1253
+ }
1254
+ lines.push(`session: ${row.session.id}`, `status: ${row.running ? "running" : "idle"}`, `created: ${formatAge(row.session.created)}`, `last activity: ${formatAge(row.session.updated ?? row.session.created)}`);
1071
1255
  if (details) {
1072
1256
  lines.push(`messages: ${details.messageCount}`);
1073
1257
  if (details.model)
@@ -1347,140 +1531,140 @@ function SizeSliderDialog(props) {
1347
1531
  popMode();
1348
1532
  });
1349
1533
  return (() => {
1350
- var _el$56 = _$createElement("box"), _el$57 = _$createElement("box"), _el$58 = _$createElement("text"), _el$59 = _$createElement("b"), _el$61 = _$createElement("text"), _el$63 = _$createElement("box"), _el$64 = _$createElement("text"), _el$66 = _$createElement("text"), _el$67 = _$createElement("b"), _el$68 = _$createElement("text"), _el$69 = _$createTextNode(` (`), _el$70 = _$createTextNode(` cols, w to cycle) Height: `), _el$71 = _$createElement("text"), _el$72 = _$createElement("b"), _el$73 = _$createTextNode(`%`), _el$74 = _$createElement("box"), _el$75 = _$createElement("text"), _el$76 = _$createTextNode(`25% `), _el$78 = _$createElement("text"), _el$79 = _$createTextNode(` 100%`), _el$82 = _$createElement("box"), _el$83 = _$createElement("box"), _el$84 = _$createElement("text"), _el$86 = _$createElement("text"), _el$88 = _$createElement("text"), _el$90 = _$createElement("text"), _el$92 = _$createElement("box"), _el$93 = _$createElement("box"), _el$94 = _$createElement("text"), _el$96 = _$createElement("box"), _el$97 = _$createElement("text"), _el$98 = _$createElement("b");
1351
- _$insertNode(_el$56, _el$57);
1352
- _$insertNode(_el$56, _el$63);
1353
- _$insertNode(_el$56, _el$74);
1354
- _$insertNode(_el$56, _el$82);
1355
- _$insertNode(_el$56, _el$93);
1356
- _$setProp(_el$56, "flexDirection", "column");
1357
- _$setProp(_el$56, "width", "100%");
1358
- _$setProp(_el$56, "paddingLeft", 2);
1359
- _$setProp(_el$56, "paddingRight", 2);
1360
- _$setProp(_el$56, "paddingTop", 1);
1361
- _$setProp(_el$56, "paddingBottom", 1);
1362
- _$insertNode(_el$57, _el$58);
1363
- _$insertNode(_el$57, _el$61);
1364
- _$setProp(_el$57, "flexDirection", "row");
1365
- _$setProp(_el$57, "justifyContent", "space-between");
1366
- _$setProp(_el$57, "width", "100%");
1367
- _$setProp(_el$57, "marginBottom", 1);
1368
- _$insertNode(_el$58, _el$59);
1369
- _$insertNode(_el$59, _$createTextNode(`Dialog size`));
1370
- _$insertNode(_el$61, _$createTextNode(`esc`));
1371
- _$setProp(_el$61, "onMouseUp", () => props.onDone(undefined));
1534
+ var _el$63 = _$createElement("box"), _el$64 = _$createElement("box"), _el$65 = _$createElement("text"), _el$66 = _$createElement("b"), _el$68 = _$createElement("text"), _el$70 = _$createElement("box"), _el$71 = _$createElement("text"), _el$73 = _$createElement("text"), _el$74 = _$createElement("b"), _el$75 = _$createElement("text"), _el$76 = _$createTextNode(` (`), _el$77 = _$createTextNode(` cols, w to cycle) Height: `), _el$78 = _$createElement("text"), _el$79 = _$createElement("b"), _el$80 = _$createTextNode(`%`), _el$81 = _$createElement("box"), _el$82 = _$createElement("text"), _el$83 = _$createTextNode(`25% `), _el$85 = _$createElement("text"), _el$86 = _$createTextNode(` 100%`), _el$89 = _$createElement("box"), _el$90 = _$createElement("box"), _el$91 = _$createElement("text"), _el$93 = _$createElement("text"), _el$95 = _$createElement("text"), _el$97 = _$createElement("text"), _el$99 = _$createElement("box"), _el$100 = _$createElement("box"), _el$101 = _$createElement("text"), _el$103 = _$createElement("box"), _el$104 = _$createElement("text"), _el$105 = _$createElement("b");
1372
1535
  _$insertNode(_el$63, _el$64);
1373
- _$insertNode(_el$63, _el$66);
1374
- _$insertNode(_el$63, _el$68);
1375
- _$insertNode(_el$63, _el$71);
1376
- _$setProp(_el$63, "flexDirection", "row");
1377
- _$setProp(_el$63, "gap", 0);
1536
+ _$insertNode(_el$63, _el$70);
1537
+ _$insertNode(_el$63, _el$81);
1538
+ _$insertNode(_el$63, _el$89);
1539
+ _$insertNode(_el$63, _el$100);
1540
+ _$setProp(_el$63, "flexDirection", "column");
1378
1541
  _$setProp(_el$63, "width", "100%");
1379
- _$setProp(_el$63, "marginBottom", 1);
1380
- _$insertNode(_el$64, _$createTextNode(`Width: `));
1381
- _$insertNode(_el$66, _el$67);
1382
- _$insert(_el$67, () => dialogSizeOf(props.api));
1383
- _$insertNode(_el$68, _el$69);
1384
- _$insertNode(_el$68, _el$70);
1385
- _$insert(_el$68, () => DIALOG_WIDTH_COLUMNS[dialogSizeOf(props.api)], _el$70);
1386
- _$insertNode(_el$71, _el$72);
1387
- _$insertNode(_el$72, _el$73);
1388
- _$insert(_el$72, height, _el$73);
1389
- _$insertNode(_el$74, _el$75);
1390
- _$insertNode(_el$74, _el$78);
1391
- _$setProp(_el$74, "flexDirection", "row");
1392
- _$setProp(_el$74, "width", "100%");
1393
- _$setProp(_el$74, "marginBottom", 1);
1542
+ _$setProp(_el$63, "paddingLeft", 2);
1543
+ _$setProp(_el$63, "paddingRight", 2);
1544
+ _$setProp(_el$63, "paddingTop", 1);
1545
+ _$setProp(_el$63, "paddingBottom", 1);
1546
+ _$insertNode(_el$64, _el$65);
1547
+ _$insertNode(_el$64, _el$68);
1548
+ _$setProp(_el$64, "flexDirection", "row");
1549
+ _$setProp(_el$64, "justifyContent", "space-between");
1550
+ _$setProp(_el$64, "width", "100%");
1551
+ _$setProp(_el$64, "marginBottom", 1);
1552
+ _$insertNode(_el$65, _el$66);
1553
+ _$insertNode(_el$66, _$createTextNode(`Dialog size`));
1554
+ _$insertNode(_el$68, _$createTextNode(`esc`));
1555
+ _$setProp(_el$68, "onMouseUp", () => props.onDone(undefined));
1556
+ _$insertNode(_el$70, _el$71);
1557
+ _$insertNode(_el$70, _el$73);
1558
+ _$insertNode(_el$70, _el$75);
1559
+ _$insertNode(_el$70, _el$78);
1560
+ _$setProp(_el$70, "flexDirection", "row");
1561
+ _$setProp(_el$70, "gap", 0);
1562
+ _$setProp(_el$70, "width", "100%");
1563
+ _$setProp(_el$70, "marginBottom", 1);
1564
+ _$insertNode(_el$71, _$createTextNode(`Width: `));
1565
+ _$insertNode(_el$73, _el$74);
1566
+ _$insert(_el$74, () => dialogSizeOf(props.api));
1394
1567
  _$insertNode(_el$75, _el$76);
1395
- _$insert(_el$74, _$createComponent(For, {
1568
+ _$insertNode(_el$75, _el$77);
1569
+ _$insert(_el$75, () => DIALOG_WIDTH_COLUMNS[dialogSizeOf(props.api)], _el$77);
1570
+ _$insertNode(_el$78, _el$79);
1571
+ _$insertNode(_el$79, _el$80);
1572
+ _$insert(_el$79, height, _el$80);
1573
+ _$insertNode(_el$81, _el$82);
1574
+ _$insertNode(_el$81, _el$85);
1575
+ _$setProp(_el$81, "flexDirection", "row");
1576
+ _$setProp(_el$81, "width", "100%");
1577
+ _$setProp(_el$81, "marginBottom", 1);
1578
+ _$insertNode(_el$82, _el$83);
1579
+ _$insert(_el$81, _$createComponent(For, {
1396
1580
  get each() {
1397
1581
  return sliderCells();
1398
1582
  },
1399
1583
  children: (cell) => (() => {
1400
- var _el$100 = _$createElement("text");
1401
- _$insert(_el$100, () => cell.char);
1402
- _$effect((_$p) => _$setProp(_el$100, "fg", cell.color, _$p));
1403
- return _el$100;
1584
+ var _el$107 = _$createElement("text");
1585
+ _$insert(_el$107, () => cell.char);
1586
+ _$effect((_$p) => _$setProp(_el$107, "fg", cell.color, _$p));
1587
+ return _el$107;
1404
1588
  })()
1405
- }), _el$78);
1406
- _$insertNode(_el$78, _el$79);
1407
- _$insertNode(_el$82, _el$83);
1408
- _$insertNode(_el$82, _el$92);
1409
- _$setProp(_el$82, "flexDirection", "row");
1410
- _$setProp(_el$82, "gap", 2);
1411
- _$setProp(_el$82, "marginBottom", 1);
1412
- _$insertNode(_el$83, _el$84);
1413
- _$insertNode(_el$83, _el$86);
1414
- _$insertNode(_el$83, _el$88);
1415
- _$insertNode(_el$83, _el$90);
1416
- _$setProp(_el$83, "flexDirection", "column");
1417
- _$setProp(_el$83, "gap", 0);
1418
- _$insert(_el$83, _$createComponent(For, {
1589
+ }), _el$85);
1590
+ _$insertNode(_el$85, _el$86);
1591
+ _$insertNode(_el$89, _el$90);
1592
+ _$insertNode(_el$89, _el$99);
1593
+ _$setProp(_el$89, "flexDirection", "row");
1594
+ _$setProp(_el$89, "gap", 2);
1595
+ _$setProp(_el$89, "marginBottom", 1);
1596
+ _$insertNode(_el$90, _el$91);
1597
+ _$insertNode(_el$90, _el$93);
1598
+ _$insertNode(_el$90, _el$95);
1599
+ _$insertNode(_el$90, _el$97);
1600
+ _$setProp(_el$90, "flexDirection", "column");
1601
+ _$setProp(_el$90, "gap", 0);
1602
+ _$insert(_el$90, _$createComponent(For, {
1419
1603
  each: HEIGHT_PRESETS,
1420
1604
  children: (preset) => (() => {
1421
- var _el$101 = _$createElement("text"), _el$102 = _$createTextNode(` `), _el$103 = _$createTextNode(`: `), _el$104 = _$createTextNode(`%`);
1422
- _$insertNode(_el$101, _el$102);
1423
- _$insertNode(_el$101, _el$103);
1424
- _$insertNode(_el$101, _el$104);
1425
- _$insert(_el$101, () => preset.key, _el$102);
1426
- _$insert(_el$101, () => preset.label, _el$103);
1427
- _$insert(_el$101, () => preset.value, _el$104);
1428
- _$effect((_$p) => _$setProp(_el$101, "fg", height() === preset.value ? theme().primary : theme().textMuted, _$p));
1429
- return _el$101;
1605
+ var _el$108 = _$createElement("text"), _el$109 = _$createTextNode(` `), _el$110 = _$createTextNode(`: `), _el$111 = _$createTextNode(`%`);
1606
+ _$insertNode(_el$108, _el$109);
1607
+ _$insertNode(_el$108, _el$110);
1608
+ _$insertNode(_el$108, _el$111);
1609
+ _$insert(_el$108, () => preset.key, _el$109);
1610
+ _$insert(_el$108, () => preset.label, _el$110);
1611
+ _$insert(_el$108, () => preset.value, _el$111);
1612
+ _$effect((_$p) => _$setProp(_el$108, "fg", height() === preset.value ? theme().primary : theme().textMuted, _$p));
1613
+ return _el$108;
1430
1614
  })()
1431
- }), _el$84);
1432
- _$insertNode(_el$84, _$createTextNode(` `));
1433
- _$insertNode(_el$86, _$createTextNode(`left/right 1%`));
1434
- _$insertNode(_el$88, _$createTextNode(`up/down 5%`));
1435
- _$insertNode(_el$90, _$createTextNode(`c custom`));
1436
- _$setProp(_el$92, "flexDirection", "column");
1437
- _$setProp(_el$92, "gap", 0);
1438
- _$insert(_el$92, _$createComponent(For, {
1615
+ }), _el$91);
1616
+ _$insertNode(_el$91, _$createTextNode(` `));
1617
+ _$insertNode(_el$93, _$createTextNode(`left/right 1%`));
1618
+ _$insertNode(_el$95, _$createTextNode(`up/down 5%`));
1619
+ _$insertNode(_el$97, _$createTextNode(`c custom`));
1620
+ _$setProp(_el$99, "flexDirection", "column");
1621
+ _$setProp(_el$99, "gap", 0);
1622
+ _$insert(_el$99, _$createComponent(For, {
1439
1623
  get each() {
1440
1624
  return preview();
1441
1625
  },
1442
1626
  children: (line) => (() => {
1443
- var _el$105 = _$createElement("text");
1444
- _$insert(_el$105, line);
1445
- _$effect((_$p) => _$setProp(_el$105, "fg", theme().textMuted, _$p));
1446
- return _el$105;
1627
+ var _el$112 = _$createElement("text");
1628
+ _$insert(_el$112, line);
1629
+ _$effect((_$p) => _$setProp(_el$112, "fg", theme().textMuted, _$p));
1630
+ return _el$112;
1447
1631
  })()
1448
1632
  }));
1449
- _$insertNode(_el$93, _el$94);
1450
- _$insertNode(_el$93, _el$96);
1451
- _$setProp(_el$93, "flexDirection", "row");
1452
- _$setProp(_el$93, "justifyContent", "space-between");
1453
- _$setProp(_el$93, "width", "100%");
1454
- _$insertNode(_el$94, _$createTextNode(`enter save`));
1455
- _$insertNode(_el$96, _el$97);
1456
- _$setProp(_el$96, "paddingLeft", 3);
1457
- _$setProp(_el$96, "paddingRight", 3);
1458
- _$setProp(_el$96, "onMouseUp", () => {
1633
+ _$insertNode(_el$100, _el$101);
1634
+ _$insertNode(_el$100, _el$103);
1635
+ _$setProp(_el$100, "flexDirection", "row");
1636
+ _$setProp(_el$100, "justifyContent", "space-between");
1637
+ _$setProp(_el$100, "width", "100%");
1638
+ _$insertNode(_el$101, _$createTextNode(`enter save`));
1639
+ _$insertNode(_el$103, _el$104);
1640
+ _$setProp(_el$103, "paddingLeft", 3);
1641
+ _$setProp(_el$103, "paddingRight", 3);
1642
+ _$setProp(_el$103, "onMouseUp", () => {
1459
1643
  setDialogHeightPercent(props.api, height());
1460
1644
  props.onDone({
1461
1645
  action: "save",
1462
1646
  height: height()
1463
1647
  });
1464
1648
  });
1465
- _$insertNode(_el$97, _el$98);
1466
- _$insertNode(_el$98, _$createTextNode(`save`));
1649
+ _$insertNode(_el$104, _el$105);
1650
+ _$insertNode(_el$105, _$createTextNode(`save`));
1467
1651
  _$effect((_p$) => {
1468
- var _v$24 = theme().accent, _v$25 = theme().textMuted, _v$26 = theme().textMuted, _v$27 = theme().primary, _v$28 = theme().textMuted, _v$29 = theme().primary, _v$30 = theme().textMuted, _v$31 = theme().textMuted, _v$32 = theme().textMuted, _v$33 = theme().textMuted, _v$34 = theme().textMuted, _v$35 = theme().textMuted, _v$36 = theme().textMuted, _v$37 = theme().primary, _v$38 = theme().background;
1469
- _v$24 !== _p$.e && (_p$.e = _$setProp(_el$58, "fg", _v$24, _p$.e));
1470
- _v$25 !== _p$.t && (_p$.t = _$setProp(_el$61, "fg", _v$25, _p$.t));
1471
- _v$26 !== _p$.a && (_p$.a = _$setProp(_el$64, "fg", _v$26, _p$.a));
1472
- _v$27 !== _p$.o && (_p$.o = _$setProp(_el$66, "fg", _v$27, _p$.o));
1473
- _v$28 !== _p$.i && (_p$.i = _$setProp(_el$68, "fg", _v$28, _p$.i));
1474
- _v$29 !== _p$.n && (_p$.n = _$setProp(_el$71, "fg", _v$29, _p$.n));
1475
- _v$30 !== _p$.s && (_p$.s = _$setProp(_el$75, "fg", _v$30, _p$.s));
1476
- _v$31 !== _p$.h && (_p$.h = _$setProp(_el$78, "fg", _v$31, _p$.h));
1477
- _v$32 !== _p$.r && (_p$.r = _$setProp(_el$84, "fg", _v$32, _p$.r));
1478
- _v$33 !== _p$.d && (_p$.d = _$setProp(_el$86, "fg", _v$33, _p$.d));
1479
- _v$34 !== _p$.l && (_p$.l = _$setProp(_el$88, "fg", _v$34, _p$.l));
1480
- _v$35 !== _p$.u && (_p$.u = _$setProp(_el$90, "fg", _v$35, _p$.u));
1481
- _v$36 !== _p$.c && (_p$.c = _$setProp(_el$94, "fg", _v$36, _p$.c));
1482
- _v$37 !== _p$.w && (_p$.w = _$setProp(_el$96, "backgroundColor", _v$37, _p$.w));
1483
- _v$38 !== _p$.m && (_p$.m = _$setProp(_el$97, "fg", _v$38, _p$.m));
1652
+ var _v$27 = theme().accent, _v$28 = theme().textMuted, _v$29 = theme().textMuted, _v$30 = theme().primary, _v$31 = theme().textMuted, _v$32 = theme().primary, _v$33 = theme().textMuted, _v$34 = theme().textMuted, _v$35 = theme().textMuted, _v$36 = theme().textMuted, _v$37 = theme().textMuted, _v$38 = theme().textMuted, _v$39 = theme().textMuted, _v$40 = theme().primary, _v$41 = theme().background;
1653
+ _v$27 !== _p$.e && (_p$.e = _$setProp(_el$65, "fg", _v$27, _p$.e));
1654
+ _v$28 !== _p$.t && (_p$.t = _$setProp(_el$68, "fg", _v$28, _p$.t));
1655
+ _v$29 !== _p$.a && (_p$.a = _$setProp(_el$71, "fg", _v$29, _p$.a));
1656
+ _v$30 !== _p$.o && (_p$.o = _$setProp(_el$73, "fg", _v$30, _p$.o));
1657
+ _v$31 !== _p$.i && (_p$.i = _$setProp(_el$75, "fg", _v$31, _p$.i));
1658
+ _v$32 !== _p$.n && (_p$.n = _$setProp(_el$78, "fg", _v$32, _p$.n));
1659
+ _v$33 !== _p$.s && (_p$.s = _$setProp(_el$82, "fg", _v$33, _p$.s));
1660
+ _v$34 !== _p$.h && (_p$.h = _$setProp(_el$85, "fg", _v$34, _p$.h));
1661
+ _v$35 !== _p$.r && (_p$.r = _$setProp(_el$91, "fg", _v$35, _p$.r));
1662
+ _v$36 !== _p$.d && (_p$.d = _$setProp(_el$93, "fg", _v$36, _p$.d));
1663
+ _v$37 !== _p$.l && (_p$.l = _$setProp(_el$95, "fg", _v$37, _p$.l));
1664
+ _v$38 !== _p$.u && (_p$.u = _$setProp(_el$97, "fg", _v$38, _p$.u));
1665
+ _v$39 !== _p$.c && (_p$.c = _$setProp(_el$101, "fg", _v$39, _p$.c));
1666
+ _v$40 !== _p$.w && (_p$.w = _$setProp(_el$103, "backgroundColor", _v$40, _p$.w));
1667
+ _v$41 !== _p$.m && (_p$.m = _$setProp(_el$104, "fg", _v$41, _p$.m));
1484
1668
  return _p$;
1485
1669
  }, {
1486
1670
  e: undefined,
@@ -1499,7 +1683,7 @@ function SizeSliderDialog(props) {
1499
1683
  w: undefined,
1500
1684
  m: undefined
1501
1685
  });
1502
- return _el$56;
1686
+ return _el$63;
1503
1687
  })();
1504
1688
  }
1505
1689
  async function mainMenu(api) {
@@ -1513,7 +1697,7 @@ async function mainMenu(api) {
1513
1697
  }
1514
1698
  let rootOverride;
1515
1699
  while (true) {
1516
- const data = await loadExplorerData(api, directory);
1700
+ const data = await loadExplorerData(api, directory, rootOverride);
1517
1701
  if (!data) {
1518
1702
  await showInfo(api, {
1519
1703
  title: "Subagent Explorer",
@@ -1561,20 +1745,21 @@ The host client does not expose session.list for this directory.`
1561
1745
  continue;
1562
1746
  }
1563
1747
  if (action.type === "details") {
1564
- await showDetails(api, action.row);
1748
+ await showDetails(api, action.row, data);
1565
1749
  continue;
1566
1750
  }
1567
1751
  if (action.type === "cleanup") {
1568
- await runCleanup(api, cleanupTargets(tree, data.activeIDs), rootSession?.title ?? rootID);
1752
+ await runCleanup(api, cleanupTargets(tree, data.activeIDs), rootSession?.title ?? rootID, directory, rootID);
1569
1753
  continue;
1570
1754
  }
1571
1755
  }
1572
1756
  }
1573
1757
  export {
1574
- rowTitle,
1575
- mainMenu,
1576
- loadExplorerData,
1577
- explorerRows,
1758
+ SubagentTreeDialog,
1759
+ agentLabelOf,
1578
1760
  computeDialogRows,
1579
- SubagentTreeDialog
1761
+ explorerRows,
1762
+ loadExplorerData,
1763
+ mainMenu,
1764
+ rowTitle
1580
1765
  };