pantheon-opencode 1.5.0-beta.1 → 1.5.0-beta.10

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.
Files changed (82) hide show
  1. package/.pantheon/code-mode/manifest.json +21 -0
  2. package/CHANGELOG.md +1026 -0
  3. package/README.md +171 -16
  4. package/README.pt-BR.md +99 -13
  5. package/bin/pantheon-init.mjs +178 -21
  6. package/package.json +23 -14
  7. package/plugin.json +1 -1
  8. package/pyproject.toml +65 -0
  9. package/scripts/code_mode_server.py +431 -55
  10. package/scripts/cost.mjs +30 -14
  11. package/scripts/doctor.mjs +178 -9
  12. package/scripts/install/cli-ui.mjs +17 -11
  13. package/scripts/install/config-migration.mjs +4 -6
  14. package/scripts/install/opencode.mjs +137 -35
  15. package/scripts/install/plugin.mjs +3 -33
  16. package/scripts/install/shared.mjs +43 -2
  17. package/scripts/install/strings.mjs +129 -0
  18. package/scripts/install-mcp.mjs +1 -0
  19. package/scripts/manifest-inventory.mjs +410 -0
  20. package/scripts/mcp_persistence_server.py +537 -128
  21. package/scripts/package-evidence.mjs +242 -0
  22. package/scripts/probe-context-rehydrate.mjs +240 -0
  23. package/scripts/probe-pantheon-cost.mjs +313 -0
  24. package/scripts/release-beta-version.mjs +56 -60
  25. package/scripts/sync-artifacts.mjs +169 -0
  26. package/scripts/sync-tui.mjs +121 -53
  27. package/scripts/test-opencode-v1-v2-sandbox.sh +367 -96
  28. package/scripts/validation-policy.mjs +7 -7
  29. package/scripts/version-check.mjs +113 -108
  30. package/scripts/versioning.mjs +230 -86
  31. package/src/agents/mnemosyne.md +1 -1
  32. package/src/agents/zeus.md +2 -2
  33. package/src/mcp/code_mode_server.py +431 -55
  34. package/src/mcp/mcp_persistence_server.py +537 -128
  35. package/src/pantheon/auto-wake.ts +1 -5
  36. package/src/pantheon/background-job-board.ts +43 -1
  37. package/src/pantheon/compaction-handoff.ts +304 -0
  38. package/src/pantheon/cost-command.ts +345 -62
  39. package/src/pantheon/delegate-manager.ts +390 -8
  40. package/src/pantheon/delegation-finalize.ts +32 -0
  41. package/src/pantheon/delegation.ts +235 -18
  42. package/src/pantheon/file-persistence-salvage.ts +64 -0
  43. package/src/pantheon/file-persistence.ts +194 -45
  44. package/src/pantheon/native-probe.ts +234 -0
  45. package/src/pantheon/native-task-status.ts +139 -0
  46. package/src/pantheon/provider-capabilities.ts +221 -0
  47. package/src/pantheon/provider-stream-tracker.ts +182 -0
  48. package/src/pantheon/shared-board.ts +37 -0
  49. package/src/pantheon/task-result-guard.ts +107 -0
  50. package/src/pantheon/token-opt.ts +146 -12
  51. package/src/pantheon/tool-ceiling.ts +159 -0
  52. package/src/pantheon/v2-events.ts +18 -1
  53. package/src/plugin-v2.ts +2 -14
  54. package/src/plugin.ts +28 -17
  55. package/src/plugins/pantheon-hooks.ts +51 -0
  56. package/src/plugins/tui/dist/server.d.ts +1 -1
  57. package/src/plugins/tui/dist/server.d.ts.map +1 -1
  58. package/src/plugins/tui/dist/server.js +1 -1
  59. package/src/plugins/tui/dist/server.js.map +1 -1
  60. package/src/plugins/tui/dist/tui.d.ts +116 -27
  61. package/src/plugins/tui/dist/tui.d.ts.map +1 -1
  62. package/src/plugins/tui/dist/tui.js +414 -150
  63. package/src/plugins/tui/dist/tui.js.map +1 -1
  64. package/src/plugins/tui/dist/tui.tsx +447 -84
  65. package/src/plugins/tui/package-lock.json +2 -2
  66. package/src/plugins/tui/package.json +1 -1
  67. package/src/plugins/tui/src/index.tsx +447 -84
  68. package/.pantheon/code-mode/.gitkeep +0 -0
  69. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-1.json +0 -8
  70. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-10.json +0 -8
  71. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-11.json +0 -8
  72. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-2.json +0 -8
  73. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-3.json +0 -8
  74. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-4.json +0 -8
  75. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-5.json +0 -8
  76. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-6.json +0 -8
  77. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-7.json +0 -8
  78. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-8.json +0 -8
  79. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-9.json +0 -8
  80. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/heartbeat.json +0 -8
  81. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/session.json +0 -11
  82. package/src/pantheon/delegate-task-adapter.ts +0 -444
@@ -1,6 +1,6 @@
1
1
  import { createComponent, createElement, createTextNode, effect, insert, insertNode, memo, setProp } from "@opentui/solid";
2
2
  import { Buffer } from "node:buffer";
3
- import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
3
+ import { appendFile, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
4
4
  import { homedir } from "node:os";
5
5
  import { dirname, join } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
@@ -164,7 +164,7 @@ async function detectVersion(api) {
164
164
  if (ver) return ver;
165
165
  }
166
166
  } catch {}
167
- return "1.5.0";
167
+ return "1.5.0-beta.10";
168
168
  }
169
169
  /**
170
170
  * usage-bar — AI subscription usage gauge for the opencode TUI.
@@ -868,19 +868,32 @@ function tuiLogPath(projectRoot) {
868
868
  * falling back to startedAt, descending). Shared by the md reader and
869
869
  * mergeDelegationSources. */
870
870
  function compareDelegationEntries(a, b) {
871
- const aRun = a.state === "running" || a.state === "stale-running" ? 1 : 0;
872
- const bRun = b.state === "running" || b.state === "stale-running" ? 1 : 0;
871
+ const isActive = (st) => st === "running" || st === "retry" || st === "stale-running" ? 1 : 0;
872
+ const aRun = isActive(a.state);
873
+ const bRun = isActive(b.state);
873
874
  if (aRun !== bRun) return bRun - aRun;
874
875
  return (b.updatedAt ?? b.startedAt) - (a.updatedAt ?? a.startedAt);
875
876
  }
