@esso0428/pi-subagents 0.15.0 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.15.1] - 2026-09-10
11
+
12
+ ### Added
13
+ - **Markdown-rendered subagent results.** Expanded foreground results and background completion notifications now render headings, lists, inline code, and fenced code through pi's Markdown renderer. Display output is capped to keep terminal notifications bounded; use `get_subagent_result` for the complete result.
14
+
10
15
  ## [0.14.3] - 2026-07-23
11
16
 
12
17
  ### Fixed
package/README.md CHANGED
@@ -28,7 +28,7 @@ https://github.com/user-attachments/assets/8685261b-9338-4fea-8dfe-1c590d5df543
28
28
  - **Git worktree isolation** — run agents in isolated repo copies; changes auto-committed to branches on completion
29
29
  - **Skill preloading** — inject named skills into agent system prompts, discovered from `.pi/skills/`, `.agents/skills/`, and global locations (Pi-standard `<name>/SKILL.md` directory layout supported)
30
30
  - **Tool denylist** — block specific tools via `disallowed_tools` frontmatter
31
- - **Styled completion notifications** — background agent results render as themed, compact notification boxes (icon, stats, result preview) instead of raw XML. Expandable to show full output. Group completions render each agent individually
31
+ - **Styled completion notifications** — background agent results render as themed, compact notification boxes (icon, stats, result preview) instead of raw XML. Expanded results render headings, lists, inline code, and fenced code as Markdown, with a bounded display to keep the TUI responsive. Group completions render each agent individually
32
32
  - **Event bus** — lifecycle events (`subagents:created`, `started`, `completed`, `failed`, `steered`, `compacted`) emitted via `pi.events`, enabling other extensions to react to sub-agent activity
33
33
  - **Cross-extension RPC** — other pi extensions can spawn and stop subagents via the `pi.events` event bus (`subagents:rpc:ping`, `subagents:rpc:spawn`, `subagents:rpc:stop`). Standardized reply envelopes with protocol versioning. Emits `subagents:ready` on session start
34
34
  - **Schedule subagents** — pass `schedule` to the `Agent` tool to fire on cron / interval / one-shot. Session-scoped jobs with PID-locked persistence; results land via the same `subagent-notification` followUp path as manual background completions; manage via `/agents → Scheduled jobs`
@@ -138,7 +138,7 @@ Individual agent results render Claude Code-style in the conversation:
138
138
  | **Error** | `✗ ↻3 · 3 tool uses · 12.4k token (8%)` / `⎿ Error: timeout` |
139
139
  | **Aborted** | `✗ ↻55≤50 · 55 tool uses · 102.3k token (95% · ⇊3)` / `⎿ Aborted (max turns exceeded)` |
140
140
 
141
- Completed results can be expanded (ctrl+o in pi) to show the full agent output inline.
141
+ Completed results can be expanded (ctrl+o in pi) to show the agent output inline with Markdown formatting. Expanded output is capped at 50 rendered lines (background notification results at 30 lines) and ends with a marker when more output is available; use `get_subagent_result` for the complete result.
142
142
 
