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

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/CHANGELOG.md CHANGED
@@ -18,6 +18,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
18
18
 
19
19
  ## ✅ Closed Issues
20
20
 
21
+ ## [v1.5.0-beta.14] - 2026-09-14
22
+
23
+ <!-- Add new changes here. Running `node scripts/versioning.mjs apply` will
24
+ move this section to a versioned entry and reset the template below. -->
25
+
26
+ ## 🆕 What's New
27
+
28
+ - **Ordenação active-first:** delegações ativas aparecem antes das concluídas,
29
+ falhas e canceladas no painel TUI.
30
+ - **Overflow active:** quando há mais delegações ativas que o teto visível, o
31
+ excedente ativo permanece representado na linha de overflow.
32
+
33
+ ## 🐞 Fixed
34
+
35
+ - **Retenção visual:** concluídas permanecem visíveis por 2 min e falhas por
36
+ 10 min antes de saírem do painel; canceladas seguem a retenção configurada.
37
+
38
+ ## ⚠️ Known Issues
39
+
40
+ - **Stale running warning:** jobs em estado `running` além do limite esperado
41
+ recebem um aviso visual de stale no painel.
21
42
  ## [v1.5.0-beta.13] - 2026-09-14
22
43
 
23
44
  <!-- Add new changes here. Running `node scripts/versioning.mjs apply` will
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pantheon-opencode",
3
- "version": "1.5.0-beta.13",
3
+ "version": "1.5.0-beta.14",
4
4
  "description": "Multi-agent orchestration for OpenCode — 14 specialized agents, TDD, Themis review gates.",
5
5
  "type": "module",