876
- /** The list the panel actually renders: running jobs first, then the most
877
- * recent terminal reports (capped). Pure — so the history-only panel (no
878
- * sessionID) is testable without the TUI runtime. The header count uses the
879
- * same "running + recentes" list. */
877
+ /** 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
880
+ * without the TUI runtime. */
881
+ function splitDelegationList(all, maxRecent = 8, now = Date.now(), staleThresholdMs = 1800 * 1e3) {
882
+ 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
+ return {
886
+ active,
887
+ recent: terminal.slice(0, maxRecent),
888
+ archived: terminal.slice(maxRecent)
889
+ };
890
+ }
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. */
880
894
  function visibleDelegationList(all, maxTerminal = 8, now = Date.now(), staleThresholdMs = 1800 * 1e3) {
881
- const running = all.filter((d) => d.state === "running").map((d) => markStaleIfRunning(d, now, staleThresholdMs));
882
- const terminal = all.filter((d) => d.state !== "running").slice(0, maxTerminal);
883
- return [...running, ...terminal];
895
+ const { active, recent } = splitDelegationList(all, maxTerminal, now, staleThresholdMs);
896
+ return [...active, ...recent];
884
897
  }
885
898
  /** Default stale-running threshold: 30 minutes. */
886
899
  const STALE_RUNNING_THRESHOLD_MS = 1800 * 1e3;
@@ -918,10 +931,11 @@ function fmtElapsed(ms) {
918
931
  if (minutes > 0) return `${minutes}m ${seconds}s`;
919
932
  return `${seconds}s`;
920
933
  }
921
- /** Elapsed label for one entry: running → ticks `now - startedAt`, terminal
922
- * → fixed `updatedAt - startedAt` (em dash when no finalized timestamp). */
934
+ /** Elapsed label for one entry: an ACTIVE entry (running/retry/stale-running)
935
+ * ticks `now - startedAt`; a terminal one is fixed at
936
+ * `updatedAt - startedAt` (em dash when no finalized timestamp). */
923
937
  function delegationElapsed(entry, now) {
924
- if (entry.state === "running" || entry.state === "stale-running") return fmtElapsed(now - entry.startedAt);
938
+ if (entry.state === "running" || entry.state === "retry" || entry.state === "stale-running") return fmtElapsed(now - entry.startedAt);
925
939
  return entry.updatedAt !== null ? fmtElapsed(entry.updatedAt - entry.startedAt) : "—";
926
940
  }
927
941
  /** The activity labels shown by the animated row. Keeping this pure makes the
@@ -935,6 +949,7 @@ function delegationActivity(entry) {
935
949
  return "working";
936
950
  }
937
951
  function delegationActivityLabel(entry) {
952
+ if (entry.state === "retry") return "RETRYING";
938
953
  switch (delegationActivity(entry)) {
939
954
  case "delegating": return "DELEGATING";
940
955
  case "working": return "WORKING";
@@ -956,11 +971,71 @@ const DELEGATION_SPINNER_FRAMES = [
956
971
  "⠇",
957
972
  "⠏"
958
973
  ];
959
- /** Return a deterministic spinner frame. The View ticks this every 140ms. */
974
+ /** Return a deterministic spinner frame. The View ticks this every 1000ms
975
+ * (not 140ms — the fast tick flickered without adding information). */
960
976
  function delegationSpinnerFrame(now) {
961
- const index = Math.floor(Math.max(0, now) / 140) % DELEGATION_SPINNER_FRAMES.length;
977
+ const index = Math.floor(Math.max(0, now) / 1e3) % DELEGATION_SPINNER_FRAMES.length;
962
978
  return DELEGATION_SPINNER_FRAMES[index] ?? DELEGATION_SPINNER_FRAMES[0];
963
979
  }
980
+ /** Bounded tracker: child session id → latest running tool call. */
981
+ const latestToolActivity = /* @__PURE__ */ new Map();
982
+ const MAX_TOOL_ACTIVITY_ENTRIES = 200;
983
+ const TOOL_ACTIVITY_TTL_MS = 300 * 1e3;
984
+ /** Archived-section pagination state — module-level so it survives sidebar
985
+ * remounts (route changes) without leaking into the component tree. */
986
+ const archivedUiState = { page: 0 };
987
+ /** First usable summary string from common tool input fields. */
988
+ function summarizeToolInput(input) {
989
+ if (!input) return "";
990
+ for (const key of [
991
+ "command",
992
+ "description",
993
+ "prompt",
994
+ "filePath",
995
+ "pattern",
996
+ "url",
997
+ "query"
998
+ ]) {
999
+ const value = input[key];
1000
+ if (typeof value === "string" && value.trim() !== "") return value;
1001
+ }
1002
+ return "";
1003
+ }
1004
+ /** Reduce one message.part.updated tool part to displayable activity.
1005
+ * Returns null for non-tool parts, completed/error parts (no live activity)
1006
+ * or parts without a session id. Pure. */
1007
+ function extractToolActivity(part, now = Date.now()) {
1008
+ if (part?.type !== "tool") return null;
1009
+ const status = part.state?.status;
1010
+ if (status !== "pending" && status !== "running") return null;
1011
+ const sessionID = typeof part.sessionID === "string" ? part.sessionID : "";
1012
+ if (sessionID === "") return null;
1013
+ return {
1014
+ sessionID,
1015
+ activity: {
1016
+ tool: typeof part.tool === "string" && part.tool !== "" ? part.tool : "tool",
1017
+ summary: summarizeToolInput(part.state?.input).replace(/\s+/g, " ").trim().slice(0, 48),
1018
+ at: now
1019
+ }
1020
+ };
1021
+ }
1022
+ /** Record an activity sample (bounded map, oldest dropped). */
1023
+ function trackToolActivity(map, sessionID, activity) {
1024
+ if (sessionID === "") return;
1025
+ if (map.size >= MAX_TOOL_ACTIVITY_ENTRIES) {
1026
+ const oldest = map.keys().next().value;
1027
+ if (oldest !== void 0) map.delete(oldest);
1028
+ }
1029
+ map.set(sessionID, activity);
1030
+ }
1031
+ /** Latest live activity for a child session, or null when absent/stale. */
1032
+ function latestToolActivityFor(map, sessionID, now = Date.now(), ttlMs = TOOL_ACTIVITY_TTL_MS) {
1033
+ if (!sessionID) return null;
1034
+ const hit = map.get(sessionID);
1035
+ if (hit === void 0) return null;
1036
+ if (now - hit.at > ttlMs) return null;
1037
+ return hit;
1038
+ }
964
1039
  /** Merge the immediate tool-event channel into the child-session channel.
965
1040
  *
966
1041
  * Children remain the durable source, while live entries make a delegation
@@ -1023,10 +1098,12 @@ const DELEGATE_TASKID_PATTERN = /\(task\s+([a-z0-9_]+)\)/i;
1023
1098
  const READ_ALIAS_PATTERN = /^[a-z]{2,8}-\d+$/i;
1024
1099
  /** Extract the tool name + args from a `message.part.updated` part and
1025
1100
  * reduce it to what the panel needs. Returns null for anything that is
1026
- * not a pantheon delegation tool part (or is missing its callID). */
1101
+ * not a pantheon delegation tool part, the native `task` subagent tool
1102
+ * (same parentID === caller mechanism — its children render `nat:`),
1103
+ * or is missing its callID. */
1027
1104
  function parseDelegationToolPart(part, now = Date.now()) {
1028
1105
  if (part.type !== "tool") return null;
1029
- if (part.tool !== "pantheon_delegate" && part.tool !== "pantheon_delegation_read") return null;
1106
+ if (part.tool !== "pantheon_delegate" && part.tool !== "pantheon_delegation_read" && part.tool !== "task") return null;
1030
1107
  const callID = part.callID;
1031
1108
  if (callID === void 0 || callID === "") return null;
1032
1109
  const sessionID = part.sessionID ?? "";
@@ -1058,8 +1135,8 @@ function parseDelegationToolPart(part, now = Date.now()) {
1058
1135
  endAt
1059
1136
  };
1060
1137
  }
