@esso0428/pi-subagents 0.15.5 → 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 -22
  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 -286
  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 -89
  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/index.js CHANGED
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  /**
3
2
  * pi-agents — A pi extension providing Claude Code-style autonomous sub-agents.
4
3
  *
@@ -10,34 +9,31 @@
10
9
  * Commands:
11
10
  * /agents — Interactive agent management menu
12
11
  */
13
- Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.renderRunningAgentStatus = renderRunningAgentStatus;
15
- exports.default = default_1;
16
- const node_fs_1 = require("node:fs");
17
- const node_path_1 = require("node:path");
18
- const pi_coding_agent_1 = require("@earendil-works/pi-coding-agent");
19
- const pi_tui_1 = require("@earendil-works/pi-tui");
20
- const typebox_1 = require("@sinclair/typebox");
21
- const agent_manager_js_1 = require("./agent-manager.js");
22
- const agent_runner_js_1 = require("./agent-runner.js");
23
- const agent_types_js_1 = require("./agent-types.js");
24
- const cross_extension_rpc_js_1 = require("./cross-extension-rpc.js");
25
- const custom_agents_js_1 = require("./custom-agents.js");
26
- const enabled_models_js_1 = require("./enabled-models.js");
27
- const group_join_js_1 = require("./group-join.js");
28
- const invocation_config_js_1 = require("./invocation-config.js");
29
- const model_resolver_js_1 = require("./model-resolver.js");
30
- const agent_history_js_1 = require("./agent-history.js");
31
- const output_file_js_1 = require("./output-file.js");
32
- const schedule_js_1 = require("./schedule.js");
33
- const schedule_store_js_1 = require("./schedule-store.js");
34
- const settings_js_1 = require("./settings.js");
35
- const status_note_js_1 = require("./status-note.js");
36
- const agent_widget_js_1 = require("./ui/agent-widget.js");
37
- const fleet_list_js_1 = require("./ui/fleet-list.js");
38
- const markdown_result_js_1 = require("./ui/markdown-result.js");
39
- const schedule_menu_js_1 = require("./ui/schedule-menu.js");
40
- const usage_js_1 = require("./usage.js");
12
+ import { existsSync, mkdirSync, readFileSync, unlinkSync } from "node:fs";
13
+ import { join } from "node:path";
14
+ import { defineTool, getAgentDir, getSettingsListTheme } from "@earendil-works/pi-coding-agent";
15
+ import { Container, Key, matchesKey, SettingsList, Spacer, Text } from "@earendil-works/pi-tui";
16
+ import { Type } from "@sinclair/typebox";
17
+ import { AgentManager } from "./agent-manager.js";
18
+ import { getAgentConversation, getDefaultMaxTurns, getGraceTurns, normalizeMaxTurns, SUBAGENT_TOOL_NAMES, setDefaultMaxTurns, setGraceTurns, steerAgent } from "./agent-runner.js";
19
+ import { applyNicoOverrides, BUILTIN_TOOL_NAMES, getAgentConfig, getAllTypes, getAvailableTypes, isDefaultsDisabled, registerAgents, resolveType, setDefaultsDisabled } from "./agent-types.js";
20
+ import { registerRpcHandlers } from "./cross-extension-rpc.js";
21
+ import { loadCustomAgents } from "./custom-agents.js";
22
+ import { isModelInScope, readEnabledModels, resolveEnabledModels } from "./enabled-models.js";
23
+ import { GroupJoinManager } from "./group-join.js";
24
+ import { resolveAgentInvocationConfig, resolveJoinMode } from "./invocation-config.js";
25
+ import { resolveModel } from "./model-resolver.js";
26
+ import { agentHistoryLocator, createAgentHistoryPath, readAgentHistory } from "./agent-history.js";
27
+ import { buildAgentStatusMenuEntries, canOpenActiveAgent, canOpenAgentHistory, formatAgentHistoryOption, splitAgentRecords } from "./agent-history-list.js";
28
+ import { createOutputFilePath, streamToOutputFile, writeInitialEntry } from "./output-file.js";
29
+ import { SubagentScheduler } from "./schedule.js";
30
+ import { resolveStorePath, ScheduleStore } from "./schedule-store.js";
31
+ import { applyAndEmitLoaded, saveAndEmitChanged } from "./settings.js";
32
+ import { getStatusNote } from "./status-note.js";
33
+ import { AgentWidget, buildInvocationTags, describeActivity, fgPreservingNestedStyles, formatDuration, formatMs, formatTokens, formatTurns, getDisplayName, getPromptModeLabel, SPINNER, } from "./ui/agent-widget.js";
34
+ import { FleetList } from "./ui/fleet-list.js";
35
+ import { showSchedulesMenu } from "./ui/schedule-menu.js";
36
+ import { addUsage, getLifetimeTotal, getSessionContextPercent } from "./usage.js";
41
37
  // ---- Shared helpers ----
42
38
  /** Tool execute return value for a text response. */
43
39
  function textResult(msg, details) {
@@ -75,16 +71,16 @@ function abortable(promise, signal) {
75
71
  });
76
72
  });
77
73
  }
