@esso0428/pi-subagents 0.15.4 → 0.15.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -17
- package/README.md +5 -5
- package/dist/agent-history-list.d.ts +18 -0
- package/dist/agent-history-list.js +68 -0
- package/dist/agent-history.d.ts +1 -1
- package/dist/agent-history.js +31 -34
- package/dist/agent-manager.d.ts +22 -8
- package/dist/agent-manager.js +100 -93
- package/dist/agent-runner.js +67 -86
- package/dist/agent-types.js +27 -45
- package/dist/context.js +2 -6
- package/dist/cross-extension-rpc.js +5 -9
- package/dist/custom-agents.js +15 -18
- package/dist/default-agents.js +1 -4
- package/dist/enabled-models.js +11 -16
- package/dist/env.js +1 -4
- package/dist/group-join.js +1 -5
- package/dist/index.js +273 -284
- package/dist/invocation-config.js +2 -6
- package/dist/memory.js +24 -34
- package/dist/model-resolver.js +1 -4
- package/dist/nico-overrides.js +14 -20
- package/dist/output-file.js +16 -22
- package/dist/prompts.js +1 -4
- package/dist/schedule-store.js +16 -21
- package/dist/schedule.js +8 -12
- package/dist/settings.js +15 -23
- package/dist/skill-loader.js +20 -23
- package/dist/status-note.js +1 -4
- package/dist/types.d.ts +0 -1
- package/dist/types.js +1 -4
- package/dist/ui/agent-widget.d.ts +1 -5
- package/dist/ui/agent-widget.js +25 -49
- package/dist/ui/conversation-blocks.d.ts +18 -0
- package/dist/ui/conversation-blocks.js +214 -0
- package/dist/ui/conversation-nvim.d.ts +7 -0
- package/dist/ui/conversation-nvim.js +116 -0
- package/dist/ui/conversation-search.d.ts +38 -0
- package/dist/ui/conversation-search.js +123 -0
- package/dist/ui/conversation-viewer.d.ts +55 -19
- package/dist/ui/conversation-viewer.js +398 -215
- package/dist/ui/fleet-list.d.ts +23 -11
- package/dist/ui/fleet-list.js +85 -78
- package/dist/ui/schedule-menu.js +1 -4
- package/dist/ui/viewer-keys.js +7 -10
- package/dist/usage.js +4 -10
- package/dist/worktree.js +26 -31
- package/package.json +1 -1
- package/src/agent-history-list.ts +91 -0
- package/src/agent-history.ts +12 -17
- package/src/agent-manager.ts +108 -81
- package/src/agent-runner.ts +5 -13
- package/src/index.ts +84 -85
- package/src/output-file.ts +1 -1
- package/src/types.ts +0 -1
- package/src/ui/agent-widget.ts +1 -8
- package/src/ui/conversation-blocks.ts +270 -0
- package/src/ui/conversation-nvim.ts +127 -0
- package/src/ui/conversation-search.ts +161 -0
- package/src/ui/conversation-viewer.ts +376 -205
- package/src/ui/fleet-list.ts +73 -52
- package/test/agent-history-list.test.ts +121 -0
- package/test/agent-manager-history.test.ts +59 -58
- package/test/agent-menu-history-navigation.test.ts +86 -0
- package/test/ui/conversation-blocks.test.ts +72 -0
- package/test/ui/conversation-search.test.ts +55 -0
- package/test/ui/conversation-viewer.test.ts +132 -0
- package/test/ui/fleet-list.test.ts +131 -0
- package/dist/ui/markdown-result.d.ts +0 -3
- package/dist/ui/markdown-result.js +0 -53
- package/src/ui/markdown-result.ts +0 -56
- package/test/agent-history.test.ts +0 -63
- package/test/ui/markdown-result.test.ts +0 -45
package/dist/agent-runner.js
CHANGED
|
@@ -1,47 +1,30 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* agent-runner.ts — Core execution engine: creates sessions, runs agents, collects results.
|
|
4
3
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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");
|
|
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";
|
|
32
15
|
/**
|
|
33
16
|
* Tool names registered by THIS extension. Single source of truth so the
|
|
34
17
|
* registration sites (index.ts) and the subagent exclusion list below can't
|
|
35
18
|
* drift apart. These are our own tools, not pi built-ins, so they can't be
|
|
36
19
|
* derived from pi — but they only need defining once.
|
|
37
20
|
*/
|
|
38
|
-
|
|
21
|
+
export const SUBAGENT_TOOL_NAMES = {
|
|
39
22
|
AGENT: "Agent",
|
|
40
23
|
GET_RESULT: "get_subagent_result",
|
|
41
24
|
STEER: "steer_subagent",
|
|
42
25
|
};
|
|
43
26
|
/** Names of tools registered by this extension that subagents must NOT inherit. */
|
|
44
|
-
const EXCLUDED_TOOL_NAMES = Object.values(
|
|
27
|
+
const EXCLUDED_TOOL_NAMES = Object.values(SUBAGENT_TOOL_NAMES);
|
|
45
28
|
/**
|
|
46
29
|
* Canonical name of an extension for `extensions: [...]` allowlist matching.
|
|
47
30
|
* Lowercased — extension names match case-insensitively so `extensions: [Mcp]`
|
|
@@ -49,10 +32,10 @@ const EXCLUDED_TOOL_NAMES = Object.values(exports.SUBAGENT_TOOL_NAMES);
|
|
|
49
32
|
* Directory extensions (`foo/index.ts`) resolve to the parent directory name;
|
|
50
33
|
* single-file extensions to the basename minus `.ts`/`.js`.
|
|
51
34
|
*/
|
|
52
|
-
function extensionCanonicalName(extPath) {
|
|
53
|
-
const base =
|
|
35
|
+
export function extensionCanonicalName(extPath) {
|
|
36
|
+
const base = basename(extPath);
|
|
54
37
|
const name = base === "index.ts" || base === "index.js"
|
|
55
|
-
?
|
|
38
|
+
? basename(dirname(extPath))
|
|
56
39
|
: base.replace(/\.(ts|js)$/, "");
|
|
57
40
|
return name.toLowerCase();
|
|
58
41
|
}
|
|
@@ -76,18 +59,18 @@ function extensionCanonicalName(extPath) {
|
|
|
76
59
|
* co-located file to `pi-subagents`.
|
|
77
60
|
*/
|
|
78
61
|
function extensionPackageName(extPath) {
|
|
79
|
-
const entry =
|
|
80
|
-
let dir =
|
|
62
|
+
const entry = resolve(extPath);
|
|
63
|
+
let dir = dirname(extPath);
|
|
81
64
|
for (;;) {
|
|
82
65
|
// Climbing into node_modules means we've left the owning package's tree.
|
|
83
|
-
if (
|
|
66
|
+
if (basename(dir) === "node_modules")
|
|
84
67
|
return undefined;
|
|
85
68
|
let pkg;
|
|
86
69
|
try {
|
|
87
|
-
pkg = JSON.parse(
|
|
70
|
+
pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf-8"));
|
|
88
71
|
}
|
|
89
72
|
catch {
|
|
90
|
-
const parent =
|
|
73
|
+
const parent = dirname(dir);
|
|
91
74
|
if (parent === dir)
|
|
92
75
|
return undefined; // walked to the filesystem root
|
|
93
76
|
dir = parent;
|
|
@@ -97,7 +80,7 @@ function extensionPackageName(extPath) {
|
|
|
97
80
|
const entries = pkg.pi?.extensions;
|
|
98
81
|
if (typeof pkg.name === "string" &&
|
|
99
82
|
Array.isArray(entries) &&
|
|
100
|
-
entries.some((e) => typeof e === "string" &&
|
|
83
|
+
entries.some((e) => typeof e === "string" && resolve(dir, e) === entry)) {
|
|
101
84
|
const short = pkg.name.startsWith("@") ? pkg.name.slice(pkg.name.indexOf("/") + 1) : pkg.name;
|
|
102
85
|
return short.toLowerCase();
|
|
103
86
|
}
|
|
@@ -112,7 +95,7 @@ function extensionPackageName(extPath) {
|
|
|
112
95
|
* otherwise only ever match as `src` (the source directory), never by its
|
|
113
96
|
* package name. The path-derived name is preserved, so it keeps matching too.
|
|
114
97
|
*/
|
|
115
|
-
function extensionCanonicalNames(extPath) {
|
|
98
|
+
export function extensionCanonicalNames(extPath) {
|
|
116
99
|
const canonical = extensionCanonicalName(extPath);
|
|
117
100
|
const pkg = extensionPackageName(extPath);
|
|
118
101
|
return pkg && pkg !== canonical ? [canonical, pkg] : [canonical];
|
|
@@ -128,7 +111,7 @@ function extensionCanonicalNames(extPath) {
|
|
|
128
111
|
* everything by canonical name, so path-loaded extensions are matched via their name
|
|
129
112
|
* rather than their post-staging `Extension.path`.
|
|
130
113
|
*/
|
|
131
|
-
function parseExtensionsSpec(entries, cwd) {
|
|
114
|
+
export function parseExtensionsSpec(entries, cwd) {
|
|
132
115
|
const names = new Set();
|
|
133
116
|
const paths = [];
|
|
134
117
|
let wildcard = false;
|
|
@@ -146,9 +129,9 @@ function parseExtensionsSpec(entries, cwd) {
|
|
|
146
129
|
}
|
|
147
130
|
let p = entry;
|
|
148
131
|
if (p === "~" || p.startsWith("~/") || p.startsWith("~\\")) {
|
|
149
|
-
p =
|
|
132
|
+
p = homedir() + p.slice(1);
|
|
150
133
|
}
|
|
151
|
-
const abs =
|
|
134
|
+
const abs = isAbsolute(p) ? p : resolve(cwd, p);
|
|
152
135
|
paths.push(abs);
|
|
153
136
|
names.add(extensionCanonicalName(abs));
|
|
154
137
|
}
|
|
@@ -164,7 +147,7 @@ function parseExtensionsSpec(entries, cwd) {
|
|
|
164
147
|
* `ext:foo` alongside `ext:foo/bar` leaves narrowing in effect (narrowing wins).
|
|
165
148
|
* The split is on the first `/`; extension canonical names never contain `/`.
|
|
166
149
|
*/
|
|
167
|
-
function parseExtSelectors(entries) {
|
|
150
|
+
export function parseExtSelectors(entries) {
|
|
168
151
|
const extNames = new Set();
|
|
169
152
|
const narrowing = new Map();
|
|
170
153
|
for (const raw of entries) {
|
|
@@ -218,7 +201,7 @@ function parseExtSelectors(entries) {
|
|
|
218
201
|
* Only meaningful when extensions are loaded — under `noExtensions`/`isolated` the
|
|
219
202
|
* static `allowedToolNames` allowlist already gates the registry itself.
|
|
220
203
|
*/
|
|
221
|
-
function installExtensionToolScope(session, ctx) {
|
|
204
|
+
export function installExtensionToolScope(session, ctx) {
|
|
222
205
|
const { loader, toolNames, disallowedSet, extNames, narrowing } = ctx;
|
|
223
206
|
// The names allowed right now. Mirrors the `ext:` opt-in flip: when any `ext:`
|
|
224
207
|
// selector is present, extension tools become an explicit allowlist — a loaded
|
|
@@ -277,21 +260,21 @@ function installExtensionToolScope(session, ctx) {
|
|
|
277
260
|
/** Default max turns. undefined = unlimited (no turn limit). */
|
|
278
261
|
let defaultMaxTurns;
|
|
279
262
|
/** Normalize max turns. undefined or 0 = unlimited, otherwise minimum 1. */
|
|
280
|
-
function normalizeMaxTurns(n) {
|
|
263
|
+
export function normalizeMaxTurns(n) {
|
|
281
264
|
if (n == null || n === 0)
|
|
282
265
|
return undefined;
|
|
283
266
|
return Math.max(1, n);
|
|
284
267
|
}
|
|
285
268
|
/** Get the default max turns value. undefined = unlimited. */
|
|
286
|
-
function getDefaultMaxTurns() { return defaultMaxTurns; }
|
|
269
|
+
export function getDefaultMaxTurns() { return defaultMaxTurns; }
|
|
287
270
|
/** Set the default max turns value. undefined or 0 = unlimited, otherwise minimum 1. */
|
|
288
|
-
function setDefaultMaxTurns(n) { defaultMaxTurns = normalizeMaxTurns(n); }
|
|
271
|
+
export function setDefaultMaxTurns(n) { defaultMaxTurns = normalizeMaxTurns(n); }
|
|
289
272
|
/** Additional turns allowed after the soft limit steer message. */
|
|
290
273
|
let graceTurns = 5;
|
|
291
274
|
/** Get the grace turns value. */
|
|
292
|
-
function getGraceTurns() { return graceTurns; }
|
|
275
|
+
export function getGraceTurns() { return graceTurns; }
|
|
293
276
|
/** Set the grace turns value (minimum 1). */
|
|
294
|
-
function setGraceTurns(n) { graceTurns = Math.max(1, n); }
|
|
277
|
+
export function setGraceTurns(n) { graceTurns = Math.max(1, n); }
|
|
295
278
|
/**
|
|
296
279
|
* Try to find the right model for an agent type.
|
|
297
280
|
* Priority: explicit option > config.model > parent model.
|
|
@@ -346,7 +329,7 @@ function getLastAssistantText(session, startIndex = 0) {
|
|
|
346
329
|
const msg = session.messages[i];
|
|
347
330
|
if (msg.role !== "assistant")
|
|
348
331
|
continue;
|
|
349
|
-
const text =
|
|
332
|
+
const text = extractText(msg.content).trim();
|
|
350
333
|
if (text)
|
|
351
334
|
return text;
|
|
352
335
|
}
|
|
@@ -374,7 +357,7 @@ function finalTurnError(session, startIndex = 0) {
|
|
|
374
357
|
if (msg.stopReason === "error") {
|
|
375
358
|
return msg.errorMessage?.trim() || "provider error with no output";
|
|
376
359
|
}
|
|
377
|
-
if (msg.stopReason === "length" && !
|
|
360
|
+
if (msg.stopReason === "length" && !extractText(msg.content).trim()) {
|
|
378
361
|
return "run hit the output token limit before producing any text";
|
|
379
362
|
}
|
|
380
363
|
return undefined;
|
|
@@ -396,20 +379,20 @@ function resolveConfiguredSessionDir(sessionDir, cwd) {
|
|
|
396
379
|
if (!sessionDir)
|
|
397
380
|
return undefined;
|
|
398
381
|
if (sessionDir === "~" || sessionDir.startsWith("~/"))
|
|
399
|
-
return
|
|
400
|
-
if (
|
|
382
|
+
return resolve(homedir(), sessionDir.slice(2));
|
|
383
|
+
if (isAbsolute(sessionDir))
|
|
401
384
|
return sessionDir;
|
|
402
|
-
return
|
|
385
|
+
return resolve(cwd, sessionDir);
|
|
403
386
|
}
|
|
404
|
-
async function runAgent(ctx, type, prompt, options) {
|
|
405
|
-
const config =
|
|
406
|
-
const agentConfig =
|
|
387
|
+
export async function runAgent(ctx, type, prompt, options) {
|
|
388
|
+
const config = getConfig(type);
|
|
389
|
+
const agentConfig = getAgentConfig(type);
|
|
407
390
|
// Resolve working directory: worktree override > parent cwd
|
|
408
391
|
const effectiveCwd = options.cwd ?? ctx.cwd;
|
|
409
392
|
// Filesystem work happens in effectiveCwd; config discovery in configCwd.
|
|
410
393
|
// They differ only for SpawnOptions.cwd spawns (config stays with the parent).
|
|
411
394
|
const configCwd = options.configCwd ?? effectiveCwd;
|
|
412
|
-
const env = await
|
|
395
|
+
const env = await detectEnv(options.pi, effectiveCwd);
|
|
413
396
|
// Get parent system prompt for append-mode agents
|
|
414
397
|
const parentSystemPrompt = ctx.getSystemPrompt();
|
|
415
398
|
// Build prompt extras (memory, skill preloading)
|
|
@@ -422,12 +405,12 @@ async function runAgent(ctx, type, prompt, options) {
|
|
|
422
405
|
const skills = options.isolated ? false : config.skills;
|
|
423
406
|
// Skill preloading: when skills is string[], preload their content into prompt
|
|
424
407
|
if (Array.isArray(skills)) {
|
|
425
|
-
const loaded =
|
|
408
|
+
const loaded = preloadSkills(skills, configCwd);
|
|
426
409
|
if (loaded.length > 0) {
|
|
427
410
|
extras.skillBlocks = loaded;
|
|
428
411
|
}
|
|
429
412
|
}
|
|
430
|
-
let toolNames =
|
|
413
|
+
let toolNames = getToolNamesForType(type);
|
|
431
414
|
// Persistent memory: detect write capability and branch accordingly.
|
|
432
415
|
// Account for disallowedTools — a tool in the base set but on the denylist is not truly available.
|
|
433
416
|
if (agentConfig?.memory) {
|
|
@@ -437,36 +420,36 @@ async function runAgent(ctx, type, prompt, options) {
|
|
|
437
420
|
const hasWriteTools = effectivelyHas("write") || effectivelyHas("edit");
|
|
438
421
|
if (hasWriteTools) {
|
|
439
422
|
// Read-write memory: add any missing memory tool names (read/write/edit)
|
|
440
|
-
const extraNames =
|
|
423
|
+
const extraNames = getMemoryToolNames(existingNames);
|
|
441
424
|
if (extraNames.length > 0)
|
|
442
425
|
toolNames = [...toolNames, ...extraNames];
|
|
443
|
-
extras.memoryBlock =
|
|
426
|
+
extras.memoryBlock = buildMemoryBlock(agentConfig.name, agentConfig.memory, configCwd);
|
|
444
427
|
}
|
|
445
428
|
else {
|
|
446
429
|
// Read-only memory: only add read tool name, use read-only prompt
|
|
447
|
-
const extraNames =
|
|
430
|
+
const extraNames = getReadOnlyMemoryToolNames(existingNames);
|
|
448
431
|
if (extraNames.length > 0)
|
|
449
432
|
toolNames = [...toolNames, ...extraNames];
|
|
450
|
-
extras.memoryBlock =
|
|
433
|
+
extras.memoryBlock = buildReadOnlyMemoryBlock(agentConfig.name, agentConfig.memory, configCwd);
|
|
451
434
|
}
|
|
452
435
|
}
|
|
453
436
|
// Build system prompt from agent config
|
|
454
437
|
let systemPrompt;
|
|
455
438
|
if (agentConfig) {
|
|
456
|
-
systemPrompt =
|
|
439
|
+
systemPrompt = buildAgentPrompt(agentConfig, effectiveCwd, env, parentSystemPrompt, extras);
|
|
457
440
|
}
|
|
458
441
|
else {
|
|
459
442
|
// Unknown type fallback: spread the canonical general-purpose config (defensive —
|
|
460
443
|
// unreachable in practice since index.ts resolves unknown types before calling runAgent).
|
|
461
|
-
const fallback =
|
|
444
|
+
const fallback = DEFAULT_AGENTS.get("general-purpose");
|
|
462
445
|
if (!fallback)
|
|
463
446
|
throw new Error(`No fallback config available for unknown type "${type}"`);
|
|
464
|
-
systemPrompt =
|
|
447
|
+
systemPrompt = buildAgentPrompt({ ...fallback, name: type }, effectiveCwd, env, parentSystemPrompt, extras);
|
|
465
448
|
}
|
|
466
449
|
// When skills is string[], we've already preloaded them into the prompt.
|
|
467
450
|
// Still pass noSkills: true since we don't need the skill loader to load them again.
|
|
468
451
|
const noSkills = skills === false || Array.isArray(skills);
|
|
469
|
-
const agentDir =
|
|
452
|
+
const agentDir = getAgentDir();
|
|
470
453
|
// Extension loading:
|
|
471
454
|
// - true → all default-discovered extensions
|
|
472
455
|
// - false → none (noExtensions)
|
|
@@ -519,7 +502,7 @@ async function runAgent(ctx, type, prompt, options) {
|
|
|
519
502
|
}),
|
|
520
503
|
};
|
|
521
504
|
};
|
|
522
|
-
const loader = new
|
|
505
|
+
const loader = new DefaultResourceLoader({
|
|
523
506
|
cwd: configCwd,
|
|
524
507
|
agentDir,
|
|
525
508
|
noExtensions,
|
|
@@ -538,7 +521,7 @@ async function runAgent(ctx, type, prompt, options) {
|
|
|
538
521
|
// this produced a silently broken agent (#75) — pi-mono accepted the bogus name
|
|
539
522
|
// into the allowlist, then dropped it at registration with no signal back.
|
|
540
523
|
if (agentConfig?.builtinToolNames?.length) {
|
|
541
|
-
const knownBuiltins = new Set(
|
|
524
|
+
const knownBuiltins = new Set(BUILTIN_TOOL_NAMES);
|
|
542
525
|
for (const name of agentConfig.builtinToolNames) {
|
|
543
526
|
if (!knownBuiltins.has(name)) {
|
|
544
527
|
options.onToolActivity?.({
|
|
@@ -641,7 +624,7 @@ async function runAgent(ctx, type, prompt, options) {
|
|
|
641
624
|
else {
|
|
642
625
|
const denyTools = new Set(EXCLUDED_TOOL_NAMES);
|
|
643
626
|
// Keep only the built-ins the agent asked for — deny the rest.
|
|
644
|
-
for (const name of
|
|
627
|
+
for (const name of BUILTIN_TOOL_NAMES) {
|
|
645
628
|
if (!builtinToolNameSet.has(name))
|
|
646
629
|
denyTools.add(name);
|
|
647
630
|
}
|
|
@@ -651,25 +634,23 @@ async function runAgent(ctx, type, prompt, options) {
|
|
|
651
634
|
}
|
|
652
635
|
sessionExcludeTools = [...denyTools];
|
|
653
636
|
}
|
|
654
|
-
const settingsManager =
|
|
637
|
+
const settingsManager = SettingsManager.create(configCwd, agentDir);
|
|
655
638
|
const configuredSessionDir = resolveConfiguredSessionDir(agentConfig?.sessionDir, effectiveCwd);
|
|
656
639
|
const defaultSessionDir = process.env.PI_CODING_AGENT_SESSION_DIR ?? settingsManager.getSessionDir?.();
|
|
657
640
|
const sessionManager = agentConfig?.persistSession
|
|
658
|
-
?
|
|
659
|
-
:
|
|
641
|
+
? SessionManager.create(effectiveCwd, configuredSessionDir ?? defaultSessionDir)
|
|
642
|
+
: SessionManager.inMemory(effectiveCwd);
|
|
660
643
|
// Pi 0.80.8 replaced createAgentSession's modelRegistry option with
|
|
661
644
|
// modelRuntime, but ExtensionContext still exposes only the registry facade.
|
|
662
645
|
// Pass both so the full supported Pi range retains the parent's providers.
|
|
663
|
-
const parentModelRuntime = ctx.modelRegistry.runtime;
|
|
646
|
+
const parentModelRuntime = ctx.modelRegistry.runtime ?? undefined;
|
|
664
647
|
const sessionOpts = {
|
|
665
648
|
cwd: effectiveCwd,
|
|
666
649
|
agentDir,
|
|
667
650
|
sessionManager,
|
|
668
651
|
settingsManager,
|
|
669
652
|
modelRegistry: ctx.modelRegistry,
|
|
670
|
-
...(parentModelRuntime
|
|
671
|
-
modelRuntime: parentModelRuntime,
|
|
672
|
-
}),
|
|
653
|
+
...(parentModelRuntime !== undefined && { modelRuntime: parentModelRuntime }),
|
|
673
654
|
model,
|
|
674
655
|
tools: sessionTools,
|
|
675
656
|
resourceLoader: loader,
|
|
@@ -680,7 +661,7 @@ async function runAgent(ctx, type, prompt, options) {
|
|
|
680
661
|
if (thinkingLevel) {
|
|
681
662
|
sessionOpts.thinkingLevel = thinkingLevel;
|
|
682
663
|
}
|
|
683
|
-
const { session } = await
|
|
664
|
+
const { session } = await createAgentSession(sessionOpts);
|
|
684
665
|
const baseSessionName = agentConfig?.name ?? type;
|
|
685
666
|
session.setSessionName(options.agentId ? `${baseSessionName}#${options.agentId.slice(0, 8)}` : baseSessionName);
|
|
686
667
|
// Bind extensions so that session_start fires and extensions can initialize
|
|
@@ -763,7 +744,7 @@ async function runAgent(ctx, type, prompt, options) {
|
|
|
763
744
|
// Build the effective prompt: optionally prepend parent context
|
|
764
745
|
let effectivePrompt = prompt;
|
|
765
746
|
if (options.inheritContext) {
|
|
766
|
-
const parentContext =
|
|
747
|
+
const parentContext = buildParentContext(ctx);
|
|
767
748
|
if (parentContext) {
|
|
768
749
|
effectivePrompt = parentContext + prompt;
|
|
769
750
|
}
|
|
@@ -785,7 +766,7 @@ async function runAgent(ctx, type, prompt, options) {
|
|
|
785
766
|
/**
|
|
786
767
|
* Send a new prompt to an existing session (resume).
|
|
787
768
|
*/
|
|
788
|
-
async function resumeAgent(session, prompt, options = {}) {
|
|
769
|
+
export async function resumeAgent(session, prompt, options = {}) {
|
|
789
770
|
// Boundary for the history fallback: the session already holds prior turns,
|
|
790
771
|
// so only assistant text produced by THIS resume prompt counts as its output
|
|
791
772
|
// — a failed resume must not surface the previous turn's answer (#144).
|
|
@@ -829,19 +810,19 @@ async function resumeAgent(session, prompt, options = {}) {
|
|
|
829
810
|
* Send a steering message to a running subagent.
|
|
830
811
|
* The message will interrupt the agent after its current tool execution.
|
|
831
812
|
*/
|
|
832
|
-
async function steerAgent(session, message) {
|
|
813
|
+
export async function steerAgent(session, message) {
|
|
833
814
|
await session.steer(message);
|
|
834
815
|
}
|
|
835
816
|
/**
|
|
836
817
|
* Get the subagent's conversation messages as formatted text.
|
|
837
818
|
*/
|
|
838
|
-
function getAgentConversation(session) {
|
|
819
|
+
export function getAgentConversation(session) {
|
|
839
820
|
const parts = [];
|
|
840
821
|
for (const msg of session.messages) {
|
|
841
822
|
if (msg.role === "user") {
|
|
842
823
|
const text = typeof msg.content === "string"
|
|
843
824
|
? msg.content
|
|
844
|
-
:
|
|
825
|
+
: extractText(msg.content);
|
|
845
826
|
if (text.trim())
|
|
846
827
|
parts.push(`[User]: ${text.trim()}`);
|
|
847
828
|
}
|
|
@@ -860,7 +841,7 @@ function getAgentConversation(session) {
|
|
|
860
841
|
parts.push(`[Tool Calls]:\n${toolCalls.join("\n")}`);
|
|
861
842
|
}
|
|
862
843
|
else if (msg.role === "toolResult") {
|
|
863
|
-
const text =
|
|
844
|
+
const text = extractText(msg.content);
|
|
864
845
|
const truncated = text.length > 200 ? text.slice(0, 200) + "..." : text;
|
|
865
846
|
parts.push(`[Tool Result (${msg.toolName})]: ${truncated}`);
|
|
866
847
|
}
|
package/dist/agent-types.js
CHANGED
|
@@ -1,30 +1,12 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* agent-types.ts — Unified agent type registry.
|
|
4
3
|
*
|
|
5
4
|
* Merges embedded default agents with user-defined agents from .pi/agents/*.md, .agents/agents/*.md, and global agents.
|
|
6
5
|
* User agents override defaults with the same name. Disabled agents are kept but excluded from spawning.
|
|
7
6
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
exports.setDefaultsDisabled = setDefaultsDisabled;
|
|
12
|
-
exports.registerAgents = registerAgents;
|
|
13
|
-
exports.applyNicoOverrides = applyNicoOverrides;
|
|
14
|
-
exports.resolveType = resolveType;
|
|
15
|
-
exports.getAgentConfig = getAgentConfig;
|
|
16
|
-
exports.getAvailableTypes = getAvailableTypes;
|
|
17
|
-
exports.getAllTypes = getAllTypes;
|
|
18
|
-
exports.getDefaultAgentNames = getDefaultAgentNames;
|
|
19
|
-
exports.getUserAgentNames = getUserAgentNames;
|
|
20
|
-
exports.isValidType = isValidType;
|
|
21
|
-
exports.getMemoryToolNames = getMemoryToolNames;
|
|
22
|
-
exports.getReadOnlyMemoryToolNames = getReadOnlyMemoryToolNames;
|
|
23
|
-
exports.getToolNamesForType = getToolNamesForType;
|
|
24
|
-
exports.getConfig = getConfig;
|
|
25
|
-
const pi_coding_agent_1 = require("@earendil-works/pi-coding-agent");
|
|
26
|
-
const default_agents_js_1 = require("./default-agents.js");
|
|
27
|
-
const nico_overrides_js_1 = require("./nico-overrides.js");
|
|
7
|
+
import { createCodingTools, createReadOnlyTools } from "@earendil-works/pi-coding-agent";
|
|
8
|
+
import { DEFAULT_AGENTS } from "./default-agents.js";
|
|
9
|
+
import { applyNicoOverridesToMap, readNicoAgentOverrides } from "./nico-overrides.js";
|
|
28
10
|
/**
|
|
29
11
|
* All known built-in tool names, derived from pi's own tool factories rather
|
|
30
12
|
* than hardcoded so the set tracks pi-mono if it adds/renames a built-in.
|
|
@@ -33,27 +15,27 @@ const nico_overrides_js_1 = require("./nico-overrides.js");
|
|
|
33
15
|
* (read, bash, edit, write, grep, find, ls). The `cwd` only binds tool
|
|
34
16
|
* operations we never invoke here — we read each tool's `.name` and discard it.
|
|
35
17
|
*/
|
|
36
|
-
|
|
37
|
-
...new Set([...
|
|
18
|
+
export const BUILTIN_TOOL_NAMES = [
|
|
19
|
+
...new Set([...createCodingTools("."), ...createReadOnlyTools(".")].map((t) => t.name)),
|
|
38
20
|
];
|
|
39
21
|
/** Unified runtime registry of all agents (defaults + user-defined). */
|
|
40
22
|
const agents = new Map();
|
|
41
23
|
/** When true, DEFAULT_AGENTS are skipped during registration. */
|
|
42
24
|
let disableDefaults = false;
|
|
43
25
|
/** Check whether default agents are disabled. */
|
|
44
|
-
function isDefaultsDisabled() { return disableDefaults; }
|
|
26
|
+
export function isDefaultsDisabled() { return disableDefaults; }
|
|
45
27
|
/** Set whether default agents are disabled. */
|
|
46
|
-
function setDefaultsDisabled(b) { disableDefaults = b; }
|
|
28
|
+
export function setDefaultsDisabled(b) { disableDefaults = b; }
|
|
47
29
|
/**
|
|
48
30
|
* Register agents into the unified registry.
|
|
49
31
|
* Starts with DEFAULT_AGENTS, then overlays user agents (overrides defaults with same name).
|
|
50
32
|
* Disabled agents (enabled === false) are kept in the registry but excluded from spawning.
|
|
51
33
|
*/
|
|
52
|
-
function registerAgents(userAgents) {
|
|
34
|
+
export function registerAgents(userAgents) {
|
|
53
35
|
agents.clear();
|
|
54
36
|
// Start with defaults (unless disabled via settings)
|
|
55
37
|
if (!disableDefaults) {
|
|
56
|
-
for (const [name, config] of
|
|
38
|
+
for (const [name, config] of DEFAULT_AGENTS) {
|
|
57
39
|
agents.set(name, config);
|
|
58
40
|
}
|
|
59
41
|
}
|
|
@@ -68,9 +50,9 @@ function registerAgents(userAgents) {
|
|
|
68
50
|
* them as the highest-priority layer. Auto-registers agents that don't
|
|
69
51
|
* exist in the registry yet.
|
|
70
52
|
*/
|
|
71
|
-
function applyNicoOverrides() {
|
|
72
|
-
const { overrides, defaultModel } =
|
|
73
|
-
|
|
53
|
+
export function applyNicoOverrides() {
|
|
54
|
+
const { overrides, defaultModel } = readNicoAgentOverrides(process.cwd());
|
|
55
|
+
applyNicoOverridesToMap(agents, overrides, defaultModel);
|
|
74
56
|
}
|
|
75
57
|
/** Case-insensitive key resolution. */
|
|
76
58
|
function resolveKey(name) {
|
|
@@ -84,38 +66,38 @@ function resolveKey(name) {
|
|
|
84
66
|
return undefined;
|
|
85
67
|
}
|
|
86
68
|
/** Resolve a type name case-insensitively. Returns the canonical key or undefined. */
|
|
87
|
-
function resolveType(name) {
|
|
69
|
+
export function resolveType(name) {
|
|
88
70
|
return resolveKey(name);
|
|
89
71
|
}
|
|
90
72
|
/** Get the agent config for a type (case-insensitive). */
|
|
91
|
-
function getAgentConfig(name) {
|
|
73
|
+
export function getAgentConfig(name) {
|
|
92
74
|
const key = resolveKey(name);
|
|
93
75
|
return key ? agents.get(key) : undefined;
|
|
94
76
|
}
|
|
95
77
|
/** Get all enabled type names (for spawning and tool descriptions). */
|
|
96
|
-
function getAvailableTypes() {
|
|
78
|
+
export function getAvailableTypes() {
|
|
97
79
|
return [...agents.entries()]
|
|
98
80
|
.filter(([_, config]) => config.enabled !== false)
|
|
99
81
|
.map(([name]) => name);
|
|
100
82
|
}
|
|
101
83
|
/** Get all type names including disabled (for UI listing). */
|
|
102
|
-
function getAllTypes() {
|
|
84
|
+
export function getAllTypes() {
|
|
103
85
|
return [...agents.keys()];
|
|
104
86
|
}
|
|
105
87
|
/** Get names of default agents currently in the registry. */
|
|
106
|
-
function getDefaultAgentNames() {
|
|
88
|
+
export function getDefaultAgentNames() {
|
|
107
89
|
return [...agents.entries()]
|
|
108
90
|
.filter(([_, config]) => config.isDefault === true)
|
|
109
91
|
.map(([name]) => name);
|
|
110
92
|
}
|
|
111
93
|
/** Get names of user-defined agents (non-defaults) currently in the registry. */
|
|
112
|
-
function getUserAgentNames() {
|
|
94
|
+
export function getUserAgentNames() {
|
|
113
95
|
return [...agents.entries()]
|
|
114
96
|
.filter(([_, config]) => config.isDefault !== true)
|
|
115
97
|
.map(([name]) => name);
|
|
116
98
|
}
|
|
117
99
|
/** Check if a type is valid and enabled (case-insensitive). */
|
|
118
|
-
function isValidType(type) {
|
|
100
|
+
export function isValidType(type) {
|
|
119
101
|
const key = resolveKey(type);
|
|
120
102
|
if (!key)
|
|
121
103
|
return false;
|
|
@@ -126,7 +108,7 @@ const MEMORY_TOOL_NAMES = ["read", "write", "edit"];
|
|
|
126
108
|
/**
|
|
127
109
|
* Get memory tool names (read/write/edit) not already in the provided set.
|
|
128
110
|
*/
|
|
129
|
-
function getMemoryToolNames(existingToolNames) {
|
|
111
|
+
export function getMemoryToolNames(existingToolNames) {
|
|
130
112
|
return MEMORY_TOOL_NAMES.filter(n => !existingToolNames.has(n));
|
|
131
113
|
}
|
|
132
114
|
/** Tool names needed for read-only memory access. */
|
|
@@ -134,27 +116,27 @@ const READONLY_MEMORY_TOOL_NAMES = ["read"];
|
|
|
134
116
|
/**
|
|
135
117
|
* Get read-only memory tool names not already in the provided set.
|
|
136
118
|
*/
|
|
137
|
-
function getReadOnlyMemoryToolNames(existingToolNames) {
|
|
119
|
+
export function getReadOnlyMemoryToolNames(existingToolNames) {
|
|
138
120
|
return READONLY_MEMORY_TOOL_NAMES.filter(n => !existingToolNames.has(n));
|
|
139
121
|
}
|
|
140
122
|
/** Get built-in tool names for a type (case-insensitive). */
|
|
141
|
-
function getToolNamesForType(type) {
|
|
123
|
+
export function getToolNamesForType(type) {
|
|
142
124
|
const key = resolveKey(type);
|
|
143
125
|
const raw = key ? agents.get(key) : undefined;
|
|
144
126
|
const config = raw?.enabled !== false ? raw : undefined;
|
|
145
127
|
// `undefined` (definition omitted the field) → all built-ins; an explicit `[]`
|
|
146
128
|
// (`tools: none` or a `tools:` with only `ext:` entries) → zero built-ins.
|
|
147
|
-
return config?.builtinToolNames ?? [...
|
|
129
|
+
return config?.builtinToolNames ?? [...BUILTIN_TOOL_NAMES];
|
|
148
130
|
}
|
|
149
131
|
/** Get config for a type (case-insensitive, returns a SubagentTypeConfig-compatible object). Falls back to general-purpose. */
|
|
150
|
-
function getConfig(type) {
|
|
132
|
+
export function getConfig(type) {
|
|
151
133
|
const key = resolveKey(type);
|
|
152
134
|
const config = key ? agents.get(key) : undefined;
|
|
153
135
|
if (config && config.enabled !== false) {
|
|
154
136
|
return {
|
|
155
137
|
displayName: config.displayName ?? config.name,
|
|
156
138
|
description: config.description,
|
|
157
|
-
builtinToolNames: config.builtinToolNames ??
|
|
139
|
+
builtinToolNames: config.builtinToolNames ?? BUILTIN_TOOL_NAMES,
|
|
158
140
|
extensions: config.extensions,
|
|
159
141
|
excludeExtensions: config.excludeExtensions,
|
|
160
142
|
skills: config.skills,
|
|
@@ -167,7 +149,7 @@ function getConfig(type) {
|
|
|
167
149
|
return {
|
|
168
150
|
displayName: gp.displayName ?? gp.name,
|
|
169
151
|
description: gp.description,
|
|
170
|
-
builtinToolNames: gp.builtinToolNames ??
|
|
152
|
+
builtinToolNames: gp.builtinToolNames ?? BUILTIN_TOOL_NAMES,
|
|
171
153
|
extensions: gp.extensions,
|
|
172
154
|
excludeExtensions: gp.excludeExtensions,
|
|
173
155
|
skills: gp.skills,
|
|
@@ -178,7 +160,7 @@ function getConfig(type) {
|
|
|
178
160
|
return {
|
|
179
161
|
displayName: "Agent",
|
|
180
162
|
description: "General-purpose agent for complex, multi-step tasks",
|
|
181
|
-
builtinToolNames:
|
|
163
|
+
builtinToolNames: BUILTIN_TOOL_NAMES,
|
|
182
164
|
extensions: true,
|
|
183
165
|
skills: true,
|
|
184
166
|
promptMode: "append",
|
package/dist/context.js
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* context.ts — Extract parent conversation context for subagent inheritance.
|
|
4
3
|
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.extractText = extractText;
|
|
7
|
-
exports.buildParentContext = buildParentContext;
|
|
8
4
|
/** Extract text from a message content block array. */
|
|
9
|
-
function extractText(content) {
|
|
5
|
+
export function extractText(content) {
|
|
10
6
|
return content
|
|
11
7
|
.filter((c) => c.type === "text")
|
|
12
8
|
.map((c) => c.text ?? "")
|
|
@@ -17,7 +13,7 @@ function extractText(content) {
|
|
|
17
13
|
* Used when inherit_context is true to give the subagent visibility
|
|
18
14
|
* into what has been discussed/done so far.
|
|
19
15
|
*/
|
|
20
|
-
function buildParentContext(ctx) {
|
|
16
|
+
export function buildParentContext(ctx) {
|
|
21
17
|
const entries = ctx.sessionManager.getBranch();
|
|
22
18
|
if (!entries || entries.length === 0)
|
|
23
19
|
return "";
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Cross-extension RPC handlers for the subagents extension.
|
|
4
3
|
*
|
|
@@ -9,12 +8,9 @@
|
|
|
9
8
|
* success → { success: true, data?: T }
|
|
10
9
|
* error → { success: false, error: string }
|
|
11
10
|
*/
|
|
12
|
-
|
|
13
|
-
exports.PROTOCOL_VERSION = void 0;
|
|
14
|
-
exports.registerRpcHandlers = registerRpcHandlers;
|
|
15
|
-
const model_resolver_js_1 = require("./model-resolver.js");
|
|
11
|
+
import { resolveModel } from "./model-resolver.js";
|
|
16
12
|
/** RPC protocol version — bumped when the envelope or method contracts change. */
|
|
17
|
-
|
|
13
|
+
export const PROTOCOL_VERSION = 2;
|
|
18
14
|
/**
|
|
19
15
|
* Wire a single RPC handler: listen on `channel`, run `fn(params)`,
|
|
20
16
|
* emit the reply envelope on `channel:reply:${requestId}`.
|
|
@@ -40,10 +36,10 @@ function handleRpc(events, channel, fn) {
|
|
|
40
36
|
* Register ping, spawn, and stop RPC handlers on the event bus.
|
|
41
37
|
* Returns unsub functions for cleanup.
|
|
42
38
|
*/
|
|
43
|
-
function registerRpcHandlers(deps) {
|
|
39
|
+
export function registerRpcHandlers(deps) {
|
|
44
40
|
const { events, pi, getCtx, manager } = deps;
|
|
45
41
|
const unsubPing = handleRpc(events, "subagents:rpc:ping", () => {
|
|
46
|
-
return { version:
|
|
42
|
+
return { version: PROTOCOL_VERSION };
|
|
47
43
|
});
|
|
48
44
|
const unsubSpawn = handleRpc(events, "subagents:rpc:spawn", ({ type, prompt, options }) => {
|
|
49
45
|
const ctx = getCtx();
|
|
@@ -61,7 +57,7 @@ function registerRpcHandlers(deps) {
|
|
|
61
57
|
if (!registry) {
|
|
62
58
|
throw new Error(`Model override "${normalizedOptions.model}" provided but ctx.modelRegistry is unavailable`);
|
|
63
59
|
}
|
|
64
|
-
const resolved =
|
|
60
|
+
const resolved = resolveModel(normalizedOptions.model, registry);
|
|
65
61
|
if (typeof resolved === "string") {
|
|
66
62
|
// resolveModel returns a human-readable error string when the
|
|
67
63
|
// input doesn't match any available model. Surface it instead of
|