1061
- const agent = typeof input.agent === "string" ? input.agent : "agent";
1062
- const description = typeof input.description === "string" ? input.description : "";
1138
+ const agent = typeof input.agent === "string" ? input.agent : typeof input.subagent_type === "string" ? input.subagent_type : "agent";
1139
+ const description = typeof input.description === "string" ? input.description : typeof input.prompt === "string" ? input.prompt.slice(0, 120) : "";
1063
1140
  let alias = null;
1064
1141
  let taskID = null;
1065
1142
  if (status === "completed") {
@@ -1071,7 +1148,7 @@ function parseDelegationToolPart(part, now = Date.now()) {
1071
1148
  callID,
1072
1149
  partID: part.id ?? "",
1073
1150
  sessionID,
1074
- tool: "pantheon_delegate",
1151
+ tool: part.tool === "task" ? "task" : "pantheon_delegate",
1075
1152
  agent,
1076
1153
  description,
1077
1154
  status,
@@ -1120,7 +1197,7 @@ function reduceDelegationToolPart(map, part, now = Date.now()) {
1120
1197
  callID: parsed.callID,
1121
1198
  partID: parsed.partID,
1122
1199
  sessionID: parsed.sessionID,
1123
- tool: "pantheon_delegate",
1200
+ tool: parsed.tool,
1124
1201
  agent: parsed.agent ?? "agent",
1125
1202
  description: parsed.description,
1126
1203
  alias: existing?.alias ?? null,
@@ -1137,7 +1214,7 @@ function reduceDelegationToolPart(map, part, now = Date.now()) {
1137
1214
  callID: parsed.callID,
1138
1215
  partID: parsed.partID,
1139
1216
  sessionID: parsed.sessionID,
1140
- tool: "pantheon_delegate",
1217
+ tool: parsed.tool,
1141
1218
  agent: parsed.agent ?? "agent",
1142
1219
  description: parsed.description,
1143
1220
  alias: parsed.alias,
@@ -1182,11 +1259,14 @@ function removeDelegationEntry(map, partIDOrCallID) {
1182
1259
  }
1183
1260
  return false;
1184
1261
  }
1185
- /** Collect pantheon delegation tool parts from a session's messages.
1262
+ /** Collect pantheon delegation + native task tool parts from a session's messages.
1186
1263
  * Messages may carry their parts inline (duck-typed `msg.parts`); when
1187
1264
  * they don't, the optional `getParts(messageID)` callback is used (the TUI
1188
- * SDK exposes `api.state.part(messageID)`). Pure w.r.t. I/O — used by the
1189
- * mount re-scan to re-seed the live map after compaction/attach. */
1265
+ * SDK exposes `api.state.part(messageID)`). The native `task` tool spawns a
1266
+ * child session with parentID = caller — the same mechanism as
1267
+ * pantheon_delegate — so its parts feed the live-map as the native signal
1268
+ * (rows render `nat:` via the children channel). Pure w.r.t. I/O — used
1269
+ * by the mount re-scan to re-seed the live map after compaction/attach. */
1190
1270
  function collectDelegationToolParts(messages, getParts) {
1191
1271
  const out = [];
1192
1272
  for (const msg of messages ?? []) {
@@ -1196,7 +1276,7 @@ function collectDelegationToolParts(messages, getParts) {
1196
1276
  if (!parts) continue;
1197
1277
  for (const raw of parts) {
1198
1278
  const part = raw;
1199
- if (part?.type === "tool" && (part.tool === "pantheon_delegate" || part.tool === "pantheon_delegation_read")) out.push(part);
1279
+ if (part?.type === "tool" && (part.tool === "pantheon_delegate" || part.tool === "pantheon_delegation_read" || part.tool === "task")) out.push(part);
1200
1280
  }
1201
1281
  }
1202
1282
  return out;
@@ -1309,16 +1389,102 @@ function buildChildrenPath(id) {
1309
1389
  * correct it as soon as terminal data exists). */
1310
1390
  function childStatusToState(status) {
1311
1391
  if (status === "idle") return "completed";
1392
+ if (status === "retry") return "retry";
1312
1393
  return "running";
1313
1394
  }
1314
- /** Row tag for a delegation entry: `[native]` for native task() children
1315
- * (source 'children-only' — no board report), `[pantheon:<alias>]` for board
1316
- * rows ([pantheon:apo-1]) — the same tags the manager prints in
1317
- * pantheon_delegation_list, so panel rows and CLI list lines match. The
1318
- * panel renders the tag with a distinct style so native task() children are
1319
- * visually separable from pantheon_delegate jobs. */
1395
+ /** Short row prefix for a delegation entry (one of the two visual channels
1396
+ * that split native task() work from pantheon_delegate jobs — the other is
1397
+ * {@link delegationIcon}):
1398
+ *
1399
+ * - `nat:<agent>` — a native task() child (source 'children-only', no board
1400
+ * report). The child session carries no board alias, so the agent IS the
1401
+ * identity.
1402
+ * - `pan:<alias>` — a pantheon_delegate board row (`pan:apo-1`), the same
1403
+ * alias the manager prints in pantheon_delegation_list.
1404
+ *
1405
+ * Short prefixes keep the narrow sidebar readable (the old
1406
+ * `pan:apo-1`/`nat:` tags ate the row width). */
1320
1407
  function delegationTag(entry) {
1321
- return entry.source === "children-only" ? "[native]" : `[pantheon:${entry.alias}]`;
1408
+ return entry.source === "children-only" ? `nat:${entry.agent}` : `pan:${entry.alias}`;
1409
+ }
1410
+ /** Row glyph: hollow diamond `◇` for native task() children (info color,
1411
+ * outline) vs filled diamond `◆` for pantheon_delegate rows (state color).
1412
+ * Shape + color are independent channels, so the split stays legible even
1413
+ * without color. */
1414
+ function delegationIcon(entry) {
1415
+ return entry.source === "children-only" ? "◇" : "◆";
1416
+ }
1417
+ /** Row identity after the status marker + glyph. Pantheon rows append the
1418
+ * agent (`pan:apo-1 apollo`); native rows already carry it inside the tag
1419
+ * (`nat:apollo`) and must not duplicate it. */
1420
+ function formatDelegationIdentity(entry) {
1421
+ return entry.source === "children-only" ? delegationTag(entry) : `${delegationTag(entry)} ${entry.agent}`;
1422
+ }
1423
+ /** Max description width on the row detail line — the old 180-char slice
1424
+ * wrapped the sidebar; 44 keeps one readable line. */
1425
+ const DELEGATION_DESCRIPTION_MAX = 44;
1426
+ /** Lazily-built grapheme segmenter. `Intl.Segmenter` keeps ZWJ emoji families
1427
+ * (and skin-tone/variation sequences) intact; `Array.from` (code points) is
1428
+ * the fallback for runtimes without it. */
1429
+ let graphemeSegmenter;
1430
+ /** Split `text` into display graphemes so truncation never cuts a multi-unit
1431
+ * emoji in half (surrogate pair → mojibake). */
1432
+ function splitGraphemes(text) {
1433
+ if (typeof Intl !== "undefined" && typeof Intl.Segmenter === "function") {
1434
+ graphemeSegmenter ??= new Intl.Segmenter("pt", { granularity: "grapheme" });
1435
+ return Array.from(graphemeSegmenter.segment(text), (s) => s.segment);
1436
+ }
1437
+ return Array.from(text);
1438
+ }
1439
+ /** Truncate a description to `max` graphemes appending `…` when cut. Exact
1440
+ * `max`-length text is left untouched. Grapheme-granular, so an emoji made of
1441
+ * several code points is never split into mojibake at the boundary. Pure. */
1442
+ function truncateDelegationDescription(text, max = 44) {
1443
+ const graphemes = splitGraphemes(text);
1444
+ if (graphemes.length <= max) return text;
1445
+ return `${graphemes.slice(0, Math.max(0, max - 1)).join("")}\u2026`;
1446
+ }
1447
+ /** Fixed elapsed-time box: right-aligned to `width` (default 8) so elapsed
1448
+ * values line up across rows, e.g. ` 12s`. A label that already fills
1449
+ * the box is returned untouched. Pure. */
1450
+ const DELEGATION_ELAPSED_WIDTH = 8;
1451
+ function formatDelegationElapsed(entry, now, width = 8) {
1452
+ const label = delegationElapsed(entry, now);
1453
+ if (label.length >= width) return label;
1454
+ return `${" ".repeat(width - label.length)}${label}`;
1455
+ }
1456
+ /** First row line: `<marker><glyph> <identity>`. The marker already carries
1457
+ * its trailing space. Pure — the row renders the returned string verbatim. */
1458
+ function formatDelegationRow(entry, marker) {
1459
+ return `${marker}${delegationIcon(entry)} ${formatDelegationIdentity(entry)}`;
1460
+ }
1461
+ /** Header summary: `(N active · M done · nat:K pan:M)`. Active counts
1462
+ * running/retry AND display-only stale-running (a stale row is still a live
1463
+ * job — it must never read as done). Pure. */
1464
+ function formatDelegationHeader(entries) {
1465
+ let active = 0;
1466
+ for (const e of entries) if (e.state === "running" || e.state === "retry" || e.state === "stale-running") active++;
1467
+ const done = entries.length - active;
1468
+ const counts = countDelegationSources(entries);
1469
+ return `(${active} active \u00b7 ${done} done \u00b7 nat:${counts.native} pan:${counts.pantheon})`;
1470
+ }
1471
+ /** Split a display list into native task() rows vs pantheon_delegate rows.
1472
+ * Native = source 'children-only' (no board report); everything else counts
1473
+ * as pantheon. Pure — powers the header breakdown + the hooks.log line. */
1474
+ function countDelegationSources(entries) {
1475
+ let native = 0;
1476
+ for (const e of entries) if (e.source === "children-only") native++;
1477
+ return {
1478
+ native,
1479
+ pantheon: entries.length - native,
1480
+ total: entries.length
1481
+ };
1482
+ }
1483
+ /** Diagnostic hooks.log line for a panel re-fetch, with the children
1484
+ * breakdown (pantheon = children WITH a board report, native = children
1485
+ * WITHOUT one). Pure — the View logs the returned string verbatim. */
1486
+ function formatPanelLogLine(children, pantheon, native, md, events) {
1487
+ return `panel: children=${children}(pantheon=${pantheon} native=${native}) md=${md} events=${events}`;
1322
1488
  }
1323
1489
  /** Turn child sessions (PRIMARY) enriched with md reports into the display
1324
1490
  * list. One entry per child id (duplicates across re-fetches collapse).
@@ -1328,10 +1494,14 @@ function delegationTag(entry) {
1328
1494
  * itself (fallback 'agent'), state derived from its status, startedAt from
1329
1495
  * time.created. A report-less child is a NATIVE task() child (every
1330
1496
  * child of the current session — pantheon_delegate OR the native `task()`
1331
- * tool — carries parentID = caller), so it gets source 'children-only'
1332
- * and the `[native]` tag instead of a board alias.
1497
+ * tool — carries parentID = caller), so it gets source 'children-only',
1498
+ * the internal alias 'native-task' and the `nat:` tag instead of a
1499
+ * board alias. The 'task nativa' description fallback keeps the row
1500
+ * non-empty when the child carries no title.
1333
1501
  * Terminal md state wins over the derived state; a running md defers to
1334
- * the child's live status. Sorted running-first (compareDelegationEntries).
1502
+ * the child's live status. A running child is NEVER archived — it always
1503
+ * lands in the active split (splitDelegationList active = running/retry).
1504
+ * Sorted running-first (compareDelegationEntries).
1335
1505
  * Pure — no I/O. */
1336
1506
  function childrenToDelegationEntries(children, md, now = Date.now()) {
1337
1507
  const byTaskID = /* @__PURE__ */ new Map();
@@ -1344,7 +1514,7 @@ function childrenToDelegationEntries(children, md, now = Date.now()) {
1344
1514
  const mdEntry = byTaskID.get(child.id);
1345
1515
  const state = mdEntry !== void 0 && mdEntry.state !== "running" ? mdEntry.state : childStatusToState(child.status);
1346
1516
  out.push({
1347
- alias: mdEntry?.alias ?? "task",
1517
+ alias: mdEntry?.alias ?? "native-task",
1348
1518
  sessionID: mdEntry?.sessionID ?? "",
1349
1519
  taskID: child.id,
1350
1520
  agent: mdEntry?.agent ?? child.agent ?? "agent",
@@ -1352,7 +1522,7 @@ function childrenToDelegationEntries(children, md, now = Date.now()) {
1352
1522
  startedAt: mdEntry?.startedAt ?? child.time?.created ?? now,
1353
1523
  updatedAt: mdEntry?.updatedAt ?? (state === "running" || state === "stale-running" ? null : child.time?.updated ?? null),
1354
1524
  timedOut: mdEntry?.timedOut ?? false,
1355
- description: mdEntry !== void 0 && mdEntry.description !== "" ? mdEntry.description : child.title ?? "",
1525
+ description: mdEntry !== void 0 && mdEntry.description !== "" ? mdEntry.description : child.title !== void 0 && child.title !== "" ? child.title : "task nativa",
1356
1526
  source: mdEntry !== void 0 ? "md" : "children-only"
1357
1527
  });
1358
1528
  }
@@ -1395,7 +1565,11 @@ function createTuiLogger(projectRoot, module = "pantheon-tui") {
1395
1565
  await mkdir(dirname(logPath), { recursive: true });
1396
1566
  const stamp = (/* @__PURE__ */ new Date()).toISOString();
1397
1567
  await appendFile(logPath, `${lines.map((l) => `[${stamp}] [${module}] ${l}`).join("\n")}\n`, "utf8");
1398
- } catch {}
1568
+ } catch (err) {
1569
+ if (echo) try {
1570
+ process.stderr.write(`[${module}] file-log failed: ${formatArg(err)}\n`);
1571
+ } catch {}
1572
+ }
1399
1573
  })();
1400
1574
  if (echo) for (const line of lines) console.log(`[${module}] ${line}`);
1401
1575
  };
@@ -1431,6 +1605,7 @@ function DelegationRow(props) {
1431
1605
  const t = theme();
1432
1606
  switch (props.job.state) {
1433
1607
  case "running": return t.warning;
1608
+ case "retry": return t.warning;
1434
1609
  case "stale-running": return t.error;
1435
1610
  case "completed": return t.success;
1436
1611
  case "error": return t.error;
@@ -1439,6 +1614,7 @@ function DelegationRow(props) {
1439
1614
  });
1440
1615
  const marker = createMemo(() => {
1441
1616
  if (props.job.state === "running") return `${delegationSpinnerFrame(props.animationNow)} `;
1617
+ if (props.job.state === "retry") return "⟳ ";
1442
1618
  if (props.job.state === "stale-running") return "⚠ ";
1443
1619
  switch (props.job.state) {
1444
1620
  case "completed": return "✓ ";
@@ -1447,38 +1623,55 @@ function DelegationRow(props) {
1447
1623
  }
1448
1624
  });
1449
1625
  const tagColor = createMemo(() => props.job.source === "children-only" ? theme().info : color());
1450
- const detail = createMemo(() => {
1451
- const line = `${delegationElapsed(props.job, props.now)}${props.job.description !== "" ? ` \u2014 ${props.job.description}` : ""}`;
1452
- return line.length > 180 ? `${line.slice(0, 177)}\u2026` : line;
1453
- });
1626
+ const description = createMemo(() => truncateDelegationDescription(props.job.description));
1627
+ const elapsed = createMemo(() => formatDelegationElapsed(props.job, props.now));
1628
+ const activity = createMemo(() => latestToolActivityFor(latestToolActivity, props.job.taskID, props.now));
1454
1629
  const open = () => {
1455
1630
  navigateToDelegationSession(props.api.route, props.job.taskID);
1456
1631
  };
1457
1632
  return (() => {
1458
- var _el$13 = createElement("box"), _el$14 = createElement("box"), _el$15 = createElement("text"), _el$16 = createElement("text"), _el$17 = createElement("text"), _el$18 = createElement("text");
1633
+ var _el$13 = createElement("box"), _el$14 = createElement("box"), _el$15 = createElement("text"), _el$16 = createElement("box"), _el$18 = createElement("text");
1459
1634
  insertNode(_el$13, _el$14);
1460
- insertNode(_el$13, _el$18);
1635
+ insertNode(_el$13, _el$16);
1461
1636
  setProp(_el$13, "onMouseDown", open);
1462
1637
  insertNode(_el$14, _el$15);
1463
- insertNode(_el$14, _el$16);
1464
- insertNode(_el$14, _el$17);
1465
1638
  setProp(_el$14, "flexDirection", "row");
1466
- insert(_el$15, marker);
1467
- insert(_el$16, () => delegationTag(props.job));
1468
- insert(_el$17, () => ` ${props.job.agent} \u2014 ${delegationActivityLabel(props.job)}`);
1469
- insert(_el$18, detail);
1639
+ insert(_el$15, () => formatDelegationRow(props.job, marker()));
1640
+ insertNode(_el$16, _el$18);
1641
+ setProp(_el$16, "flexDirection", "row");
1642
+ insert(_el$16, createComponent(Show, {
1643
+ get when() {
1644
+ return description() !== "";
1645
+ },
1646
+ get children() {
1647
+ var _el$17 = createElement("text");
1648
+ insert(_el$17, description);
1649
+ effect((_$p) => setProp(_el$17, "fg", theme().textMuted, _$p));
1650
+ return _el$17;
1651
+ }
1652
+ }), _el$18);
1653
+ insert(_el$18, elapsed);
1654
+ insert(_el$13, createComponent(Show, {
1655
+ get when() {
1656
+ return activity();
1657
+ },
1658
+ children: (a) => (() => {
1659
+ var _el$19 = createElement("text");
1660
+ insert(_el$19, () => ` \u21b3 ${truncateDelegationDescription(`${a().tool}${a().summary !== "" ? ` ${a().summary}` : ""}`)}`);
1661
+ effect((_$p) => setProp(_el$19, "fg", theme().textMuted, _$p));
1662
+ return _el$19;
1663
+ })()
1664
+ }), null);
1470
1665
  effect((_p$) => {
1471
- var _v$5 = color(), _v$6 = tagColor(), _v$7 = color(), _v$8 = theme().textMuted;
1666
+ var _v$5 = tagColor(), _v$6 = description() !== "" ? "space-between" : "flex-end", _v$7 = theme().textMuted;
1472
1667
  _v$5 !== _p$.e && (_p$.e = setProp(_el$15, "fg", _v$5, _p$.e));
1473
- _v$6 !== _p$.t && (_p$.t = setProp(_el$16, "fg", _v$6, _p$.t));
1474
- _v$7 !== _p$.a && (_p$.a = setProp(_el$17, "fg", _v$7, _p$.a));
1475
- _v$8 !== _p$.o && (_p$.o = setProp(_el$18, "fg", _v$8, _p$.o));
1668
+ _v$6 !== _p$.t && (_p$.t = setProp(_el$16, "justifyContent", _v$6, _p$.t));
1669
+ _v$7 !== _p$.a && (_p$.a = setProp(_el$18, "fg", _v$7, _p$.a));
1476
1670
  return _p$;
1477
1671
  }, {
1478
1672
  e: void 0,
1479
1673
  t: void 0,
1480
- a: void 0,
1481
- o: void 0
1674
+ a: void 0
1482
1675
  });
1483
1676
  return _el$13;
1484
1677
  })();
@@ -1560,7 +1753,7 @@ function View(props) {
1560
1753
  });
1561
1754
  if (sessionID === null) {
1562
1755
  setChildDelegations(md);
1563
- panelLog.info(`panel: children=0 md=${md.length} events=${eventRefreshCount} (no sessionID — history shown)`);
1756
+ panelLog.info(`${formatPanelLogLine(0, 0, 0, md.length, eventRefreshCount)} (no sessionID — history shown)`);
1564
1757
  return;
1565
1758
  }
1566
1759
  let children = [];
@@ -1587,14 +1780,23 @@ function View(props) {
1587
1780
  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);
1588
1781
  const liveEntries = [...props.liveStore.map.values()].filter((entry) => entry.sessionID === sessionID);
1589
1782
  setChildDelegations(mergeChildDelegationSources(childEntries, liveEntries));
1590
- panelLog.info(`panel: children=${children.length} md=${md.length} events=${eventRefreshCount}`);
1783
+ const counts = countDelegationSources(childEntries);
1784
+ panelLog.info(formatPanelLogLine(children.length, counts.pantheon, counts.native, md.length, eventRefreshCount));
1591
1785
  } finally {
1592
1786
  delegationsInflight = null;
1593
1787
  }
1594
1788
  })();