143
143
  By default, foreground and background agents each stream their full conversation to a per-subagent transcript — a JSON-lines file at `<os-tmpdir>/pi-subagents-<uid>/<cwd>/<session>/tasks/<agent-id>.output` (owner-only `0700`, cleared on reboot). Set `output_transcript: false` on a custom agent to write no transcript path or file for it, or set `outputTranscript: false` in `subagents.json` to make transcripts opt-in for the whole project (frontmatter overrides the project default). This governs **only** the transcript: it is independent of `persist_session` (the pi session on disk), and it does not affect `isolation: worktree` (which commits the agent's work to a git branch) or `memory:` (durable files) — set those accordingly if the goal is to keep a run off disk entirely. Background agent completion notifications render as styled boxes:
144
144
 
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  /**
2
3
  * agent-manager.ts — Tracks agents, background execution, resume support.
3
4
  *
@@ -5,12 +6,14 @@
5
6
  * Excess agents are queued and auto-started as running agents complete.
6
7
  * Foreground agents bypass the queue (they block the parent anyway).
7
8
  */
8
- import { randomUUID } from "node:crypto";
9
- import { statSync } from "node:fs";
10
- import { isAbsolute } from "node:path";
11
- import { resumeAgent, runAgent } from "./agent-runner.js";
12
- import { addUsage } from "./usage.js";
13
- import { cleanupWorktree, createWorktree, pruneWorktrees, } from "./worktree.js";
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.AgentManager = void 0;
11
+ const node_crypto_1 = require("node:crypto");
12
+ const node_fs_1 = require("node:fs");
13
+ const node_path_1 = require("node:path");
14
+ const agent_runner_js_1 = require("./agent-runner.js");
15
+ const usage_js_1 = require("./usage.js");
16
+ const worktree_js_1 = require("./worktree.js");
14
17
  /** Default max concurrent background agents. */
15
18
  const DEFAULT_MAX_CONCURRENT = 4;
16
19
  /**
@@ -22,12 +25,12 @@ const DEFAULT_MAX_CONCURRENT = 4;
22
25
  function assertValidSpawnCwd(cwd) {
23
26
  if (cwd == null)
24
27
  return;
25
- if (typeof cwd !== "string" || !isAbsolute(cwd)) {
28
+ if (typeof cwd !== "string" || !(0, node_path_1.isAbsolute)(cwd)) {
26
29
  throw new Error(`SpawnOptions.cwd must be an absolute path: "${String(cwd)}"`);
27
30
  }
28
31
  let isDirectory = false;
29
32
  try {
30
- isDirectory = statSync(cwd).isDirectory();
33
+ isDirectory = (0, node_fs_1.statSync)(cwd).isDirectory();
31
34
  }
32
35
  catch {
33
36
  throw new Error(`SpawnOptions.cwd does not exist: "${cwd}"`);
@@ -36,7 +39,7 @@ function assertValidSpawnCwd(cwd) {
36
39
  throw new Error(`SpawnOptions.cwd is not a directory: "${cwd}"`);
37
40
  }
38
41
  }
39
- export class AgentManager {
42
+ class AgentManager {
40
43
  agents = new Map();
41
44
  cleanupInterval;
42
45
  onComplete;
@@ -77,7 +80,7 @@ export class AgentManager {
77
80
  // call, not minutes later at drain. Throw (not warn): programmatic callers
78
81
  // can fix and retry; the RPC layer converts throws into error envelopes.
79
82
  assertValidSpawnCwd(options.cwd);
80
- const id = randomUUID().slice(0, 17);
83
+ const id = (0, node_crypto_1.randomUUID)().slice(0, 17);
81
84
  const abortController = new AbortController();
82
85
  const record = {
83
86
  id,
@@ -130,7 +133,7 @@ export class AgentManager {
130
133
  // BEFORE state mutation so a throw doesn't leave the record half-running.
131
134
  let worktreeCwd;
132
135
  if (options.isolation === "worktree") {
133
- const wt = createWorktree(baseCwd, id);
136
+ const wt = (0, worktree_js_1.createWorktree)(baseCwd, id);
134
137
  if (!wt) {
135
138
  throw new Error('Cannot run with isolation: "worktree" — not a git repo, no commits yet, or `git worktree add` failed. ' +
136
139
  'Initialize git and commit at least once, or omit `isolation`.');
@@ -158,7 +161,7 @@ export class AgentManager {
158
161
  detachParentSignal = () => options.signal.removeEventListener("abort", onParentAbort);
159
162
  }
160
163
  const detach = () => { detachParentSignal?.(); detachParentSignal = undefined; };
161
- const promise = runAgent(ctx, type, prompt, {
164
+ const promise = (0, agent_runner_js_1.runAgent)(ctx, type, prompt, {
162
165
  pi,
163
166
  agentId: id,
164
167
  model: options.model,
@@ -182,7 +185,7 @@ export class AgentManager {
182
185
  onTurnEnd: options.onTurnEnd,
183
186
  onTextDelta: options.onTextDelta,
184
187
  onAssistantUsage: (usage) => {
185
- addUsage(record.lifetimeUsage, usage);
188
+ (0, usage_js_1.addUsage)(record.lifetimeUsage, usage);
186
189
  options.onAssistantUsage?.(usage);
187
190
  },
188
191
  onCompaction: (info) => {
@@ -233,7 +236,7 @@ export class AgentManager {
233
236
  }
234
237
  // Clean up worktree if used
235
238
  if (record.worktree) {
236
- const wtResult = cleanupWorktree(baseCwd, record.worktree, options.description);
239
+ const wtResult = (0, worktree_js_1.cleanupWorktree)(baseCwd, record.worktree, options.description);
237
240
  record.worktreeResult = wtResult;
238
241
  if (wtResult.hasChanges && wtResult.branch) {
239
242
  // With a caller-supplied cwd the branch lives in THAT repo, not the
@@ -281,7 +284,7 @@ export class AgentManager {
281
284
  // Best-effort worktree cleanup on error
282
285
  if (record.worktree) {
283
286
  try {
284
- const wtResult = cleanupWorktree(baseCwd, record.worktree, options.description);
287
+ const wtResult = (0, worktree_js_1.cleanupWorktree)(baseCwd, record.worktree, options.description);
285
288
  record.worktreeResult = wtResult;
286
289
  }
287
290
  catch { /* ignore cleanup errors */ }
