pantheon-opencode 1.5.0-beta.11 → 1.5.0-beta.13

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.
@@ -10,9 +10,10 @@ import { For, Show, createEffect, createMemo, createResource, createSignal, onCl
10
10
  /**
11
11
  * pantheon-tui — Pantheon TUI plugin for opencode.
12
12
  *
13
- * Single self-contained entry file: `tsdown && cp src/index.tsx dist/tui.tsx`
14
- * keeps the no-build load path working (dist/tui.tsx is raw source, transpiled
15
- * on the fly by opencode). All feature code lives in this one file.
13
+ * Bundled entry: `tsdown` emits `dist/tui.js`, and the package `exports`
14
+ * (`./tui` → ./dist/tui.js, `./server` → ./dist/server.js) is the ONLY load
15
+ * path — no raw TSX copy is shipped. Because the bundle inlines relative
16
+ * imports, this entry may be split into modules without breaking the loader.
16
17
  *
17
18
  * Slots:
18
19
  * - sidebar_content (order 900) — Pantheon sidebar (header/version/
@@ -164,7 +165,7 @@ async function detectVersion(api) {
164
165
  if (ver) return ver;
165
166
  }
166
167
  } catch {}
167
- return "1.5.0-beta.11";
168
+ return "1.5.0-beta.13";
168
169
  }
169
170
  /**
170
171
  * usage-bar — AI subscription usage gauge for the opencode TUI.
@@ -839,17 +840,74 @@ async function readDelegationEntries(dir) {
839
840
  async function readAllDelegationEntries(root) {
840
841
  return readDelegationEntries(join(root, ".pantheon", "delegations"));
841
842
  }
843
+ /** The persisted subset of BackgroundJobRecord the panel consumes. Duck-typed
844
+ * so a corrupt/older record never breaks parsing. */
845
+ /** The FilePersistence state path: `<root>/.pantheon/board/state.json`. */
846
+ function boardStatePath(root) {
847
+ return join(root, ".pantheon", "board", "state.json");
848
+ }
849
+ /** Validate one parsed record, keeping only the fields the panel uses.
850
+ * Returns null for anything without a taskID/state (a corrupt entry is
851
+ * skipped individually — the rest of the snapshot still loads). */
852
+ function normalizeBoardRecord(item) {
853
+ if (typeof item !== "object" || item === null) return null;
854
+ const rec = item;
855
+ if (typeof rec.taskID !== "string" || rec.taskID === "") return null;
856
+ if (typeof rec.state !== "string" || rec.state === "") return null;
857
+ const record = {
858
+ taskID: rec.taskID,
859
+ parentSessionID: typeof rec.parentSessionID === "string" ? rec.parentSessionID : "",
860
+ agent: typeof rec.agent === "string" && rec.agent !== "" ? rec.agent : "agent",
861
+ state: rec.state,
862
+ alias: typeof rec.alias === "string" ? rec.alias : ""
863
+ };
864
+ if (typeof rec.description === "string") record.description = rec.description;
865
+ if (typeof rec.launchedAt === "number" && Number.isFinite(rec.launchedAt)) record.launchedAt = rec.launchedAt;
866
+ if (typeof rec.completedAt === "number" && Number.isFinite(rec.completedAt)) record.completedAt = rec.completedAt;
867
+ if (typeof rec.updatedAt === "number" && Number.isFinite(rec.updatedAt)) record.updatedAt = rec.updatedAt;
868
+ if (typeof rec.timedOut === "boolean") record.timedOut = rec.timedOut;
869
+ return record;
870
+ }
871
+ /** Read `.pantheon/board/state.json` (the cross-session job board snapshot).
872
+ * Fail-open: missing file, corrupt JSON, non-array payload or an unreadable
873
+ * path all yield [] — the panel keeps rendering from the other channels. */
874
+ async function readBoardState(root) {
875
+ let raw;
876
+ try {
877
+ raw = await readFile(boardStatePath(root), "utf8");
878
+ } catch {
879
+ return [];
880
+ }
881
+ let parsed;
882
+ try {
883
+ parsed = JSON.parse(raw);
884
+ } catch {
885
+ return [];
886
+ }
887
+ if (!Array.isArray(parsed)) return [];
888
+ const records = [];
889
+ for (const item of parsed) {
890
+ const record = normalizeBoardRecord(item);
891
+ if (record !== null) records.push(record);
892
+ }
893
+ return records;
894
+ }
895
+ /** Resolve the PROJECT ROOT used by every pantheon file channel. `directory`
896
+ * wins over `worktree` (the old `resolveDelegationsDir` already did this);
897
+ * an absent/empty root or `/` (no git — e.g. the sandbox test project) falls
898
+ * back to cwd. Standardised here so the delegations md, the board state file
899
+ * and the panel logger all read the SAME root (audit finding: the channels
900
+ * resolved the root independently). Pure — no I/O. */
901
+ function resolvePantheonRoot(state, cwd = process.cwd()) {
902
+ const root = state?.directory ?? state?.worktree ?? "";
903
+ if (root === "" || root === "/") return cwd;
904
+ return root;
905
+ }
842
906
  /** Resolve the directory where the job board writes delegation md reports.
843
907
  * The board writes `.pantheon/delegations` RELATIVE to the server cwd,
844
- * which the TUI exposes as `TuiState.path.directory`. `project` does NOT
845
- * exist on `TuiState.path` (the old `state?.project ?? state?.worktree`
846
- * resolution was always undefined for the first term) and `worktree` is
847
- * `/` when there is no git (e.g. the sandbox test project) — a root of
848
- * `''` or `'/'` must fall back to `process.cwd()`. */
908
+ * which the TUI exposes as `TuiState.path.directory`. */
849
909
  function resolveDelegationsDir(state, cwd = process.cwd()) {
850
- const root = state?.directory ?? state?.worktree ?? "";
851
- if (root === "" || root === "/") return join(cwd, ".pantheon", "delegations");
852
- return join(root, ".pantheon", "delegations");
910
+ return join(resolvePantheonRoot(state, cwd), ".pantheon", "delegations");
853
911
  }
854
912
  /** Project root derived from the delegations dir: `<root>/.pantheon/delegations`
855
913
  * → `<root>`. Used to point the panel logger at the REAL hooks.log — passing
@@ -875,22 +933,19 @@ function compareDelegationEntries(a, b) {
875
933
  return (b.updatedAt ?? b.startedAt) - (a.updatedAt ?? a.startedAt);
876
934
  }
877
935
  /** Split the panel list: active jobs (running/retry, stale-marked) first,
878
- * then the most recent terminal reports, then the archived tail (paginated
879
- * in the View). Pure — so the history-only panel (no sessionID) is testable
936
+ * then the most recent terminal reports; the remaining tail is collapsed
937
+ * by the View into a single "… +N more" line. Pure — so the history-only panel (no sessionID) is testable
880
938
  * without the TUI runtime. */
881
939
  function splitDelegationList(all, maxRecent = 8, now = Date.now(), staleThresholdMs = 1800 * 1e3) {
882
940
  const isActiveState = (st) => st === "running" || st === "retry" || st === "stale-running";
883
- const active = all.filter((d) => isActiveState(d.state)).map((d) => markStaleIfRunning(d, now, staleThresholdMs));
884
- const terminal = all.filter((d) => !isActiveState(d.state));
885
941
  return {
886
- active,
887
- recent: terminal.slice(0, maxRecent),
888
- archived: terminal.slice(maxRecent)
942
+ active: all.filter((d) => isActiveState(d.state)).map((d) => markStaleIfRunning(d, now, staleThresholdMs)),
943
+ recent: all.filter((d) => !isActiveState(d.state)).slice(0, maxRecent)
889
944
  };
890
945
  }
891
- /** The list the panel actually renders (kept for the header count and
892
- * existing tests): active jobs first, then the most recent terminal
893
- * reports (capped) — the archived tail is rendered separately. Pure. */
946
+ /** Live-first window used by {@link ceilingDelegationList} (kept for the
947
+ * ceiling helper and existing tests): active jobs first, then the most
948
+ * recent terminal reports (capped). Pure. */
894
949
  function visibleDelegationList(all, maxTerminal = 8, now = Date.now(), staleThresholdMs = 1800 * 1e3) {
895
950
  const { active, recent } = splitDelegationList(all, maxTerminal, now, staleThresholdMs);
896
951
  return [...active, ...recent];
@@ -918,7 +973,7 @@ function markStaleIfRunning(entry, now, thresholdMs = STALE_RUNNING_THRESHOLD_MS
918
973
  state: "stale-running"
919
974
  };
920
975
  }
921
- /** Compact elapsed-time label: "5m 12s", "1h 30m", "2d 4h" — ticks every
976
+ /** Compact elapsed-time label, single unit only: "12s"/"3m"/"1h"/"2d" — ticks every
922
977
  * second for running jobs. */
923
978
  function fmtElapsed(ms) {
924
979
  const total = Math.max(0, Math.floor(ms / 1e3));
@@ -926,9 +981,9 @@ function fmtElapsed(ms) {
926
981
  const hours = Math.floor(total % 86400 / 3600);
927
982
  const minutes = Math.floor(total % 3600 / 60);
928
983
  const seconds = total % 60;
929
- if (days > 0) return `${days}d ${hours}h`;
930
- if (hours > 0) return `${hours}h ${minutes}m`;
931
- if (minutes > 0) return `${minutes}m ${seconds}s`;
984
+ if (days > 0) return `${days}d`;
985
+ if (hours > 0) return `${hours}h`;
986
+ if (minutes > 0) return `${minutes}m`;
932
987
  return `${seconds}s`;
933
988
  }
934
989
  /** Elapsed label for one entry: an ACTIVE entry (running/retry/stale-running)
@@ -982,32 +1037,12 @@ function delegationSpinnerFrame(now) {
982
1037
  * (`retry`, `stale-running`). Fase 1 deliberately omits speculative
983
1038
  * blocked/paused/scheduled/skipped. There is no `pending` display state: a
984
1039
  * pre-dispatch tool part maps to `running` in {@link reduceDelegationToolPart}. */
985
- /** Static glyph per display state. `running` shows its base spinner frame;
986
- * callers that animate must prefer {@link delegationStateMarker}. Unicode
987
- * geometric shapes only (no Nerd Font) — shape is an independent channel
988
- * from color, so rows stay legible without color. */
989
- const DELEGATION_STATE_GLYPHS = {
990
- running: "⠋",
991
- retry: "⟳",
992
- "stale-running": "⚠",
993
- completed: "✓",
994
- error: "✕",
995
- cancelled: "−",
996
- startup_failed: "⚠",
997
- startup_unknown: "⚠"
998
- };
999
- function delegationStateGlyph(state) {
1000
- return DELEGATION_STATE_GLYPHS[state];
1001
- }
1002
- /** Row marker (`<glyph> `) — the state channel. `running` animates through
1003
- * {@link delegationSpinnerFrame} for the given tick; every other state is
1004
- * static. Pure. */
1005
- function delegationStateMarker(state, now = Date.now()) {
1006
- return `${state === "running" ? delegationSpinnerFrame(now) : delegationStateGlyph(state)} `;
1007
- }
1008
1040
  /** Semantic tone mapped to the TUI theme at the row ({@link DelegationRow}).
1009
1041
  * Kept separate + pure so the color channel is testable without booting the
1010
1042
  * renderer. */
1043
+ /** Color is the ONLY channel separating `stale-running` from a live `running`
1044
+ * row — the 4-glyph row design has no dedicated stale shape. Display-only;
1045
+ * no behavior change. */
1011
1046
  function delegationStateTone(state) {
1012
1047
  switch (state) {
1013
1048
  case "running":
@@ -1024,9 +1059,6 @@ function delegationStateTone(state) {
1024
1059
  const latestToolActivity = /* @__PURE__ */ new Map();
1025
1060
  const MAX_TOOL_ACTIVITY_ENTRIES = 200;
1026
1061
  const TOOL_ACTIVITY_TTL_MS = 300 * 1e3;
1027
- /** Archived-section pagination state — module-level so it survives sidebar
1028
- * remounts (route changes) without leaking into the component tree. */
1029
- const archivedUiState = { page: 0 };
1030
1062
  /** First usable summary string from common tool input fields. */
1031
1063
  function summarizeToolInput(input) {
1032
1064
  if (!input) return "";
@@ -1097,6 +1129,11 @@ function mergeChildDelegationSources(children, live) {
1097
1129
  for (const liveEntry of live) {
1098
1130
  if (liveEntry.alias === null && liveEntry.taskID === null && Date.now() - liveEntry.startedAt > 3e4) continue;
1099
1131
  const incoming = toDelegationEntry(liveEntry);
1132
+ const isNativeLive = liveEntry.tool === "task" && liveEntry.alias === null;
1133
+ if (isNativeLive) {
1134
+ incoming.source = "children-only";
1135
+ incoming.alias = "native-task";
1136
+ }
1100
1137
  const index = (incoming.taskID !== void 0 ? byTask.get(incoming.taskID) : void 0) ?? (incoming.alias !== "" ? bySessionAlias.get(key(incoming.sessionID, incoming.alias)) : void 0);
1101
1138
  if (index === void 0) {
1102
1139
  result.push(incoming);
@@ -1124,7 +1161,7 @@ function mergeChildDelegationSources(children, live) {
1124
1161
  startedAt: Math.min(existing.startedAt, incoming.startedAt),
1125
1162
  updatedAt: incoming.updatedAt,
1126
1163
  read: incoming.read,
1127
- source: "live"
1164
+ source: isNativeLive ? existing.source : "live"
1128
1165
  };
1129
1166
  }
1130
1167
  result.sort(compareDelegationEntries);
@@ -1308,7 +1345,7 @@ function removeDelegationEntry(map, partIDOrCallID) {
1308
1345
  * SDK exposes `api.state.part(messageID)`). The native `task` tool spawns a
1309
1346
  * child session with parentID = caller — the same mechanism as
1310
1347
  * pantheon_delegate — so its parts feed the live-map as the native signal
1311
- * (rows render `nat:` via the children channel). Pure w.r.t. I/O — used
1348
+ * (rows come from the children channel). Pure w.r.t. I/O — used
1312
1349
  * by the mount re-scan to re-seed the live map after compaction/attach. */
1313
1350
  function collectDelegationToolParts(messages, getParts) {
1314
1351
  const out = [];
@@ -1379,6 +1416,76 @@ function mergeDelegationSources(live, md) {
1379
1416
  all.sort(compareDelegationEntries);
1380
1417
  return all;
1381
1418
  }
1419
+ /** Job-board FSM → panel display state. `reconciled` is intentionally absent:
1420
+ * the panel parser rejects it (a reconciled job is done and folded away). */
1421
+ const BOARD_STATE_TO_DISPLAY = {
1422
+ running: "running",
1423
+ completed: "completed",
1424
+ error: "error",
1425
+ startup_failed: "startup_failed",
1426
+ startup_unknown: "startup_unknown",
1427
+ cancelled: "cancelled"
1428
+ };
1429
+ /** Map one persisted board record into the display shape. Returns null for
1430
+ * states the panel does not render (reconciled / unknown). Pure. */
1431
+ function boardRecordToDelegationEntry(record) {
1432
+ const state = BOARD_STATE_TO_DISPLAY[record.state];
1433
+ if (state === void 0) return null;
1434
+ const running = state === "running";
1435
+ return {
1436
+ alias: record.alias !== "" ? record.alias : `job-${record.taskID.slice(0, 8)}`,
1437
+ sessionID: record.parentSessionID,
1438
+ taskID: record.taskID,
1439
+ agent: record.agent,
1440
+ state,
1441
+ startedAt: record.launchedAt ?? record.updatedAt ?? 0,
1442
+ updatedAt: running ? null : record.completedAt ?? record.updatedAt ?? null,
1443
+ timedOut: record.timedOut === true,
1444
+ description: record.description ?? "",
1445
+ source: "board"
1446
+ };
1447
+ }
1448
+ /** Map a whole board snapshot, dropping unrenderable states. Sorted like every
1449
+ * other channel (running first, then most recent). Pure. */
1450
+ function boardRecordsToDelegationEntries(records) {
1451
+ const out = [];
1452
+ for (const record of records) {
1453
+ const entry = boardRecordToDelegationEntry(record);
1454
+ if (entry !== null) out.push(entry);
1455
+ }
1456
+ out.sort(compareDelegationEntries);
1457
+ return out;
1458
+ }
1459
+ /** Merge the board over any other channel. The board is AUTHORITATIVE for
1460
+ * state/alias/agent (it is the persisted FSM) and it is the only channel
1461
+ * that carries jobs from OTHER sessions. Dedup by taskID first, then by
1462
+ * (sessionID, alias) — aliases are per parent session. Pure. */
1463
+ function mergeBoardDelegationSources(base, board) {
1464
+ const key = (sessionID, alias) => `${sessionID}\u0000${alias}`;
1465
+ const byTaskID = /* @__PURE__ */ new Map();
1466
+ const bySessionAlias = /* @__PURE__ */ new Map();
1467
+ const out = [...base];
1468
+ out.forEach((entry, index) => {
1469
+ if (entry.taskID !== void 0) byTaskID.set(entry.taskID, index);
1470
+ bySessionAlias.set(key(entry.sessionID, entry.alias), index);
1471
+ });
1472
+ for (const incoming of board) {
1473
+ const index = (incoming.taskID !== void 0 ? byTaskID.get(incoming.taskID) : void 0) ?? bySessionAlias.get(key(incoming.sessionID, incoming.alias));
1474
+ if (index === void 0) {
1475
+ out.push(incoming);
1476
+ const next = out.length - 1;
1477
+ if (incoming.taskID !== void 0) byTaskID.set(incoming.taskID, next);
1478
+ bySessionAlias.set(key(incoming.sessionID, incoming.alias), next);
1479
+ continue;
1480
+ }
1481
+ out[index] = {
1482
+ ...incoming,
1483
+ sessionID: incoming.sessionID !== "" ? incoming.sessionID : out[index]?.sessionID ?? ""
1484
+ };
1485
+ }
1486
+ out.sort(compareDelegationEntries);
1487
+ return out;
1488
+ }
1382
1489
  /** Server-aligned session id validity: opencode rejects anything not starting
1383
1490
  * with "ses" (SchemaError). This deliberately mirrors that exact contract —
1384
1491
  * nothing stricter, nothing looser — so a template placeholder ("{sessionID}"),
@@ -1435,33 +1542,59 @@ function childStatusToState(status) {
1435
1542
  if (status === "retry") return "retry";
1436
1543
  return "running";
1437
1544
  }
1438
- /** Short row prefix for a delegation entry (one of the two visual channels
1439
- * that split native task() work from pantheon_delegate jobs — the other is
1440
- * {@link delegationIcon}):
1441
- *
1442
- * - `nat:<agent>` — a native task() child (source 'children-only', no board
1443
- * report). The child session carries no board alias, so the agent IS the
1444
- * identity.
1445
- * - `pan:<alias>` — a pantheon_delegate board row (`pan:apo-1`), the same
1446
- * alias the manager prints in pantheon_delegation_list.
1545
+ /** Status-only row model: ONE glyph + ONE identity per row.
1447
1546
  *
1448
- * Short prefixes keep the narrow sidebar readable (the old
1449
- * `pan:apo-1`/`nat:` tags ate the row width). */
1450
- function delegationTag(entry) {
1451
- return entry.source === "children-only" ? `nat:${entry.agent}` : `pan:${entry.alias}`;
1452
- }
1453
- /** Row glyph: hollow diamond `◇` for native task() children (info color,
1454
- * outline) vs filled diamond `◆` for pantheon_delegate rows (state color).
1455
- * Shape + color are independent channels, so the split stays legible even
1456
- * without color. */
1457
- function delegationIcon(entry) {
1458
- return entry.source === "children-only" ? "◇" : "◆";
1459
- }
1460
- /** Row identity after the status marker + glyph. Pantheon rows append the
1461
- * agent (`pan:apo-1 apollo`); native rows already carry it inside the tag
1462
- * (`nat:apollo`) and must not duplicate it. */
1463
- function formatDelegationIdentity(entry) {
1464
- return entry.source === "children-only" ? delegationTag(entry) : `${delegationTag(entry)} ${entry.agent}`;
1547
+ * A row is
1548
+ * `{glyph} {alias:7} {elapsed:>5}` plus a muted description line. The short
1549
+ * alias (`apo-1`) is the single identity; rows without a board alias show
1550
+ * the agent instead (never both). */
1551
+ /** Map every FSM/display state to one of the 4 row kinds. reconciled never
1552
+ * reaches the panel (the board parser drops it) but reads as done;
1553
+ * cancelled reads as done; startup_failed reads as failed while
1554
+ * startup_unknown (no error known) reads as retry. Pure. */
1555
+ function delegationRowStatus(state) {
1556
+ switch (state) {
1557
+ case "running":
1558
+ case "stale-running": return "active";
1559
+ case "retry":
1560
+ case "startup_unknown": return "retry";
1561
+ case "completed":
1562
+ case "cancelled": return "done";
1563
+ case "error":
1564
+ case "startup_failed": return "failed";
1565
+ }
1566
+ }
1567
+ /** The only 4 glyphs a row may show: animated active, done, failed, retry.
1568
+ * Shape is redundant with color (never the only signal). Pure. */
1569
+ const DELEGATION_ROW_GLYPHS = {
1570
+ active: "⠋",
1571
+ done: "✓",
1572
+ failed: "✕",
1573
+ retry: "⟳"
1574
+ };
1575
+ function delegationRowGlyph(status) {
1576
+ return DELEGATION_ROW_GLYPHS[status];
1577
+ }
1578
+ /** Row marker (`<glyph> `) — the single state channel. `active` animates
1579
+ * through the 1s spinner for the given tick; every other kind is static.
1580
+ * Pure. */
1581
+ function delegationRowMarker(state, now = Date.now()) {
1582
+ const status = delegationRowStatus(state);
1583
+ return `${status === "active" ? delegationSpinnerFrame(now) : delegationRowGlyph(status)} `;
1584
+ }
1585
+ /** ONE identity per row: the short board alias (`apo-1`); a row without a
1586
+ * board alias (native task() child, alias `native-task`) shows the agent
1587
+ * instead — never alias + agent stacked. Pure. */
1588
+ function delegationRowIdentity(entry) {
1589
+ if (entry.alias === "native-task" && entry.agent !== "") return entry.agent;
1590
+ return entry.alias;
1591
+ }
1592
+ /** Fixed alias cell, pad-right + hard-truncate to `width` (default 7) so
1593
+ * identities line up across rows. Pure. */
1594
+ const DELEGATION_ALIAS_WIDTH = 7;
1595
+ function formatDelegationAlias(identity, width = 7) {
1596
+ if (identity.length >= width) return identity.slice(0, width);
1597
+ return `${identity}${" ".repeat(width - identity.length)}`;
1465
1598
  }
1466
1599
  /** Max description width on the row detail line — the old 180-char slice
1467
1600
  * wrapped the sidebar; 44 keeps one readable line. */
@@ -1487,33 +1620,61 @@ function truncateDelegationDescription(text, max = 44) {
1487
1620
  if (graphemes.length <= max) return text;
1488
1621
  return `${graphemes.slice(0, Math.max(0, max - 1)).join("")}\u2026`;
1489
1622
  }
1490
- /** Fixed elapsed-time box: right-aligned to `width` (default 8) so elapsed
1491
- * values line up across rows, e.g. ` 12s`. A label that already fills
1623
+ /** Fixed elapsed-time box: right-aligned to `width` (default 5) so elapsed
1624
+ * values line up across rows, e.g. ` 12s`. A label that already fills
1492
1625
  * the box is returned untouched. Pure. */
1493
- const DELEGATION_ELAPSED_WIDTH = 8;
1494
- function formatDelegationElapsed(entry, now, width = 8) {
1626
+ const DELEGATION_ELAPSED_WIDTH = 5;
1627
+ function formatDelegationElapsed(entry, now, width = 5) {
1495
1628
  const label = delegationElapsed(entry, now);
1496
1629
  if (label.length >= width) return label;
1497
1630
  return `${" ".repeat(width - label.length)}${label}`;
1498
1631
  }
1499
- /** First row line: `<marker><glyph> <identity>`. The marker already carries
1500
- * its trailing space. Pure — the row renders the returned string verbatim. */
1501
- function formatDelegationRow(entry, marker) {
1502
- return `${marker}${delegationIcon(entry)} ${formatDelegationIdentity(entry)}`;
1503
- }
1504
- /** Header summary: `(N active · M done · nat:K pan:M)`. Active counts
1505
- * running/retry AND display-only stale-running (a stale row is still a live
1506
- * job — it must never read as done). Pure. */
1632
+ /** Colored left half of a row line: `<marker><alias:7> ` — the state glyph
1633
+ * plus the single identity, padded so the muted elapsed column aligns. The
1634
+ * row renders this colored lead separately from the muted elapsed tail. Pure. */
1635
+ function formatDelegationRowLead(entry, marker) {
1636
+ return `${marker}${formatDelegationAlias(delegationRowIdentity(entry))} `;
1637
+ }
1638
+ /** Visible-row ceiling for the panel: the remainder collapses into a single
1639
+ * "… +N more" line. Live rows render first, so a running job is never hidden
1640
+ * by the cap. */
1641
+ const DELEGATION_VISIBLE_CEILING = 8;
1642
+ /** Header summary: `(N active · M done)` plus `· K failed` only when K > 0.
1643
+ * Active counts running/retry AND display-only stale-running (a stale row is
1644
+ * still a live job — it must never read as done); failed counts
1645
+ * error/startup_failed; cancelled reads as done. Pure. */
1507
1646
  function formatDelegationHeader(entries) {
1508
1647
  let active = 0;
1509
- for (const e of entries) if (e.state === "running" || e.state === "retry" || e.state === "stale-running") active++;
1510
- const done = entries.length - active;
1511
- const counts = countDelegationSources(entries);
1512
- return `(${active} active \u00b7 ${done} done \u00b7 nat:${counts.native} pan:${counts.pantheon})`;
1648
+ let done = 0;
1649
+ let failed = 0;
1650
+ for (const e of entries) switch (delegationRowStatus(e.state)) {
1651
+ case "active":
1652
+ case "retry":
1653
+ active++;
1654
+ break;
1655
+ case "failed":
1656
+ failed++;
1657
+ break;
1658
+ case "done":
1659
+ done++;
1660
+ break;
1661
+ }
1662
+ const tail = failed > 0 ? ` · ${failed} failed` : "";
1663
+ return `(${active} active · ${done} done${tail})`;
1664
+ }
1665
+ /** Cap the panel: live rows first, then most-recent terminal rows, at most
1666
+ * `maxVisible` total. `hidden` is how many were dropped (rendered as
1667
+ * "… +N more"). Pure. */
1668
+ function ceilingDelegationList(all, maxVisible = 8, now = Date.now()) {
1669
+ const visible = visibleDelegationList(all, maxVisible, now).slice(0, maxVisible);
1670
+ return {
1671
+ visible,
1672
+ hidden: Math.max(0, all.length - visible.length)
1673
+ };
1513
1674
  }
1514
1675
  /** Split a display list into native task() rows vs pantheon_delegate rows.
1515
1676
  * Native = source 'children-only' (no board report); everything else counts
1516
- * as pantheon. Pure — powers the header breakdown + the hooks.log line. */
1677
+ * as pantheon. Pure — powers the hooks.log line. */
1517
1678
  function countDelegationSources(entries) {
1518
1679
  let native = 0;
1519
1680
  for (const e of entries) if (e.source === "children-only") native++;
@@ -1538,15 +1699,18 @@ function formatPanelLogLine(children, pantheon, native, md, events) {
1538
1699
  * time.created. A report-less child is a NATIVE task() child (every
1539
1700
  * child of the current session — pantheon_delegate OR the native `task()`
1540
1701
  * tool — carries parentID = caller), so it gets source 'children-only',
1541
- * the internal alias 'native-task' and the `nat:` tag instead of a
1702
+ * the internal alias 'native-task' instead of a
1542
1703
  * board alias. The 'task nativa' description fallback keeps the row
1543
1704
  * non-empty when the child carries no title.
1544
1705
  * Terminal md state wins over the derived state; a running md defers to
1545
1706
  * the child's live status. A running child is NEVER archived — it always
1546
1707
  * lands in the active split (splitDelegationList active = running/retry).
1708
+ * `parentSessionID` (the focused session id) is stamped on report-less
1709
+ * children so the row carries its origin session; omit it and they stay
1710
+ * unscoped ('').
1547
1711
  * Sorted running-first (compareDelegationEntries).
1548
1712
  * Pure — no I/O. */
1549
- function childrenToDelegationEntries(children, md, now = Date.now()) {
1713
+ function childrenToDelegationEntries(children, md, now = Date.now(), parentSessionID = "") {
1550
1714
  const byTaskID = /* @__PURE__ */ new Map();
1551
1715
  for (const m of md) if (m.taskID !== void 0 && !byTaskID.has(m.taskID)) byTaskID.set(m.taskID, m);
1552
1716
  const out = [];
@@ -1558,7 +1722,7 @@ function childrenToDelegationEntries(children, md, now = Date.now()) {
1558
1722
  const state = mdEntry !== void 0 && mdEntry.state !== "running" ? mdEntry.state : childStatusToState(child.status);
1559
1723
  out.push({
1560
1724
  alias: mdEntry?.alias ?? "native-task",
1561
- sessionID: mdEntry?.sessionID ?? "",
1725
+ sessionID: mdEntry?.sessionID ?? parentSessionID,
1562
1726
  taskID: child.id,
1563
1727
  agent: mdEntry?.agent ?? child.agent ?? "agent",
1564
1728
  state,
@@ -1644,7 +1808,7 @@ function SessionRow(props) {
1644
1808
  }
1645
1809
  function DelegationRow(props) {
1646
1810
  const theme = () => props.api.theme.current;
1647
- const color = createMemo(() => {
1811
+ const tone = createMemo(() => {
1648
1812
  const t = theme();
1649
1813
  switch (delegationStateTone(props.job.state)) {
1650
1814
  case "warning": return t.warning;
@@ -1653,57 +1817,54 @@ function DelegationRow(props) {
1653
1817
  default: return t.textMuted;
1654
1818
  }
1655
1819
  });
1656
- const marker = createMemo(() => delegationStateMarker(props.job.state, props.animationNow));
1657
- const tagColor = createMemo(() => props.job.source === "children-only" ? theme().info : color());
1658
- const description = createMemo(() => truncateDelegationDescription(props.job.description));
1820
+ const marker = createMemo(() => delegationRowMarker(props.job.state, props.animationNow));
1821
+ const lead = createMemo(() => formatDelegationRowLead(props.job, marker()));
1659
1822
  const elapsed = createMemo(() => formatDelegationElapsed(props.job, props.now));
1823
+ const description = createMemo(() => truncateDelegationDescription(props.job.description));
1660
1824
  const activity = createMemo(() => latestToolActivityFor(latestToolActivity, props.job.taskID, props.now));
1661
1825
  const open = () => {
1662
1826
  navigateToDelegationSession(props.api.route, props.job.taskID);
1663
1827
  };
1664
1828
  return (() => {
1665
- var _el$13 = createElement("box"), _el$14 = createElement("box"), _el$15 = createElement("text"), _el$16 = createElement("box"), _el$18 = createElement("text");
1829
+ var _el$13 = createElement("box"), _el$14 = createElement("box"), _el$15 = createElement("text"), _el$16 = createElement("span"), _el$17 = createElement("span");
1666
1830
  insertNode(_el$13, _el$14);
1667
- insertNode(_el$13, _el$16);
1668
1831
  setProp(_el$13, "onMouseDown", open);
1669
1832
  insertNode(_el$14, _el$15);
1670
1833
  setProp(_el$14, "flexDirection", "row");
1671
- insert(_el$15, () => formatDelegationRow(props.job, marker()));
1672
- insertNode(_el$16, _el$18);
1673
- setProp(_el$16, "flexDirection", "row");
1674
- insert(_el$16, createComponent(Show, {
1834
+ insertNode(_el$15, _el$16);
1835
+ insertNode(_el$15, _el$17);
1836
+ insert(_el$16, lead);
1837
+ insert(_el$17, elapsed);
1838
+ insert(_el$13, createComponent(Show, {
1675
1839
  get when() {
1676
1840
  return description() !== "";
1677
1841
  },
1678
1842
  get children() {
1679
- var _el$17 = createElement("text");
1680
- insert(_el$17, description);
1681
- effect((_$p) => setProp(_el$17, "fg", theme().textMuted, _$p));
1682
- return _el$17;
1843
+ var _el$18 = createElement("text");
1844
+ insert(_el$18, () => ` ${description()}`);
1845
+ effect((_$p) => setProp(_el$18, "fg", theme().textMuted, _$p));
1846
+ return _el$18;
1683
1847
  }
1684
- }), _el$18);
1685
- insert(_el$18, elapsed);
1848
+ }), null);
1686
1849
  insert(_el$13, createComponent(Show, {
1687
1850
  get when() {
1688
1851
  return activity();
1689
1852
  },
1690
1853
  children: (a) => (() => {
1691
1854
  var _el$19 = createElement("text");
1692
- insert(_el$19, () => ` \u21b3 ${truncateDelegationDescription(`${a().tool}${a().summary !== "" ? ` ${a().summary}` : ""}`)}`);
1855
+ insert(_el$19, () => ` ↳ ${truncateDelegationDescription(`${a().tool}${a().summary !== "" ? ` ${a().summary}` : ""}`)}`);
1693
1856
  effect((_$p) => setProp(_el$19, "fg", theme().textMuted, _$p));
1694
1857
  return _el$19;
1695
1858
  })()
1696
1859
  }), null);
1697
1860
  effect((_p$) => {
1698
- var _v$5 = tagColor(), _v$6 = description() !== "" ? "space-between" : "flex-end", _v$7 = theme().textMuted;
1699
- _v$5 !== _p$.e && (_p$.e = setProp(_el$15, "fg", _v$5, _p$.e));
1700
- _v$6 !== _p$.t && (_p$.t = setProp(_el$16, "justifyContent", _v$6, _p$.t));
1701
- _v$7 !== _p$.a && (_p$.a = setProp(_el$18, "fg", _v$7, _p$.a));
1861
+ var _v$5 = tone(), _v$6 = theme().textMuted;
1862
+ _v$5 !== _p$.e && (_p$.e = setProp(_el$16, "fg", _v$5, _p$.e));
1863
+ _v$6 !== _p$.t && (_p$.t = setProp(_el$17, "fg", _v$6, _p$.t));
1702
1864
  return _p$;
1703
1865
  }, {
1704
1866
  e: void 0,
1705
- t: void 0,
1706
- a: void 0
1867
+ t: void 0
1707
1868
  });
1708
1869
  return _el$13;
1709
1870
  })();
@@ -1761,8 +1922,8 @@ function View(props) {
1761
1922
  return (b.time?.updated ?? b.updated ?? 0) - ta;
1762
1923
  }).slice(0, 8);
1763
1924
  });
1764
- const delegationsDir = createMemo(() => resolveDelegationsDir(props.api.state.path));
1765
- const panelLog = createTuiLogger(panelLogDir(delegationsDir()));
1925
+ const projectRoot = createMemo(() => resolvePantheonRoot(props.api.state.path));
1926
+ const panelLog = createTuiLogger(projectRoot());
1766
1927
  const [childDelegations, setChildDelegations] = createSignal([]);
1767
1928
  const [now, setNow] = createSignal(Date.now());
1768
1929
  const [animationNow, setAnimationNow] = createSignal(Date.now());
@@ -1775,17 +1936,23 @@ function View(props) {
1775
1936
  const state = props.api.state;
1776
1937
  let md = [];
1777
1938
  try {
1778
- md = await readAllDelegationEntries(panelLogDir(delegationsDir()));
1939
+ md = await readAllDelegationEntries(projectRoot());
1779
1940
  } catch {
1780
1941
  md = [];
1781
1942
  }
1943
+ let board = [];
1944
+ try {
1945
+ board = boardRecordsToDelegationEntries(await readBoardState(projectRoot()));
1946
+ } catch {
1947
+ board = [];
1948
+ }
1782
1949
  const sessionID = resolveCurrentSessionID({
1783
1950
  sessionID: props.sessionID,
1784
1951
  api: props.api
1785
1952
  });
1786
1953
  if (sessionID === null) {
1787
- setChildDelegations(md);
1788
- panelLog.info(`${formatPanelLogLine(0, 0, 0, md.length, eventRefreshCount)} (no sessionID — history shown)`);
1954
+ setChildDelegations(mergeBoardDelegationSources(md, board));
1955
+ panelLog.info(`${formatPanelLogLine(0, 0, 0, md.length, eventRefreshCount)} (no sessionID — history+board shown)`);
1789
1956
  return;
1790
1957
  }
1791
1958
  let children = [];
@@ -1808,10 +1975,10 @@ function View(props) {
1808
1975
  const childEntries = childrenToDelegationEntries(children.map((c) => ({
1809
1976
  ...c,
1810
1977
  status: resolveStatus(c.id)
1811
- })), md, now());
1978
+ })), md, now(), sessionID);
1812
1979
  for (const [k, v] of props.liveStore.map) if (v.alias === null && v.taskID === null && Date.now() - v.startedAt > 3e4) props.liveStore.map.delete(k);
1813
1980
  const liveEntries = [...props.liveStore.map.values()].filter((entry) => entry.sessionID === sessionID);
1814
- setChildDelegations(mergeChildDelegationSources(childEntries, liveEntries));
1981
+ setChildDelegations(mergeBoardDelegationSources(mergeChildDelegationSources(childEntries, liveEntries), board));
1815
1982
  const counts = countDelegationSources(childEntries);
1816
1983
  panelLog.info(formatPanelLogLine(children.length, counts.pantheon, counts.native, md.length, eventRefreshCount));
1817
1984
  } finally {
@@ -1820,15 +1987,8 @@ function View(props) {
1820
1987
  })();
1821
1988
  return delegationsInflight;
1822
1989
  };
1823
- const delegationSplit = createMemo(() => splitDelegationList(childDelegations()));
1824
- const visibleDelegations = createMemo(() => [...delegationSplit().active, ...delegationSplit().recent]);
1990
+ const delegationCeiling = createMemo(() => ceilingDelegationList(childDelegations(), 8, now()));
1825
1991
  const delegationHeader = createMemo(() => formatDelegationHeader(childDelegations()));
1826
- const [archivedPage, setArchivedPage] = createSignal(archivedUiState.page);
1827
- const clampArchivedPage = (page, pages) => {
1828
- const clamped = Math.max(0, Math.min(page, Math.max(0, pages - 1)));
1829
- archivedUiState.page = clamped;
1830
- setArchivedPage(clamped);
1831
- };
1832
1992
  onMount(() => {
1833
1993
  const cleanup = [];
1834
1994
  try {
@@ -1896,10 +2056,10 @@ function View(props) {
1896
2056
  return branch();
1897
2057
  },
1898
2058
  children: (b) => (() => {
1899
- var _el$32 = createElement("text");
1900
- insert(_el$32, b);
1901
- effect((_$p) => setProp(_el$32, "fg", theme().textMuted, _$p));
1902
- return _el$32;
2059
+ var _el$33 = createElement("text");
2060
+ insert(_el$33, b);
2061
+ effect((_$p) => setProp(_el$33, "fg", theme().textMuted, _$p));
2062
+ return _el$33;
1903
2063
  })()
1904
2064
  }), _el$24);
1905
2065
  insert(_el$22, createComponent(Show, {
@@ -1908,37 +2068,37 @@ function View(props) {
1908
2068
  },
1909
2069
  get fallback() {
1910
2070
  return (() => {
1911
- var _el$33 = createElement("box"), _el$34 = createElement("text");
1912
- insertNode(_el$33, _el$34);
1913
- setProp(_el$33, "flexDirection", "row");
1914
- setProp(_el$33, "gap", 1);
1915
- insertNode(_el$34, createTextNode(`Preset: default`));
1916
- effect((_$p) => setProp(_el$34, "fg", theme().textMuted, _$p));
1917
- return _el$33;
2071
+ var _el$34 = createElement("box"), _el$35 = createElement("text");
2072
+ insertNode(_el$34, _el$35);
2073
+ setProp(_el$34, "flexDirection", "row");
2074
+ setProp(_el$34, "gap", 1);
2075
+ insertNode(_el$35, createTextNode(`Preset: default`));
2076
+ effect((_$p) => setProp(_el$35, "fg", theme().textMuted, _$p));
2077
+ return _el$34;
1918
2078
  })();
1919
2079
  },
1920
2080
  children: (name) => (() => {
1921
- var _el$36 = createElement("box"), _el$37 = createElement("text"), _el$39 = createElement("text"), _el$40 = createElement("text");
1922
- insertNode(_el$36, _el$37);
1923
- insertNode(_el$36, _el$39);
1924
- insertNode(_el$36, _el$40);
1925
- setProp(_el$36, "flexDirection", "row");
1926
- setProp(_el$36, "gap", 1);
1927
- insertNode(_el$37, createTextNode(`⚡ Preset:`));
1928
- insert(_el$39, name);
1929
- insert(_el$40, () => `(${preset().source ?? ""})`);
2081
+ var _el$37 = createElement("box"), _el$38 = createElement("text"), _el$40 = createElement("text"), _el$41 = createElement("text");
2082
+ insertNode(_el$37, _el$38);
2083
+ insertNode(_el$37, _el$40);
2084
+ insertNode(_el$37, _el$41);
2085
+ setProp(_el$37, "flexDirection", "row");
2086
+ setProp(_el$37, "gap", 1);
2087
+ insertNode(_el$38, createTextNode(`⚡ Preset:`));
2088
+ insert(_el$40, name);
2089
+ insert(_el$41, () => `(${preset().source ?? ""})`);
1930
2090
  effect((_p$) => {
1931
- var _v$11 = theme().textMuted, _v$12 = theme().accent, _v$13 = theme().textMuted;
1932
- _v$11 !== _p$.e && (_p$.e = setProp(_el$37, "fg", _v$11, _p$.e));
1933
- _v$12 !== _p$.t && (_p$.t = setProp(_el$39, "fg", _v$12, _p$.t));
1934
- _v$13 !== _p$.a && (_p$.a = setProp(_el$40, "fg", _v$13, _p$.a));
2091
+ var _v$10 = theme().textMuted, _v$11 = theme().accent, _v$12 = theme().textMuted;
2092
+ _v$10 !== _p$.e && (_p$.e = setProp(_el$38, "fg", _v$10, _p$.e));
2093
+ _v$11 !== _p$.t && (_p$.t = setProp(_el$40, "fg", _v$11, _p$.t));
2094
+ _v$12 !== _p$.a && (_p$.a = setProp(_el$41, "fg", _v$12, _p$.a));
1935
2095
  return _p$;
1936
2096
  }, {
1937
2097
  e: void 0,
1938
2098
  t: void 0,
1939
2099
  a: void 0
1940
2100
  });
1941
- return _el$36;
2101
+ return _el$37;
1942
2102
  })()
1943
2103
  }), _el$24);
1944
2104
  insert(_el$22, createComponent(HR, {}), _el$24);
@@ -1959,12 +2119,12 @@ function View(props) {
1959
2119
  },
1960
2120
  get fallback() {
1961
2121
  return (() => {
1962
- var _el$41 = createElement("box"), _el$42 = createElement("text");
1963
- insertNode(_el$41, _el$42);
1964
- setProp(_el$41, "marginLeft", 1);
1965
- insertNode(_el$42, createTextNode(`No recent sessions`));
1966
- effect((_$p) => setProp(_el$42, "fg", theme().textMuted, _$p));
1967
- return _el$41;
2122
+ var _el$42 = createElement("box"), _el$43 = createElement("text");
2123
+ insertNode(_el$42, _el$43);
2124
+ setProp(_el$42, "marginLeft", 1);
2125
+ insertNode(_el$43, createTextNode(`No recent sessions`));
2126
+ effect((_$p) => setProp(_el$43, "fg", theme().textMuted, _$p));
2127
+ return _el$42;
1968
2128
  })();
1969
2129
  },
1970
2130
  get children() {
@@ -1992,7 +2152,10 @@ function View(props) {
1992
2152
  setProp(_el$28, "onMouseDown", () => setShowDelegations((x) => !x));
1993
2153
  setProp(_el$29, "attributes", 1);
1994
2154
  insert(_el$29, () => `${showDelegations() ? "▼" : "▶"} Delegations`);
1995
- insert(_el$30, () => ` ${delegationHeader()}`);
2155
+ insert(_el$30, (() => {
2156
+ var _c$ = memo(() => childDelegations().length > 0);
2157
+ return () => _c$() ? ` ${delegationHeader()}` : " — idle";
2158
+ })());
1996
2159
  insert(_el$22, createComponent(Show, {
1997
2160
  get when() {
1998
2161
  return showDelegations();
@@ -2000,16 +2163,16 @@ function View(props) {
2000
2163
  get children() {
2001
2164
  return createComponent(Show, {
2002
2165
  get when() {
2003
- return visibleDelegations().length > 0;
2166
+ return delegationCeiling().visible.length > 0;
2004
2167
  },
2005
2168
  get fallback() {
2006
2169
  return (() => {
2007
- var _el$44 = createElement("box"), _el$45 = createElement("text");
2008
- insertNode(_el$44, _el$45);
2009
- setProp(_el$44, "marginLeft", 1);
2010
- insertNode(_el$45, createTextNode(`No delegations`));
2011
- effect((_$p) => setProp(_el$45, "fg", theme().textMuted, _$p));
2012
- return _el$44;
2170
+ var _el$45 = createElement("box"), _el$46 = createElement("text");
2171
+ insertNode(_el$45, _el$46);
2172
+ setProp(_el$45, "marginLeft", 1);
2173
+ insertNode(_el$46, createTextNode(`No delegations`));
2174
+ effect((_$p) => setProp(_el$46, "fg", theme().textMuted, _$p));
2175
+ return _el$45;
2013
2176
  })();
2014
2177
  },
2015
2178
  get children() {
@@ -2018,7 +2181,7 @@ function View(props) {
2018
2181
  setProp(_el$31, "flexDirection", "column");
2019
2182
  insert(_el$31, createComponent(For, {
2020
2183
  get each() {
2021
- return visibleDelegations();
2184
+ return delegationCeiling().visible;
2022
2185
  },
2023
2186
  children: (job) => createComponent(DelegationRow, {
2024
2187
  get api() {
@@ -2035,62 +2198,13 @@ function View(props) {
2035
2198
  }), null);
2036
2199
  insert(_el$31, createComponent(Show, {
2037
2200
  get when() {
2038
- return delegationSplit().archived.length > 0;
2201
+ return delegationCeiling().hidden > 0;
2039
2202
  },
2040
2203
  get children() {
2041
- return (() => {
2042
- const archived = delegationSplit().archived;
2043
- const pageSize = 8;
2044
- const pages = Math.ceil(archived.length / pageSize);
2045
- const page = Math.min(archivedPage(), pages - 1);
2046
- const slice = archived.slice(page * pageSize, page * pageSize + pageSize);
2047
- return (() => {
2048
- var _el$47 = createElement("box"), _el$48 = createElement("box"), _el$49 = createElement("text"), _el$51 = createElement("text"), _el$52 = createElement("text"), _el$53 = createElement("text");
2049
- insertNode(_el$47, _el$48);
2050
- setProp(_el$47, "flexDirection", "column");
2051
- insertNode(_el$48, _el$49);
2052
- insertNode(_el$48, _el$51);
2053
- insertNode(_el$48, _el$52);
2054
- insertNode(_el$48, _el$53);
2055
- setProp(_el$48, "flexDirection", "row");
2056
- setProp(_el$48, "marginLeft", 0);
2057
- insertNode(_el$49, createTextNode(`‹ `));
2058
- setProp(_el$49, "onMouseDown", () => clampArchivedPage(page - 1, pages));
2059
- insert(_el$51, () => `Archived (${archived.length}) `);
2060
- insert(_el$52, `${page + 1}/${pages}`);
2061
- insertNode(_el$53, createTextNode(` ›`));
2062
- setProp(_el$53, "onMouseDown", () => clampArchivedPage(page + 1, pages));
2063
- insert(_el$47, createComponent(For, {
2064
- each: slice,
2065
- children: (job) => createComponent(DelegationRow, {
2066
- get api() {
2067
- return props.api;
2068
- },
2069
- job,
2070
- get now() {
2071
- return now();
2072
- },
2073
- get animationNow() {
2074
- return animationNow();
2075
- }
2076
- })
2077
- }), null);
2078
- effect((_p$) => {
2079
- var _v$14 = theme().textMuted, _v$15 = theme().textMuted, _v$16 = theme().textMuted, _v$17 = theme().textMuted;
2080
- _v$14 !== _p$.e && (_p$.e = setProp(_el$49, "fg", _v$14, _p$.e));
2081
- _v$15 !== _p$.t && (_p$.t = setProp(_el$51, "fg", _v$15, _p$.t));
2082
- _v$16 !== _p$.a && (_p$.a = setProp(_el$52, "fg", _v$16, _p$.a));
2083
- _v$17 !== _p$.o && (_p$.o = setProp(_el$53, "fg", _v$17, _p$.o));
2084
- return _p$;
2085
- }, {
2086
- e: void 0,
2087
- t: void 0,
2088
- a: void 0,
2089
- o: void 0
2090
- });
2091
- return _el$47;
2092
- })();
2093
- })();
2204
+ var _el$32 = createElement("text");
2205
+ insert(_el$32, () => `… +${delegationCeiling().hidden} more`);
2206
+ effect((_$p) => setProp(_el$32, "fg", theme().textMuted, _$p));
2207
+ return _el$32;
2094
2208
  }
2095
2209
  }), null);
2096
2210
  return _el$31;
@@ -2099,12 +2213,12 @@ function View(props) {
2099
2213
  }
2100
2214
  }), null);
2101
2215
  effect((_p$) => {
2102
- var _v$8 = theme().accent, _v$9 = theme().text, _v$0 = theme().textMuted, _v$1 = theme().text, _v$10 = theme().textMuted;
2103
- _v$8 !== _p$.e && (_p$.e = setProp(_el$23, "fg", _v$8, _p$.e));
2104
- _v$9 !== _p$.t && (_p$.t = setProp(_el$25, "fg", _v$9, _p$.t));
2105
- _v$0 !== _p$.a && (_p$.a = setProp(_el$26, "fg", _v$0, _p$.a));
2106
- _v$1 !== _p$.o && (_p$.o = setProp(_el$29, "fg", _v$1, _p$.o));
2107
- _v$10 !== _p$.i && (_p$.i = setProp(_el$30, "fg", _v$10, _p$.i));
2216
+ var _v$7 = theme().accent, _v$8 = theme().text, _v$9 = theme().textMuted, _v$0 = theme().text, _v$1 = theme().textMuted;
2217
+ _v$7 !== _p$.e && (_p$.e = setProp(_el$23, "fg", _v$7, _p$.e));
2218
+ _v$8 !== _p$.t && (_p$.t = setProp(_el$25, "fg", _v$8, _p$.t));
2219
+ _v$9 !== _p$.a && (_p$.a = setProp(_el$26, "fg", _v$9, _p$.a));
2220
+ _v$0 !== _p$.o && (_p$.o = setProp(_el$29, "fg", _v$0, _p$.o));
2221
+ _v$1 !== _p$.i && (_p$.i = setProp(_el$30, "fg", _v$1, _p$.i));
2108
2222
  return _p$;
2109
2223
  }, {
2110
2224
  e: void 0,
@@ -2173,6 +2287,6 @@ const plugin = {
2173
2287
  setup: async () => {}
2174
2288
  };
2175
2289
  //#endregion
2176
- export { DELEGATION_DESCRIPTION_MAX, DELEGATION_ELAPSED_WIDTH, DELEGATION_STATE_GLYPHS, IDLE_SILENCE_MS, STALE_RUNNING_THRESHOLD_MS, buildChildrenPath, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, countDelegationSources, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationIcon, delegationSpinnerFrame, delegationStateGlyph, delegationStateMarker, delegationStateTone, delegationTag, extractToolActivity, fmtElapsed, formatDelegationElapsed, formatDelegationHeader, formatDelegationIdentity, formatDelegationRow, formatPanelLogLine, isValidSessionId, latestToolActivityFor, markStaleIfRunning, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, safeSessionPath, seedLiveDelegationMap, splitDelegationList, toDelegationEntry, trackToolActivity, truncateDelegationDescription, tuiLogPath, visibleDelegationList };
2290
+ export { DELEGATION_ALIAS_WIDTH, DELEGATION_DESCRIPTION_MAX, DELEGATION_ELAPSED_WIDTH, DELEGATION_ROW_GLYPHS, DELEGATION_VISIBLE_CEILING, IDLE_SILENCE_MS, STALE_RUNNING_THRESHOLD_MS, boardRecordToDelegationEntry, boardRecordsToDelegationEntries, boardStatePath, buildChildrenPath, ceilingDelegationList, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, countDelegationSources, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationRowGlyph, delegationRowIdentity, delegationRowMarker, delegationRowStatus, delegationSpinnerFrame, delegationStateTone, extractToolActivity, fmtElapsed, formatDelegationAlias, formatDelegationElapsed, formatDelegationHeader, formatDelegationRowLead, formatPanelLogLine, isValidSessionId, latestToolActivityFor, markStaleIfRunning, mergeBoardDelegationSources, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readBoardState, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, resolvePantheonRoot, safeSessionPath, seedLiveDelegationMap, splitDelegationList, toDelegationEntry, trackToolActivity, truncateDelegationDescription, tuiLogPath, visibleDelegationList };
2177
2291
 
2178
2292
  //# sourceMappingURL=tui.js.map