1595
1789
  return delegationsInflight;
1596
1790
  };
1597
- const visibleDelegations = createMemo(() => visibleDelegationList(childDelegations()));
1791
+ const delegationSplit = createMemo(() => splitDelegationList(childDelegations()));
1792
+ const visibleDelegations = createMemo(() => [...delegationSplit().active, ...delegationSplit().recent]);
1793
+ const delegationHeader = createMemo(() => formatDelegationHeader(childDelegations()));
1794
+ const [archivedPage, setArchivedPage] = createSignal(archivedUiState.page);
1795
+ const clampArchivedPage = (page, pages) => {
1796
+ const clamped = Math.max(0, Math.min(page, Math.max(0, pages - 1)));
1797
+ archivedUiState.page = clamped;
1798
+ setArchivedPage(clamped);
1799
+ };
1598
1800
  onMount(() => {
1599
1801
  const cleanup = [];
1600
1802
  try {
@@ -1623,7 +1825,7 @@ function View(props) {
1623
1825
  refreshDelegations();
1624
1826
  }, 1e3);
1625
1827
  cleanup.push(() => clearInterval(poll));
1626
- const animation = setInterval(() => setAnimationNow(Date.now()), 140);
1828
+ const animation = setInterval(() => setAnimationNow(Date.now()), 1e3);
1627
1829
  cleanup.push(() => clearInterval(animation));
1628
1830
  try {
1629
1831
  const sdk = props.api.state?.session;
@@ -1643,78 +1845,78 @@ function View(props) {
1643
1845
  }));
1644
1846
  });
