@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/tui.js CHANGED
@@ -66,6 +66,7 @@ import { useTerminalDimensions } from "@opentui/solid";
66
66
  import { createEffect, createMemo, createSignal, For, onCleanup, Show } from "solid-js";
67
67
 
68
68
  // src/sessions.ts
69
+ import { existsSync, readFileSync } from "fs";
69
70
  var CALL_TIMEOUT_MS = 5000;
70
71
  var BUILTIN_AGENTS = new Set(["build", "plan"]);
71
72
  function withTimeout(fn, ms = CALL_TIMEOUT_MS) {
@@ -107,16 +108,17 @@ function normalizeSession(raw) {
107
108
  updated: toMillis(time.updated ?? record.time_updated)
108
109
  };
109
110
  }
111
+ var SESSION_LIST_LIMIT = 5000;
110
112
  async function fetchSessions(client, directory) {
111
113
  const namespace = client?.session;
112
114
  if (!namespace?.list)
113
115
  return;
114
116
  const calls = [
115
- () => namespace.list({ directory }),
116
- () => namespace.list({ query: { directory } })
117
+ () => namespace.list({ directory, limit: SESSION_LIST_LIMIT }),
118
+ () => namespace.list({ query: { directory, limit: SESSION_LIST_LIMIT } })
117
119
  ];
118
120
  for (const call of calls) {
119
- const result = unwrap(await withTimeout(call));
121
+ const result = unwrap(await withTimeout(call, 8000));
120
122
  if (Array.isArray(result))
121
123
  return result.map(normalizeSession).filter((s) => !!s);
122
124
  }
@@ -127,23 +129,20 @@ async function fetchActiveSessionIDs(client) {
127
129
  if (!namespace)
128
130
  return;
129
131
  if (typeof namespace.active === "function") {
130
- const active = unwrap(await withTimeout(() => namespace.active()));
131
- if (active && typeof active === "object") {
132
- const ids = new Set;
133
- for (const [id, value] of Object.entries(active)) {
134
- if (value)
135
- ids.add(id);
136
- }
137
- return ids;
132
+ const result = await withTimeout(() => namespace.active());
133
+ const data = result && typeof result === "object" ? result.data : undefined;
134
+ if (data && typeof data === "object" && !Array.isArray(data)) {
135
+ return new Set(Object.keys(data));
138
136
  }
139
137
  }
140
138
  if (typeof namespace.status === "function") {
141
- const status = unwrap(await withTimeout(() => namespace.status()));
142
- if (status && typeof status === "object") {
139
+ const result = await withTimeout(() => namespace.status());
140
+ const data = result && typeof result === "object" ? result.data : undefined;
141
+ if (data && typeof data === "object" && !Array.isArray(data)) {
143
142
  const ids = new Set;
144
- for (const [id, value] of Object.entries(status)) {
145
- const state = value?.status ?? value;
146
- if (state === "busy" || state === "retry")
143
+ for (const [id, value] of Object.entries(data)) {
144
+ const type = value && typeof value === "object" ? value.type ?? value.status : value;
145
+ if (type === undefined || type === "busy" || type === "retry")
147
146
  ids.add(id);
148
147
  }
149
148
  return ids;
@@ -151,6 +150,88 @@ async function fetchActiveSessionIDs(client) {
151
150
  }
152
151
  return;
153
152
  }
153
+ function loadAvSidecar(globalConfigDir) {
154
+ const parents = new Map;
155
+ if (!globalConfigDir)
156
+ return { parents };
157
+ try {
158
+ const path = `${globalConfigDir.replace(/[\\/]+$/, "")}/agent-variants.jsonc`;
159
+ if (!existsSync(path))
160
+ return { parents };
161
+ const raw = readFileSync(path, "utf8");
162
+ const json = JSON.parse(stripJsoncComments(raw));
163
+ for (const [parent, entry] of Object.entries(json.agents ?? {})) {
164
+ if (!entry || typeof entry !== "object")
165
+ continue;
166
+ parents.set(parent, { disableBase: entry.disable_base === true && entry.disable !== true });
167
+ }
168
+ } catch {}
169
+ return { parents };
170
+ }
171
+ function stripJsoncComments(text) {
172
+ return text.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:"'\\])\/\/.*$/gm, "$1").replace(/,(\s*[}\]])/g, "$1");
173
+ }
174
+ var ATTRIBUTION_PAGE_SIZE = 100;
175
+ var ATTRIBUTION_PAGES = 5;
176
+ async function fetchVariantAttribution(client, parentSessionID, directory, avParents) {
177
+ const map = new Map;
178
+ if (avParents.size === 0 || !parentSessionID)
179
+ return map;
180
+ const namespace = client?.session;
181
+ if (!namespace || typeof namespace.messages !== "function")
182
+ return map;
183
+ const fetchPage = (before) => {
184
+ const query = { directory, limit: ATTRIBUTION_PAGE_SIZE };
185
+ if (before)
186
+ query.before = before;
187
+ const flat = namespace.messages({ sessionID: parentSessionID, ...query });
188
+ return flat instanceof Promise ? flat : namespace.messages({ path: { id: parentSessionID }, query });
189
+ };
190
+ const asList = (value) => {
191
+ if (Array.isArray(value))
192
+ return value;
193
+ if (value && typeof value === "object") {
194
+ const unwrapped = value.data;
195
+ if (Array.isArray(unwrapped))
196
+ return unwrapped;
197
+ }
198
+ return;
199
+ };
200
+ let before;
201
+ const seenCursors = new Set;
202
+ for (let page = 0;page < ATTRIBUTION_PAGES; page++) {
203
+ const messages = await withTimeout(() => fetchPage(before), 8000);
204
+ const list = asList(messages);
205
+ if (!list || list.length === 0)
206
+ break;
207
+ for (const message of list) {
208
+ for (const part of message.parts ?? []) {
209
+ if (part?.type !== "tool" || part.tool !== "task")
210
+ continue;
211
+ const metadata = part.state?.metadata;
212
+ const child = metadata?.sessionId;
213
+ const variantMeta = metadata?.agentVariants;
214
+ const alias = variantMeta?.alias;
215
+ const routedAgent = variantMeta?.routedAgent;
216
+ if (typeof child === "string" && typeof alias === "string" && !map.has(child)) {
217
+ map.set(child, {
218
+ alias,
219
+ parent: typeof routedAgent === "string" ? routedAgent : alias.replace(/-[a-z0-9-]+$/, "")
220
+ });
221
+ }
222
+ }
223
+ }
224
+ if (list.length < ATTRIBUTION_PAGE_SIZE)
225
+ break;
226
+ const last = list[list.length - 1];
227
+ const next = last?.info?.id ?? last?.id;
228
+ if (!next || seenCursors.has(next))
229
+ break;
230
+ seenCursors.add(next);
231
+ before = next;
232
+ }
233
+ return map;
234
+ }
154
235
  function buildSessionTree(sessions, rootID) {
155
236
  const byParent = new Map;
156
237
  for (const session of sessions) {
@@ -223,10 +304,18 @@ function isHiddenAgent(agent, hidden, configAgents) {
223
304
  return true;
224
305
  return !configAgents.has(agent) && !BUILTIN_AGENTS.has(agent) && !agent.startsWith("av:");
225
306
  }
307
+ function rowHidden(variant, agent, avParents, hidden, configAgents) {
308
+ if (variant)
309
+ return false;
310
+ if (agent && avParents.has(agent))
311
+ return false;
312
+ return isHiddenAgent(agent, hidden, configAgents);
313
+ }
226
314
  async function deleteSession(client, sessionID) {
227
315
  const namespace = client?.session;
228
316
  if (!namespace)
229
317
  return false;
318
+ const isErrorEnvelope = (value) => !!value && typeof value === "object" && ("error" in value) && !("data" in value);
230
319
  const shapes = [];
231
320
  if (typeof namespace.delete === "function") {
232
321
  shapes.push(() => namespace.delete({ sessionID }), () => namespace.delete({ path: { sessionID } }), () => namespace.delete({ path: { id: sessionID } }), () => namespace.delete({ id: sessionID }));
@@ -236,8 +325,11 @@ async function deleteSession(client, sessionID) {
236
325
  }
237
326
  for (const call of shapes) {
238
327
  const result = await withTimeout(call);
239
- if (result !== undefined)
240
- return true;
328
+ if (result === undefined)
329
+ continue;
330
+ if (isErrorEnvelope(result))
331
+ continue;
332
+ return true;
241
333
  }
242
334
  return false;
243
335
  }
@@ -313,28 +405,42 @@ function parentCandidates(sessions) {
313
405
  }
314
406
 
315
407
  // src/wizard.tsx
316
- async function loadExplorerData(api, directory) {
408
+ function globalConfigDirOf(api) {
409
+ const config = api.state.path?.config;
410
+ if (!config)
411
+ return;
412
+ const index = Math.max(config.lastIndexOf("/"), config.lastIndexOf("\\"));
413
+ return index > 0 ? config.slice(0, index) : undefined;
414
+ }
415
+ async function loadExplorerData(api, directory, rootOverride) {
317
416
  const sessions = await fetchSessions(api.client, directory);
318
417
  if (!sessions)
319
418
  return;
320
- const activeIDs = await fetchActiveSessionIDs(api.client);
419
+ const [activeIDs, avSidecar] = await Promise.all([fetchActiveSessionIDs(api.client), Promise.resolve(loadAvSidecar(globalConfigDirOf(api)))]);
420
+ const rootID = rootOverride ?? currentSessionID(api.currentSessionID?.(), sessions);
421
+ const variantOf = await fetchVariantAttribution(api.client, rootID, directory, new Set(avSidecar.parents.keys()));
321
422
  return {
322
423
  sessions,
323
424
  activeIDs,
324
425
  activeKnown: activeIDs !== undefined,
325
426
  hidden: hiddenAgentIDs(api.state.config),
326
- configAgents: configAgentIDs(api.state.config)
427
+ configAgents: configAgentIDs(api.state.config),
428
+ variantOf,
429
+ avParents: avSidecar.parents
327
430
  };
328
431
  }
329
432
  function explorerRows(tree, data) {
330
433
  const rows = [];
331
434
  const walk = (nodes, depth) => {
332
435
  for (const node of nodes) {
436
+ const variant = data.variantOf.get(node.session.id);
437
+ const agent = node.session.agent;
333
438
  rows.push({
334
439
  session: node.session,
335
440
  depth,
336
441
  running: data.activeIDs?.has(node.session.id) ?? false,
337
- hidden: isHiddenAgent(node.session.agent, data.hidden, data.configAgents)
442
+ hidden: rowHidden(variant, agent, data.avParents, data.hidden, data.configAgents),
443
+ variant
338
444
  });
339
445
  walk(node.children, depth + 1);
340
446
  }
@@ -342,6 +448,9 @@ function explorerRows(tree, data) {
342
448
  walk(tree, 0);
343
449
  return rows;
344
450
  }
451
+ function agentLabelOf(row) {
452
+ return row.variant?.alias ?? row.session.agent ?? "unknown";
453
+ }
345
454
  function themeOf(api) {
346
455
  return api.theme.current;
347
456
  }
@@ -714,7 +823,7 @@ function SubagentTreeDialog(props) {
714
823
  _$setProp(_el$30, "flexShrink", 0);
715
824
  _$setProp(_el$30, "wrapMode", "none");
716
825
  _$setProp(_el$30, "overflow", "hidden");
717
- _$insert(_el$31, () => truncate(row.session.agent ?? "agent", agentWidth));
826
+ _$insert(_el$31, () => truncate(agentLabelOf(row), agentWidth));
718
827
  _$insert(_el$28, _$createComponent(Show, {
719
828
  get when() {
720
829
  return row.hidden;
@@ -1039,7 +1148,7 @@ function showConfirm(api, props) {
1039
1148
  async function deleteOne(api, row) {
1040
1149
  const label = `${rowTitle(row)}${row.hidden ? " [hidden agent]" : ""}`;
1041
1150
  const summary = `${label}
1042
- agent: ${row.session.agent ?? "unknown"}
1151
+ ${row.variant ? `variant: ${row.variant.alias} (of ${row.variant.parent})` : `agent: ${row.session.agent ?? "unknown"}`}
1043
1152
  last activity: ${formatAge(row.session.updated ?? row.session.created)}
1044
1153
 
1045
1154
  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.`;
@@ -1063,7 +1172,50 @@ Deleting a session permanently removes it, its messages, and its history. The pa
1063
1172
  });
1064
1173
  return ok;
1065
1174
  }
1066
- async function runCleanup(api, plan, rootTitle) {
1175
+ function CleanupProgressDialog(props) {
1176
+ const theme = () => themeOf(props.api);
1177
+ const popMode = props.api.mode.push("subagent-explorer.cleanup");
1178
+ onCleanup(() => popMode());
1179
+ const done = props.done;
1180
+ const total = props.total;
1181
+ return (() => {
1182
+ var _el$56 = _$createElement("box"), _el$57 = _$createElement("text"), _el$58 = _$createElement("b"), _el$60 = _$createElement("text"), _el$61 = _$createElement("text");
1183
+ _$insertNode(_el$56, _el$57);
1184
+ _$insertNode(_el$56, _el$60);
1185
+ _$insertNode(_el$56, _el$61);
1186
+ _$setProp(_el$56, "flexDirection", "column");
1187
+ _$setProp(_el$56, "width", "100%");
1188
+ _$setProp(_el$56, "paddingLeft", 2);
1189
+ _$setProp(_el$56, "paddingRight", 2);
1190
+ _$setProp(_el$56, "paddingTop", 2);
1191
+ _$setProp(_el$56, "paddingBottom", 2);
1192
+ _$setProp(_el$56, "gap", 1);
1193
+ _$insertNode(_el$57, _el$58);
1194
+ _$insertNode(_el$58, _$createTextNode(`Cleaning up idle subagent sessions\u2026`));
1195
+ _$insert(_el$60, () => `${done()} / ${total} deleted`);
1196
+ _$insertNode(_el$61, _$createTextNode(`This dialog closes when the batch finishes.`));
1197
+ _$effect((_p$) => {
1198
+ var _v$24 = theme().text, _v$25 = theme().textMuted, _v$26 = theme().textMuted;
1199
+ _v$24 !== _p$.e && (_p$.e = _$setProp(_el$57, "fg", _v$24, _p$.e));
1200
+ _v$25 !== _p$.t && (_p$.t = _$setProp(_el$60, "fg", _v$25, _p$.t));
1201
+ _v$26 !== _p$.a && (_p$.a = _$setProp(_el$61, "fg", _v$26, _p$.a));
1202
+ return _p$;
1203
+ }, {
1204
+ e: undefined,
1205
+ t: undefined,
1206
+ a: undefined
1207
+ });
1208
+ return _el$56;
1209
+ })();
1210
+ }
1211
+ function showCleanupProgress(api, done, total) {
1212
+ api.ui.dialog.replace(() => _$createComponent(CleanupProgressDialog, {
1213
+ api,
1214
+ done,
1215
+ total
1216
+ }), () => {});
1217
+ }
1218
+ async function runCleanup(api, plan, rootTitle, directory, rootID) {
1067
1219
  if (plan.deletable.length === 0) {
1068
1220
  await showInfo(api, {
1069
1221
  title: "Nothing to clean up",
@@ -1082,18 +1234,40 @@ ${plan.sparedActive.length} running session(s) will be spared.` : "";
1082
1234
  if (!confirmed)
1083
1235
  return 0;
1084
1236
  let deleted = 0;
1237
+ const [progressDone, setProgressDone] = createSignal(0);
1238
+ showCleanupProgress(api, progressDone, plan.deletable.length);
1085
1239
  for (const session of plan.deletable) {
1086
- if (await deleteSession(api.client, session.id))
1240
+ if (await deleteSession(api.client, session.id)) {
1087
1241
  deleted += 1;
1242
+ setProgressDone(deleted);
1243
+ }
1088
1244
  }
1245
+ const remainingIdle = await remainingIdleCount(api, directory, rootID);
1246
+ api.ui.dialog.clear();
1089
1247
  const failed = plan.deletable.length - deleted;
1090
1248
  api.ui.toast({
1091
- variant: failed > 0 ? "warning" : "default",
1249
+ variant: failed > 0 || remainingIdle > 0 ? "warning" : "default",
1092
1250
  title: "Cleanup finished",
1093
- message: failed > 0 ? `Deleted ${deleted}, failed ${failed}.` : `Deleted ${deleted} session(s).`
1251
+ 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).`
1094
1252
  });
1095
1253
  return deleted;
1096
1254
  }
1255
+ async function remainingIdleCount(api, directory, rootID) {
1256
+ const sessions = await fetchSessions(api.client, directory);
1257
+ if (!sessions)
1258
+ return 0;
1259
+ const active = await fetchActiveSessionIDs(api.client);
1260
+ const rows = explorerRows(buildSessionTree(sessions, rootID), {
1261
+ sessions,
1262
+ activeIDs: active,
1263
+ activeKnown: active !== undefined,
1264
+ hidden: new Set,
1265
+ configAgents: new Set,
1266
+ variantOf: new Map,
1267
+ avParents: new Map
1268
+ });
1269
+ return rows.filter((row) => !row.running).length;
1270
+ }
1097
1271
  function showRootPicker(api, sessions, currentRoot) {
1098
1272
  return new Promise((resolve) => {
1099
1273
  let settled = false;
@@ -1118,9 +1292,19 @@ function showRootPicker(api, sessions, currentRoot) {
1118
1292
  }), () => done(undefined));
1119
1293
  });
1120
1294
  }
1121
- async function showDetails(api, row) {
1295
+ async function showDetails(api, row, data) {
1122
1296
  const details = await fetchSessionDetails(api.client, row.session.id);
1123
- 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)}`];
1297
+ const parentAgent = row.session.agent ?? "unknown";
1298
+ const lines = [`title: ${rowTitle(row)}`, row.variant ? `variant: ${row.variant.alias} (of ${row.variant.parent})` : `agent: ${parentAgent}${row.hidden ? " (hidden/background)" : ""}`];
1299
+ if (row.variant) {
1300
+ const parentEntry = data.avParents.get(row.variant.parent);
1301
+ if (parentEntry) {
1302
+ lines.push(`base ${row.variant.parent}: ${parentEntry.disableBase ? "disabled (variants only)" : "enabled"}`);
1303
+ }
1304
+ } else if (data.avParents.has(parentAgent)) {
1305
+ lines.push(`base ${parentAgent}: ${data.avParents.get(parentAgent)?.disableBase ? "disabled (variants only)" : "enabled"}`);
1306
+ }
1307
+ 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)}`);
1124
1308
  if (details) {
1125
1309
  lines.push(`messages: ${details.messageCount}`);
1126
1310
  if (details.model)
@@ -1400,140 +1584,140 @@ function SizeSliderDialog(props) {
1400
1584
  popMode();
1401
1585
  });
1402
1586
  return (() => {
1403
- 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");
1404
- _$insertNode(_el$56, _el$57);
1405
- _$insertNode(_el$56, _el$63);
1406
- _$insertNode(_el$56, _el$74);
1407
- _$insertNode(_el$56, _el$82);
1408
- _$insertNode(_el$56, _el$93);
1409
- _$setProp(_el$56, "flexDirection", "column");
1410
- _$setProp(_el$56, "width", "100%");
1411
- _$setProp(_el$56, "paddingLeft", 2);
1412
- _$setProp(_el$56, "paddingRight", 2);
1413
- _$setProp(_el$56, "paddingTop", 1);
1414
- _$setProp(_el$56, "paddingBottom", 1);
1415
- _$insertNode(_el$57, _el$58);
1416
- _$insertNode(_el$57, _el$61);
1417
- _$setProp(_el$57, "flexDirection", "row");
1418
- _$setProp(_el$57, "justifyContent", "space-between");
1419
- _$setProp(_el$57, "width", "100%");
1420
- _$setProp(_el$57, "marginBottom", 1);
1421
- _$insertNode(_el$58, _el$59);
1422
- _$insertNode(_el$59, _$createTextNode(`Dialog size`));
1423
- _$insertNode(_el$61, _$createTextNode(`esc`));
1424
- _$setProp(_el$61, "onMouseUp", () => props.onDone(undefined));
1587
+ 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");
1425
1588
  _$insertNode(_el$63, _el$64);
1426
- _$insertNode(_el$63, _el$66);
1427
- _$insertNode(_el$63, _el$68);
1428
- _$insertNode(_el$63, _el$71);
1429
- _$setProp(_el$63, "flexDirection", "row");
1430
- _$setProp(_el$63, "gap", 0);
1589
+ _$insertNode(_el$63, _el$70);
1590
+ _$insertNode(_el$63, _el$81);
1591
+ _$insertNode(_el$63, _el$89);
1592
+ _$insertNode(_el$63, _el$100);
1593
+ _$setProp(_el$63, "flexDirection", "column");
1431
1594
  _$setProp(_el$63, "width", "100%");
1432
- _$setProp(_el$63, "marginBottom", 1);
1433
- _$insertNode(_el$64, _$createTextNode(`Width: `));
1434
- _$insertNode(_el$66, _el$67);
1435
- _$insert(_el$67, () => dialogSizeOf(props.api));
1436
- _$insertNode(_el$68, _el$69);
1437
- _$insertNode(_el$68, _el$70);
1438
- _$insert(_el$68, () => DIALOG_WIDTH_COLUMNS[dialogSizeOf(props.api)], _el$70);
1439
- _$insertNode(_el$71, _el$72);
1440
- _$insertNode(_el$72, _el$73);
1441
- _$insert(_el$72, height, _el$73);
1442
- _$insertNode(_el$74, _el$75);
1443
- _$insertNode(_el$74, _el$78);
1444
- _$setProp(_el$74, "flexDirection", "row");
1445
- _$setProp(_el$74, "width", "100%");
1446
- _$setProp(_el$74, "marginBottom", 1);
1595
+ _$setProp(_el$63, "paddingLeft", 2);
1596
+ _$setProp(_el$63, "paddingRight", 2);
1597
+ _$setProp(_el$63, "paddingTop", 1);
1598
+ _$setProp(_el$63, "paddingBottom", 1);
1599
+ _$insertNode(_el$64, _el$65);
1600
+ _$insertNode(_el$64, _el$68);
1601
+ _$setProp(_el$64, "flexDirection", "row");
1602
+ _$setProp(_el$64, "justifyContent", "space-between");
1603
+ _$setProp(_el$64, "width", "100%");
1604
+ _$setProp(_el$64, "marginBottom", 1);
1605
+ _$insertNode(_el$65, _el$66);
1606
+ _$insertNode(_el$66, _$createTextNode(`Dialog size`));
1607
+ _$insertNode(_el$68, _$createTextNode(`esc`));
1608
+ _$setProp(_el$68, "onMouseUp", () => props.onDone(undefined));
1609
+ _$insertNode(_el$70, _el$71);
1610
+ _$insertNode(_el$70, _el$73);
1611
+ _$insertNode(_el$70, _el$75);
1612
+ _$insertNode(_el$70, _el$78);
1613
+ _$setProp(_el$70, "flexDirection", "row");
1614
+ _$setProp(_el$70, "gap", 0);
1615
+ _$setProp(_el$70, "width", "100%");
1616
+ _$setProp(_el$70, "marginBottom", 1);
1617
+ _$insertNode(_el$71, _$createTextNode(`Width: `));
1618
+ _$insertNode(_el$73, _el$74);
1619
+ _$insert(_el$74, () => dialogSizeOf(props.api));
1447
1620
  _$insertNode(_el$75, _el$76);
1448
- _$insert(_el$74, _$createComponent(For, {
1621
+ _$insertNode(_el$75, _el$77);
1622
+ _$insert(_el$75, () => DIALOG_WIDTH_COLUMNS[dialogSizeOf(props.api)], _el$77);
1623
+ _$insertNode(_el$78, _el$79);
1624
+ _$insertNode(_el$79, _el$80);
1625
+ _$insert(_el$79, height, _el$80);
1626
+ _$insertNode(_el$81, _el$82);
1627
+ _$insertNode(_el$81, _el$85);
1628
+ _$setProp(_el$81, "flexDirection", "row");
1629
+ _$setProp(_el$81, "width", "100%");
1630
+ _$setProp(_el$81, "marginBottom", 1);
1631
+ _$insertNode(_el$82, _el$83);
1632
+ _$insert(_el$81, _$createComponent(For, {
1449
1633
  get each() {
1450
1634
  return sliderCells();
1451
1635
  },
1452
1636
  children: (cell) => (() => {
1453
- var _el$100 = _$createElement("text");
1454
- _$insert(_el$100, () => cell.char);
1455
- _$effect((_$p) => _$setProp(_el$100, "fg", cell.color, _$p));
1456
- return _el$100;
1637
+ var _el$107 = _$createElement("text");
1638
+ _$insert(_el$107, () => cell.char);
1639
+ _$effect((_$p) => _$setProp(_el$107, "fg", cell.color, _$p));
1640
+ return _el$107;
1457
1641
  })()
1458
- }), _el$78);
1459
- _$insertNode(_el$78, _el$79);
1460
- _$insertNode(_el$82, _el$83);
1461
- _$insertNode(_el$82, _el$92);
1462
- _$setProp(_el$82, "flexDirection", "row");
1463
- _$setProp(_el$82, "gap", 2);
1464
- _$setProp(_el$82, "marginBottom", 1);
1465
- _$insertNode(_el$83, _el$84);
1466
- _$insertNode(_el$83, _el$86);
1467
- _$insertNode(_el$83, _el$88);
1468
- _$insertNode(_el$83, _el$90);
1469
- _$setProp(_el$83, "flexDirection", "column");
1470
- _$setProp(_el$83, "gap", 0);
1471
- _$insert(_el$83, _$createComponent(For, {
1642
+ }), _el$85);
1643
+ _$insertNode(_el$85, _el$86);
1644
+ _$insertNode(_el$89, _el$90);
1645
+ _$insertNode(_el$89, _el$99);
1646
+ _$setProp(_el$89, "flexDirection", "row");
1647
+ _$setProp(_el$89, "gap", 2);
1648
+ _$setProp(_el$89, "marginBottom", 1);
1649
+ _$insertNode(_el$90, _el$91);
1650
+ _$insertNode(_el$90, _el$93);
1651
+ _$insertNode(_el$90, _el$95);
1652
+ _$insertNode(_el$90, _el$97);
1653
+ _$setProp(_el$90, "flexDirection", "column");
1654
+ _$setProp(_el$90, "gap", 0);
1655
+ _$insert(_el$90, _$createComponent(For, {
1472
1656
  each: HEIGHT_PRESETS,
1473
1657
  children: (preset) => (() => {
1474
- var _el$101 = _$createElement("text"), _el$102 = _$createTextNode(` `), _el$103 = _$createTextNode(`: `), _el$104 = _$createTextNode(`%`);
1475
- _$insertNode(_el$101, _el$102);
1476
- _$insertNode(_el$101, _el$103);
1477
- _$insertNode(_el$101, _el$104);
1478
- _$insert(_el$101, () => preset.key, _el$102);
1479
- _$insert(_el$101, () => preset.label, _el$103);
1480
- _$insert(_el$101, () => preset.value, _el$104);
1481
- _$effect((_$p) => _$setProp(_el$101, "fg", height() === preset.value ? theme().primary : theme().textMuted, _$p));
1482
- return _el$101;
1658
+ var _el$108 = _$createElement("text"), _el$109 = _$createTextNode(` `), _el$110 = _$createTextNode(`: `), _el$111 = _$createTextNode(`%`);
1659
+ _$insertNode(_el$108, _el$109);
1660
+ _$insertNode(_el$108, _el$110);
1661
+ _$insertNode(_el$108, _el$111);
1662
+ _$insert(_el$108, () => preset.key, _el$109);
1663
+ _$insert(_el$108, () => preset.label, _el$110);
1664
+ _$insert(_el$108, () => preset.value, _el$111);
1665
+ _$effect((_$p) => _$setProp(_el$108, "fg", height() === preset.value ? theme().primary : theme().textMuted, _$p));
1666
+ return _el$108;
1483
1667
  })()
1484
- }), _el$84);
1485
- _$insertNode(_el$84, _$createTextNode(` `));
1486
- _$insertNode(_el$86, _$createTextNode(`left/right 1%`));
1487
- _$insertNode(_el$88, _$createTextNode(`up/down 5%`));
1488
- _$insertNode(_el$90, _$createTextNode(`c custom`));
1489
- _$setProp(_el$92, "flexDirection", "column");
1490
- _$setProp(_el$92, "gap", 0);
1491
- _$insert(_el$92, _$createComponent(For, {
1668
+ }), _el$91);
1669
+ _$insertNode(_el$91, _$createTextNode(` `));
1670
+ _$insertNode(_el$93, _$createTextNode(`left/right 1%`));
1671
+ _$insertNode(_el$95, _$createTextNode(`up/down 5%`));
1672
+ _$insertNode(_el$97, _$createTextNode(`c custom`));
1673
+ _$setProp(_el$99, "flexDirection", "column");
1674
+ _$setProp(_el$99, "gap", 0);
1675
+ _$insert(_el$99, _$createComponent(For, {
1492
1676
  get each() {
1493
1677
  return preview();
1494
1678
  },
1495
1679
  children: (line) => (() => {
1496
- var _el$105 = _$createElement("text");
1497
- _$insert(_el$105, line);
1498
- _$effect((_$p) => _$setProp(_el$105, "fg", theme().textMuted, _$p));
1499
- return _el$105;
1680
+ var _el$112 = _$createElement("text");
1681
+ _$insert(_el$112, line);
1682
+ _$effect((_$p) => _$setProp(_el$112, "fg", theme().textMuted, _$p));
1683
+ return _el$112;
1500
1684
  })()
1501
1685
  }));
1502
- _$insertNode(_el$93, _el$94);
1503
- _$insertNode(_el$93, _el$96);
1504
- _$setProp(_el$93, "flexDirection", "row");
1505
- _$setProp(_el$93, "justifyContent", "space-between");
1506
- _$setProp(_el$93, "width", "100%");
1507
- _$insertNode(_el$94, _$createTextNode(`enter save`));
1508
- _$insertNode(_el$96, _el$97);
1509
- _$setProp(_el$96, "paddingLeft", 3);
1510
- _$setProp(_el$96, "paddingRight", 3);
1511
- _$setProp(_el$96, "onMouseUp", () => {
1686
+ _$insertNode(_el$100, _el$101);
1687
+ _$insertNode(_el$100, _el$103);
1688
+ _$setProp(_el$100, "flexDirection", "row");
1689
+ _$setProp(_el$100, "justifyContent", "space-between");
1690
+ _$setProp(_el$100, "width", "100%");
1691
+ _$insertNode(_el$101, _$createTextNode(`enter save`));
1692
+ _$insertNode(_el$103, _el$104);
1693
+ _$setProp(_el$103, "paddingLeft", 3);
1694
+ _$setProp(_el$103, "paddingRight", 3);
1695
+ _$setProp(_el$103, "onMouseUp", () => {
1512
1696
  setDialogHeightPercent(props.api, height());
1513
1697
  props.onDone({
1514
1698
  action: "save",
1515
1699
  height: height()
1516
1700
  });
1517
1701
  });
1518
- _$insertNode(_el$97, _el$98);
1519
- _$insertNode(_el$98, _$createTextNode(`save`));
1702
+ _$insertNode(_el$104, _el$105);
1703
+ _$insertNode(_el$105, _$createTextNode(`save`));
1520
1704
  _$effect((_p$) => {
1521
- 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;
1522
- _v$24 !== _p$.e && (_p$.e = _$setProp(_el$58, "fg", _v$24, _p$.e));
1523
- _v$25 !== _p$.t && (_p$.t = _$setProp(_el$61, "fg", _v$25, _p$.t));
1524
- _v$26 !== _p$.a && (_p$.a = _$setProp(_el$64, "fg", _v$26, _p$.a));
1525
- _v$27 !== _p$.o && (_p$.o = _$setProp(_el$66, "fg", _v$27, _p$.o));
1526
- _v$28 !== _p$.i && (_p$.i = _$setProp(_el$68, "fg", _v$28, _p$.i));
1527
- _v$29 !== _p$.n && (_p$.n = _$setProp(_el$71, "fg", _v$29, _p$.n));
1528
- _v$30 !== _p$.s && (_p$.s = _$setProp(_el$75, "fg", _v$30, _p$.s));
1529
- _v$31 !== _p$.h && (_p$.h = _$setProp(_el$78, "fg", _v$31, _p$.h));
1530
- _v$32 !== _p$.r && (_p$.r = _$setProp(_el$84, "fg", _v$32, _p$.r));
1531
- _v$33 !== _p$.d && (_p$.d = _$setProp(_el$86, "fg", _v$33, _p$.d));
1532
- _v$34 !== _p$.l && (_p$.l = _$setProp(_el$88, "fg", _v$34, _p$.l));
1533
- _v$35 !== _p$.u && (_p$.u = _$setProp(_el$90, "fg", _v$35, _p$.u));
1534
- _v$36 !== _p$.c && (_p$.c = _$setProp(_el$94, "fg", _v$36, _p$.c));
1535
- _v$37 !== _p$.w && (_p$.w = _$setProp(_el$96, "backgroundColor", _v$37, _p$.w));
1536
- _v$38 !== _p$.m && (_p$.m = _$setProp(_el$97, "fg", _v$38, _p$.m));
1705
+ 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;
1706
+ _v$27 !== _p$.e && (_p$.e = _$setProp(_el$65, "fg", _v$27, _p$.e));
1707
+ _v$28 !== _p$.t && (_p$.t = _$setProp(_el$68, "fg", _v$28, _p$.t));
1708
+ _v$29 !== _p$.a && (_p$.a = _$setProp(_el$71, "fg", _v$29, _p$.a));
1709
+ _v$30 !== _p$.o && (_p$.o = _$setProp(_el$73, "fg", _v$30, _p$.o));
1710
+ _v$31 !== _p$.i && (_p$.i = _$setProp(_el$75, "fg", _v$31, _p$.i));
1711
+ _v$32 !== _p$.n && (_p$.n = _$setProp(_el$78, "fg", _v$32, _p$.n));
1712
+ _v$33 !== _p$.s && (_p$.s = _$setProp(_el$82, "fg", _v$33, _p$.s));
1713
+ _v$34 !== _p$.h && (_p$.h = _$setProp(_el$85, "fg", _v$34, _p$.h));
1714
+ _v$35 !== _p$.r && (_p$.r = _$setProp(_el$91, "fg", _v$35, _p$.r));
1715
+ _v$36 !== _p$.d && (_p$.d = _$setProp(_el$93, "fg", _v$36, _p$.d));
1716
+ _v$37 !== _p$.l && (_p$.l = _$setProp(_el$95, "fg", _v$37, _p$.l));
1717
+ _v$38 !== _p$.u && (_p$.u = _$setProp(_el$97, "fg", _v$38, _p$.u));
1718
+ _v$39 !== _p$.c && (_p$.c = _$setProp(_el$101, "fg", _v$39, _p$.c));
1719
+ _v$40 !== _p$.w && (_p$.w = _$setProp(_el$103, "backgroundColor", _v$40, _p$.w));
1720
+ _v$41 !== _p$.m && (_p$.m = _$setProp(_el$104, "fg", _v$41, _p$.m));
1537
1721
  return _p$;
1538
1722
  }, {
1539
1723
  e: undefined,
@@ -1552,7 +1736,7 @@ function SizeSliderDialog(props) {
1552
1736
  w: undefined,
1553
1737
  m: undefined
1554
1738
  });
1555
- return _el$56;
1739
+ return _el$63;
1556
1740
  })();
1557
1741
  }
1558
1742
  async function mainMenu(api) {
@@ -1566,7 +1750,7 @@ async function mainMenu(api) {
1566
1750
  }
1567
1751
  let rootOverride;
1568
1752
  while (true) {
1569
- const data = await loadExplorerData(api, directory);
1753
+ const data = await loadExplorerData(api, directory, rootOverride);
1570
1754
  if (!data) {
1571
1755
  await showInfo(api, {
1572
1756
  title: "Subagent Explorer",
@@ -1614,11 +1798,11 @@ The host client does not expose session.list for this directory.`
1614
1798
  continue;
1615
1799
  }
1616
1800
  if (action.type === "details") {
1617
- await showDetails(api, action.row);
1801
+ await showDetails(api, action.row, data);
1618
1802
  continue;
1619
1803
  }
1620
1804
  if (action.type === "cleanup") {
1621
- await runCleanup(api, cleanupTargets(tree, data.activeIDs), rootSession?.title ?? rootID);
1805
+ await runCleanup(api, cleanupTargets(tree, data.activeIDs), rootSession?.title ?? rootID, directory, rootID);
1622
1806
  continue;
1623
1807
  }
1624
1808
  }