@esso0428/pi-subagents 0.15.4 → 0.15.6

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 (73) hide show
  1. package/CHANGELOG.md +4 -17
  2. package/README.md +5 -5
  3. package/dist/agent-history-list.d.ts +18 -0
  4. package/dist/agent-history-list.js +68 -0
  5. package/dist/agent-history.d.ts +1 -1
  6. package/dist/agent-history.js +31 -34
  7. package/dist/agent-manager.d.ts +22 -8
  8. package/dist/agent-manager.js +100 -93
  9. package/dist/agent-runner.js +67 -86
  10. package/dist/agent-types.js +27 -45
  11. package/dist/context.js +2 -6
  12. package/dist/cross-extension-rpc.js +5 -9
  13. package/dist/custom-agents.js +15 -18
  14. package/dist/default-agents.js +1 -4
  15. package/dist/enabled-models.js +11 -16
  16. package/dist/env.js +1 -4
  17. package/dist/group-join.js +1 -5
  18. package/dist/index.js +273 -284
  19. package/dist/invocation-config.js +2 -6
  20. package/dist/memory.js +24 -34
  21. package/dist/model-resolver.js +1 -4
  22. package/dist/nico-overrides.js +14 -20
  23. package/dist/output-file.js +16 -22
  24. package/dist/prompts.js +1 -4
  25. package/dist/schedule-store.js +16 -21
  26. package/dist/schedule.js +8 -12
  27. package/dist/settings.js +15 -23
  28. package/dist/skill-loader.js +20 -23
  29. package/dist/status-note.js +1 -4
  30. package/dist/types.d.ts +0 -1
  31. package/dist/types.js +1 -4
  32. package/dist/ui/agent-widget.d.ts +1 -5
  33. package/dist/ui/agent-widget.js +25 -49
  34. package/dist/ui/conversation-blocks.d.ts +18 -0
  35. package/dist/ui/conversation-blocks.js +214 -0
  36. package/dist/ui/conversation-nvim.d.ts +7 -0
  37. package/dist/ui/conversation-nvim.js +116 -0
  38. package/dist/ui/conversation-search.d.ts +38 -0
  39. package/dist/ui/conversation-search.js +123 -0
  40. package/dist/ui/conversation-viewer.d.ts +55 -19
  41. package/dist/ui/conversation-viewer.js +398 -215
  42. package/dist/ui/fleet-list.d.ts +23 -11
  43. package/dist/ui/fleet-list.js +85 -78
  44. package/dist/ui/schedule-menu.js +1 -4
  45. package/dist/ui/viewer-keys.js +7 -10
  46. package/dist/usage.js +4 -10
  47. package/dist/worktree.js +26 -31
  48. package/package.json +1 -1
  49. package/src/agent-history-list.ts +91 -0
  50. package/src/agent-history.ts +12 -17
  51. package/src/agent-manager.ts +108 -81
  52. package/src/agent-runner.ts +5 -13
  53. package/src/index.ts +84 -85
  54. package/src/output-file.ts +1 -1
  55. package/src/types.ts +0 -1
  56. package/src/ui/agent-widget.ts +1 -8
  57. package/src/ui/conversation-blocks.ts +270 -0
  58. package/src/ui/conversation-nvim.ts +127 -0
  59. package/src/ui/conversation-search.ts +161 -0
  60. package/src/ui/conversation-viewer.ts +376 -205
  61. package/src/ui/fleet-list.ts +73 -52
  62. package/test/agent-history-list.test.ts +121 -0
  63. package/test/agent-manager-history.test.ts +59 -58
  64. package/test/agent-menu-history-navigation.test.ts +86 -0
  65. package/test/ui/conversation-blocks.test.ts +72 -0
  66. package/test/ui/conversation-search.test.ts +55 -0
  67. package/test/ui/conversation-viewer.test.ts +132 -0
  68. package/test/ui/fleet-list.test.ts +131 -0
  69. package/dist/ui/markdown-result.d.ts +0 -3
  70. package/dist/ui/markdown-result.js +0 -53
  71. package/src/ui/markdown-result.ts +0 -56
  72. package/test/agent-history.test.ts +0 -63
  73. package/test/ui/markdown-result.test.ts +0 -45