1645
1847
  const HR = () => (() => {
1646
- var _el$19 = createElement("text");
1647
- insertNode(_el$19, createTextNode(`────────────────────────────`));
1648
- effect((_$p) => setProp(_el$19, "fg", theme().textMuted, _$p));
1649
- return _el$19;
1848
+ var _el$20 = createElement("text");
1849
+ insertNode(_el$20, createTextNode(`────────────────────────────`));
1850
+ effect((_$p) => setProp(_el$20, "fg", theme().textMuted, _$p));
1851
+ return _el$20;
1650
1852
  })();
1651
1853
  return (() => {
1652
- var _el$21 = createElement("box"), _el$22 = createElement("text"), _el$23 = createElement("box"), _el$24 = createElement("text"), _el$25 = createElement("text"), _el$27 = createElement("box"), _el$28 = createElement("text"), _el$29 = createElement("text");
1653
- insertNode(_el$21, _el$22);
1654
- insertNode(_el$21, _el$23);
1655
- insertNode(_el$21, _el$27);
1656
- setProp(_el$21, "flexDirection", "column");
1657
- setProp(_el$21, "width", "100%");
1658
- setProp(_el$22, "attributes", 1);
1659
- insert(_el$22, () => `Pantheon${props.version ? ` v${props.version}` : ""}`);
1660
- insert(_el$21, createComponent(Show, {
1854
+ var _el$22 = createElement("box"), _el$23 = createElement("text"), _el$24 = createElement("box"), _el$25 = createElement("text"), _el$26 = createElement("text"), _el$28 = createElement("box"), _el$29 = createElement("text"), _el$30 = createElement("text");
1855
+ insertNode(_el$22, _el$23);
1856
+ insertNode(_el$22, _el$24);
1857
+ insertNode(_el$22, _el$28);
1858
+ setProp(_el$22, "flexDirection", "column");
1859
+ setProp(_el$22, "width", "100%");
1860
+ setProp(_el$23, "attributes", 1);
1861
+ insert(_el$23, () => `Pantheon${props.version ? ` v${props.version}` : ""}`);
1862
+ insert(_el$22, createComponent(Show, {
1661
1863
  get when() {
1662
1864
  return branch();
1663
1865
  },
1664
1866
  children: (b) => (() => {
1665
- var _el$31 = createElement("text");
1666
- insert(_el$31, b);
1667
- effect((_$p) => setProp(_el$31, "fg", theme().textMuted, _$p));
1668
- return _el$31;
1867
+ var _el$32 = createElement("text");
1868
+ insert(_el$32, b);
1869
+ effect((_$p) => setProp(_el$32, "fg", theme().textMuted, _$p));
1870
+ return _el$32;
1669
1871
  })()
1670
- }), _el$23);
1671
- insert(_el$21, createComponent(Show, {
1872
+ }), _el$24);
1873
+ insert(_el$22, createComponent(Show, {
1672
1874
  get when() {
1673
1875
  return preset().name;
1674
1876
  },
1675
1877
  get fallback() {
1676
1878
  return (() => {
1677
- var _el$32 = createElement("box"), _el$33 = createElement("text");
1678
- insertNode(_el$32, _el$33);
1679
- setProp(_el$32, "flexDirection", "row");
1680
- setProp(_el$32, "gap", 1);
1681
- insertNode(_el$33, createTextNode(`Preset: default`));
1682
- effect((_$p) => setProp(_el$33, "fg", theme().textMuted, _$p));
1683
- return _el$32;
1879
+ var _el$33 = createElement("box"), _el$34 = createElement("text");
1880
+ insertNode(_el$33, _el$34);
1881
+ setProp(_el$33, "flexDirection", "row");
1882
+ setProp(_el$33, "gap", 1);
1883
+ insertNode(_el$34, createTextNode(`Preset: default`));
1884
+ effect((_$p) => setProp(_el$34, "fg", theme().textMuted, _$p));
1885
+ return _el$33;
1684
1886
  })();
1685
1887
  },
1686
1888
  children: (name) => (() => {
1687
- var _el$35 = createElement("box"), _el$36 = createElement("text"), _el$38 = createElement("text"), _el$39 = createElement("text");
1688
- insertNode(_el$35, _el$36);
1689
- insertNode(_el$35, _el$38);
1690
- insertNode(_el$35, _el$39);
1691
- setProp(_el$35, "flexDirection", "row");
1692
- setProp(_el$35, "gap", 1);
1693
- insertNode(_el$36, createTextNode(`⚡ Preset:`));
1694
- insert(_el$38, name);
1695
- insert(_el$39, () => `(${preset().source ?? ""})`);
1889
+ var _el$36 = createElement("box"), _el$37 = createElement("text"), _el$39 = createElement("text"), _el$40 = createElement("text");
1890
+ insertNode(_el$36, _el$37);
1891
+ insertNode(_el$36, _el$39);
1892
+ insertNode(_el$36, _el$40);
1893
+ setProp(_el$36, "flexDirection", "row");
1894
+ setProp(_el$36, "gap", 1);
1895
+ insertNode(_el$37, createTextNode(`⚡ Preset:`));
1896
+ insert(_el$39, name);
1897
+ insert(_el$40, () => `(${preset().source ?? ""})`);
1696
1898
  effect((_p$) => {
1697
- var _v$12 = theme().textMuted, _v$13 = theme().accent, _v$14 = theme().textMuted;
1698
- _v$12 !== _p$.e && (_p$.e = setProp(_el$36, "fg", _v$12, _p$.e));
1699
- _v$13 !== _p$.t && (_p$.t = setProp(_el$38, "fg", _v$13, _p$.t));
1700
- _v$14 !== _p$.a && (_p$.a = setProp(_el$39, "fg", _v$14, _p$.a));
1899
+ var _v$11 = theme().textMuted, _v$12 = theme().accent, _v$13 = theme().textMuted;
1900
+ _v$11 !== _p$.e && (_p$.e = setProp(_el$37, "fg", _v$11, _p$.e));
1901
+ _v$12 !== _p$.t && (_p$.t = setProp(_el$39, "fg", _v$12, _p$.t));
1902
+ _v$13 !== _p$.a && (_p$.a = setProp(_el$40, "fg", _v$13, _p$.a));
1701
1903
  return _p$;
1702
1904
  }, {
1703
1905
  e: void 0,
1704
1906
  t: void 0,
1705
1907
  a: void 0
1706
1908
  });
1707
- return _el$35;
1909
+ return _el$36;
1708
1910
  })()
1709
- }), _el$23);
1710
- insert(_el$21, createComponent(HR, {}), _el$23);
1711
- insertNode(_el$23, _el$24);
1712
- insertNode(_el$23, _el$25);
1713
- setProp(_el$23, "onMouseDown", () => setShowSessions((x) => !x));
1714
- setProp(_el$24, "attributes", 1);
1715
- insert(_el$24, () => `${showSessions() ? "▼" : "▶"} Sessions`);
1716
- insert(_el$25, () => ` (${String(totalSessions())})`);
1717
- insert(_el$21, createComponent(Show, {
1911
+ }), _el$24);
1912
+ insert(_el$22, createComponent(HR, {}), _el$24);
1913
+ insertNode(_el$24, _el$25);
1914
+ insertNode(_el$24, _el$26);
1915
+ setProp(_el$24, "onMouseDown", () => setShowSessions((x) => !x));
1916
+ setProp(_el$25, "attributes", 1);
1917
+ insert(_el$25, () => `${showSessions() ? "▼" : "▶"} Sessions`);
1918
+ insert(_el$26, () => ` (${String(totalSessions())})`);
1919
+ insert(_el$22, createComponent(Show, {
1718
1920
  get when() {
1719
1921
  return showSessions();
1720
1922
  },
@@ -1725,19 +1927,19 @@ function View(props) {
1725
1927
  },
1726
1928
  get fallback() {
1727
1929
  return (() => {
1728
- var _el$40 = createElement("box"), _el$41 = createElement("text");
1729
- insertNode(_el$40, _el$41);
1730
- setProp(_el$40, "marginLeft", 1);
1731
- insertNode(_el$41, createTextNode(`No recent sessions`));
1732
- effect((_$p) => setProp(_el$41, "fg", theme().textMuted, _$p));
1733
- return _el$40;
1930
+ var _el$41 = createElement("box"), _el$42 = createElement("text");
1931
+ insertNode(_el$41, _el$42);
1932
+ setProp(_el$41, "marginLeft", 1);
1933
+ insertNode(_el$42, createTextNode(`No recent sessions`));
1934
+ effect((_$p) => setProp(_el$42, "fg", theme().textMuted, _$p));
1935
+ return _el$41;
1734
1936
  })();
1735
1937
  },
1736
1938
  get children() {
1737
- var _el$26 = createElement("box");
1738
- setProp(_el$26, "marginLeft", 1);
1739
- setProp(_el$26, "flexDirection", "column");
1740
- insert(_el$26, createComponent(For, {
1939
+ var _el$27 = createElement("box");
1940
+ setProp(_el$27, "marginLeft", 1);
1941
+ setProp(_el$27, "flexDirection", "column");
1942
+ insert(_el$27, createComponent(For, {
1741
1943
  get each() {
1742
1944
  return recentSessions();
1743
1945
  },
@@ -1748,18 +1950,18 @@ function View(props) {
1748
1950
  session: ses
1749
1951
  })
1750
1952
  }));
1751
- return _el$26;
1953
+ return _el$27;
1752
1954
  }
1753
1955
  });
1754
1956
  }
1755
- }), _el$27);
1756
- insertNode(_el$27, _el$28);
1757
- insertNode(_el$27, _el$29);
1758
- setProp(_el$27, "onMouseDown", () => setShowDelegations((x) => !x));
1759
- setProp(_el$28, "attributes", 1);
1760
- insert(_el$28, () => `${showDelegations() ? "▼" : "▶"} Delegations`);
1761
- insert(_el$29, () => ` (${String(visibleDelegations().length)})`);
1762
- insert(_el$21, createComponent(Show, {
1957
+ }), _el$28);
1958
+ insertNode(_el$28, _el$29);
1959
+ insertNode(_el$28, _el$30);
1960
+ setProp(_el$28, "onMouseDown", () => setShowDelegations((x) => !x));
1961
+ setProp(_el$29, "attributes", 1);
1962
+ insert(_el$29, () => `${showDelegations() ? "▼" : "▶"} Delegations`);
1963
+ insert(_el$30, () => ` ${delegationHeader()}`);
1964
+ insert(_el$22, createComponent(Show, {
1763
1965
  get when() {
1764
1966
  return showDelegations();
1765
1967
  },
@@ -1770,19 +1972,19 @@ function View(props) {
1770
1972
  },
1771
1973
  get fallback() {
1772
1974
  return (() => {
1773
- var _el$43 = createElement("box"), _el$44 = createElement("text");
1774
- insertNode(_el$43, _el$44);
1775
- setProp(_el$43, "marginLeft", 1);
1776
- insertNode(_el$44, createTextNode(`No delegations`));
1777
- effect((_$p) => setProp(_el$44, "fg", theme().textMuted, _$p));
1778
- return _el$43;
1975
+ var _el$44 = createElement("box"), _el$45 = createElement("text");
1976
+ insertNode(_el$44, _el$45);
1977
+ setProp(_el$44, "marginLeft", 1);
1978
+ insertNode(_el$45, createTextNode(`No delegations`));
1979
+ effect((_$p) => setProp(_el$45, "fg", theme().textMuted, _$p));
1980
+ return _el$44;
1779
1981
  })();
1780
1982
  },
1781
1983
  get children() {
1782
- var _el$30 = createElement("box");
1783
- setProp(_el$30, "marginLeft", 1);
1784
- setProp(_el$30, "flexDirection", "column");
1785
- insert(_el$30, createComponent(For, {
1984
+ var _el$31 = createElement("box");
1985
+ setProp(_el$31, "marginLeft", 1);
1986
+ setProp(_el$31, "flexDirection", "column");
1987
+ insert(_el$31, createComponent(For, {
1786
1988
  get each() {
1787
1989
  return visibleDelegations();
1788
1990
  },
@@ -1798,19 +2000,79 @@ function View(props) {
1798
2000
  return animationNow();
1799
2001
  }
1800
2002
  })
1801
- }));
1802
- return _el$30;
2003
+ }), null);
2004
+ insert(_el$31, createComponent(Show, {
2005
+ get when() {
2006
+ return delegationSplit().archived.length > 0;
2007
+ },
2008
+ get children() {
2009
+ return (() => {
2010
+ const archived = delegationSplit().archived;
2011
+ const pageSize = 8;
2012
+ const pages = Math.ceil(archived.length / pageSize);
2013
+ const page = Math.min(archivedPage(), pages - 1);
2014
+ const slice = archived.slice(page * pageSize, page * pageSize + pageSize);
2015
+ return (() => {
2016
+ 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");
2017
+ insertNode(_el$47, _el$48);
2018
+ setProp(_el$47, "flexDirection", "column");
2019
+ insertNode(_el$48, _el$49);
2020
+ insertNode(_el$48, _el$51);
2021
+ insertNode(_el$48, _el$52);
2022
+ insertNode(_el$48, _el$53);
2023
+ setProp(_el$48, "flexDirection", "row");
2024
+ setProp(_el$48, "marginLeft", 0);
2025
+ insertNode(_el$49, createTextNode(`‹ `));
2026
+ setProp(_el$49, "onMouseDown", () => clampArchivedPage(page - 1, pages));
2027
+ insert(_el$51, () => `Archived (${archived.length}) `);
2028
+ insert(_el$52, `${page + 1}/${pages}`);
2029
+ insertNode(_el$53, createTextNode(` ›`));
2030
+ setProp(_el$53, "onMouseDown", () => clampArchivedPage(page + 1, pages));
2031
+ insert(_el$47, createComponent(For, {
2032
+ each: slice,
2033
+ children: (job) => createComponent(DelegationRow, {
2034
+ get api() {
2035
+ return props.api;
2036
+ },
2037
+ job,
2038
+ get now() {
2039
+ return now();
2040
+ },
2041
+ get animationNow() {
2042
+ return animationNow();
2043
+ }
2044
+ })
2045
+ }), null);
2046
+ effect((_p$) => {
2047
+ var _v$14 = theme().textMuted, _v$15 = theme().textMuted, _v$16 = theme().textMuted, _v$17 = theme().textMuted;
2048
+ _v$14 !== _p$.e && (_p$.e = setProp(_el$49, "fg", _v$14, _p$.e));
2049
+ _v$15 !== _p$.t && (_p$.t = setProp(_el$51, "fg", _v$15, _p$.t));
2050
+ _v$16 !== _p$.a && (_p$.a = setProp(_el$52, "fg", _v$16, _p$.a));
2051
+ _v$17 !== _p$.o && (_p$.o = setProp(_el$53, "fg", _v$17, _p$.o));
2052
+ return _p$;
2053
+ }, {
2054
+ e: void 0,
2055
+ t: void 0,
2056
+ a: void 0,
2057
+ o: void 0
2058
+ });
2059
+ return _el$47;
2060
+ })();
2061
+ })();
2062
+ }
2063
+ }), null);
2064
+ return _el$31;
1803
2065
  }
1804
2066
  });
1805
2067
  }
1806
2068
  }), null);