@@ -366,13 +369,13 @@ export class AgentManager {
366
369
  record.result = undefined;
367
370
  record.error = undefined;
368
371
  try {
369
- const { text, failure } = await resumeAgent(record.session, prompt, {
372
+ const { text, failure } = await (0, agent_runner_js_1.resumeAgent)(record.session, prompt, {
370
373
  onToolActivity: (activity) => {
371
374
  if (activity.type === "end")
372
375
  record.toolUses++;
373
376
  },
374
377
  onAssistantUsage: (usage) => {
375
- addUsage(record.lifetimeUsage, usage);
378
+ (0, usage_js_1.addUsage)(record.lifetimeUsage, usage);
376
379
  },
377
380
  onCompaction: (info) => {
378
381
  record.compactionCount++;
@@ -527,16 +530,17 @@ export class AgentManager {
527
530
  this.agents.clear();
528
531
  // Prune any orphaned git worktrees (crash recovery)
529
532
  try {
530
- pruneWorktrees(process.cwd());
533
+ (0, worktree_js_1.pruneWorktrees)(process.cwd());
531
534
  }
532
535
  catch { /* ignore */ }
533
536
  // Also prune repos that caller-supplied cwds created worktrees in — a clean
534
537
  // exit with in-flight agents would otherwise leave stale registrations there.
535
538
  for (const repo of this.worktreeRepos) {
536
539
  try {
537
- pruneWorktrees(repo);
540
+ (0, worktree_js_1.pruneWorktrees)(repo);
538
541
  }
539
542
  catch { /* ignore */ }
540
543
  }
541
544
  }
542
545
  }
546
+ exports.AgentManager = AgentManager;
@@ -1,30 +1,47 @@
1
+ "use strict";
1
2
  /**
2
3
  * agent-runner.ts — Core execution engine: creates sessions, runs agents, collects results.
3
4
  */
4
- import { readFileSync } from "node:fs";
5
- import { homedir } from "node:os";
6
- import { basename, dirname, isAbsolute, join, resolve } from "node:path";
7
- import { createAgentSession, DefaultResourceLoader, getAgentDir, SessionManager, SettingsManager, } from "@earendil-works/pi-coding-agent";
8
- import { BUILTIN_TOOL_NAMES, getAgentConfig, getConfig, getMemoryToolNames, getReadOnlyMemoryToolNames, getToolNamesForType } from "./agent-types.js";
9
- import { buildParentContext, extractText } from "./context.js";
10
- import { DEFAULT_AGENTS } from "./default-agents.js";
11
- import { detectEnv } from "./env.js";
12
- import { buildMemoryBlock, buildReadOnlyMemoryBlock } from "./memory.js";
13
- import { buildAgentPrompt } from "./prompts.js";
14
- import { preloadSkills } from "./skill-loader.js";
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SUBAGENT_TOOL_NAMES = void 0;
7
+ exports.extensionCanonicalName = extensionCanonicalName;
8
+ exports.extensionCanonicalNames = extensionCanonicalNames;
9
+ exports.parseExtensionsSpec = parseExtensionsSpec;
10
+ exports.parseExtSelectors = parseExtSelectors;
11
+ exports.installExtensionToolScope = installExtensionToolScope;
12
+ exports.normalizeMaxTurns = normalizeMaxTurns;
13
+ exports.getDefaultMaxTurns = getDefaultMaxTurns;
14
+ exports.setDefaultMaxTurns = setDefaultMaxTurns;
15
+ exports.getGraceTurns = getGraceTurns;
16
+ exports.setGraceTurns = setGraceTurns;
17
+ exports.runAgent = runAgent;
18
+ exports.resumeAgent = resumeAgent;
19
+ exports.steerAgent = steerAgent;
20
+ exports.getAgentConversation = getAgentConversation;
21
+ const node_fs_1 = require("node:fs");
22
+ const node_os_1 = require("node:os");
23
+ const node_path_1 = require("node:path");
24
+ const pi_coding_agent_1 = require("@earendil-works/pi-coding-agent");
25
+ const agent_types_js_1 = require("./agent-types.js");
26
+ const context_js_1 = require("./context.js");
27
+ const default_agents_js_1 = require("./default-agents.js");
28
+ const env_js_1 = require("./env.js");
29
+ const memory_js_1 = require("./memory.js");
30
+ const prompts_js_1 = require("./prompts.js");
31
+ const skill_loader_js_1 = require("./skill-loader.js");
15
32
  /**
16
33
  * Tool names registered by THIS extension. Single source of truth so the
17
34
  * registration sites (index.ts) and the subagent exclusion list below can't
18
35
  * drift apart. These are our own tools, not pi built-ins, so they can't be
19
36
  * derived from pi — but they only need defining once.
20
37
  */
21
- export const SUBAGENT_TOOL_NAMES = {
38
+ exports.SUBAGENT_TOOL_NAMES = {
22
39
  AGENT: "Agent",
23
40
  GET_RESULT: "get_subagent_result",
24
41
  STEER: "steer_subagent",
25
42
  };
26
43
  /** Names of tools registered by this extension that subagents must NOT inherit. */
27
- const EXCLUDED_TOOL_NAMES = Object.values(SUBAGENT_TOOL_NAMES);
44
+ const EXCLUDED_TOOL_NAMES = Object.values(exports.SUBAGENT_TOOL_NAMES);
28
45
  /**
29
46
  * Canonical name of an extension for `extensions: [...]` allowlist matching.
30
47
  * Lowercased — extension names match case-insensitively so `extensions: [Mcp]`
@@ -32,10 +49,10 @@ const EXCLUDED_TOOL_NAMES = Object.values(SUBAGENT_TOOL_NAMES);
32
49
  * Directory extensions (`foo/index.ts`) resolve to the parent directory name;
33
50
  * single-file extensions to the basename minus `.ts`/`.js`.
34
51
  */
35
- export function extensionCanonicalName(extPath) {
36
- const base = basename(extPath);
52
+ function extensionCanonicalName(extPath) {
53
+ const base = (0, node_path_1.basename)(extPath);
37
54
  const name = base === "index.ts" || base === "index.js"
38
- ? basename(dirname(extPath))
55
+ ? (0, node_path_1.basename)((0, node_path_1.dirname)(extPath))
39
56
  : base.replace(/\.(ts|js)$/, "");
40
57
  return name.toLowerCase();
41
58
  }
@@ -59,18 +76,18 @@ export function extensionCanonicalName(extPath) {
59
76
  * co-located file to `pi-subagents`.
60
77
  */
61
78
  function extensionPackageName(extPath) {
62
- const entry = resolve(extPath);
63
- let dir = dirname(extPath);
79
+ const entry = (0, node_path_1.resolve)(extPath);
80
+ let dir = (0, node_path_1.dirname)(extPath);
64
81
  for (;;) {
65
82
  // Climbing into node_modules means we've left the owning package's tree.
66
- if (basename(dir) === "node_modules")
83
+ if ((0, node_path_1.basename)(dir) === "node_modules")
67
84
  return undefined;
68
85
  let pkg;
69
86
  try {
70
- pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf-8"));
87
+ pkg = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(dir, "package.json"), "utf-8"));
71
88
  }
72
89
  catch {
73
- const parent = dirname(dir);
90
+ const parent = (0, node_path_1.dirname)(dir);
74
91
  if (parent === dir)
75
92
  return undefined; // walked to the filesystem root
76
93
  dir = parent;
@@ -80,7 +97,7 @@ function extensionPackageName(extPath) {
80
97
  const entries = pkg.pi?.extensions;
81
98
  if (typeof pkg.name === "string" &&
82
99
  Array.isArray(entries) &&
83
- entries.some((e) => typeof e === "string" && resolve(dir, e) === entry)) {
100
+ entries.some((e) => typeof e === "string" && (0, node_path_1.resolve)(dir, e) === entry)) {
84
101
  const short = pkg.name.startsWith("@") ? pkg.name.slice(pkg.name.indexOf("/") + 1) : pkg.name;
85
102
  return short.toLowerCase();
86
103
  }
@@ -95,7 +112,7 @@ function extensionPackageName(extPath) {
95
112
  * otherwise only ever match as `src` (the source directory), never by its
96
113
  * package name. The path-derived name is preserved, so it keeps matching too.
97
114
  */
98
- export function extensionCanonicalNames(extPath) {
115
+ function extensionCanonicalNames(extPath) {
99
116
  const canonical = extensionCanonicalName(extPath);
100
117
  const pkg = extensionPackageName(extPath);
101
118
  return pkg && pkg !== canonical ? [canonical, pkg] : [canonical];
@@ -111,7 +128,7 @@ export function extensionCanonicalNames(extPath) {
111
128
  * everything by canonical name, so path-loaded extensions are matched via their name
112
129
  * rather than their post-staging `Extension.path`.
113
130
  */
114
- export function parseExtensionsSpec(entries, cwd) {
131
+ function parseExtensionsSpec(entries, cwd) {
115
132
  const names = new Set();
116
133
  const paths = [];
117
134
  let wildcard = false;
@@ -129,9 +146,9 @@ export function parseExtensionsSpec(entries, cwd) {
129
146
  }
130
147
  let p = entry;
131
148
  if (p === "~" || p.startsWith("~/") || p.startsWith("~\\")) {
132
- p = homedir() + p.slice(1);
149
+ p = (0, node_os_1.homedir)() + p.slice(1);
133
150
  }
134
- const abs = isAbsolute(p) ? p : resolve(cwd, p);
151
+ const abs = (0, node_path_1.isAbsolute)(p) ? p : (0, node_path_1.resolve)(cwd, p);
135
152
  paths.push(abs);
136
153
  names.add(extensionCanonicalName(abs));
137
154
  }
@@ -147,7 +164,7 @@ export function parseExtensionsSpec(entries, cwd) {
147
164
  * `ext:foo` alongside `ext:foo/bar` leaves narrowing in effect (narrowing wins).
148
165
  * The split is on the first `/`; extension canonical names never contain `/`.
149
166
  */
150
- export function parseExtSelectors(entries) {
167
+ function parseExtSelectors(entries) {
151
168
  const extNames = new Set();
152
169
  const narrowing = new Map();
153
170
  for (const raw of entries) {
@@ -201,7 +218,7 @@ export function parseExtSelectors(entries) {
201
218
  * Only meaningful when extensions are loaded — under `noExtensions`/`isolated` the
202
219
  * static `allowedToolNames` allowlist already gates the registry itself.
203
220
  */
204
- export function installExtensionToolScope(session, ctx) {
221
+ function installExtensionToolScope(session, ctx) {
205
222
  const { loader, toolNames, disallowedSet, extNames, narrowing } = ctx;
206
223
  // The names allowed right now. Mirrors the `ext:` opt-in flip: when any `ext:`
207
224
  // selector is present, extension tools become an explicit allowlist — a loaded
@@ -260,21 +277,21 @@ export function installExtensionToolScope(session, ctx) {
260
277
  /** Default max turns. undefined = unlimited (no turn limit). */
261
278
  let defaultMaxTurns;
262
279
  /** Normalize max turns. undefined or 0 = unlimited, otherwise minimum 1. */
263
- export function normalizeMaxTurns(n) {
280
+ function normalizeMaxTurns(n) {
264
281
  if (n == null || n === 0)
265
282
  return undefined;
266
283
  return Math.max(1, n);
267
284
  }
268
285
  /** Get the default max turns value. undefined = unlimited. */
269
- export function getDefaultMaxTurns() { return defaultMaxTurns; }
286
+ function getDefaultMaxTurns() { return defaultMaxTurns; }
270
287
  /** Set the default max turns value. undefined or 0 = unlimited, otherwise minimum 1. */
271
- export function setDefaultMaxTurns(n) { defaultMaxTurns = normalizeMaxTurns(n); }
288
+ function setDefaultMaxTurns(n) { defaultMaxTurns = normalizeMaxTurns(n); }
272
289
  /** Additional turns allowed after the soft limit steer message. */
273
290
  let graceTurns = 5;
274
291
  /** Get the grace turns value. */
275
- export function getGraceTurns() { return graceTurns; }
292
+ function getGraceTurns() { return graceTurns; }
276
293
  /** Set the grace turns value (minimum 1). */
277
- export function setGraceTurns(n) { graceTurns = Math.max(1, n); }
294
+ function setGraceTurns(n) { graceTurns = Math.max(1, n); }
278
295
  /**
279
296
  * Try to find the right model for an agent type.
280
297
  * Priority: explicit option > config.model > parent model.
@@ -329,7 +346,7 @@ function getLastAssistantText(session, startIndex = 0) {
329
346
  const msg = session.messages[i];
330
347
  if (msg.role !== "assistant")
331
348
  continue;
332
- const text = extractText(msg.content).trim();
349
+ const text = (0, context_js_1.extractText)(msg.content).trim();
333
350
  if (text)
334
351
  return text;
335
352
  }
@@ -357,7 +374,7 @@ function finalTurnError(session, startIndex = 0) {
357
374
  if (msg.stopReason === "error") {
358
375
  return msg.errorMessage?.trim() || "provider error with no output";
359
376
  }
360
- if (msg.stopReason === "length" && !extractText(msg.content).trim()) {
377
+ if (msg.stopReason === "length" && !(0, context_js_1.extractText)(msg.content).trim()) {
361
378
  return "run hit the output token limit before producing any text";
362
379
  }
363
380
  return undefined;
@@ -379,20 +396,20 @@ function resolveConfiguredSessionDir(sessionDir, cwd) {
379
396
  if (!sessionDir)
380
397
  return undefined;
381
398
  if (sessionDir === "~" || sessionDir.startsWith("~/"))
382
- return resolve(homedir(), sessionDir.slice(2));
383
- if (isAbsolute(sessionDir))
399
+ return (0, node_path_1.resolve)((0, node_os_1.homedir)(), sessionDir.slice(2));
400
+ if ((0, node_path_1.isAbsolute)(sessionDir))
384
401
  return sessionDir;
385
- return resolve(cwd, sessionDir);
402
+ return (0, node_path_1.resolve)(cwd, sessionDir);
386
403
  }
387
- export async function runAgent(ctx, type, prompt, options) {
388
- const config = getConfig(type);
389
- const agentConfig = getAgentConfig(type);
404
+ async function runAgent(ctx, type, prompt, options) {
405
+ const config = (0, agent_types_js_1.getConfig)(type);
406
+ const agentConfig = (0, agent_types_js_1.getAgentConfig)(type);
390
407
  // Resolve working directory: worktree override > parent cwd
391
408
  const effectiveCwd = options.cwd ?? ctx.cwd;
392
409
  // Filesystem work happens in effectiveCwd; config discovery in configCwd.
393
410
  // They differ only for SpawnOptions.cwd spawns (config stays with the parent).
394
411
  const configCwd = options.configCwd ?? effectiveCwd;
395
- const env = await detectEnv(options.pi, effectiveCwd);
412
+ const env = await (0, env_js_1.detectEnv)(options.pi, effectiveCwd);
396
413
  // Get parent system prompt for append-mode agents
397
414
  const parentSystemPrompt = ctx.getSystemPrompt();
398
415
  // Build prompt extras (memory, skill preloading)
@@ -405,12 +422,12 @@ export async function runAgent(ctx, type, prompt, options) {
405
422
  const skills = options.isolated ? false : config.skills;
406
423
  // Skill preloading: when skills is string[], preload their content into prompt
407
424
  if (Array.isArray(skills)) {
408
- const loaded = preloadSkills(skills, configCwd);
425
+ const loaded = (0, skill_loader_js_1.preloadSkills)(skills, configCwd);
409
426
  if (loaded.length > 0) {
410
427
  extras.skillBlocks = loaded;
411
428
  }
412
429
  }
413
- let toolNames = getToolNamesForType(type);
430
+ let toolNames = (0, agent_types_js_1.getToolNamesForType)(type);
414
431
  // Persistent memory: detect write capability and branch accordingly.
415
432
  // Account for disallowedTools — a tool in the base set but on the denylist is not truly available.
416
433
  if (agentConfig?.memory) {
@@ -420,36 +437,36 @@ export async function runAgent(ctx, type, prompt, options) {
420
437
  const hasWriteTools = effectivelyHas("write") || effectivelyHas("edit");
421
438
  if (hasWriteTools) {
422
439
  // Read-write memory: add any missing memory tool names (read/write/edit)
423
- const extraNames = getMemoryToolNames(existingNames);
440
+ const extraNames = (0, agent_types_js_1.getMemoryToolNames)(existingNames);
424
441
  if (extraNames.length > 0)
425
442
  toolNames = [...toolNames, ...extraNames];
426
- extras.memoryBlock = buildMemoryBlock(agentConfig.name, agentConfig.memory, configCwd);
443
+ extras.memoryBlock = (0, memory_js_1.buildMemoryBlock)(agentConfig.name, agentConfig.memory, configCwd);
427
444
  }
428
445
  else {
429
446
  // Read-only memory: only add read tool name, use read-only prompt
430
- const extraNames = getReadOnlyMemoryToolNames(existingNames);
447
+ const extraNames = (0, agent_types_js_1.getReadOnlyMemoryToolNames)(existingNames);
431
448
  if (extraNames.length > 0)
432
449
  toolNames = [...toolNames, ...extraNames];
433
- extras.memoryBlock = buildReadOnlyMemoryBlock(agentConfig.name, agentConfig.memory, configCwd);
450
+ extras.memoryBlock = (0, memory_js_1.buildReadOnlyMemoryBlock)(agentConfig.name, agentConfig.memory, configCwd);
434
451
  }
435
452
  }
436
453
  // Build system prompt from agent config
437
454
  let systemPrompt;
438
455
  if (agentConfig) {
439
- systemPrompt = buildAgentPrompt(agentConfig, effectiveCwd, env, parentSystemPrompt, extras);
456
+ systemPrompt = (0, prompts_js_1.buildAgentPrompt)(agentConfig, effectiveCwd, env, parentSystemPrompt, extras);
440
457
  }
441
458
  else {
442
459
  // Unknown type fallback: spread the canonical general-purpose config (defensive —
443
460
  // unreachable in practice since index.ts resolves unknown types before calling runAgent).
444
- const fallback = DEFAULT_AGENTS.get("general-purpose");
461
+ const fallback = default_agents_js_1.DEFAULT_AGENTS.get("general-purpose");
445
462
  if (!fallback)
446
463
  throw new Error(`No fallback config available for unknown type "${type}"`);
447
- systemPrompt = buildAgentPrompt({ ...fallback, name: type }, effectiveCwd, env, parentSystemPrompt, extras);
464
+ systemPrompt = (0, prompts_js_1.buildAgentPrompt)({ ...fallback, name: type }, effectiveCwd, env, parentSystemPrompt, extras);
448
465
  }
449
466
  // When skills is string[], we've already preloaded them into the prompt.
450
467
  // Still pass noSkills: true since we don't need the skill loader to load them again.
451
468
  const noSkills = skills === false || Array.isArray(skills);
452
- const agentDir = getAgentDir();
469
+ const agentDir = (0, pi_coding_agent_1.getAgentDir)();
453
470
  // Extension loading:
454
471
  // - true → all default-discovered extensions
455
472
  // - false → none (noExtensions)
@@ -502,7 +519,7 @@ export async function runAgent(ctx, type, prompt, options) {
502
519
  }),
503
520
  };
504
521
  };
505
- const loader = new DefaultResourceLoader({
522
+ const loader = new pi_coding_agent_1.DefaultResourceLoader({
506
523
  cwd: configCwd,
507
524
  agentDir,
508
525
  noExtensions,
@@ -521,7 +538,7 @@ export async function runAgent(ctx, type, prompt, options) {
521
538
  // this produced a silently broken agent (#75) — pi-mono accepted the bogus name
522
539
  // into the allowlist, then dropped it at registration with no signal back.
523
540
  if (agentConfig?.builtinToolNames?.length) {
524
- const knownBuiltins = new Set(BUILTIN_TOOL_NAMES);
541
+ const knownBuiltins = new Set(agent_types_js_1.BUILTIN_TOOL_NAMES);
525
542
  for (const name of agentConfig.builtinToolNames) {
526
543
  if (!knownBuiltins.has(name)) {
527
544
  options.onToolActivity?.({
@@ -624,7 +641,7 @@ export async function runAgent(ctx, type, prompt, options) {
624
641
  else {
625
642
  const denyTools = new Set(EXCLUDED_TOOL_NAMES);
626
643
  // Keep only the built-ins the agent asked for — deny the rest.
627
- for (const name of BUILTIN_TOOL_NAMES) {
644
+ for (const name of agent_types_js_1.BUILTIN_TOOL_NAMES) {
628
645
  if (!builtinToolNameSet.has(name))
629
646
  denyTools.add(name);
630
647
  }
@@ -634,12 +651,12 @@ export async function runAgent(ctx, type, prompt, options) {
634
651
  }
635
652
  sessionExcludeTools = [...denyTools];
636
653
  }
637
- const settingsManager = SettingsManager.create(configCwd, agentDir);
654
+ const settingsManager = pi_coding_agent_1.SettingsManager.create(configCwd, agentDir);
638
655
  const configuredSessionDir = resolveConfiguredSessionDir(agentConfig?.sessionDir, effectiveCwd);
639
656
  const defaultSessionDir = process.env.PI_CODING_AGENT_SESSION_DIR ?? settingsManager.getSessionDir?.();
640
657
  const sessionManager = agentConfig?.persistSession
641
- ? SessionManager.create(effectiveCwd, configuredSessionDir ?? defaultSessionDir)
642
- : SessionManager.inMemory(effectiveCwd);
658
+ ? pi_coding_agent_1.SessionManager.create(effectiveCwd, configuredSessionDir ?? defaultSessionDir)
659
+ : pi_coding_agent_1.SessionManager.inMemory(effectiveCwd);
643
660
  // Pi 0.80.8 replaced createAgentSession's modelRegistry option with
644
661
  // modelRuntime, but ExtensionContext still exposes only the registry facade.
645
662
  // Pass both so the full supported Pi range retains the parent's providers.
@@ -650,7 +667,9 @@ export async function runAgent(ctx, type, prompt, options) {
650
667
  sessionManager,
651
668
  settingsManager,
652
669
  modelRegistry: ctx.modelRegistry,
653
- ...(parentModelRuntime !== undefined && { modelRuntime: parentModelRuntime }),
670
+ ...(parentModelRuntime != null && {
671
+ modelRuntime: parentModelRuntime,
672
+ }),
654
673
  model,
655
674
  tools: sessionTools,
656
675
  resourceLoader: loader,
@@ -661,7 +680,7 @@ export async function runAgent(ctx, type, prompt, options) {
661
680
  if (thinkingLevel) {
662
681
  sessionOpts.thinkingLevel = thinkingLevel;
663
682
  }
664
- const { session } = await createAgentSession(sessionOpts);
683
+ const { session } = await (0, pi_coding_agent_1.createAgentSession)(sessionOpts);
665
684
  const baseSessionName = agentConfig?.name ?? type;
666
685
  session.setSessionName(options.agentId ? `${baseSessionName}#${options.agentId.slice(0, 8)}` : baseSessionName);
667
686
  // Bind extensions so that session_start fires and extensions can initialize
@@ -744,7 +763,7 @@ export async function runAgent(ctx, type, prompt, options) {
744
763
  // Build the effective prompt: optionally prepend parent context
745
764
  let effectivePrompt = prompt;
746
765
  if (options.inheritContext) {
747
- const parentContext = buildParentContext(ctx);
766
+ const parentContext = (0, context_js_1.buildParentContext)(ctx);
748
767
  if (parentContext) {
749
768
  effectivePrompt = parentContext + prompt;
750
769
  }
@@ -766,7 +785,7 @@ export async function runAgent(ctx, type, prompt, options) {
766
785
  /**
767
786
  * Send a new prompt to an existing session (resume).
768
787
  */
769
- export async function resumeAgent(session, prompt, options = {}) {
788
+ async function resumeAgent(session, prompt, options = {}) {
770
789
  // Boundary for the history fallback: the session already holds prior turns,
771
790
  // so only assistant text produced by THIS resume prompt counts as its output
772
791
  // — a failed resume must not surface the previous turn's answer (#144).
@@ -810,19 +829,19 @@ export async function resumeAgent(session, prompt, options = {}) {
810
829
  * Send a steering message to a running subagent.
811
830
  * The message will interrupt the agent after its current tool execution.
812
831
  */
813
- export async function steerAgent(session, message) {
832
+ async function steerAgent(session, message) {
814
833
  await session.steer(message);
815
834
  }
816
835
  /**
817
836
  * Get the subagent's conversation messages as formatted text.
818
837
  */
819
- export function getAgentConversation(session) {
838
+ function getAgentConversation(session) {
820
839
  const parts = [];
821
840
  for (const msg of session.messages) {
822
841
  if (msg.role === "user") {
823
842
  const text = typeof msg.content === "string"
824
843
  ? msg.content
825
- : extractText(msg.content);
844
+ : (0, context_js_1.extractText)(msg.content);
826
845
  if (text.trim())
827
846
  parts.push(`[User]: ${text.trim()}`);
828
847
  }
@@ -841,7 +860,7 @@ export function getAgentConversation(session) {
841
860
  parts.push(`[Tool Calls]:\n${toolCalls.join("\n")}`);
842
861
  }
843
862
  else if (msg.role === "toolResult") {
844
- const text = extractText(msg.content);
863
+ const text = (0, context_js_1.extractText)(msg.content);
845
864
  const truncated = text.length > 200 ? text.slice(0, 200) + "..." : text;
846
865
  parts.push(`[Tool Result (${msg.toolName})]: ${truncated}`);
847
866
  }