package/dist/settings.js CHANGED
@@ -1,17 +1,9 @@
1
- "use strict";
2
1
  // Persistence for pi-subagents operational settings.
3
2
  // - Global: ~/.pi/agent/subagents.json (via getAgentDir()) — manual defaults, never written here
4
3
  // - Project: <cwd>/.pi/subagents.json — written by /agents → Settings; overrides global on load
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.loadSettings = loadSettings;
7
- exports.saveSettings = saveSettings;
8
- exports.applySettings = applySettings;
9
- exports.persistToastFor = persistToastFor;
10
- exports.applyAndEmitLoaded = applyAndEmitLoaded;
11
- exports.saveAndEmitChanged = saveAndEmitChanged;
12
- const node_fs_1 = require("node:fs");
13
- const node_path_1 = require("node:path");
14
- const pi_coding_agent_1 = require("@earendil-works/pi-coding-agent");
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
+ import { dirname, join } from "node:path";
6
+ import { getAgentDir } from "@earendil-works/pi-coding-agent";
15
7
  const VALID_JOIN_MODES = new Set(["async", "group", "smart"]);
16
8
  const VALID_TOOL_DESCRIPTION_MODES = new Set(["full", "compact", "custom"]);
17
9
  const VALID_WIDGET_MODES = new Set(["all", "background", "off"]);
@@ -69,10 +61,10 @@ function sanitize(raw) {
69
61
  return out;
70
62
  }
71
63
  function globalPath() {
72
- return (0, node_path_1.join)((0, pi_coding_agent_1.getAgentDir)(), "subagents.json");
64
+ return join(getAgentDir(), "subagents.json");
73
65
  }
74
66
  function projectPath(cwd) {
75
- return (0, node_path_1.join)(cwd, ".pi", "subagents.json");
67
+ return join(cwd, ".pi", "subagents.json");
76
68
  }
77
69
  /**
78
70
  * Read a settings file. Missing file is silent (returns `{}`). A file that
@@ -80,10 +72,10 @@ function projectPath(cwd) {
80
72
  * silently reverted to defaults — and still returns `{}` so startup proceeds.
81
73
  */