1807
2069
  effect((_p$) => {
1808
- var _v$9 = theme().accent, _v$0 = theme().text, _v$1 = theme().textMuted, _v$10 = theme().text, _v$11 = theme().textMuted;
1809
- _v$9 !== _p$.e && (_p$.e = setProp(_el$22, "fg", _v$9, _p$.e));
1810
- _v$0 !== _p$.t && (_p$.t = setProp(_el$24, "fg", _v$0, _p$.t));
1811
- _v$1 !== _p$.a && (_p$.a = setProp(_el$25, "fg", _v$1, _p$.a));
1812
- _v$10 !== _p$.o && (_p$.o = setProp(_el$28, "fg", _v$10, _p$.o));
1813
- _v$11 !== _p$.i && (_p$.i = setProp(_el$29, "fg", _v$11, _p$.i));
2070
+ var _v$8 = theme().accent, _v$9 = theme().text, _v$0 = theme().textMuted, _v$1 = theme().text, _v$10 = theme().textMuted;
2071
+ _v$8 !== _p$.e && (_p$.e = setProp(_el$23, "fg", _v$8, _p$.e));
2072
+ _v$9 !== _p$.t && (_p$.t = setProp(_el$25, "fg", _v$9, _p$.t));
2073
+ _v$0 !== _p$.a && (_p$.a = setProp(_el$26, "fg", _v$0, _p$.a));
2074
+ _v$1 !== _p$.o && (_p$.o = setProp(_el$29, "fg", _v$1, _p$.o));
2075
+ _v$10 !== _p$.i && (_p$.i = setProp(_el$30, "fg", _v$10, _p$.i));
1814
2076
  return _p$;
1815
2077
  }, {
1816
2078
  e: void 0,
@@ -1819,7 +2081,7 @@ function View(props) {
1819
2081
  o: void 0,
1820
2082
  i: void 0
1821
2083
  });
1822
- return _el$21;
2084
+ return _el$22;
1823
2085
  })();