6
6
  "publishConfig": {
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pantheon",
3
3
  "publisher": "ils15",
4
- "version": "1.5.0-beta.13",
4
+ "version": "1.5.0-beta.14",
5
5
  "displayName": "Pantheon",
6
6
  "description": "Conductor-Delegate orchestration platform with 14 specialized agents, 21 skills, and OpenCode support.",
7
7
  "agents": "./src/agents",
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.backends._legacy:_Backend"
4
4
 
5
5
  [project]
6
6
  name = "pantheon"
7
- version = "1.5.0-beta.13"
7
+ version = "1.5.0-beta.14"
8
8
  description = "Multi-agent orchestration framework"
9
9
  requires-python = ">=3.11"
10
10
  dependencies = []
@@ -118,6 +118,10 @@ declare const STALE_RUNNING_THRESHOLD_MS: number;
118
118
  * considered stale. Combined with the stale-running threshold to produce the
119
119
  * display-only `stale-running` state. */
120
120
  declare const IDLE_SILENCE_MS: number;
121
+ /** Visual-only terminal retention windows. Reports remain on disk and in the
122
+ * board; these constants only control which rows enter the TUI window. */
123
+ declare const DELEGATION_DONE_RETENTION_MS: number;
124
+ declare const DELEGATION_FAILED_RETENTION_MS: number;
121
125
  /**
122
126
  * Mark a running entry as `stale-running` if it has been running longer than
123
127
  * the threshold AND has no recent activity (no `updatedAt` change in the last
@@ -422,12 +426,14 @@ declare const DELEGATION_VISIBLE_CEILING = 8;
422
426
  * still a live job — it must never read as done); failed counts
423
427
  * error/startup_failed; cancelled reads as done. Pure. */
424
428
  declare function formatDelegationHeader(entries: readonly DelegationEntry[]): string;
425
- /** Cap the panel: live rows first, then most-recent terminal rows, at most
426
- * `maxVisible` total. `hidden` is how many were dropped (rendered as
427
- * "… +N more"). Pure. */
429
+ /** Cap the panel: live rows first, then most-recent retained terminal rows, at
430
+ * most `maxVisible` total. Hidden counts describe the retained render list,
431
+ * not expired history. */
428
432
  declare function ceilingDelegationList(all: readonly DelegationEntry[], maxVisible?: number, now?: number): {
429
433
  visible: DelegationEntry[];
430
434
  hidden: number;
435
+ hiddenActive: number;
436
+ hiddenTerminal: number;
431
437
  };
432
438
  /** Split a display list into native task() rows vs pantheon_delegate rows.
433
439
  * Native = source 'children-only' (no board report); everything else counts
@@ -468,8 +474,12 @@ declare function childrenToDelegationEntries(children: readonly ChildDelegationL
468
474
  * server-valid session id ("ses...") ever reaches the router, so an
469
475
  * unsubstituted "{sessionID}" placeholder can never be routed. */
470
476
  declare function navigateToDelegationSession(route: {
471
- navigate?: (name: string, params?: Record<string, unknown>) => void;
477
+ navigate?: (name: string, params?: Record<string, unknown>) => void | PromiseLike<void>;
472
478
  } | undefined, taskID: string | undefined): boolean;
479
+ /** Build the mouse handler used by each delegation row. */
480
+ declare function createDelegationRowOpenHandler(route: {
481
+ navigate?: (name: string, params?: Record<string, unknown>) => void | PromiseLike<void>;
482
+ } | undefined, taskID: string | undefined): () => void;
473
483
  /** Plugin-level live delegation store shared with the event subscriptions
474
484
  * in `tui()`: the map of live entries + a version signal bumped on every
475
485
  * mutation. The View subscribes to the version (in an effect) to refresh the
@@ -486,5 +496,5 @@ declare const plugin: TuiPluginModule & {
486
496
  setup: () => Promise<void>;
487
497
  };
488
498
  //#endregion
489
- export { BoardJobRecord, ChildDelegationLike, DELEGATION_ALIAS_WIDTH, DELEGATION_DESCRIPTION_MAX, DELEGATION_ELAPSED_WIDTH, DELEGATION_ROW_GLYPHS, DELEGATION_VISIBLE_CEILING, DelegationActivity, DelegationDisplayState, DelegationEntry, DelegationRowStatus, DelegationStateTone, DelegationToolPart, IDLE_SILENCE_MS, LiveDelegationEntry, LiveDelegationStore, ParsedDelegationToolPart, STALE_RUNNING_THRESHOLD_MS, ToolActivity, TuiSessionSources, 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 };
499
+ export { BoardJobRecord, ChildDelegationLike, DELEGATION_ALIAS_WIDTH, DELEGATION_DESCRIPTION_MAX, DELEGATION_DONE_RETENTION_MS, DELEGATION_ELAPSED_WIDTH, DELEGATION_FAILED_RETENTION_MS, DELEGATION_ROW_GLYPHS, DELEGATION_VISIBLE_CEILING, DelegationActivity, DelegationDisplayState, DelegationEntry, DelegationRowStatus, DelegationStateTone, DelegationToolPart, IDLE_SILENCE_MS, LiveDelegationEntry, LiveDelegationStore, ParsedDelegationToolPart, STALE_RUNNING_THRESHOLD_MS, ToolActivity, TuiSessionSources, boardRecordToDelegationEntry, boardRecordsToDelegationEntries, boardStatePath, buildChildrenPath, ceilingDelegationList, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, countDelegationSources, createDelegationRowOpenHandler, 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 };
490
500
  //# sourceMappingURL=tui.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tui.d.ts","names":[],"sources":["../src/index.tsx"],"mappings":"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"}
1
+ {"version":3,"file":"tui.d.ts","names":[],"sources":["../src/index.tsx"],"mappings":"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"}
@@ -165,7 +165,7 @@ async function detectVersion(api) {
165
165
  if (ver) return ver;
166
166
  }
167
167
  } catch {}
168
- return "1.5.0-beta.13";
168
+ return "1.5.0-beta.14";
169
169
  }
170
170
  /**
171
171
  * usage-bar — AI subscription usage gauge for the opencode TUI.
@@ -939,8 +939,13 @@ function compareDelegationEntries(a, b) {
939
939
  function splitDelegationList(all, maxRecent = 8, now = Date.now(), staleThresholdMs = 1800 * 1e3) {
940
940
  const isActiveState = (st) => st === "running" || st === "retry" || st === "stale-running";
941
941
  return {
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)
942
+ active: all.filter((d) => isActiveState(d.state)).map((d) => markStaleIfRunning(d, now, staleThresholdMs)).sort(compareDelegationEntries),
943
+ recent: all.filter((d) => !isActiveState(d.state)).filter((d) => {
944
+ if (d.updatedAt === null || !Number.isFinite(d.updatedAt)) return true;
945
+ const age = now - d.updatedAt;
946
+ if (age < 0) return true;
947
+ return age < (delegationRowStatus(d.state) === "failed" ? DELEGATION_FAILED_RETENTION_MS : DELEGATION_DONE_RETENTION_MS);
948
+ }).sort(compareDelegationEntries).slice(0, maxRecent)
944
949
  };
945
950
  }
946
951
  /** Live-first window used by {@link ceilingDelegationList} (kept for the
@@ -956,6 +961,10 @@ const STALE_RUNNING_THRESHOLD_MS = 1800 * 1e3;
956
961
  * considered stale. Combined with the stale-running threshold to produce the
957
962
  * display-only `stale-running` state. */
958
963
  const IDLE_SILENCE_MS = 60 * 1e3;
964
+ /** Visual-only terminal retention windows. Reports remain on disk and in the
965
+ * board; these constants only control which rows enter the TUI window. */
966
+ const DELEGATION_DONE_RETENTION_MS = 120 * 1e3;
967
+ const DELEGATION_FAILED_RETENTION_MS = 600 * 1e3;
959
968
  /**
960
969
  * Mark a running entry as `stale-running` if it has been running longer than
961
970
  * the threshold AND has no recent activity (no `updatedAt` change in the last
@@ -1048,7 +1057,7 @@ function delegationStateTone(state) {
1048
1057
  case "running":
1049
1058
  case "retry":
1050
1059
  case "startup_unknown": return "warning";
1051
- case "stale-running":
1060
+ case "stale-running": return "warning";
1052
1061
  case "error":
1053
1062
  case "startup_failed": return "error";
1054
1063
  case "completed": return "success";
@@ -1662,14 +1671,20 @@ function formatDelegationHeader(entries) {
1662
1671
  const tail = failed > 0 ? ` · ${failed} failed` : "";
1663
1672
  return `(${active} active · ${done} done${tail})`;
1664
1673
  }
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. */
1674
+ /** Cap the panel: live rows first, then most-recent retained terminal rows, at
1675
+ * most `maxVisible` total. Hidden counts describe the retained render list,
1676
+ * not expired history. */
1668
1677
  function ceilingDelegationList(all, maxVisible = 8, now = Date.now()) {
1669
- const visible = visibleDelegationList(all, maxVisible, now).slice(0, maxVisible);
1678
+ const { active, recent } = splitDelegationList(all, all.length, now);
1679
+ const visibleActive = active.slice(0, maxVisible);
1680
+ const visible = [...visibleActive, ...recent.slice(0, Math.max(0, maxVisible - visibleActive.length))];
1681
+ const hiddenActive = Math.max(0, active.length - visibleActive.length);
1682
+ const hiddenTerminal = Math.max(0, recent.length - (visible.length - visibleActive.length));
1670
1683
  return {
1671
1684
  visible,
1672
- hidden: Math.max(0, all.length - visible.length)
1685
+ hidden: hiddenActive + hiddenTerminal,
1686
+ hiddenActive,
1687
+ hiddenTerminal
1673
1688
  };
1674
1689
  }
1675
1690
  /** Split a display list into native task() rows vs pantheon_delegate rows.
@@ -1743,8 +1758,19 @@ function childrenToDelegationEntries(children, md, now = Date.now(), parentSessi
1743
1758
  * unsubstituted "{sessionID}" placeholder can never be routed. */
1744
1759
  function navigateToDelegationSession(route, taskID) {
1745
1760
  if (typeof route?.navigate !== "function" || !isValidSessionId(taskID)) return false;
1746
- route.navigate("session", { sessionID: taskID });
1747
- return true;
1761
+ try {
1762
+ const navigation = route.navigate("session", { sessionID: taskID });
1763
+ if (navigation !== void 0) Promise.resolve(navigation).catch(() => void 0);
1764
+ return true;
1765
+ } catch {
1766
+ return false;
1767
+ }
1768
+ }
1769
+ /** Build the mouse handler used by each delegation row. */
1770
+ function createDelegationRowOpenHandler(route, taskID) {
1771
+ return () => {
1772
+ navigateToDelegationSession(route, taskID);
1773
+ };
1748
1774
  }
1749
1775
  /** Silence-by-default panel logger.
1750
1776
  * @param projectRoot the PROJECT ROOT — the logger appends to
@@ -1822,9 +1848,7 @@ function DelegationRow(props) {
1822
1848
  const elapsed = createMemo(() => formatDelegationElapsed(props.job, props.now));
1823
1849
  const description = createMemo(() => truncateDelegationDescription(props.job.description));
1824
1850
  const activity = createMemo(() => latestToolActivityFor(latestToolActivity, props.job.taskID, props.now));
1825
- const open = () => {
1826
- navigateToDelegationSession(props.api.route, props.job.taskID);
1827
- };
1851
+ const open = createDelegationRowOpenHandler(props.api.route, props.job.taskID);
1828
1852
  return (() => {
1829
1853
  var _el$13 = createElement("box"), _el$14 = createElement("box"), _el$15 = createElement("text"), _el$16 = createElement("span"), _el$17 = createElement("span");
1830
1854
  insertNode(_el$13, _el$14);
@@ -1988,7 +2012,7 @@ function View(props) {
1988
2012
  return delegationsInflight;
1989
2013
  };
1990
2014
  const delegationCeiling = createMemo(() => ceilingDelegationList(childDelegations(), 8, now()));
1991
- const delegationHeader = createMemo(() => formatDelegationHeader(childDelegations()));
2015
+ const delegationHeader = createMemo(() => formatDelegationHeader(delegationCeiling().visible));
1992
2016
  onMount(() => {
1993
2017
  const cleanup = [];
1994
2018
  try {
@@ -2153,7 +2177,7 @@ function View(props) {
2153
2177
  setProp(_el$29, "attributes", 1);
2154
2178
  insert(_el$29, () => `${showDelegations() ? "▼" : "▶"} Delegations`);
2155
2179
  insert(_el$30, (() => {
2156
- var _c$ = memo(() => childDelegations().length > 0);
2180
+ var _c$ = memo(() => delegationCeiling().visible.length > 0);
2157
2181
  return () => _c$() ? ` ${delegationHeader()}` : " — idle";
2158
2182
  })());
2159
2183
  insert(_el$22, createComponent(Show, {
@@ -2202,7 +2226,10 @@ function View(props) {
2202
2226
  },
2203
2227
  get children() {
2204
2228
  var _el$32 = createElement("text");
2205
- insert(_el$32, () => `… +${delegationCeiling().hidden} more`);
2229
+ insert(_el$32, (() => {
2230
+ var _c$2 = memo(() => delegationCeiling().hiddenActive > 0);
2231
+ return () => _c$2() ? `… +${delegationCeiling().hiddenActive} active` : `… +${delegationCeiling().hiddenTerminal} more`;
2232
+ })());
2206
2233
  effect((_$p) => setProp(_el$32, "fg", theme().textMuted, _$p));
2207
2234
  return _el$32;
2208
2235
  }
@@ -2287,6 +2314,6 @@ const plugin = {
2287
2314
  setup: async () => {}
2288
2315
  };
2289
2316
  //#endregion
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 };
2317
+ export { DELEGATION_ALIAS_WIDTH, DELEGATION_DESCRIPTION_MAX, DELEGATION_DONE_RETENTION_MS, DELEGATION_ELAPSED_WIDTH, DELEGATION_FAILED_RETENTION_MS, DELEGATION_ROW_GLYPHS, DELEGATION_VISIBLE_CEILING, IDLE_SILENCE_MS, STALE_RUNNING_THRESHOLD_MS, boardRecordToDelegationEntry, boardRecordsToDelegationEntries, boardStatePath, buildChildrenPath, ceilingDelegationList, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, countDelegationSources, createDelegationRowOpenHandler, 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 };
2291
2318
 
2292
2319
  //# sourceMappingURL=tui.js.map
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pantheon-tui",
3
- "version": "1.5.0-beta.13",
3
+ "version": "1.5.0-beta.14",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pantheon-tui",
9
- "version": "1.5.0-beta.13",
9
+ "version": "1.5.0-beta.14",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@opencode-ai/plugin": ">=1.14.0",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pantheon-tui",
3
3
  "type": "module",
4
- "version": "1.5.0-beta.13",
4
+ "version": "1.5.0-beta.14",
5
5
  "description": "Pantheon TUI plugin — sidebar (version, preset, sessions, real-time delegations) + AI subscription usage gauges incl. OpenCode Go/Zen dollar limits (vendored, MIT)",
6
6
  "license": "MIT",
7
7
  "exports": {
@@ -1320,7 +1320,22 @@ export function splitDelegationList(
1320
1320
  const active = all
1321
1321
  .filter((d) => isActiveState(d.state))
1322
1322
  .map((d) => markStaleIfRunning(d, now, staleThresholdMs))
1323
- const terminal = all.filter((d) => !isActiveState(d.state))
1323
+ .sort(compareDelegationEntries)
1324
+ const terminal = all
1325
+ .filter((d) => !isActiveState(d.state))
1326
+ .filter((d) => {
1327
+ // A missing/invalid finalized timestamp is not evidence that a report is
1328
+ // old. Keep it visible rather than accidentally hiding a terminal job.
1329
+ if (d.updatedAt === null || !Number.isFinite(d.updatedAt)) return true
1330
+ const age = now - d.updatedAt
1331
+ if (age < 0) return true
1332
+ const retention =
1333
+ delegationRowStatus(d.state) === 'failed'
1334
+ ? DELEGATION_FAILED_RETENTION_MS
1335
+ : DELEGATION_DONE_RETENTION_MS
1336
+ return age < retention
1337
+ })
1338
+ .sort(compareDelegationEntries)
1324
1339
  return {
1325
1340
  active,
1326
1341
  recent: terminal.slice(0, maxRecent),
@@ -1348,6 +1363,11 @@ export const STALE_RUNNING_THRESHOLD_MS = 30 * 60 * 1000
1348
1363
  * display-only `stale-running` state. */
1349
1364
  export const IDLE_SILENCE_MS = 60 * 1000
1350
1365
 
1366
+ /** Visual-only terminal retention windows. Reports remain on disk and in the
1367
+ * board; these constants only control which rows enter the TUI window. */
1368
+ export const DELEGATION_DONE_RETENTION_MS = 2 * 60 * 1000
1369
+ export const DELEGATION_FAILED_RETENTION_MS = 10 * 60 * 1000
1370
+
1351
1371
  /**
1352
1372
  * Mark a running entry as `stale-running` if it has been running longer than
1353
1373
  * the threshold AND has no recent activity (no `updatedAt` change in the last
@@ -1465,6 +1485,7 @@ export function delegationStateTone(state: DelegationDisplayState): DelegationSt
1465
1485
  case 'startup_unknown':
1466
1486
  return 'warning'
1467
1487
  case 'stale-running':
1488
+ return 'warning'
1468
1489
  case 'error':
1469
1490
  case 'startup_failed':
1470
1491
  return 'error'
@@ -2410,16 +2431,23 @@ export function formatDelegationHeader(entries: readonly DelegationEntry[]): str
2410
2431
  return `(${active} active · ${done} done${tail})`
2411
2432
  }
2412
2433
 
2413
- /** Cap the panel: live rows first, then most-recent terminal rows, at most
2414
- * `maxVisible` total. `hidden` is how many were dropped (rendered as
2415
- * "… +N more"). Pure. */
2434
+ /** Cap the panel: live rows first, then most-recent retained terminal rows, at
2435
+ * most `maxVisible` total. Hidden counts describe the retained render list,
2436
+ * not expired history. */
2416
2437
  export function ceilingDelegationList(
2417
2438
  all: readonly DelegationEntry[],
2418
2439
  maxVisible = DELEGATION_VISIBLE_CEILING,
2419
2440
  now = Date.now(),
2420
- ): { visible: DelegationEntry[]; hidden: number } {
2421
- const visible = visibleDelegationList(all, maxVisible, now).slice(0, maxVisible)
2422
- return { visible, hidden: Math.max(0, all.length - visible.length) }
2441
+ ): { visible: DelegationEntry[]; hidden: number; hiddenActive: number; hiddenTerminal: number } {
2442
+ const { active, recent } = splitDelegationList(all, all.length, now)
2443
+ const visibleActive = active.slice(0, maxVisible)
2444
+ const visible = [
2445
+ ...visibleActive,
2446
+ ...recent.slice(0, Math.max(0, maxVisible - visibleActive.length)),
2447
+ ]
2448
+ const hiddenActive = Math.max(0, active.length - visibleActive.length)
2449
+ const hiddenTerminal = Math.max(0, recent.length - (visible.length - visibleActive.length))
2450
+ return { visible, hidden: hiddenActive + hiddenTerminal, hiddenActive, hiddenTerminal }
2423
2451
  }
2424
2452
 
2425
2453
  /** Split a display list into native task() rows vs pantheon_delegate rows.
@@ -2522,12 +2550,41 @@ export function childrenToDelegationEntries(
2522
2550
  * server-valid session id ("ses...") ever reaches the router, so an
2523
2551
  * unsubstituted "{sessionID}" placeholder can never be routed. */
2524
2552
  export function navigateToDelegationSession(
2525
- route: { navigate?: (name: string, params?: Record<string, unknown>) => void } | undefined,
2553
+ route:
2554
+ | {
2555
+ navigate?: (name: string, params?: Record<string, unknown>) => void | PromiseLike<void>
2556
+ }
2557
+ | undefined,
2526
2558
  taskID: string | undefined,
2527
2559
  ): boolean {
2528
2560
  if (typeof route?.navigate !== 'function' || !isValidSessionId(taskID)) return false
2529
- route.navigate('session', { sessionID: taskID })
2530
- return true
2561
+ try {
2562
+ const navigation = route.navigate('session', { sessionID: taskID })
2563
+ if (navigation !== undefined) {
2564
+ // A removed session can reject route.navigate for historical rows. That
2565
+ // rejection is expected and intentionally absorbed so it cannot break
2566
+ // the TUI; the no-op avoids logging sensitive session details.
2567
+ void Promise.resolve(navigation).catch(() => undefined)
2568
+ }
2569
+ return true
2570
+ } catch {
2571
+ // Synchronous router failures are also fail-open for an inert historical row.
2572
+ return false
2573
+ }
2574
+ }
2575
+
2576
+ /** Build the mouse handler used by each delegation row. */
2577
+ export function createDelegationRowOpenHandler(
2578
+ route:
2579
+ | {
2580
+ navigate?: (name: string, params?: Record<string, unknown>) => void | PromiseLike<void>
2581
+ }
2582
+ | undefined,
2583
+ taskID: string | undefined,
2584
+ ): () => void {
2585
+ return () => {
2586
+ navigateToDelegationSession(route, taskID)
2587
+ }
2531
2588
  }
2532
2589
 
2533
2590
  /* ─── Silence-by-default panel logger ──────────────────────
@@ -2674,9 +2731,7 @@ function DelegationRow(props: {
2674
2731
  // Click a row → navigate to the child session (route API guarded in the
2675
2732
  // helper). Enter-key binding is left out (per-row keymap wiring is not
2676
2733
  // worth the complexity in this sidebar); mouse is enabled in tui.json.
2677
- const open = () => {
2678
- navigateToDelegationSession(props.api.route, props.job.taskID)
2679
- }
2734
+ const open = createDelegationRowOpenHandler(props.api.route, props.job.taskID)
2680
2735
 
2681
2736
  return (
2682
2737
  <box onMouseDown={open}>
@@ -2944,7 +2999,9 @@ function View(props: {
2944
2999
  const delegationCeiling = createMemo(() =>
2945
3000
  ceilingDelegationList(childDelegations(), DELEGATION_VISIBLE_CEILING, now()),
2946
3001
  )
2947
- const delegationHeader = createMemo(() => formatDelegationHeader(childDelegations()))
3002
+ // Header counts the same retained rows shown below, so hidden/expired board
3003
+ // history cannot inflate the summary.
3004
+ const delegationHeader = createMemo(() => formatDelegationHeader(delegationCeiling().visible))
2948
3005
 
2949
3006
  // ── Event subscriptions for live session/delegation updates ──
2950
3007
  onMount(() => {
@@ -3090,7 +3147,7 @@ function View(props: {
3090
3147
  {`${showDelegations() ? '▼' : '▶'} Delegations`}
3091
3148
  </text>
3092
3149
  <text fg={theme().textMuted}>
3093
- {childDelegations().length > 0 ? ` ${delegationHeader()}` : ' — idle'}
3150
+ {delegationCeiling().visible.length > 0 ? ` ${delegationHeader()}` : ' — idle'}
3094
3151
  </text>
3095
3152
  </box>
3096
3153
  <Show when={showDelegations()}>
@@ -3114,7 +3171,11 @@ function View(props: {
3114
3171
  )}
3115
3172
  </For>
3116
3173
  <Show when={delegationCeiling().hidden > 0}>
3117
- <text fg={theme().textMuted}>{`… +${delegationCeiling().hidden} more`}</text>
3174
+ <text fg={theme().textMuted}>
3175
+ {delegationCeiling().hiddenActive > 0
3176
+ ? `… +${delegationCeiling().hiddenActive} active`
3177
+ : `… +${delegationCeiling().hiddenTerminal} more`}
3178
+ </text>
3118
3179
  </Show>
3119
3180
  </box>
3120
3181
  </Show>