82
74
  function readSettingsFile(path) {
83
- if (!(0, node_fs_1.existsSync)(path))
75
+ if (!existsSync(path))
84
76
  return {};
85
77
  try {
86
- return sanitize(JSON.parse((0, node_fs_1.readFileSync)(path, "utf-8")));
78
+ return sanitize(JSON.parse(readFileSync(path, "utf-8")));
87
79
  }
88
80
  catch (err) {
89
81
  const reason = err instanceof Error ? err.message : String(err);
@@ -92,7 +84,7 @@ function readSettingsFile(path) {
92
84
  }
93
85
  }
94
86
  /** Load merged settings: global provides defaults, project overrides. */
95
- function loadSettings(cwd = process.cwd()) {
87
+ export function loadSettings(cwd = process.cwd()) {
96
88
  return { ...readSettingsFile(globalPath()), ...readSettingsFile(projectPath(cwd)) };
97
89
  }
98
90
  /**
@@ -100,11 +92,11 @@ function loadSettings(cwd = process.cwd()) {
100
92
  * Returns `true` on success, `false` if the write (or mkdir) failed so the
101
93
  * caller can surface a warning — persistence isn't fatal but isn't silent.
102
94
  */
103
- function saveSettings(s, cwd = process.cwd()) {
95
+ export function saveSettings(s, cwd = process.cwd()) {
104
96
  const path = projectPath(cwd);
105
97
  try {
106
- (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true });
107
- (0, node_fs_1.writeFileSync)(path, JSON.stringify(s, null, 2), "utf-8");
98
+ mkdirSync(dirname(path), { recursive: true });
99
+ writeFileSync(path, JSON.stringify(s, null, 2), "utf-8");
108
100
  return true;
109
101
  }
110
102
  catch {
@@ -112,7 +104,7 @@ function saveSettings(s, cwd = process.cwd()) {
112
104
  }
113
105
  }
114
106
  /** Apply persisted settings to the in-memory state via caller-supplied setters. */
115
- function applySettings(s, appliers) {
107
+ export function applySettings(s, appliers) {
116
108
  if (typeof s.maxConcurrent === "number")
117
109
  appliers.setMaxConcurrent(s.maxConcurrent);
118
110
  if (typeof s.defaultMaxTurns === "number")
@@ -141,7 +133,7 @@ function applySettings(s, appliers) {
141
133
  * routes the success/failure of `saveSettings` into the right message + level
142
134
  * so the UI layer (index.ts) stays a thin wire between input and notification.
143
135
  */
144
- function persistToastFor(successMsg, persisted) {
136
+ export function persistToastFor(successMsg, persisted) {
145
137
  return persisted
146
138
  ? { message: successMsg, level: "info" }
147
139
  : { message: `${successMsg} (session only; failed to persist)`, level: "warning" };
@@ -151,7 +143,7 @@ function persistToastFor(successMsg, persisted) {
151
143
  * `subagents:settings_loaded` lifecycle event. Returns the loaded settings so
152
144
  * callers can log/inspect. Extension init wires this once.
153
145
  */
154
- function applyAndEmitLoaded(appliers, emit, cwd = process.cwd()) {
146
+ export function applyAndEmitLoaded(appliers, emit, cwd = process.cwd()) {
155
147
  const settings = loadSettings(cwd);
156
148
  applySettings(settings, appliers);
157
149
  emit("subagents:settings_loaded", { settings });
@@ -163,7 +155,7 @@ function applyAndEmitLoaded(appliers, emit, cwd = process.cwd()) {
163
155
  * return the toast the UI should display. Event payload carries the `persisted`
164
156
  * flag so listeners can react to write failures.
165
157
  */
166
- function saveAndEmitChanged(snapshot, successMsg, emit, cwd = process.cwd()) {
158
+ export function saveAndEmitChanged(snapshot, successMsg, emit, cwd = process.cwd()) {
167
159
  const persisted = saveSettings(snapshot, cwd);
168
160
  emit("subagents:settings_changed", { settings: snapshot, persisted });
169
161
  return persistToastFor(successMsg, persisted);
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  /**
3
2
  * skill-loader.ts — Preload named skills.
4
3
  *
@@ -18,26 +17,24 @@
18
17
  *
19
18
  * Symlinks are rejected for security (deviation from Pi, which follows them).
20
19
  */
21
- Object.defineProperty(exports, "__esModule", { value: true });
22
- exports.preloadSkills = preloadSkills;
23
- const node_fs_1 = require("node:fs");
24
- const node_os_1 = require("node:os");
25
- const node_path_1 = require("node:path");
26
- const pi_coding_agent_1 = require("@earendil-works/pi-coding-agent");
27
- const memory_js_1 = require("./memory.js");
28
- function preloadSkills(skillNames, cwd) {
20
+ import { existsSync, readdirSync } from "node:fs";
21
+ import { homedir } from "node:os";
22
+ import { join } from "node:path";
23
+ import { getAgentDir } from "@earendil-works/pi-coding-agent";
24
+ import { isSymlink, isUnsafeName, safeReadFile } from "./memory.js";
25
+ export function preloadSkills(skillNames, cwd) {
29
26
  return skillNames.map((name) => ({ name, content: loadSkillContent(name, cwd) }));
30
27
  }
31
28
  function loadSkillContent(name, cwd) {
32
- if ((0, memory_js_1.isUnsafeName)(name)) {
29
+ if (isUnsafeName(name)) {
33
30
  return `(Skill "${name}" skipped: name contains path traversal characters)`;
34
31
  }
35
32
  const roots = [
36
- (0, node_path_1.join)(cwd, ".pi", "skills"), // project — Pi standard
37
- (0, node_path_1.join)(cwd, ".agents", "skills"), // project — Agent Skills spec
38
- (0, node_path_1.join)((0, pi_coding_agent_1.getAgentDir)(), "skills"), // user — Pi standard
39
- (0, node_path_1.join)((0, node_os_1.homedir)(), ".agents", "skills"), // user — Agent Skills spec
40
- (0, node_path_1.join)((0, node_os_1.homedir)(), ".pi", "skills"), // legacy global, pre-Pi
33
+ join(cwd, ".pi", "skills"), // project — Pi standard
34
+ join(cwd, ".agents", "skills"), // project — Agent Skills spec
35
+ join(getAgentDir(), "skills"), // user — Pi standard
36
+ join(homedir(), ".agents", "skills"), // user — Agent Skills spec
37
+ join(homedir(), ".pi", "skills"), // legacy global, pre-Pi
41
38
  ];
42
39
  for (const root of roots) {
43
40
  const content = findInRoot(root, name);
@@ -47,16 +44,16 @@ function loadSkillContent(name, cwd) {
47
44
  return `(Skill "${name}" not found in .pi/skills/, .agents/skills/, or global skill locations)`;
48
45
  }
49
46
  function findInRoot(root, name) {
50
- if ((0, memory_js_1.isSymlink)(root))
47
+ if (isSymlink(root))
51
48
  return undefined; // reject symlinked roots entirely
52
- const flat = (0, memory_js_1.safeReadFile)((0, node_path_1.join)(root, `${name}.md`))?.trim();
49
+ const flat = safeReadFile(join(root, `${name}.md`))?.trim();
53
50
  if (flat !== undefined)
54
51
  return flat;
55
52
  return findSkillDirectory(root, name);
56
53
  }
57
54
  /** BFS under `root` for a directory named `name` containing `SKILL.md`. Pi-conforming filters. */
58
55
  function findSkillDirectory(root, name) {
59
- if (!(0, node_fs_1.existsSync)(root))
56
+ if (!existsSync(root))
60
57
  return undefined;
61
58
  const queue = [root];
62
59
  while (queue.length > 0) {
@@ -65,7 +62,7 @@ function findSkillDirectory(root, name) {
65
62
  continue;
66
63
  let entries;
67
64
  try {
68
- entries = (0, node_fs_1.readdirSync)(current, { withFileTypes: true });
65
+ entries = readdirSync(current, { withFileTypes: true });
69
66
  }
70
67
  catch {
71
68
  continue;
@@ -78,12 +75,12 @@ function findSkillDirectory(root, name) {
78
75
  if (entry.name.startsWith(".") || entry.name === "node_modules")
79
76
  continue;
80
77
  // Symlinked dirs already filtered by entry.isDirectory() — Dirent uses lstat semantics.
81
- const path = (0, node_path_1.join)(current, entry.name);
82
- const skillMd = (0, node_path_1.join)(path, "SKILL.md");
83
- const isSkillDir = (0, node_fs_1.existsSync)(skillMd);
78
+ const path = join(current, entry.name);
79
+ const skillMd = join(path, "SKILL.md");
80
+ const isSkillDir = existsSync(skillMd);
84
81
  if (isSkillDir) {
85
82
  if (entry.name === name) {
86
- const content = (0, memory_js_1.safeReadFile)(skillMd)?.trim();
83
+ const content = safeReadFile(skillMd)?.trim();
87
84
  if (content !== undefined)
88
85
  return content;
89
86
  }
@@ -1,9 +1,6 @@
1
- "use strict";
2
1
  /**
3
2
  * status-note.ts — Parenthetical status note appended to agent result text.
4
3
  */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getStatusNote = getStatusNote;
7
4
  /**
8
5
  * Explicit parenthetical note for a non-normal terminal outcome, so the parent
9
6
  * agent can't mistake partial output for a completed result. Empty string for a
@@ -13,7 +10,7 @@ exports.getStatusNote = getStatusNote;
13
10
  * turn limit was hit) — the parent should treat human intervention differently
14
11
  * from a budget cutoff.
15
12
  */
16
- function getStatusNote(status) {
13
+ export function getStatusNote(status) {
17
14
  switch (status) {
18
15
  case "stopped":
19
16
  return " (STOPPED BY THE USER before completion — output is partial; the task was NOT finished)";
package/dist/types.d.ts CHANGED
@@ -154,7 +154,6 @@ export interface NotificationDetails {
154
154
  outputFile?: string;
155
155
  error?: string;
156
156
  resultPreview: string;
157
- resultText?: string;
158
157
  /** Additional agents in a group notification. */
159
158
  others?: NotificationDetails[];
160
159
  }
package/dist/types.js CHANGED
@@ -1,8 +1,5 @@
1
- "use strict";
2
1
  /**
3
2
  * types.ts — Type definitions for the subagent system.
4
3
  */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.DEFAULT_AGENT_NAMES = void 0;
7
4
  /** Names of the three embedded default agents. */
8
- exports.DEFAULT_AGENT_NAMES = ["general-purpose", "Explore", "Plan"];
5
+ export const DEFAULT_AGENT_NAMES = ["general-purpose", "Explore", "Plan"];
@@ -102,8 +102,6 @@ export declare class AgentWidget {
102
102
  * extension supplies one defaulting to `"background"`.
103
103
  */
104
104
  private mode;
105
- /** Current project cwd used to validate reload-restored transcripts. */
106
- private getCwd;
107
105
  private uiCtx;
108
106
  private widgetFrame;
109
107
  private widgetInterval;
@@ -123,9 +121,7 @@ export declare class AgentWidget {
123
121
  * `WidgetMode`. Defaults to `"all"` when a caller supplies no policy; the
124
122
  * extension supplies one defaulting to `"background"`.
125
123
  */
126
- mode?: () => WidgetMode,
127
- /** Current project cwd used to validate reload-restored transcripts. */
128
- getCwd?: () => string | undefined);
124
+ mode?: () => WidgetMode);
129
125
  /**
130
126
  * Agents eligible for the widget, per the current `WidgetMode`:
131
127
  * - `off`: none (the widget's existing empty-state path hides it entirely).
@@ -1,33 +1,19 @@
1
- "use strict";
2
1
  /**
3
2
  * agent-widget.ts — Persistent widget showing running/completed agents above the editor.
4
3
  *
5
4
  * Displays a tree of agents with animated spinners, live stats, and activity descriptions.
6
5
  * Uses the callback form of setWidget for themed rendering.
7
6
  */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.AgentWidget = exports.ERROR_STATUSES = exports.SPINNER = void 0;
10
- exports.fgPreservingNestedStyles = fgPreservingNestedStyles;
11
- exports.formatTokens = formatTokens;
12
- exports.formatSessionTokens = formatSessionTokens;
13
- exports.formatTurns = formatTurns;
14
- exports.formatMs = formatMs;
15
- exports.formatDuration = formatDuration;
16
- exports.getDisplayName = getDisplayName;
17
- exports.getPromptModeLabel = getPromptModeLabel;
18
- exports.buildInvocationTags = buildInvocationTags;
19
- exports.describeActivity = describeActivity;
20
- const pi_tui_1 = require("@earendil-works/pi-tui");
21
- const agent_history_js_1 = require("../agent-history.js");
22
- const agent_types_js_1 = require("../agent-types.js");
23
- const usage_js_1 = require("../usage.js");
7
+ import { truncateToWidth } from "@earendil-works/pi-tui";
8
+ import { getConfig } from "../agent-types.js";
9
+ import { getLifetimeTotal, getSessionContextPercent } from "../usage.js";
24
10
  // ---- Constants ----
25
11
  /** Maximum number of rendered lines before overflow collapse kicks in. */
26
12
  const MAX_WIDGET_LINES = 12;
27
13
  /** Braille spinner frames for animated running indicator. */
28
- exports.SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
14
+ export const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
29
15
  /** Statuses that indicate an error/non-success outcome (used for linger behavior and icon rendering). */
30
- exports.ERROR_STATUSES = new Set(["error", "aborted", "steered", "stopped"]);
16
+ export const ERROR_STATUSES = new Set(["error", "aborted", "steered", "stopped"]);
31
17
  /** Tool name → human-readable action for activity descriptions. */
32
18
  const TOOL_DISPLAY = {
33
19
  read: "reading",
@@ -40,13 +26,13 @@ const TOOL_DISPLAY = {
40
26
  };
41
27
  // ---- Formatting helpers ----
42
28
  /** Apply foreground styling while restoring it after nested foreground/full ANSI resets. */
43
- function fgPreservingNestedStyles(theme, color, text) {
29
+ export function fgPreservingNestedStyles(theme, color, text) {
44
30
  const styledEmpty = theme.fg(color, "");
45
31
  const styleStart = styledEmpty.replace(/\u001b\[(?:0|39)m/g, "");
46
32
  return theme.fg(color, text.replace(/\u001b\[(?:0|39)m/g, reset => `${reset}${styleStart}`));
47
33
  }
48
34
  /** Format a token count compactly: "33.8k token", "1.2M token". */
49
- function formatTokens(count) {
35
+ export function formatTokens(count) {
50
36
  if (count >= 1_000_000)
51
37
  return `${(count / 1_000_000).toFixed(1)}M token`;
52
38
  if (count >= 1_000)
@@ -63,7 +49,7 @@ function formatTokens(count) {
63
49
  * "12.3k token (⇊2)" — compactions only (e.g. right after compact)
64
50
  * "12.3k token (45% · ⇊2)" — both
65
51
  */
66
- function formatSessionTokens(tokens, percent, theme, compactions = 0) {
52
+ export function formatSessionTokens(tokens, percent, theme, compactions = 0) {
67
53
  const tokenStr = formatTokens(tokens);
68
54
  const annot = [];
69
55
  if (percent !== null) {
@@ -78,30 +64,30 @@ function formatSessionTokens(tokens, percent, theme, compactions = 0) {
78
64
  return `${tokenStr} (${annot.join(" · ")})`;
79
65
  }
80
66
  /** Format turn count with optional max limit: "↻5≤30" or "↻5". */
81
- function formatTurns(turnCount, maxTurns) {
67
+ export function formatTurns(turnCount, maxTurns) {
82
68
  return maxTurns != null ? `↻${turnCount}≤${maxTurns}` : `↻${turnCount}`;
83
69
  }
84
70
  /** Format milliseconds as human-readable duration. */
85
- function formatMs(ms) {
71
+ export function formatMs(ms) {
86
72
  return `${(ms / 1000).toFixed(1)}s`;
87
73
  }
88
74
  /** Format duration from start/completed timestamps. */
89
- function formatDuration(startedAt, completedAt) {
75
+ export function formatDuration(startedAt, completedAt) {
90
76
  if (completedAt)
91
77
  return formatMs(completedAt - startedAt);
92
78
  return `${formatMs(Date.now() - startedAt)} (running)`;
93
79
  }
94
80
  /** Get display name for any agent type (built-in or custom). */
95
- function getDisplayName(type) {
96
- return (0, agent_types_js_1.getConfig)(type).displayName;
81
+ export function getDisplayName(type) {
82
+ return getConfig(type).displayName;
97
83
  }
98
84
  /** Short label for prompt mode: "twin" for append, nothing for replace (the default). */
99
- function getPromptModeLabel(type) {
100
- const config = (0, agent_types_js_1.getConfig)(type);
85
+ export function getPromptModeLabel(type) {
86
+ const config = getConfig(type);
101
87
  return config.promptMode === "append" ? "twin" : undefined;
102
88
  }
103
89
  /** Mode label is not included — callers add it where they want it. */
104
- function buildInvocationTags(invocation) {
90
+ export function buildInvocationTags(invocation) {
105
91
  const tags = [];
106
92
  if (!invocation)
107
93
  return { tags };
@@ -127,7 +113,7 @@ function truncateLine(text, len = 60) {
127
113
  return line.slice(0, len) + "…";
128
114
  }
129
115
  /** Build a human-readable activity string from currently-running tools or response text. */
130
- function describeActivity(activeTools, responseText) {
116
+ export function describeActivity(activeTools, responseText) {
131
117
  if (activeTools.size > 0) {
132
118
  const groups = new Map();
133
119
  for (const toolName of activeTools.values()) {
@@ -152,11 +138,10 @@ function describeActivity(activeTools, responseText) {
152
138
  return "thinking…";
153
139
  }
154
140
  // ---- Widget manager ----
155
- class AgentWidget {
141
+ export class AgentWidget {
156
142
  manager;
157
143
  agentActivity;
158
144
  mode;
159
- getCwd;
160
145
  uiCtx;
161
146
  widgetFrame = 0;
162
147
  widgetInterval;
@@ -176,13 +161,10 @@ class AgentWidget {
176
161
  * `WidgetMode`. Defaults to `"all"` when a caller supplies no policy; the
177
162
  * extension supplies one defaulting to `"background"`.
178
163
  */
179
- mode = () => "all",
180
- /** Current project cwd used to validate reload-restored transcripts. */
181
- getCwd = () => undefined) {
164
+ mode = () => "all") {
182
165
  this.manager = manager;
183
166
  this.agentActivity = agentActivity;
184
167
  this.mode = mode;
185
- this.getCwd = getCwd;
186
168
  }
187
169
  /**
188
170
  * Agents eligible for the widget, per the current `WidgetMode`:
@@ -196,12 +178,7 @@ class AgentWidget {
196
178
  * - `all`: every agent.
197
179
  */
198
180
  widgetAgents() {
199
- const all = this.manager.listAgents().filter((record) => {
200
- if (record.status === "running" || record.status === "queued")
201
- return true;
202
- const cwd = this.getCwd();
203
- return cwd !== undefined && (0, agent_history_js_1.hasAgentHistory)(cwd, record.transcriptPath);
204
- });
181
+ const all = this.manager.listAgents();
205
182
  switch (this.mode()) {
206
183
  case "off": return [];
207
184
  case "background": return all.filter(a => a.isBackground !== false);
@@ -240,7 +217,7 @@ class AgentWidget {
240
217
  /** Check if a finished agent should still be shown in the widget. */
241
218
  shouldShowFinished(agentId, status) {
242
219
  const age = this.finishedTurnAge.get(agentId) ?? 0;
243
- const maxAge = exports.ERROR_STATUSES.has(status) ? AgentWidget.ERROR_LINGER_TURNS : 1;
220
+ const maxAge = ERROR_STATUSES.has(status) ? AgentWidget.ERROR_LINGER_TURNS : 1;
244
221
  return age < maxAge;
245
222
  }
246
223
  /** Record an agent as finished (call when agent completes). */
@@ -304,10 +281,10 @@ class AgentWidget {
304
281
  if (!hasActive && !hasFinished)
305
282
  return [];
306
283
  const w = tui.terminal.columns;
307
- const truncate = (line) => (0, pi_tui_1.truncateToWidth)(line, w);
284
+ const truncate = (line) => truncateToWidth(line, w);
308
285
  const headingColor = hasActive ? "accent" : "dim";
309
286
  const headingIcon = hasActive ? "●" : "○";
310
- const frame = exports.SPINNER[this.widgetFrame % exports.SPINNER.length];
287
+ const frame = SPINNER[this.widgetFrame % SPINNER.length];
311
288
  // Build sections separately for overflow-aware assembly.
312
289
  // Each running agent = 2 lines (header + activity), finished = 1 line, queued = 1 line.
313
290
  const finishedLines = [];
@@ -322,8 +299,8 @@ class AgentWidget {
322
299
  const elapsed = formatMs(Date.now() - a.startedAt);
323
300
  const bg = this.agentActivity.get(a.id);
324
301
  const toolUses = bg?.toolUses ?? a.toolUses;
325
- const tokens = (0, usage_js_1.getLifetimeTotal)(bg?.lifetimeUsage);
326
- const contextPercent = (0, usage_js_1.getSessionContextPercent)(bg?.session);
302
+ const tokens = getLifetimeTotal(bg?.lifetimeUsage);
303
+ const contextPercent = getSessionContextPercent(bg?.session);
327
304
  const tokenText = tokens > 0 ? formatSessionTokens(tokens, contextPercent, theme, a.compactionCount) : "";
328
305
  const parts = [];
329
306
  if (bg)
@@ -505,4 +482,3 @@ class AgentWidget {
505
482
  this.lastStatusText = undefined;
506
483
  }
507
484
  }
508
- exports.AgentWidget = AgentWidget;
@@ -0,0 +1,18 @@
1
+ import type { AgentSession } from "@earendil-works/pi-coding-agent";
2
+ export type ConversationBlockKind = "text" | "tool" | "meta";
3
+ export type ConversationBlockRole = "user" | "assistant" | "tool" | "custom" | "meta";
4
+ export interface ConversationBlock {
5
+ id: string;
6
+ kind: ConversationBlockKind;
7
+ role: ConversationBlockRole;
8
+ header: string;
9
+ markdown: string;
10
+ copyText: string;
11
+ fullText: string;
12
+ toolLine?: string;
13
+ }
14
+ export interface ConversationFormatOptions {
15
+ maxEntryChars?: number;
16
+ maxToolResultChars?: number;
17
+ }
18
+ export declare function formatConversationMessages(messages: AgentSession["messages"], options?: ConversationFormatOptions): ConversationBlock[];