1824
2086
  }
1825
2087
  const tui = (api, _options, _meta) => {
@@ -1839,6 +2101,8 @@ const tui = (api, _options, _meta) => {
1839
2101
  const props = event?.properties ?? {};
1840
2102
  const part = props.part ?? props.info?.part;
1841
2103
  if (part === void 0) return;
2104
+ const sample = extractToolActivity(part);
2105
+ if (sample !== null) trackToolActivity(latestToolActivity, sample.sessionID, sample.activity);
1842
2106
  if (reduceDelegationToolPart(liveStore.map, part)) liveStore.bump();
1843
2107
  }));
1844
2108
  } catch {}
@@ -1877,6 +2141,6 @@ const plugin = {
1877
2141
  setup: async () => {}
1878
2142
  };
1879
2143
  //#endregion
1880
- export { IDLE_SILENCE_MS, STALE_RUNNING_THRESHOLD_MS, buildChildrenPath, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationSpinnerFrame, delegationTag, fmtElapsed, isValidSessionId, markStaleIfRunning, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, safeSessionPath, seedLiveDelegationMap, toDelegationEntry, tuiLogPath, visibleDelegationList };
2144
+ export { DELEGATION_DESCRIPTION_MAX, DELEGATION_ELAPSED_WIDTH, IDLE_SILENCE_MS, STALE_RUNNING_THRESHOLD_MS, buildChildrenPath, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, countDelegationSources, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationIcon, delegationSpinnerFrame, 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 };
1881
2145
 
1882
2146
  //# sourceMappingURL=tui.js.map