78
- function renderRunningAgentStatus(frame, statsText, activity, theme) {
79
- const container = new pi_tui_1.Container();
80
- container.addChild(new pi_tui_1.Text(theme.fg("accent", frame) + (statsText ? " " + statsText : ""), 0, 0));
81
- container.addChild(new pi_tui_1.Text(theme.fg("dim", ` ⎿ ${activity}`), 0, 0));
74
+ export function renderRunningAgentStatus(frame, statsText, activity, theme) {
75
+ const container = new Container();
76
+ container.addChild(new Text(theme.fg("accent", frame) + (statsText ? " " + statsText : ""), 0, 0));
77
+ container.addChild(new Text(theme.fg("dim", ` ⎿ ${activity}`), 0, 0));
82
78
  return container;
83
79
  }
84
80
  /** Format an agent's lifetime token total, or "" when zero. */
85
81
  function formatLifetimeTokens(o) {
86
- const t = (0, usage_js_1.getLifetimeTotal)(o.lifetimeUsage);
87
- return t > 0 ? (0, agent_widget_js_1.formatTokens)(t) : "";
82
+ const t = getLifetimeTotal(o.lifetimeUsage);
83
+ return t > 0 ? formatTokens(t) : "";
88
84
  }
89
85
  /**
90
86
  * Create an AgentActivity state and spawn callbacks for tracking tool usage.
@@ -128,7 +124,7 @@ function createActivityTracker(maxTurns, onStreamUpdate) {
128
124
  state.session = session;
129
125
  },
130
126
  onAssistantUsage: (usage) => {
131
- (0, usage_js_1.addUsage)(state.lifetimeUsage, usage);
127
+ addUsage(state.lifetimeUsage, usage);
132
128
  onStreamUpdate?.();
133
129
  },
134
130
  };
@@ -169,8 +165,8 @@ function escapeXml(s) {
169
165
  function formatTaskNotification(record, resultMaxLen) {
170
166
  const status = getStatusLabel(record.status, record.error);
171
167
  const durationMs = record.completedAt ? record.completedAt - record.startedAt : 0;
172
- const totalTokens = (0, usage_js_1.getLifetimeTotal)(record.lifetimeUsage);
173
- const contextPercent = (0, usage_js_1.getSessionContextPercent)(record.session);
168
+ const totalTokens = getLifetimeTotal(record.lifetimeUsage);
169
+ const contextPercent = getSessionContextPercent(record.session);
174
170
  const ctxXml = contextPercent !== null ? `<context_percent>${Math.round(contextPercent)}</context_percent>` : "";
175
171
  const compactXml = record.compactionCount ? `<compactions>${record.compactionCount}</compactions>` : "";
176
172
  const resultPreview = record.result
@@ -184,7 +180,7 @@ function formatTaskNotification(record, resultMaxLen) {
184
180
  record.toolCallId ? `<tool-use-id>${escapeXml(record.toolCallId)}</tool-use-id>` : null,
185
181
  record.outputFile ? `<output-file>${escapeXml(record.outputFile)}</output-file>` : null,
186
182
  `<status>${escapeXml(status)}</status>`,
187
- `<summary>Agent "${escapeXml(record.description)}" ${record.status}${(0, status_note_js_1.getStatusNote)(record.status)}</summary>`,
183
+ `<summary>Agent "${escapeXml(record.description)}" ${record.status}${getStatusNote(record.status)}</summary>`,
188
184
  `<result>${escapeXml(resultPreview)}</result>`,
189
185
  `<usage><total_tokens>${totalTokens}</total_tokens><tool_uses>${record.toolUses}</tool_uses>${ctxXml}${compactXml}<duration_ms>${durationMs}</duration_ms></usage>`,
190
186
  `</task-notification>`,
@@ -205,11 +201,9 @@ function buildDetails(base, record, activity, overrides) {
205
201
  ...overrides,
206
202
  };
207
203
  }
208
- /** Maximum expanded notification body retained in message details. */
209
- const MAX_NOTIFICATION_RESULT_TEXT_LENGTH = 20_000;
210
204
  /** Build notification details for the custom message renderer. */
211
205
  function buildNotificationDetails(record, resultMaxLen, activity) {
212
- const totalTokens = (0, usage_js_1.getLifetimeTotal)(record.lifetimeUsage);
206
+ const totalTokens = getLifetimeTotal(record.lifetimeUsage);
213
207
  return {
214
208
  id: record.id,
215
209
  description: record.description,
@@ -221,11 +215,6 @@ function buildNotificationDetails(record, resultMaxLen, activity) {
221
215
  durationMs: record.completedAt ? record.completedAt - record.startedAt : 0,
222
216
  outputFile: record.outputFile,
223
217
  error: record.error,
224
- resultText: record.result
225
- ? record.result.length > MAX_NOTIFICATION_RESULT_TEXT_LENGTH
226
- ? record.result.slice(0, MAX_NOTIFICATION_RESULT_TEXT_LENGTH) + "…"
227
- : record.result
228
- : undefined,
229
218
  resultPreview: record.result
230
219
  ? record.result.length > resultMaxLen
231
220
  ? record.result.slice(0, resultMaxLen) + "…"
@@ -233,7 +222,7 @@ function buildNotificationDetails(record, resultMaxLen, activity) {
233
222
  : "No output.",
234
223
  };
235
224
  }
236
- function default_1(pi) {
225
+ export default function (pi) {
237
226
  // ---- Register custom notification renderer ----
238
227
  pi.registerMessageRenderer("subagent-notification", (message, { expanded }, theme) => {
239
228
  const d = message.details;
@@ -250,49 +239,40 @@ function default_1(pi) {
250
239
  // Line 2: stats
251
240
  const parts = [];
252
241
  if (d.turnCount > 0)
253
- parts.push((0, agent_widget_js_1.formatTurns)(d.turnCount, d.maxTurns));
242
+ parts.push(formatTurns(d.turnCount, d.maxTurns));
254
243
  if (d.toolUses > 0)
255
244
  parts.push(`${d.toolUses} tool use${d.toolUses === 1 ? "" : "s"}`);
256
245
  if (d.totalTokens > 0)
257
- parts.push((0, agent_widget_js_1.formatTokens)(d.totalTokens));
246
+ parts.push(formatTokens(d.totalTokens));
258
247
  if (d.durationMs > 0)
259
- parts.push((0, agent_widget_js_1.formatMs)(d.durationMs));
248
+ parts.push(formatMs(d.durationMs));
260
249
  if (parts.length) {
261
250
  line += "\n " + parts.map(p => theme.fg("dim", p)).join(" " + theme.fg("dim", "·") + " ");
262
251
  }
263
- if (!expanded) {
264
- // Collapsed previews intentionally remain compact plain text.
252
+ // Line 3: result preview (collapsed) or full (expanded)
253
+ if (expanded) {
254
+ const lines = d.resultPreview.split("\n").slice(0, 30);
255
+ for (const l of lines)
256
+ line += "\n" + theme.fg("dim", ` ${l}`);
257
+ }
258
+ else {
265
259
  const preview = d.resultPreview.split("\n")[0]?.slice(0, 80) ?? "";
266
260
  line += "\n " + theme.fg("dim", `⎿ ${preview}`);
267
- if (d.outputFile) {
268
- line += "\n " + theme.fg("muted", `transcript: ${d.outputFile}`);
269
- }
270
- return line;
271
261
  }
272
- const container = new pi_tui_1.Container();
273
- container.addChild(new pi_tui_1.Text(line, 0, 0));
274
- container.addChild((0, markdown_result_js_1.createMarkdownResult)(d.resultText ?? d.resultPreview, theme, 30));
262
+ // Line 4: output file link (if present)
275
263
  if (d.outputFile) {
276
- container.addChild(new pi_tui_1.Text(theme.fg("muted", ` transcript: ${d.outputFile}`), 0, 0));
264
+ line += "\n " + theme.fg("muted", `transcript: ${d.outputFile}`);
277
265
  }
278
- return container;
266
+ return line;
279
267
  }
280
268
  const all = [d, ...(d.others ?? [])];
281
- if (!expanded)
282
- return new pi_tui_1.Text(all.map(renderOne).join("\n"), 0, 0);
283
- const container = new pi_tui_1.Container();
284
- all.forEach((item, index) => {
285
- if (index > 0)
286
- container.addChild(new pi_tui_1.Spacer(1));
287
- container.addChild(renderOne(item));
288
- });
289
- return container;
269
+ return new Text(all.map(renderOne).join("\n"), 0, 0);
290
270
  });
291
271
  /** Reload agents from project/global custom agent dirs and merge with defaults (called on init and each Agent invocation). */
292
272
  const reloadCustomAgents = () => {
293
- const userAgents = (0, custom_agents_js_1.loadCustomAgents)(process.cwd());
294
- (0, agent_types_js_1.registerAgents)(userAgents);
295
- (0, agent_types_js_1.applyNicoOverrides)();
273
+ const userAgents = loadCustomAgents(process.cwd());
274
+ registerAgents(userAgents);
275
+ applyNicoOverrides();
296
276
  };
297
277
  // Initial load
298
278
  reloadCustomAgents();
@@ -344,7 +324,7 @@ function default_1(pi) {
344
324
  widget.update();
345
325
  }
346
326
  // ---- Group join manager ----
347
- const groupJoin = new group_join_js_1.GroupJoinManager((records, partial) => {
327
+ const groupJoin = new GroupJoinManager((records, partial) => {
348
328
  for (const r of records) {
349
329
  agentActivity.delete(r.id);
350
330
  widget.markFinished(r.id);
@@ -384,7 +364,7 @@ function default_1(pi) {
384
364
  // tokens is omitted when nothing was ever produced (e.g. agent errored before
385
365
  // any message_end fired), preserving prior payload shape.
386
366
  const u = record.lifetimeUsage;
387
- const total = (0, usage_js_1.getLifetimeTotal)(u);
367
+ const total = getLifetimeTotal(u);
388
368
  const tokens = total > 0
389
369
  ? { input: u.input, output: u.output, total }
390
370
  : undefined;
@@ -401,7 +381,7 @@ function default_1(pi) {
401
381
  };
402
382
  }
403
383
  // Background completion: route through group join or send individual nudge
404
- const manager = new agent_manager_js_1.AgentManager((record) => {
384
+ const manager = new AgentManager((record) => {
405
385
  // Emit lifecycle event based on terminal status
406
386
  const isError = record.status === "error" || record.status === "stopped" || record.status === "aborted";
407
387
  const eventData = buildEventData(record);
@@ -411,9 +391,7 @@ function default_1(pi) {
411
391
  else {
412
392
  pi.events.emit("subagents:completed", eventData);
413
393
  }
414
- // Persist final record for cross-extension history reconstruction. Keep
415
- // the transcript body in a project-local file rather than inflating the
416
- // parent session JSONL; only its project-relative locator is persisted.
394
+ // Persist final record for cross-extension history reconstruction
417
395
  pi.appendEntry("subagents:record", {
418
396
  id: record.id, type: record.type, description: record.description,
419
397
  status: record.status, result: record.result, error: record.error,
@@ -493,14 +471,14 @@ function default_1(pi) {
493
471
  // Session-scoped: store is constructed inside session_start once sessionId
494
472
  // is available. Mirrors pi-chonky-tasks's session-scoped task store —
495
473
  // schedules reset on /new, restore on /resume.
496
- const scheduler = new schedule_js_1.SubagentScheduler();
474
+ const scheduler = new SubagentScheduler();
497
475
  function startScheduler(ctx) {
498
476
  try {
499
477
  const sessionId = ctx.sessionManager?.getSessionId?.();
500
478
  if (!sessionId)
501
479
  return; // sessionId not yet available — try again on next event
502
- const path = (0, schedule_store_js_1.resolveStorePath)(ctx.cwd, sessionId);
503
- const store = new schedule_store_js_1.ScheduleStore(path);
480
+ const path = resolveStorePath(ctx.cwd, sessionId);
481
+ const store = new ScheduleStore(path);
504
482
  scheduler.start(pi, ctx, manager, store);
505
483
  pi.events.emit("subagents:scheduler_ready", { sessionId, jobCount: store.list().length });
506
484
  }
@@ -515,27 +493,15 @@ function default_1(pi) {
515
493
  // bound session_start, so a filtered-out activation never advertises (#142).
516
494
  pi.on("session_start", async (_event, ctx) => {
517
495
  currentCtx = ctx;
518
- try {
519
- (0, agent_history_js_1.ensureSubagentsGitignore)(ctx.cwd);
520
- }
521
- catch (err) {
522
- console.warn("[pi-subagents] Failed to protect project-local history:", err);
523
- }
524
496
  manager.clearCompleted(true);
525
- const historicalRecords = ctx.sessionManager
526
- .getBranch()
527
- .filter((entry) => entry?.type === "custom" && entry.customType === "subagents:record")
528
- .map((entry) => entry.data);
529
- manager.restoreCompleted(historicalRecords);
530
- // Bind the UI here as well as on tool execution so rehydrated terminal
531
- // records are immediately visible and openable from FleetView after reload.
532
- widget.setUICtx(ctx.ui);
533
- fleet.setUICtx(ctx.ui);
534
- fleet.update();
497
+ const branch = ctx.sessionManager?.getBranch?.() ?? [];
498
+ manager.restoreCompleted(branch
499
+ .filter((entry) => entry?.type === "custom" && entry?.customType === "subagents:record")
500
+ .map((entry) => entry.data));
535
501
  // Guard mirrors the `!scheduler.isActive()` pattern below: session_start
536
502
  // fires once per activation, but a double-bind must not leak listeners.
537
503
  if (!rpcHandle) {
538
- rpcHandle = (0, cross_extension_rpc_js_1.registerRpcHandlers)({
504
+ rpcHandle = registerRpcHandlers({
539
505
  events: pi.events,
540
506
  pi,
541
507
  getCtx: () => currentCtx,
@@ -581,10 +547,10 @@ function default_1(pi) {
581
547
  // everything else; "off" = hide the widget entirely. Read live at render time.
582
548
  let widgetMode = "background";
583
549
  function getWidgetMode() { return widgetMode; }
584
- const widget = new agent_widget_js_1.AgentWidget(manager, agentActivity, getWidgetMode, () => currentCtx?.cwd);
550
+ const widget = new AgentWidget(manager, agentActivity, getWidgetMode);
585
551
  function setWidgetMode(m) { widgetMode = m; widget.update(); }
586
552
  // Claude Code-style FleetView: navigable list of main + subagents below the editor.
587
- const fleet = new fleet_list_js_1.FleetList(manager, agentActivity, () => currentCtx?.cwd);
553
+ const fleet = new FleetList(manager, agentActivity, () => currentCtx?.cwd, pi, () => currentCtx);
588
554
  let fleetViewEnabled = true;
589
555
  function isFleetViewEnabled() { return fleetViewEnabled; }
590
556
  function setFleetViewEnabled(b) { fleetViewEnabled = b; fleet.setEnabled(b); }
@@ -625,7 +591,7 @@ function default_1(pi) {
625
591
  // State lives in agent-types.ts (isDefaultsDisabled) because registerAgents
626
592
  // needs it; this wrapper just re-registers after flipping it.
627
593
  function setDisableDefaultAgents(b) {
628
- (0, agent_types_js_1.setDefaultsDisabled)(b);
594
+ setDefaultsDisabled(b);
629
595
  reloadCustomAgents(); // re-register with new setting
630
596
  }
631
597
  // ---- Agent tool description mode ----
@@ -688,15 +654,15 @@ function default_1(pi) {
688
654
  const tools = cfg?.builtinToolNames;
689
655
  if (!tools || tools.length === 0)
690
656
  return "*";
691
- const isFullSet = tools.length === agent_types_js_1.BUILTIN_TOOL_NAMES.length
692
- && agent_types_js_1.BUILTIN_TOOL_NAMES.every((t) => tools.includes(t));
657
+ const isFullSet = tools.length === BUILTIN_TOOL_NAMES.length
658
+ && BUILTIN_TOOL_NAMES.every((t) => tools.includes(t));
693
659
  return isFullSet ? "*" : tools.join(", ");
694
660
  };
695
661
  /** Build the full type list text dynamically from available agents only. */
696
662
  const buildTypeListText = () => {
697
- const available = (0, agent_types_js_1.getAvailableTypes)();
663
+ const available = getAvailableTypes();
698
664
  return available.map((name) => {
699
- const cfg = (0, agent_types_js_1.getAgentConfig)(name);
665
+ const cfg = getAgentConfig(name);
700
666
  const modelSuffix = cfg?.model ? ` (${getModelLabelFromConfig(cfg.model)})` : "";
701
667
  const toolsSuffix = ` (Tools: ${formatToolsSuffix(cfg)})`;
702
668
  return `- ${name}: ${cfg?.description ?? name}${modelSuffix}${toolsSuffix}`;
@@ -708,8 +674,8 @@ function default_1(pi) {
708
674
  return (match ? match[0] : text).replace(/\s+/g, " ").trim();
709
675
  };
710
676
  /** Compact type list: one line per agent, first sentence only. */
711
- const buildCompactTypeListText = () => (0, agent_types_js_1.getAvailableTypes)().map((name) => {
712
- const cfg = (0, agent_types_js_1.getAgentConfig)(name);
677
+ const buildCompactTypeListText = () => getAvailableTypes().map((name) => {
678
+ const cfg = getAgentConfig(name);
713
679
  return `- ${name}: ${firstSentence(cfg?.description ?? name)} (Tools: ${formatToolsSuffix(cfg)})`;
714
680
  }).join("\n");
715
681
  /** Derive a short model label from a model string. */
@@ -722,10 +688,10 @@ function default_1(pi) {
722
688
  // Apply persisted settings on startup and emit `subagents:settings_loaded`.
723
689
  // Global + project merged; missing → defaults; corrupt file emits a warning
724
690
  // to stderr and falls back to defaults.
725
- (0, settings_js_1.applyAndEmitLoaded)({
691
+ applyAndEmitLoaded({
726
692
  setMaxConcurrent: (n) => manager.setMaxConcurrent(n),
727
- setDefaultMaxTurns: agent_runner_js_1.setDefaultMaxTurns,
728
- setGraceTurns: agent_runner_js_1.setGraceTurns,
693
+ setDefaultMaxTurns,
694
+ setGraceTurns,
729
695
  setDefaultJoinMode,
730
696
  setSchedulingEnabled,
731
697
  setScopeModels: setScopeModelsEnabled,
@@ -742,7 +708,7 @@ function default_1(pi) {
742
708
  // preserves Type.Object's inference when present and produces a
743
709
  // `schedule`-free schema when absent — zero LLM-context cost in disabled mode.
744
710
  const scheduleParamShape = {
745
- schedule: typebox_1.Type.Optional(typebox_1.Type.String({
711
+ schedule: Type.Optional(Type.String({
746
712
  description: 'Opt-in only — fire later instead of now. Omit to run immediately (the default, almost always correct). ' +
747
713
  'Formats: 6-field cron ("0 0 9 * * 1" = 9am Mon), interval ("5m"/"1h"), one-shot ("+10m" or ISO). ' +
748
714
  'Forces run_in_background; incompatible with inherit_context and resume. Returns job ID.',
@@ -758,7 +724,7 @@ function default_1(pi) {
758
724
  const compactAgentToolDescription = `Launch an autonomous agent for complex, multi-step tasks. Agent types:
759
725
  ${buildCompactTypeListText()}
760
726
 
761
- Custom agents: .pi/agents/<name>.md (project) or ${(0, pi_coding_agent_1.getAgentDir)()}/agents/<name>.md (global).
727
+ Custom agents: .pi/agents/<name>.md (project) or ${getAgentDir()}/agents/<name>.md (global).
762
728
 
763
729
  Notes:
764
730
  - description: 3-5 words (shown in UI). Prompts must be self-contained — the agent has not seen this conversation.
@@ -771,7 +737,7 @@ Notes:
771
737
  Available agent types and the tools they have access to:
772
738
  ${buildTypeListText()}
773
739
 
774
- Custom agents can be defined in .pi/agents/<name>.md (project) or ${(0, pi_coding_agent_1.getAgentDir)()}/agents/<name>.md (global) — they are picked up automatically. Project-level agents override global ones. Creating a .md file with the same name as a default agent overrides it.
740
+ Custom agents can be defined in .pi/agents/<name>.md (project) or ${getAgentDir()}/agents/<name>.md (global) — they are picked up automatically. Project-level agents override global ones. Creating a .md file with the same name as a default agent overrides it.
775
741
 
776
742
  When using the Agent tool, specify a subagent_type parameter to select which agent type to use.
777
743
 
@@ -816,7 +782,7 @@ Terse command-style prompts produce shallow, generic work.
816
782
  const vars = {
817
783
  typeList: buildTypeListText,
818
784
  compactTypeList: buildCompactTypeListText,
819
- agentDir: pi_coding_agent_1.getAgentDir,
785
+ agentDir: getAgentDir,
820
786
  scheduleGuideline: () => scheduleGuideline,
821
787
  };
822
788
  // Replacement callback (not a string) — agent descriptions may contain `$&` etc.
@@ -829,13 +795,13 @@ Terse command-style prompts produce shallow, generic work.
829
795
  };
830
796
  const loadCustomToolDescription = () => {
831
797
  for (const path of [
832
- (0, node_path_1.join)(process.cwd(), ".pi", "agent-tool-description.md"),
833
- (0, node_path_1.join)((0, pi_coding_agent_1.getAgentDir)(), "agent-tool-description.md"),
798
+ join(process.cwd(), ".pi", "agent-tool-description.md"),
799
+ join(getAgentDir(), "agent-tool-description.md"),
834
800
  ]) {
835
801
  try {
836
- if (!(0, node_fs_1.existsSync)(path))
802
+ if (!existsSync(path))
837
803
  continue;
838
- const text = (0, node_fs_1.readFileSync)(path, "utf-8").trim();
804
+ const text = readFileSync(path, "utf-8").trim();
839
805
  if (text)
840
806
  return renderToolDescriptionTemplate(text);
841
807
  console.warn(`[pi-subagents] ${path} is empty — ignoring`);
@@ -858,8 +824,8 @@ Terse command-style prompts produce shallow, generic work.
858
824
  }
859
825
  return fullAgentToolDescription;
860
826
  })();
861
- pi.registerTool((0, pi_coding_agent_1.defineTool)({
862
- name: agent_runner_js_1.SUBAGENT_TOOL_NAMES.AGENT,
827
+ pi.registerTool(defineTool({
828
+ name: SUBAGENT_TOOL_NAMES.AGENT,
863
829
  label: "Agent",
864
830
  description: agentToolDescription,
865
831
  promptSnippet: "Launch autonomous sub-agents for complex multi-step tasks",
@@ -869,54 +835,54 @@ Terse command-style prompts produce shallow, generic work.
869
835
  "When an agent runs in the background, you will be notified on completion — do not poll or sleep waiting for it. Continue with other work instead.",
870
836
  "Trust but verify: an agent's summary describes intent, not outcome. When an agent writes or edits code, check the actual changes before reporting work as done.",
871
837
  ],
872
- parameters: typebox_1.Type.Object({
873
- prompt: typebox_1.Type.String({
838
+ parameters: Type.Object({
839
+ prompt: Type.String({
874
840
  description: "The task for the agent to perform.",
875
841
  }),
876
- description: typebox_1.Type.String({
842
+ description: Type.String({
877
843
  description: "A short (3-5 word) description of the task (shown in UI).",
878
844
  }),
879
- subagent_type: typebox_1.Type.String({
880
- description: `The type of specialized agent to use. Available types: ${(0, agent_types_js_1.getAvailableTypes)().join(", ")}. Custom agents from .pi/agents/*.md (project) or ${(0, pi_coding_agent_1.getAgentDir)()}/agents/*.md (global) are also available.`,
845
+ subagent_type: Type.String({
846
+ description: `The type of specialized agent to use. Available types: ${getAvailableTypes().join(", ")}. Custom agents from .pi/agents/*.md (project) or ${getAgentDir()}/agents/*.md (global) are also available.`,
881
847
  }),
882
- model: typebox_1.Type.Optional(typebox_1.Type.String({
848
+ model: Type.Optional(Type.String({
883
849
  description: 'Optional model override. Accepts "provider/modelId" or fuzzy name (e.g. "haiku", "sonnet"). Omit to use the agent type\'s default.',
884
850
  })),
885
- thinking: typebox_1.Type.Optional(typebox_1.Type.String({
851
+ thinking: Type.Optional(Type.String({
886
852
  description: `Thinking level: ${THINKING_LEVELS.join(", ")}. Overrides agent default.`,
887
853
  })),
888
- max_turns: typebox_1.Type.Optional(typebox_1.Type.Number({
854
+ max_turns: Type.Optional(Type.Number({
889
855
  description: "Maximum number of agentic turns before stopping. Omit for unlimited (default).",
890
856
  minimum: 1,
891
857
  })),
892
- run_in_background: typebox_1.Type.Optional(typebox_1.Type.Boolean({
858
+ run_in_background: Type.Optional(Type.Boolean({
893
859
  description: "Set to true to run in background. Returns agent ID immediately. You will be notified on completion.",
894
860
  })),
895
- resume: typebox_1.Type.Optional(typebox_1.Type.String({
861
+ resume: Type.Optional(Type.String({
896
862
  description: "Optional agent ID to resume from. Continues from previous context.",
897
863
  })),
898
- isolated: typebox_1.Type.Optional(typebox_1.Type.Boolean({
864
+ isolated: Type.Optional(Type.Boolean({
899
865
  description: "If true, agent gets no extension/MCP tools — only built-in tools.",
900
866
  })),
901
- inherit_context: typebox_1.Type.Optional(typebox_1.Type.Boolean({
867
+ inherit_context: Type.Optional(Type.Boolean({
902
868
  description: "If true, fork parent conversation into the agent. Default: false (fresh context).",
903
869
  })),
904
- isolation: typebox_1.Type.Optional(typebox_1.Type.Literal("worktree", {
870
+ isolation: Type.Optional(Type.Literal("worktree", {
905
871
  description: 'Set to "worktree" to run the agent in a temporary git worktree (isolated copy of the repo). Changes are saved to a branch on completion.',
906
872
  })),
907
873
  ...scheduleParam,
908
874
  }),
909
875
  // ---- Custom rendering: Claude Code style ----
910
876
  renderCall(args, theme) {
911
- const displayName = args.subagent_type ? (0, agent_widget_js_1.getDisplayName)(args.subagent_type) : "Agent";
877
+ const displayName = args.subagent_type ? getDisplayName(args.subagent_type) : "Agent";
912
878
  const desc = args.description ?? "";
913
- return new pi_tui_1.Text("▸ " + theme.fg("toolTitle", theme.bold(displayName)) + (desc ? " " + theme.fg("muted", desc) : ""), 0, 0);
879
+ return new Text("▸ " + theme.fg("toolTitle", theme.bold(displayName)) + (desc ? " " + theme.fg("muted", desc) : ""), 0, 0);
914
880
  },
915
881
  renderResult(result, { expanded, isPartial }, theme) {
916
882
  const details = result.details;
917
883
  if (!details) {
918
884
  const text = result.content[0]?.type === "text" ? result.content[0].text : "";
919
- return new pi_tui_1.Text(text, 0, 0);
885
+ return new Text(text, 0, 0);
920
886
  }
921
887
  // Helper: build "haiku · thinking: high · ↻5≤30 · 3 tool uses · 33.8k tokens" stats string
922
888
  const stats = (d) => {
@@ -926,27 +892,27 @@ Terse command-style prompts produce shallow, generic work.
926
892
  if (d.tags)
927
893
  parts.push(...d.tags);
928
894
  if (d.turnCount != null && d.turnCount > 0) {
929
- parts.push((0, agent_widget_js_1.formatTurns)(d.turnCount, d.maxTurns));
895
+ parts.push(formatTurns(d.turnCount, d.maxTurns));
930
896
  }
931
897
  if (d.toolUses > 0)
932
898
  parts.push(`${d.toolUses} tool use${d.toolUses === 1 ? "" : "s"}`);
933
899
  if (d.tokens)
934
900
  parts.push(d.tokens);
935
- return parts.map(p => (0, agent_widget_js_1.fgPreservingNestedStyles)(theme, "dim", p)).join(" " + theme.fg("dim", "·") + " ");
901
+ return parts.map(p => fgPreservingNestedStyles(theme, "dim", p)).join(" " + theme.fg("dim", "·") + " ");
936
902
  };
937
903
  // ---- While running (streaming) ----
938
904
  if (isPartial || details.status === "running") {
939
- const frame = agent_widget_js_1.SPINNER[details.spinnerFrame ?? 0];
905
+ const frame = SPINNER[details.spinnerFrame ?? 0];
940
906
  const s = stats(details);
941
907
  return renderRunningAgentStatus(frame, s, details.activity ?? "thinking…", theme);
942
908
  }
943
909
  // ---- Background agent launched ----
944
910
  if (details.status === "background") {
945
- return new pi_tui_1.Text(theme.fg("dim", ` ⎿ Running in background (ID: ${details.agentId})`), 0, 0);
911
+ return new Text(theme.fg("dim", ` ⎿ Running in background (ID: ${details.agentId})`), 0, 0);
946
912
  }
947
913
  // ---- Completed / Steered ----
948
914
  if (details.status === "completed" || details.status === "steered") {
949
- const duration = (0, agent_widget_js_1.formatMs)(details.durationMs);
915
+ const duration = formatMs(details.durationMs);
950
916
  const isSteered = details.status === "steered";
951
917
  const icon = isSteered ? theme.fg("warning", "✓") : theme.fg("success", "✓");
952
918
  const s = stats(details);
@@ -954,28 +920,28 @@ Terse command-style prompts produce shallow, generic work.
954
920
  line += " " + theme.fg("dim", "·") + " " + theme.fg("dim", duration);
955
921
  if (expanded) {
956
922
  const resultText = result.content[0]?.type === "text" ? result.content[0].text : "";
957
- const container = new pi_tui_1.Container();
958
- container.addChild(new pi_tui_1.Text(line, 0, 0));
959
923
  if (resultText) {
960
- container.addChild((0, markdown_result_js_1.createMarkdownResult)(resultText, theme, 50));
924
+ const lines = resultText.split("\n").slice(0, 50);
925
+ for (const l of lines) {
926
+ line += "\n" + theme.fg("dim", ` ${l}`);
927
+ }
961
928
  if (resultText.split("\n").length > 50) {
962
- container.addChild(new pi_tui_1.Text(theme.fg("muted", " ... (use get_subagent_result with verbose for full output)"), 0, 0));
929
+ line += "\n" + theme.fg("muted", " ... (use get_subagent_result with verbose for full output)");
963
930
  }
964
931
  }
965
- return container;
966
932
  }
967
933
  else {
968
934
  const doneText = isSteered ? "Wrapped up (turn limit)" : "Done";
969
935
  line += "\n" + theme.fg("dim", ` ⎿ ${doneText}`);
970
936
  }
971
- return new pi_tui_1.Text(line, 0, 0);
937
+ return new Text(line, 0, 0);
972
938
  }
973
939
  // ---- Stopped (user-initiated abort) ----
974
940
  if (details.status === "stopped") {
975
941
  const s = stats(details);
976
942
  let line = theme.fg("dim", "■") + (s ? " " + s : "");
977
943
  line += "\n" + theme.fg("dim", " ⎿ Stopped");
978
- return new pi_tui_1.Text(line, 0, 0);
944
+ return new Text(line, 0, 0);
979
945
  }
980
946
  // ---- Error / Aborted (hard max_turns) ----
981
947
  const s = stats(details);
@@ -986,7 +952,7 @@ Terse command-style prompts produce shallow, generic work.
986
952
  else {
987
953
  line += "\n" + theme.fg("warning", " ⎿ Aborted (max turns exceeded)");
988
954
  }
989
- return new pi_tui_1.Text(line, 0, 0);
955
+ return new Text(line, 0, 0);
990
956
  },
991
957
  // ---- Execute ----
992
958
  execute: async (toolCallId, params, signal, onUpdate, ctx) => {
@@ -995,17 +961,17 @@ Terse command-style prompts produce shallow, generic work.
995
961
  // Reload custom agents so new project/global .md files are picked up without restart
996
962
  reloadCustomAgents();
997
963
  const rawType = params.subagent_type;
998
- const resolved = (0, agent_types_js_1.resolveType)(rawType);
964
+ const resolved = resolveType(rawType);
999
965
  const subagentType = resolved ?? "general-purpose";
1000
966
  const fellBack = resolved === undefined;
1001
- const displayName = (0, agent_widget_js_1.getDisplayName)(subagentType);
967
+ const displayName = getDisplayName(subagentType);
1002
968
  // Get agent config (if any)
1003
- const customConfig = (0, agent_types_js_1.getAgentConfig)(subagentType);
1004
- const resolvedConfig = (0, invocation_config_js_1.resolveAgentInvocationConfig)(customConfig, params);
969
+ const customConfig = getAgentConfig(subagentType);
970
+ const resolvedConfig = resolveAgentInvocationConfig(customConfig, params);
1005
971
  // Resolve model from agent config first; tool-call params only fill gaps.
1006
972
  let model = ctx.model;
1007
973
  if (resolvedConfig.modelInput) {
1008
- const resolved = (0, model_resolver_js_1.resolveModel)(resolvedConfig.modelInput, ctx.modelRegistry);
974
+ const resolved = resolveModel(resolvedConfig.modelInput, ctx.modelRegistry);
1009
975
  if (typeof resolved === "string") {
1010
976
  if (resolvedConfig.modelFromParams)
1011
977
  return textResult(resolved);
@@ -1025,8 +991,8 @@ Terse command-style prompts produce shallow, generic work.
1025
991
  // user authored/installed this agent or chose the parent's model; trust it).
1026
992
  // See SubagentsSettings.scopeModels docstring for the full policy.
1027
993
  if (isScopeModelsEnabled() && model) {
1028
- const allowed = (0, enabled_models_js_1.resolveEnabledModels)((0, enabled_models_js_1.readEnabledModels)(ctx.cwd), ctx.modelRegistry, ctx.cwd);
1029
- if (allowed && !(0, enabled_models_js_1.isModelInScope)(model, allowed)) {
994
+ const allowed = resolveEnabledModels(readEnabledModels(ctx.cwd), ctx.modelRegistry, ctx.cwd);
995
+ if (allowed && !isModelInScope(model, allowed)) {
1030
996
  if (resolvedConfig.modelFromParams) {
1031
997
  const list = [...allowed].sort().map(m => ` ${m}`).join("\n");
1032
998
  return textResult(`Model not in scope: "${resolvedConfig.modelInput}".\n\n` +
@@ -1052,20 +1018,17 @@ Terse command-style prompts produce shallow, generic work.
1052
1018
  const attachTranscript = (rec, agentId) => {
1053
1019
  if (!rec || !outputTranscript)
1054
1020
  return;
1055
- rec.outputFile = (0, output_file_js_1.createOutputFilePath)(ctx.cwd, agentId, ctx.sessionManager.getSessionId());
1056
- (0, output_file_js_1.writeInitialEntry)(rec.outputFile, agentId, params.prompt, ctx.cwd);
1057
- // Keep the existing /tmp output for compatibility, while also writing a
1058
- // durable project-local copy for reload-safe, read-only history viewing.
1021
+ rec.outputFile = createOutputFilePath(ctx.cwd, agentId, ctx.sessionManager.getSessionId());
1022
+ writeInitialEntry(rec.outputFile, agentId, params.prompt, ctx.cwd);
1059
1023
  try {
1060
- rec.historyFile = (0, agent_history_js_1.createAgentHistoryPath)(ctx.cwd, agentId, subagentType);
1061
- rec.transcriptPath = (0, agent_history_js_1.agentHistoryLocator)(ctx.cwd, rec.historyFile);
1062
- (0, output_file_js_1.writeInitialEntry)(rec.historyFile, agentId, params.prompt, ctx.cwd);
1024
+ rec.historyFile = createAgentHistoryPath(ctx.cwd, agentId);
1025
+ rec.transcriptPath = agentHistoryLocator(ctx.cwd, rec.historyFile);
1026
+ writeInitialEntry(rec.historyFile, agentId, params.prompt, ctx.cwd);
1063
1027
  }
1064
1028
  catch (err) {
1065
- // History is best effort; never disable the existing output transcript.
1066
1029
  rec.historyFile = undefined;
1067
1030
  rec.transcriptPath = undefined;
1068
- console.warn("[pi-subagents] Failed to create durable transcript:", err);
1031
+ ctx.ui.notify(`Could not create durable transcript for agent ${agentId}: ${err instanceof Error ? err.message : String(err)}`, "warning");
1069
1032
  }
1070
1033
  };
1071
1034
  const parentModelId = ctx.model?.id;
@@ -1073,21 +1036,21 @@ Terse command-style prompts produce shallow, generic work.
1073
1036
  const modelName = effectiveModelId && effectiveModelId !== parentModelId
1074
1037
  ? (model?.name ?? effectiveModelId).replace(/^Claude\s+/i, "").toLowerCase()
1075
1038
  : undefined;
1076
- const effectiveMaxTurns = (0, agent_runner_js_1.normalizeMaxTurns)(resolvedConfig.maxTurns ?? (0, agent_runner_js_1.getDefaultMaxTurns)());
1039
+ const effectiveMaxTurns = normalizeMaxTurns(resolvedConfig.maxTurns ?? getDefaultMaxTurns());
1077
1040
  const agentInvocation = {
1078
1041
  modelName,
1079
1042
  thinking,
1080
1043
  // Explicit value only — the default fallback would just add noise.
1081
1044
  // Normalize so `0` (unlimited) doesn't surface as a misleading "max turns: 0".
1082
- maxTurns: (0, agent_runner_js_1.normalizeMaxTurns)(resolvedConfig.maxTurns),
1045
+ maxTurns: normalizeMaxTurns(resolvedConfig.maxTurns),
1083
1046
  isolated,
1084
1047
  inheritContext,
1085
1048
  runInBackground,
1086
1049
  isolation,
1087
1050
  };
1088
1051
  // Tool-result render shows the mode label too; viewer's header already does.
1089
- const modeLabel = (0, agent_widget_js_1.getPromptModeLabel)(subagentType);
1090
- const { tags: invocationTags } = (0, agent_widget_js_1.buildInvocationTags)(agentInvocation);
1052
+ const modeLabel = getPromptModeLabel(subagentType);
1053
+ const { tags: invocationTags } = buildInvocationTags(agentInvocation);
1091
1054
  const agentTags = modeLabel ? [modeLabel, ...invocationTags] : invocationTags;
1092
1055
  const detailBase = {
1093
1056
  displayName,
@@ -1167,7 +1130,7 @@ Terse command-style prompts produce shallow, generic work.
1167
1130
  origBgOnSession(session);
1168
1131
  const rec = manager.getRecord(id);
1169
1132
  if (rec?.outputFile) {
1170
- rec.outputCleanup = (0, output_file_js_1.streamToOutputFile)(session, rec.outputFile, id, ctx.cwd, rec.historyFile);
1133
+ rec.outputCleanup = streamToOutputFile(session, rec.outputFile, id, ctx.cwd, rec.historyFile);
1171
1134
  }
1172
1135
  };
1173
1136
  try {
@@ -1189,7 +1152,7 @@ Terse command-style prompts produce shallow, generic work.
1189
1152
  }
1190
1153
  // Set output file + join mode synchronously after spawn, before the
1191
1154
  // event loop yields — onSessionCreated is async so this is safe.
1192
- const joinMode = (0, invocation_config_js_1.resolveJoinMode)(defaultJoinMode, true);
1155
+ const joinMode = resolveJoinMode(defaultJoinMode, true);
1193
1156
  const record = manager.getRecord(id);
1194
1157
  if (record && joinMode) {
1195
1158
  record.joinMode = joinMode;
@@ -1244,8 +1207,8 @@ Terse command-style prompts produce shallow, generic work.
1244
1207
  maxTurns: fgState.maxTurns,
1245
1208
  durationMs: Date.now() - startedAt,
1246
1209
  status: "running",
1247
- activity: (0, agent_widget_js_1.describeActivity)(fgState.activeTools, fgState.responseText),
1248
- spinnerFrame: spinnerFrame % agent_widget_js_1.SPINNER.length,
1210
+ activity: describeActivity(fgState.activeTools, fgState.responseText),
1211
+ spinnerFrame: spinnerFrame % SPINNER.length,
1249
1212
  };
1250
1213
  onUpdate?.({
1251
1214
  content: [{ type: "text", text: `${fgState.toolUses} tool uses...` }],
@@ -1273,7 +1236,7 @@ Terse command-style prompts produce shallow, generic work.
1273
1236
  if (fgId) {
1274
1237
  const rec = manager.getRecord(fgId);
1275
1238
  if (rec?.outputFile) {
1276
- rec.outputCleanup = (0, output_file_js_1.streamToOutputFile)(session, rec.outputFile, fgId, ctx.cwd, rec.historyFile);
1239
+ rec.outputCleanup = streamToOutputFile(session, rec.outputFile, fgId, ctx.cwd, rec.historyFile);
1277
1240
  }
1278
1241
  }
1279
1242
  };
@@ -1321,7 +1284,7 @@ Terse command-style prompts produce shallow, generic work.
1321
1284
  // "general-purpose" may itself be unregistered (defaults disabled, no
1322
1285
  // user override) — getConfig then uses the hardcoded fallback config.
1323
1286
  const fallbackNote = fellBack
1324
- ? `Note: Unknown agent type "${rawType}" — using ${(0, agent_types_js_1.resolveType)("general-purpose") ? "general-purpose" : "the fallback agent config"}.\n\n`
1287
+ ? `Note: Unknown agent type "${rawType}" — using ${resolveType("general-purpose") ? "general-purpose" : "the fallback agent config"}.\n\n`
1325
1288
  : "";
1326
1289
  if (record.status === "error") {
1327
1290
  // Error headline + any partial output the run produced before failing.
@@ -1331,24 +1294,24 @@ Terse command-style prompts produce shallow, generic work.
1331
1294
  const statsParts = [`${record.toolUses} tool uses`];
1332
1295
  if (tokenText)
1333
1296
  statsParts.push(tokenText);
1334
- return textResult(`${fallbackNote}Agent completed in ${(0, agent_widget_js_1.formatMs)(durationMs)} (${statsParts.join(", ")})${(0, status_note_js_1.getStatusNote)(record.status)}.\n\n` +
1297
+ return textResult(`${fallbackNote}Agent completed in ${formatMs(durationMs)} (${statsParts.join(", ")})${getStatusNote(record.status)}.\n\n` +
1335
1298
  (record.result?.trim() || "No output."), details);
1336
1299
  },
1337
1300
  }));
1338
1301
  // ---- get_subagent_result tool ----
1339
- pi.registerTool((0, pi_coding_agent_1.defineTool)({
1340
- name: agent_runner_js_1.SUBAGENT_TOOL_NAMES.GET_RESULT,
1302
+ pi.registerTool(defineTool({
1303
+ name: SUBAGENT_TOOL_NAMES.GET_RESULT,
1341
1304
  label: "Get Agent Result",
1342
1305
  description: "Check status and retrieve results from a background agent. Use the agent ID returned by Agent with run_in_background.",
1343
1306
  promptSnippet: "Check status and retrieve results from a background agent",
1344
- parameters: typebox_1.Type.Object({
1345
- agent_id: typebox_1.Type.String({
1307
+ parameters: Type.Object({
1308
+ agent_id: Type.String({
1346
1309
  description: "The agent ID to check.",
1347
1310
  }),
1348
- wait: typebox_1.Type.Optional(typebox_1.Type.Boolean({
1311
+ wait: Type.Optional(Type.Boolean({
1349
1312
  description: "If true, wait for the agent to complete before returning. Default: false.",
1350
1313
  })),
1351
- verbose: typebox_1.Type.Optional(typebox_1.Type.Boolean({
1314
+ verbose: Type.Optional(Type.Boolean({
1352
1315
  description: "If true, include the agent's full conversation (messages + tool calls). Default: false.",
1353
1316
  })),
1354
1317
  }),
@@ -1369,10 +1332,10 @@ Terse command-style prompts produce shallow, generic work.
1369
1332
  if (record.promise)
1370
1333
  await abortable(record.promise, signal);
1371
1334
  }
1372
- const displayName = (0, agent_widget_js_1.getDisplayName)(record.type);
1373
- const duration = (0, agent_widget_js_1.formatDuration)(record.startedAt, record.completedAt);
1335
+ const displayName = getDisplayName(record.type);
1336
+ const duration = formatDuration(record.startedAt, record.completedAt);
1374
1337
  const tokens = formatLifetimeTokens(record);
1375
- const contextPercent = (0, usage_js_1.getSessionContextPercent)(record.session);
1338
+ const contextPercent = getSessionContextPercent(record.session);
1376
1339
  const statsParts = [`Tool uses: ${record.toolUses}`];
1377
1340
  if (tokens)
1378
1341
  statsParts.push(tokens);
@@ -1382,7 +1345,7 @@ Terse command-style prompts produce shallow, generic work.
1382
1345
  statsParts.push(`Compactions: ${record.compactionCount}`);
1383
1346
  statsParts.push(`Duration: ${duration}`);
1384
1347
  let output = `Agent: ${record.id}\n` +
1385
- `Type: ${displayName} | Status: ${record.status}${(0, status_note_js_1.getStatusNote)(record.status)} | ${statsParts.join(" | ")}\n` +
1348
+ `Type: ${displayName} | Status: ${record.status}${getStatusNote(record.status)} | ${statsParts.join(" | ")}\n` +
1386
1349
  `Description: ${record.description}\n\n`;
1387
1350
  if (record.status === "running") {
1388
1351
  output += "Agent is still running. Use wait: true or check back later.";
@@ -1400,7 +1363,7 @@ Terse command-style prompts produce shallow, generic work.
1400
1363
  }
1401
1364
  // Verbose: include full conversation
1402
1365
  if (params.verbose && record.session) {
1403
- const conversation = (0, agent_runner_js_1.getAgentConversation)(record.session);
1366
+ const conversation = getAgentConversation(record.session);
1404
1367
  if (conversation) {
1405
1368
  output += `\n\n--- Agent Conversation ---\n${conversation}`;
1406
1369
  }
@@ -1409,17 +1372,17 @@ Terse command-style prompts produce shallow, generic work.
1409
1372
  },
1410
1373
  }));
1411
1374
  // ---- steer_subagent tool ----
1412
- pi.registerTool((0, pi_coding_agent_1.defineTool)({
1413
- name: agent_runner_js_1.SUBAGENT_TOOL_NAMES.STEER,
1375
+ pi.registerTool(defineTool({
1376
+ name: SUBAGENT_TOOL_NAMES.STEER,
1414
1377
  label: "Steer Agent",
1415
1378
  description: "Send a steering message to a running agent. The message will interrupt the agent after its current tool execution " +
1416
1379
  "and be injected into its conversation, allowing you to redirect its work mid-run. Only works on running agents.",
1417
1380
  promptSnippet: "Send a steering message to redirect a running background agent",
1418
- parameters: typebox_1.Type.Object({
1419
- agent_id: typebox_1.Type.String({
1381
+ parameters: Type.Object({
1382
+ agent_id: Type.String({
1420
1383
  description: "The agent ID to steer (must be currently running).",
1421
1384
  }),
1422
- message: typebox_1.Type.String({
1385
+ message: Type.String({
1423
1386
  description: "The steering message to send. This will appear as a user message in the agent's conversation.",
1424
1387
  }),
1425
1388
  }),
@@ -1440,10 +1403,10 @@ Terse command-style prompts produce shallow, generic work.
1440
1403
  return textResult(`Steering message queued for agent ${record.id}. It will be delivered once the session initializes.`);
1441
1404
  }
1442
1405
  try {
1443
- await (0, agent_runner_js_1.steerAgent)(record.session, params.message);
1406
+ await steerAgent(record.session, params.message);
1444
1407
  pi.events.emit("subagents:steered", { id: record.id, message: params.message });
1445
1408
  const tokens = formatLifetimeTokens(record);
1446
- const contextPercent = (0, usage_js_1.getSessionContextPercent)(record.session);
1409
+ const contextPercent = getSessionContextPercent(record.session);
1447
1410
  const stateParts = [];
1448
1411
  if (tokens)
1449
1412
  stateParts.push(tokens);
@@ -1461,30 +1424,30 @@ Terse command-style prompts produce shallow, generic work.
1461
1424
  },
1462
1425
  }));
1463
1426
  // ---- /agents interactive menu ----
1464
- const projectAgentsDir = () => (0, node_path_1.join)(process.cwd(), ".pi", "agents");
1465
- const workspaceAgentsDir = () => (0, node_path_1.join)(process.cwd(), ".agents", "agents");
1466
- const personalAgentsDir = () => (0, node_path_1.join)((0, pi_coding_agent_1.getAgentDir)(), "agents");
1427
+ const projectAgentsDir = () => join(process.cwd(), ".pi", "agents");
1428
+ const workspaceAgentsDir = () => join(process.cwd(), ".agents", "agents");
1429
+ const personalAgentsDir = () => join(getAgentDir(), "agents");
1467
1430
  /** Find the file path of a custom agent by name, in discovery-precedence order (project, workspace, then global). */
1468
1431
  function findAgentFile(name) {
1469
- const projectPath = (0, node_path_1.join)(projectAgentsDir(), `${name}.md`);
1470
- if ((0, node_fs_1.existsSync)(projectPath))
1432
+ const projectPath = join(projectAgentsDir(), `${name}.md`);
1433
+ if (existsSync(projectPath))
1471
1434
  return { path: projectPath, location: "project" };
1472
- const workspacePath = (0, node_path_1.join)(workspaceAgentsDir(), `${name}.md`);
1473
- if ((0, node_fs_1.existsSync)(workspacePath))
1435
+ const workspacePath = join(workspaceAgentsDir(), `${name}.md`);
1436
+ if (existsSync(workspacePath))
1474
1437
  return { path: workspacePath, location: "workspace" };
1475
- const personalPath = (0, node_path_1.join)(personalAgentsDir(), `${name}.md`);
1476
- if ((0, node_fs_1.existsSync)(personalPath))
1438
+ const personalPath = join(personalAgentsDir(), `${name}.md`);
1439
+ if (existsSync(personalPath))
1477
1440
  return { path: personalPath, location: "personal" };
1478
1441
  return undefined;
1479
1442
  }
1480
1443
  function getModelLabel(type, registry) {
1481
- const cfg = (0, agent_types_js_1.getAgentConfig)(type);
1444
+ const cfg = getAgentConfig(type);
1482
1445
  if (!cfg?.model)
1483
1446
  return "inherit"; // no model configured → really inherits parent
1484
1447
  const label = getModelLabelFromConfig(cfg.model);
1485
1448
  if (!registry)
1486
1449
  return label;
1487
- const resolved = (0, model_resolver_js_1.resolveModel)(cfg.model, registry);
1450
+ const resolved = resolveModel(cfg.model, registry);
1488
1451
  // Configured but unresolvable: the runtime silently falls back to the parent
1489
1452
  // model, so flag it (and the fallback) rather than hiding the config.
1490
1453
  if (typeof resolved === "string")
@@ -1500,18 +1463,13 @@ Terse command-style prompts produce shallow, generic work.
1500
1463
  }
1501
1464
  async function showAgentsMenu(ctx) {
1502
1465
  reloadCustomAgents();
1503
- const allNames = (0, agent_types_js_1.getAllTypes)();
1466
+ const allNames = getAllTypes();
1504
1467
  // Build select options
1505
1468
  const options = [];
1506
- // Only advertise agents that can actually be opened. Reload-restored
1507
- // metadata without a live session or durable transcript is intentionally
1508
- // invisible here, matching FleetView and showRunningAgents below.
1509
- const agents = manager.listAgents().filter((record) => record.session !== undefined || (0, agent_history_js_1.hasAgentHistory)(ctx.cwd, record.transcriptPath));
1510
- if (agents.length > 0) {
1511
- const running = agents.filter(a => a.status === "running" || a.status === "queued").length;
1512
- const done = agents.filter(a => a.status === "completed" || a.status === "steered").length;
1513
- options.push(`Running agents (${agents.length}) — ${running} running, ${done} done`);
1514
- }
1469
+ // Keep active agents and terminal history in separate menu entries.
1470
+ const records = manager.listAgents();
1471
+ const { active, history } = splitAgentRecords(records, ctx.cwd);
1472
+ options.push(...buildAgentStatusMenuEntries(records, ctx.cwd));
1515
1473
  // Agent types list
1516
1474
  if (allNames.length > 0) {
1517
1475
  options.push(`Agent types (${allNames.length})`);
@@ -1524,7 +1482,7 @@ Terse command-style prompts produce shallow, generic work.
1524
1482
  // Actions
1525
1483
  options.push("Create new agent");
1526
1484
  options.push("Settings");
1527
- const noAgentsMsg = allNames.length === 0 && agents.length === 0
1485
+ const noAgentsMsg = allNames.length === 0 && active.length === 0 && history.length === 0
1528
1486
  ? "No agents found. Create specialized subagents that can be delegated to.\n\n" +
1529
1487
  "Each subagent has its own context window, custom system prompt, and specific tools.\n\n" +
1530
1488
  "Try creating: Code Reviewer, Security Auditor, Test Writer, or Documentation Writer.\n\n"
@@ -1539,12 +1497,16 @@ Terse command-style prompts produce shallow, generic work.
1539
1497
  await showRunningAgents(ctx);
1540
1498
  await showAgentsMenu(ctx);
1541
1499
  }
1500
+ else if (choice.startsWith("Agent history (")) {
1501
+ await showAgentHistory(ctx);
1502
+ await showAgentsMenu(ctx);
1503
+ }
1542
1504
  else if (choice.startsWith("Agent types (")) {
1543
1505
  await showAllAgentsList(ctx);
1544
1506
  await showAgentsMenu(ctx);
1545
1507
  }
1546
1508
  else if (choice.startsWith("Scheduled jobs (")) {
1547
- await (0, schedule_menu_js_1.showSchedulesMenu)(ctx, scheduler);
1509
+ await showSchedulesMenu(ctx, scheduler);
1548
1510
  await showAgentsMenu(ctx);
1549
1511
  }
1550
1512
  else if (choice === "Create new agent") {
@@ -1556,7 +1518,7 @@ Terse command-style prompts produce shallow, generic work.
1556
1518
  }
1557
1519
  }
1558
1520
  async function showAllAgentsList(ctx) {
1559
- const allNames = (0, agent_types_js_1.getAllTypes)();
1521
+ const allNames = getAllTypes();
1560
1522
  if (allNames.length === 0) {
1561
1523
  ctx.ui.notify("No agents.", "info");
1562
1524
  return;
@@ -1577,7 +1539,7 @@ Terse command-style prompts produce shallow, generic work.
1577
1539
  // full description renders below the highlighted row via SettingsList,
1578
1540
  // exactly like the Settings menu — so long descriptions never wrap the list.
1579
1541
  const items = allNames.map(name => {
1580
- const cfg = (0, agent_types_js_1.getAgentConfig)(name);
1542
+ const cfg = getAgentConfig(name);
1581
1543
  const disabled = cfg?.enabled === false;
1582
1544
  const model = getModelLabel(name, ctx.modelRegistry);
1583
1545
  return {
@@ -1590,22 +1552,22 @@ Terse command-style prompts produce shallow, generic work.
1590
1552
  values: [model],
1591
1553
  };
1592
1554
  });
1593
- const hasCustom = allNames.some(n => { const c = (0, agent_types_js_1.getAgentConfig)(n); return c && !c.isDefault && c.enabled !== false; });
1594
- const hasDisabled = allNames.some(n => (0, agent_types_js_1.getAgentConfig)(n)?.enabled === false);
1555
+ const hasCustom = allNames.some(n => { const c = getAgentConfig(n); return c && !c.isDefault && c.enabled !== false; });
1556
+ const hasDisabled = allNames.some(n => getAgentConfig(n)?.enabled === false);
1595
1557
  const legendParts = [];
1596
1558
  if (hasCustom)
1597
1559
  legendParts.push("• = project ◦ = global");
1598
1560
  if (hasDisabled)
1599
1561
  legendParts.push("✕ = disabled");
1600
1562
  const selected = await ctx.ui.custom((_tui, _theme, _kb, done) => {
1601
- const slTheme = (0, pi_coding_agent_1.getSettingsListTheme)();
1602
- const list = new pi_tui_1.SettingsList(items, Math.min(items.length, 12), slTheme, id => done(id), // Enter/Space on a row → return that agent's name
1563
+ const slTheme = getSettingsListTheme();
1564
+ const list = new SettingsList(items, Math.min(items.length, 12), slTheme, id => done(id), // Enter/Space on a row → return that agent's name
1603
1565
  () => done(undefined));
1604
- const container = new pi_tui_1.Container();
1605
- container.addChild(new pi_tui_1.Text("Agent types", 0, 0));
1566
+ const container = new Container();
1567
+ container.addChild(new Text("Agent types", 0, 0));
1606
1568
  if (legendParts.length)
1607
- container.addChild(new pi_tui_1.Text(slTheme.hint(legendParts.join(" ")), 0, 0));
1608
- container.addChild(new pi_tui_1.Spacer(1));
1569
+ container.addChild(new Text(slTheme.hint(legendParts.join(" ")), 0, 0));
1570
+ container.addChild(new Spacer(1));
1609
1571
  container.addChild(list);
1610
1572
  return {
1611
1573
  render: (w) => container.render(w),
@@ -1613,20 +1575,20 @@ Terse command-style prompts produce shallow, generic work.
1613
1575
  handleInput: (data) => list.handleInput?.(data),
1614
1576
  };
1615
1577
  });
1616
- if (selected && (0, agent_types_js_1.getAgentConfig)(selected)) {
1578
+ if (selected && getAgentConfig(selected)) {
1617
1579
  await showAgentDetail(ctx, selected);
1618
1580
  await showAllAgentsList(ctx);
1619
1581
  }
1620
1582
  }
1621
1583
  async function showRunningAgents(ctx) {
1622
- const agents = manager.listAgents().filter((record) => record.session !== undefined || (0, agent_history_js_1.hasAgentHistory)(ctx.cwd, record.transcriptPath));
1584
+ const { active: agents } = splitAgentRecords(manager.listAgents(), ctx.cwd);
1623
1585
  if (agents.length === 0) {
1624
1586
  ctx.ui.notify("No agents.", "info");
1625
1587
  return;
1626
1588
  }
1627
1589
  const options = agents.map(a => {
1628
- const dn = (0, agent_widget_js_1.getDisplayName)(a.type);
1629
- const dur = (0, agent_widget_js_1.formatDuration)(a.startedAt, a.completedAt);
1590
+ const dn = getDisplayName(a.type);
1591
+ const dur = formatDuration(a.startedAt, a.completedAt);
1630
1592
  return `${dn} (${a.description}) · ${a.toolUses} tools · ${a.status} · ${dur}`;
1631
1593
  });
1632
1594
  const choice = await ctx.ui.select("Running agents", options);
@@ -1637,41 +1599,66 @@ Terse command-style prompts produce shallow, generic work.
1637
1599
  if (idx < 0)
1638
1600
  return;
1639
1601
  const record = agents[idx];
1640
- await viewAgentConversation(ctx, record);
1602
+ await viewAgentConversation(ctx, record, "live");
1641
1603
  // Back-navigation: re-show the list
1642
1604
  await showRunningAgents(ctx);
1643
1605
  }
1644
- async function viewAgentConversation(ctx, record) {
1645
- if (!record.session) {
1646
- ctx.ui.notify(`Agent is ${record.status === "queued" ? "queued" : "expired"} — no session available.`, "info");
1606
+ async function showAgentHistory(ctx) {
1607
+ const { history } = splitAgentRecords(manager.listAgents(), ctx.cwd);
1608
+ if (history.length === 0) {
1609
+ ctx.ui.notify("No agent history.", "info");
1610
+ return;
1611
+ }
1612
+ const options = history.map(record => formatAgentHistoryOption(record, Date.now()));
1613
+ const choice = await ctx.ui.select("Agent history", options);
1614
+ if (!choice)
1615
+ return;
1616
+ const record = history[options.indexOf(choice)];
1617
+ if (!record)
1618
+ return;
1619
+ await viewAgentConversation(ctx, record, "history");
1620
+ }
1621
+ async function viewAgentConversation(ctx, record, mode) {
1622
+ if (mode === "live" && !canOpenActiveAgent(record)) {
1623
+ ctx.ui.notify(`Agent is ${record.status === "queued" ? "queued" : "expired"} — no history available.`, "info");
1624
+ return;
1625
+ }
1626
+ if (mode === "history" && !canOpenAgentHistory(record, ctx.cwd)) {
1627
+ ctx.ui.notify("No agent history.", "info");
1647
1628
  return;
1648
1629
  }
1649
1630
  const { ConversationViewer, VIEWPORT_HEIGHT_PCT, createStaticConversationSource } = await import("./ui/conversation-viewer.js");
1650
- const session = record.session ?? (record.transcriptPath
1651
- ? (() => {
1652
- const messages = (0, agent_history_js_1.readAgentHistory)(ctx.cwd, record.transcriptPath);
1653
- return messages ? createStaticConversationSource(messages) : undefined;
1654
- })()
1655
- : undefined);
1631
+ const session = mode === "live"
1632
+ ? record.session
1633
+ : (() => {
1634
+ const messages = record.transcriptPath
1635
+ ? readAgentHistory(ctx.cwd, record.transcriptPath)
1636
+ : undefined;
1637
+ return messages
1638
+ ? createStaticConversationSource(messages)
1639
+ : record.session
1640
+ ? createStaticConversationSource(record.session.messages)
1641
+ : undefined;
1642
+ })();
1656
1643
  if (!session) {
1657
- ctx.ui.notify(`Agent is ${record.status === "queued" ? "queued" : "expired"} — no history available.`, "info");
1644
+ ctx.ui.notify("No agent history.", "info");
1658
1645
  return;
1659
1646
  }
1660
1647
  const activity = agentActivity.get(record.id);
1661
- const isLive = record.session !== undefined;
1648
+ const isLive = mode === "live";
1662
1649
  await ctx.ui.custom((tui, theme, keybindings, done) => {
1663
1650
  return new ConversationViewer(tui, session, record, activity, theme, done, isLive ? () => {
1664
1651
  if (manager.abort(record.id)) {
1665
1652
  ctx.ui.notify(`Stopped "${record.description}".`, "info");
1666
1653
  }
1667
- } : undefined, keybindings, isLive ? (message) => manager.steer(record.id, message) : undefined);
1654
+ } : undefined, keybindings, isLive ? (message) => manager.steer(record.id, message) : undefined, mode === "history" ? { pi, ctx, readOnly: true } : { pi, ctx });
1668
1655
  }, {
1669
1656
  overlay: true,
1670
1657
  overlayOptions: { anchor: "center", width: "90%", maxHeight: `${VIEWPORT_HEIGHT_PCT}%` },
1671
1658
  });
1672
1659
  }
1673
1660
  async function showAgentDetail(ctx, name) {
1674
- const cfg = (0, agent_types_js_1.getAgentConfig)(name);
1661
+ const cfg = getAgentConfig(name);
1675
1662
  if (!cfg) {
1676
1663
  ctx.ui.notify(`Agent config not found for "${name}".`, "warning");
1677
1664
  return;
@@ -1702,7 +1689,7 @@ Terse command-style prompts produce shallow, generic work.
1702
1689
  if (!choice || choice === "Back")
1703
1690
  return;
1704
1691
  if (choice === "Edit" && file) {
1705
- const content = (0, node_fs_1.readFileSync)(file.path, "utf-8");
1692
+ const content = readFileSync(file.path, "utf-8");
1706
1693
  const edited = await ctx.ui.editor(`Edit ${name}`, content);
1707
1694
  if (edited !== undefined && edited !== content) {
1708
1695
  const { writeFileSync } = await import("node:fs");
@@ -1715,7 +1702,7 @@ Terse command-style prompts produce shallow, generic work.
1715
1702
  if (file) {
1716
1703
  const confirmed = await ctx.ui.confirm("Delete agent", `Delete ${name} from ${file.location} (${file.path})?`);
1717
1704
  if (confirmed) {
1718
- (0, node_fs_1.unlinkSync)(file.path);
1705
+ unlinkSync(file.path);
1719
1706
  reloadCustomAgents();
1720
1707
  ctx.ui.notify(`Deleted ${file.path}`, "info");
1721
1708
  }
@@ -1724,7 +1711,7 @@ Terse command-style prompts produce shallow, generic work.
1724
1711
  else if (choice === "Reset to default" && file) {
1725
1712
  const confirmed = await ctx.ui.confirm("Reset to default", `Delete override ${file.path} and restore embedded default?`);
1726
1713
  if (confirmed) {
1727
- (0, node_fs_1.unlinkSync)(file.path);
1714
+ unlinkSync(file.path);
1728
1715
  reloadCustomAgents();
1729
1716
  ctx.ui.notify(`Restored default ${name}`, "info");
1730
1717
  }
@@ -1748,9 +1735,9 @@ Terse command-style prompts produce shallow, generic work.
1748
1735
  if (!location)
1749
1736
  return;
1750
1737
  const targetDir = location.startsWith("Project") ? projectAgentsDir() : personalAgentsDir();
1751
- (0, node_fs_1.mkdirSync)(targetDir, { recursive: true });
1752
- const targetPath = (0, node_path_1.join)(targetDir, `${name}.md`);
1753
- if ((0, node_fs_1.existsSync)(targetPath)) {
1738
+ mkdirSync(targetDir, { recursive: true });
1739
+ const targetPath = join(targetDir, `${name}.md`);
1740
+ if (existsSync(targetPath)) {
1754
1741
  const overwrite = await ctx.ui.confirm("Overwrite", `${targetPath} already exists. Overwrite?`);
1755
1742
  if (!overwrite)
1756
1743
  return;
@@ -1803,7 +1790,7 @@ Terse command-style prompts produce shallow, generic work.
1803
1790
  const file = findAgentFile(name);
1804
1791
  if (file) {
1805
1792
  // Existing file — set enabled: false in frontmatter (idempotent)
1806
- const content = (0, node_fs_1.readFileSync)(file.path, "utf-8");
1793
+ const content = readFileSync(file.path, "utf-8");
1807
1794
  if (content.includes("\nenabled: false\n")) {
1808
1795
  ctx.ui.notify(`${name} is already disabled.`, "info");
1809
1796
  return;
@@ -1823,8 +1810,8 @@ Terse command-style prompts produce shallow, generic work.
1823
1810
  if (!location)
1824
1811
  return;
1825
1812
  const targetDir = location.startsWith("Project") ? projectAgentsDir() : personalAgentsDir();
1826
- (0, node_fs_1.mkdirSync)(targetDir, { recursive: true });
1827
- const targetPath = (0, node_path_1.join)(targetDir, `${name}.md`);
1813
+ mkdirSync(targetDir, { recursive: true });
1814
+ const targetPath = join(targetDir, `${name}.md`);
1828
1815
  const { writeFileSync } = await import("node:fs");
1829
1816
  writeFileSync(targetPath, "---\nenabled: false\n---\n", "utf-8");
1830
1817
  reloadCustomAgents();
@@ -1835,12 +1822,12 @@ Terse command-style prompts produce shallow, generic work.
1835
1822
  const file = findAgentFile(name);
1836
1823
  if (!file)
1837
1824
  return;
1838
- const content = (0, node_fs_1.readFileSync)(file.path, "utf-8");
1825
+ const content = readFileSync(file.path, "utf-8");
1839
1826
  const updated = content.replace(/^(---\n)enabled: false\n/, "$1");
1840
1827
  const { writeFileSync } = await import("node:fs");
1841
1828
  // If the file was just a stub ("---\n---\n"), delete it to restore the built-in default
1842
1829
  if (updated.trim() === "---\n---" || updated.trim() === "---\n---\n") {
1843
- (0, node_fs_1.unlinkSync)(file.path);
1830
+ unlinkSync(file.path);
1844
1831
  reloadCustomAgents();
1845
1832
  ctx.ui.notify(`Enabled ${name} (removed ${file.path})`, "info");
1846
1833
  }
@@ -1878,9 +1865,9 @@ Terse command-style prompts produce shallow, generic work.
1878
1865
  const name = await ctx.ui.input("Agent name (filename, no spaces)");
1879
1866
  if (!name)
1880
1867
  return;
1881
- (0, node_fs_1.mkdirSync)(targetDir, { recursive: true });
1882
- const targetPath = (0, node_path_1.join)(targetDir, `${name}.md`);
1883
- if ((0, node_fs_1.existsSync)(targetPath)) {
1868
+ mkdirSync(targetDir, { recursive: true });
1869
+ const targetPath = join(targetDir, `${name}.md`);
1870
+ if (existsSync(targetPath)) {
1884
1871
  const overwrite = await ctx.ui.confirm("Overwrite", `${targetPath} already exists. Overwrite?`);
1885
1872
  if (!overwrite)
1886
1873
  return;
@@ -1934,7 +1921,7 @@ Write the file using the write tool. Only write the file, nothing else.`;
1934
1921
  return;
1935
1922
  }
1936
1923
  reloadCustomAgents();
1937
- if ((0, node_fs_1.existsSync)(targetPath)) {
1924
+ if (existsSync(targetPath)) {
1938
1925
  ctx.ui.notify(`Created ${targetPath}`, "info");
1939
1926
  }
1940
1927
  else {
@@ -1956,7 +1943,7 @@ Write the file using the write tool. Only write the file, nothing else.`;
1956
1943
  return;
1957
1944
  let tools;
1958
1945
  if (toolChoice === "all") {
1959
- tools = agent_types_js_1.BUILTIN_TOOL_NAMES.join(", ");
1946
+ tools = BUILTIN_TOOL_NAMES.join(", ");
1960
1947
  }
1961
1948
  else if (toolChoice === "none") {
1962
1949
  tools = "none";
@@ -1965,7 +1952,7 @@ Write the file using the write tool. Only write the file, nothing else.`;
1965
1952
  tools = "read, bash, grep, find, ls";
1966
1953
  }
1967
1954
  else {
1968
- const customTools = await ctx.ui.input("Tools (comma-separated)", agent_types_js_1.BUILTIN_TOOL_NAMES.join(", "));
1955
+ const customTools = await ctx.ui.input("Tools (comma-separated)", BUILTIN_TOOL_NAMES.join(", "));
1969
1956
  if (!customTools)
1970
1957
  return;
1971
1958
  tools = customTools;
@@ -2013,9 +2000,9 @@ prompt_mode: replace
2013
2000
 
2014
2001
  ${systemPrompt}
2015
2002
  `;
2016
- (0, node_fs_1.mkdirSync)(targetDir, { recursive: true });
2017
- const targetPath = (0, node_path_1.join)(targetDir, `${name}.md`);
2018
- if ((0, node_fs_1.existsSync)(targetPath)) {
2003
+ mkdirSync(targetDir, { recursive: true });
2004
+ const targetPath = join(targetDir, `${name}.md`);
2005
+ if (existsSync(targetPath)) {
2019
2006
  const overwrite = await ctx.ui.confirm("Overwrite", `${targetPath} already exists. Overwrite?`);
2020
2007
  if (!overwrite)
2021
2008
  return;
@@ -2030,12 +2017,12 @@ ${systemPrompt}
2030
2017
  maxConcurrent: manager.getMaxConcurrent(),
2031
2018
  // 0 = unlimited — per SubagentsSettings.defaultMaxTurns docstring and
2032
2019
  // normalizeMaxTurns() in agent-runner.ts (which maps 0 → undefined).
2033
- defaultMaxTurns: (0, agent_runner_js_1.getDefaultMaxTurns)() ?? 0,
2034
- graceTurns: (0, agent_runner_js_1.getGraceTurns)(),
2020
+ defaultMaxTurns: getDefaultMaxTurns() ?? 0,
2021
+ graceTurns: getGraceTurns(),
2035
2022
  defaultJoinMode: getDefaultJoinMode(),
2036
2023
  schedulingEnabled: isSchedulingEnabled(),
2037
2024
  scopeModels: isScopeModelsEnabled(),
2038
- disableDefaultAgents: (0, agent_types_js_1.isDefaultsDisabled)(),
2025
+ disableDefaultAgents: isDefaultsDisabled(),
2039
2026
  toolDescriptionMode: getToolDescriptionMode(),
2040
2027
  fleetView: isFleetViewEnabled(),
2041
2028
  widgetMode: getWidgetMode(),
@@ -2046,8 +2033,8 @@ ${systemPrompt}
2046
2033
  async function showSettings(ctx) {
2047
2034
  function buildItems() {
2048
2035
  const mc = manager.getMaxConcurrent();
2049
- const dmt = (0, agent_runner_js_1.getDefaultMaxTurns)() ?? 0;
2050
- const gt = (0, agent_runner_js_1.getGraceTurns)();
2036
+ const dmt = getDefaultMaxTurns() ?? 0;
2037
+ const gt = getGraceTurns();
2051
2038
  return [
2052
2039
  {
2053
2040
  id: "maxConcurrent",
@@ -2095,7 +2082,7 @@ ${systemPrompt}
2095
2082
  id: "disableDefaultAgents",
2096
2083
  label: "Disable defaults",
2097
2084
  description: "Hide built-in agents (general-purpose, Explore, Plan) — custom agents are unaffected",
2098
- currentValue: (0, agent_types_js_1.isDefaultsDisabled)() ? "on" : "off",
2085
+ currentValue: isDefaultsDisabled() ? "on" : "off",
2099
2086
  values: ["on", "off"],
2100
2087
  },
2101
2088
  {
@@ -2139,18 +2126,18 @@ ${systemPrompt}
2139
2126
  else if (id === "defaultMaxTurns") {
2140
2127
  const n = parseInt(value, 10);
2141
2128
  if (n === 0) {
2142
- (0, agent_runner_js_1.setDefaultMaxTurns)(undefined);
2129
+ setDefaultMaxTurns(undefined);
2143
2130
  notifyApplied(ctx, "Default max turns set to unlimited");
2144
2131
  }
2145
2132
  else if (n >= 1) {
2146
- (0, agent_runner_js_1.setDefaultMaxTurns)(n);
2133
+ setDefaultMaxTurns(n);
2147
2134
  notifyApplied(ctx, `Default max turns set to ${n}`);
2148
2135
  }
2149
2136
  }
2150
2137
  else if (id === "graceTurns") {
2151
2138
  const n = parseInt(value, 10);
2152
2139
  if (n >= 1) {
2153
- (0, agent_runner_js_1.setGraceTurns)(n);
2140
+ setGraceTurns(n);
2154
2141
  notifyApplied(ctx, `Grace turns set to ${n}`);
2155
2142
  }
2156
2143
  }
@@ -2205,26 +2192,26 @@ ${systemPrompt}
2205
2192
  let currentIndex = 0;
2206
2193
  const result = await ctx.ui.custom((_tui, _theme, _kb, done) => {
2207
2194
  const items = buildItems();
2208
- list = new pi_tui_1.SettingsList(items, items.length + 2, (0, pi_coding_agent_1.getSettingsListTheme)(), (id, newValue) => {
2195
+ list = new SettingsList(items, items.length + 2, getSettingsListTheme(), (id, newValue) => {
2209
2196
  applyValue(id, newValue);
2210
2197
  }, () => done(undefined));
2211
- const container = new pi_tui_1.Container();
2212
- container.addChild(new pi_tui_1.Text("⚙ Subagent Settings", 0, 0));
2213
- container.addChild(new pi_tui_1.Spacer(1));
2198
+ const container = new Container();
2199
+ container.addChild(new Text("⚙ Subagent Settings", 0, 0));
2200
+ container.addChild(new Spacer(1));
2214
2201
  container.addChild(list);
2215
2202
  return {
2216
2203
  render: (w) => container.render(w),
2217
2204
  invalidate: () => container.invalidate(),
2218
2205
  handleInput: (data) => {
2219
2206
  // Track navigation so Enter knows the current field
2220
- if ((0, pi_tui_1.matchesKey)(data, "up")) {
2207
+ if (matchesKey(data, "up")) {
2221
2208
  currentIndex = Math.max(0, currentIndex - 1);
2222
2209
  }
2223
- else if ((0, pi_tui_1.matchesKey)(data, "down")) {
2210
+ else if (matchesKey(data, "down")) {
2224
2211
  currentIndex = Math.min(items.length - 1, currentIndex + 1);
2225
2212
  }
2226
2213
  // Enter on numeric field → close and prompt for typed input
2227
- if ((0, pi_tui_1.matchesKey)(data, pi_tui_1.Key.enter) && NUMERIC_IDS.has(items[currentIndex].id)) {
2214
+ if (matchesKey(data, Key.enter) && NUMERIC_IDS.has(items[currentIndex].id)) {
2228
2215
  done(items[currentIndex].id);
2229
2216
  return;
2230
2217
  }
@@ -2237,8 +2224,8 @@ ${systemPrompt}
2237
2224
  const current = result === "maxConcurrent"
2238
2225
  ? String(manager.getMaxConcurrent())
2239
2226
  : result === "defaultMaxTurns"
2240
- ? String((0, agent_runner_js_1.getDefaultMaxTurns)() ?? 0)
2241
- : String((0, agent_runner_js_1.getGraceTurns)());
2227
+ ? String(getDefaultMaxTurns() ?? 0)
2228
+ : String(getGraceTurns());
2242
2229
  const label = result === "maxConcurrent"
2243
2230
  ? "Max concurrency (1+)"
2244
2231
  : result === "defaultMaxTurns"
@@ -2265,7 +2252,7 @@ ${systemPrompt}
2265
2252
  // to warning so users aren't silently reverted on restart. Event fires regardless
2266
2253
  // of outcome so listeners see the in-memory change.
2267
2254
  function notifyApplied(ctx, successMsg) {
2268
- const { message, level } = (0, settings_js_1.saveAndEmitChanged)(snapshotSettings(), successMsg, (event, payload) => pi.events.emit(event, payload));
2255
+ const { message, level } = saveAndEmitChanged(snapshotSettings(), successMsg, (event, payload) => pi.events.emit(event, payload));
2269
2256
  ctx.ui.notify(message, level);
2270
2257
  }
2271
2258
  pi.registerCommand("agents", {