pi-subagents-lite 0.4.1 → 1.0.0
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/README.md +128 -28
- package/package.json +6 -3
- package/src/agent-discovery.ts +11 -17
- package/src/agent-manager.ts +3 -17
- package/src/agent-runner.ts +248 -60
- package/src/agent-types.ts +65 -19
- package/src/config-io.ts +1 -3
- package/src/context.ts +9 -6
- package/src/default-agents.ts +3 -3
- package/src/index.ts +7 -4
- package/src/menus.ts +7 -7
- package/src/model-selector.ts +1 -7
- package/src/output-file.ts +3 -6
- package/src/prompts.ts +2 -3
- package/src/result-viewer.ts +1 -4
- package/src/skill-loader.ts +0 -2
- package/src/tool-execution.ts +8 -13
- package/src/types.ts +20 -22
- package/src/ui/agent-widget.ts +2 -5
- package/src/utils.ts +4 -5
package/src/menus.ts
CHANGED
|
@@ -453,8 +453,8 @@ async function handleAgentBriefing(ctx: ExtensionCommandContext): Promise<void>
|
|
|
453
453
|
lines.push(config.description);
|
|
454
454
|
lines.push("");
|
|
455
455
|
|
|
456
|
-
if (config.
|
|
457
|
-
lines.push(`**Tools:** ${config.
|
|
456
|
+
if (config.registeredTools) {
|
|
457
|
+
lines.push(`**Tools:** ${config.registeredTools.join(", ")}`);
|
|
458
458
|
}
|
|
459
459
|
if (config.model) {
|
|
460
460
|
lines.push(`**Default model:** ${config.model}`);
|
|
@@ -533,7 +533,7 @@ export async function showConcurrencySettingsMenu(
|
|
|
533
533
|
items.push(`Default concurrency limit · ${__config.concurrency.default}`);
|
|
534
534
|
actions.push(async () => {
|
|
535
535
|
await promptConcurrencyInput(
|
|
536
|
-
ctx, "Default
|
|
536
|
+
ctx, "Default limit", __config.concurrency.default,
|
|
537
537
|
(value) => { __config.concurrency.default = value; },
|
|
538
538
|
);
|
|
539
539
|
});
|
|
@@ -839,13 +839,13 @@ async function showAgentTypes(ctx: ExtensionCommandContext): Promise<void> {
|
|
|
839
839
|
for (const name of types) {
|
|
840
840
|
const cfg = getAgentConfig(name);
|
|
841
841
|
if (!cfg) continue;
|
|
842
|
-
const
|
|
842
|
+
const hidden = cfg.hidden === true ? " [HIDDEN]" : "";
|
|
843
843
|
const model = cfg.model ? ` Model: ${cfg.model}` : "";
|
|
844
|
-
const tools = cfg.
|
|
845
|
-
? ` Tools: ${cfg.
|
|
844
|
+
const tools = cfg.registeredTools
|
|
845
|
+
? ` Tools: ${cfg.registeredTools.join(", ")}`
|
|
846
846
|
: " Tools: all built-in tools";
|
|
847
847
|
const source = cfg.source ? ` Source: ${cfg.source}` : "";
|
|
848
|
-
lines.push(` ${name}${
|
|
848
|
+
lines.push(` ${name}${hidden}`);
|
|
849
849
|
lines.push(` ${cfg.description}`);
|
|
850
850
|
if (model) lines.push(model);
|
|
851
851
|
lines.push(tools);
|
package/src/model-selector.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* model-selector.ts — TUI model selection dialog.
|
|
3
3
|
*
|
|
4
|
-
* Ported from subagent-lazy/model-selector.ts verbatim.
|
|
5
|
-
* Used by the /agents menu for model selection.
|
|
6
|
-
*
|
|
7
4
|
* Reuses the same building blocks as pi's ModelSelectorComponent but without
|
|
8
5
|
* the SettingsManager dependency — no side effects, just callbacks.
|
|
9
6
|
*/
|
|
@@ -18,10 +15,7 @@ import {
|
|
|
18
15
|
Text,
|
|
19
16
|
} from "@earendil-works/pi-tui";
|
|
20
17
|
import { DynamicBorder } from "@earendil-works/pi-coding-agent";
|
|
21
|
-
|
|
22
|
-
// Theme type from ctx.ui.custom() callback — avoid deep import that may not resolve
|
|
23
|
-
// in jiti extension loader. The constructor receives the theme instance directly.
|
|
24
|
-
type Theme = any;
|
|
18
|
+
import type { Theme } from "./ui/agent-widget.js";
|
|
25
19
|
|
|
26
20
|
/* ------------------------------------------------------------------ */
|
|
27
21
|
/* Types */
|
package/src/output-file.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* output-file.ts — Human-readable output logging for agent transcripts.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* - Directory created with 0o700 permissions
|
|
8
|
-
* - Append-only, human-readable, supports `tail -f`
|
|
9
|
-
* - Lines: [USER], [TOOL], [ASSISTANT], [DONE] with ISO timestamps
|
|
4
|
+
* Path: /tmp/pi-agent-outputs/<agentId>.log
|
|
5
|
+
* Append-only, human-readable, supports `tail -f`.
|
|
6
|
+
* Lines: [USER], [TOOL], [ASSISTANT], [DONE] with ISO timestamps.
|
|
10
7
|
*/
|
|
11
8
|
|
|
12
9
|
import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
|
package/src/prompts.ts
CHANGED
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import type { AgentConfig, EnvInfo } from "./types.js";
|
|
9
9
|
import type { SkillMeta } from "./skill-loader.js";
|
|
10
|
-
export type { SkillMeta };
|
|
11
10
|
|
|
12
|
-
/** Extra sections to inject into the system prompt (skills
|
|
11
|
+
/** Extra sections to inject into the system prompt (skills). */
|
|
13
12
|
export interface PromptExtras {
|
|
14
13
|
/** Preloaded skill contents to inject (full content). */
|
|
15
14
|
skillBlocks?: { name: string; content: string }[];
|
|
@@ -45,7 +44,7 @@ export function buildAgentPrompt(
|
|
|
45
44
|
envLines.push(`Platform: ${env.platform}`);
|
|
46
45
|
const envBlock = envLines.join("\n");
|
|
47
46
|
|
|
48
|
-
// Build optional extras suffix (skills
|
|
47
|
+
// Build optional extras suffix (skills)
|
|
49
48
|
const extraSections: string[] = [];
|
|
50
49
|
|
|
51
50
|
// Skill metadata whitelist (like Pi's available_skills format)
|
package/src/result-viewer.ts
CHANGED
|
@@ -17,10 +17,7 @@ import {
|
|
|
17
17
|
} from "@earendil-works/pi-tui";
|
|
18
18
|
import { DynamicBorder } from "@earendil-works/pi-coding-agent";
|
|
19
19
|
import { type LifetimeUsage, formatTokens } from "./usage.js";
|
|
20
|
-
import { formatMs } from "./ui/agent-widget.js";
|
|
21
|
-
|
|
22
|
-
// Theme type from ctx.ui.custom() callback
|
|
23
|
-
type Theme = any;
|
|
20
|
+
import { formatMs, type Theme } from "./ui/agent-widget.js";
|
|
24
21
|
|
|
25
22
|
/* ------------------------------------------------------------------ */
|
|
26
23
|
/* Types */
|
package/src/skill-loader.ts
CHANGED
|
@@ -16,8 +16,6 @@
|
|
|
16
16
|
* SKILL.md is a skill — we don't descend into it (Pi: skills don't nest).
|
|
17
17
|
*
|
|
18
18
|
* Symlinks are rejected for security (deviation from Pi, which follows them).
|
|
19
|
-
*
|
|
20
|
-
* Changed from upstream: imports from ./utils.js instead of ./memory.js.
|
|
21
19
|
*/
|
|
22
20
|
|
|
23
21
|
import type { Dirent } from "node:fs";
|
package/src/tool-execution.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type { ExtensionContext, ToolCallEvent } from "@earendil-works/pi-coding-
|
|
|
10
10
|
import type { AgentRecord } from "./types.js";
|
|
11
11
|
import type { SpawnOptions as AgentManagerSpawnOptions } from "./agent-manager.js";
|
|
12
12
|
import type { AgentActivity } from "./ui/agent-widget.js";
|
|
13
|
-
import { resolveType, getAgentConfig } from "./agent-types.js";
|
|
13
|
+
import { resolveType, getAgentConfig, discoverNewAgents } from "./agent-types.js";
|
|
14
14
|
import { resolveModel } from "./model-precedence.js";
|
|
15
15
|
import { addUsage, getLifetimeTotal, getSessionContextPercent, type LifetimeUsage } from "./usage.js";
|
|
16
16
|
|
|
@@ -173,7 +173,12 @@ export async function executeAgentTool(
|
|
|
173
173
|
ctx: ExtensionContext,
|
|
174
174
|
): Promise<any> {
|
|
175
175
|
const type = (params.agent as string) || "general-purpose";
|
|
176
|
-
|
|
176
|
+
let resolvedType = resolveType(type);
|
|
177
|
+
if (!resolvedType) {
|
|
178
|
+
// Not found in registry — try scanning filesystem for agents added during the session
|
|
179
|
+
await discoverNewAgents();
|
|
180
|
+
resolvedType = resolveType(type);
|
|
181
|
+
}
|
|
177
182
|
if (!resolvedType) {
|
|
178
183
|
return errorResult(`Unknown agent type: ${type}`);
|
|
179
184
|
}
|
|
@@ -181,7 +186,6 @@ export async function executeAgentTool(
|
|
|
181
186
|
const prompt = params.prompt as string;
|
|
182
187
|
const description = params.description as string;
|
|
183
188
|
const runInBackground = params.run_in_background as boolean | undefined;
|
|
184
|
-
const isolated = params.isolated as boolean | undefined;
|
|
185
189
|
const maxTurns = params.max_turns as number | undefined ?? getAgentConfig(resolvedType)?.maxTurns;
|
|
186
190
|
const modelStr = params.model as string | undefined;
|
|
187
191
|
const model = findModelInRegistry(modelStr, ctx.modelRegistry, ctx.model);
|
|
@@ -201,7 +205,6 @@ export async function executeAgentTool(
|
|
|
201
205
|
description,
|
|
202
206
|
model,
|
|
203
207
|
maxTurns,
|
|
204
|
-
isolated,
|
|
205
208
|
thinkingLevel,
|
|
206
209
|
modelKey,
|
|
207
210
|
invocation: modelName ? { modelName } : undefined,
|
|
@@ -234,10 +237,7 @@ async function executeSpawnBackground(
|
|
|
234
237
|
widget?.ensureTimer();
|
|
235
238
|
widget?.update();
|
|
236
239
|
|
|
237
|
-
const record = manager.getRecord(agentId)
|
|
238
|
-
if (!record) {
|
|
239
|
-
return errorResult("Failed to create agent");
|
|
240
|
-
}
|
|
240
|
+
const record = manager.getRecord(agentId)!;
|
|
241
241
|
const details: Record<string, unknown> = { type: resolvedType, description: spawnOptions.description };
|
|
242
242
|
const suffix = `A notification will arrive when done - User asks you not to poll or duplicate the delegated work.\n\nAgent ID: ${agentId}`;
|
|
243
243
|
const label = record.status === "queued" ? "Agent queued" : "Agent running";
|
|
@@ -327,11 +327,6 @@ export async function toolCallListener(
|
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
-
// Inject isolated from agent config if not explicitly passed
|
|
331
|
-
if (input.isolated === undefined && agentConfig?.isolated !== undefined) {
|
|
332
|
-
input.isolated = agentConfig.isolated;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
330
|
// Inject thinking from agent config if not explicitly passed
|
|
336
331
|
if (input.thinking === undefined && agentConfig?.thinking !== undefined) {
|
|
337
332
|
input.thinking = agentConfig.thinking;
|
package/src/types.ts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Trimmed from upstream: removed ScheduledSubagent, ScheduleStoreData,
|
|
5
|
-
* IsolationMode, MemoryScope, JoinMode.
|
|
6
|
-
* From AgentConfig: removed memory, isolation, inheritContext, runInBackground.
|
|
7
|
-
* From AgentRecord: removed groupId, joinMode, worktree, worktreeResult.
|
|
8
|
-
* From AgentInvocation: removed inheritContext, isolation.
|
|
2
|
+
* Type definitions for the subagent system.
|
|
9
3
|
*/
|
|
10
4
|
|
|
11
5
|
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
@@ -22,11 +16,21 @@ export interface AgentConfig {
|
|
|
22
16
|
name: string;
|
|
23
17
|
displayName?: string;
|
|
24
18
|
description: string;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
19
|
+
/** Tools to register with the session (controls availability, not LLM visibility). */
|
|
20
|
+
registeredTools?: string[];
|
|
21
|
+
/**
|
|
22
|
+
* Controls which tool schemas the LLM sees. Can reference built-in tools
|
|
23
|
+
* and extension tools. true = all, string[] = listed, false = none.
|
|
24
|
+
* Supports ext/* syntax to include all tools from an extension.
|
|
25
|
+
* Mutually exclusive with excludeTools.
|
|
26
|
+
*/
|
|
27
|
+
tools?: true | string[] | false;
|
|
28
|
+
/** Tool blacklist — all tools except these are visible. Mutually exclusive with tools (when tools is string[]). */
|
|
29
|
+
excludeTools?: string[];
|
|
30
|
+
/** true = inherit all, string[] = only listed, false = none. Mutually exclusive with excludeExtensions. */
|
|
29
31
|
extensions: true | string[] | false;
|
|
32
|
+
/** Extension blacklist — all extensions except these load. Mutually exclusive with extensions (when extensions is string[]). */
|
|
33
|
+
excludeExtensions?: string[];
|
|
30
34
|
/** Whitelist of allowed skills (metadata only in system prompt). true = all, string[] = listed, false = none */
|
|
31
35
|
skills: true | string[] | false;
|
|
32
36
|
/** Skills to preload with full content into system prompt. string[] = listed, false/undefined = none */
|
|
@@ -35,14 +39,13 @@ export interface AgentConfig {
|
|
|
35
39
|
thinking?: ThinkingLevel;
|
|
36
40
|
maxTurns?: number;
|
|
37
41
|
systemPrompt: string;
|
|
38
|
-
|
|
39
|
-
isolated?: boolean;
|
|
42
|
+
|
|
40
43
|
/** true = this is an embedded default agent (informational) */
|
|
41
44
|
isDefault?: boolean;
|
|
42
|
-
/**
|
|
43
|
-
|
|
45
|
+
/** true = agent is hidden from the schema enum but can still be called by name. */
|
|
46
|
+
hidden?: boolean;
|
|
44
47
|
/** Where this agent was loaded from */
|
|
45
|
-
source?: "
|
|
48
|
+
source?: "project" | "global";
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
export interface AgentRecord {
|
|
@@ -87,7 +90,6 @@ export interface AgentInvocation {
|
|
|
87
90
|
modelName?: string;
|
|
88
91
|
thinking?: ThinkingLevel;
|
|
89
92
|
maxTurns?: number;
|
|
90
|
-
isolated?: boolean;
|
|
91
93
|
runInBackground?: boolean;
|
|
92
94
|
}
|
|
93
95
|
|
|
@@ -109,8 +111,4 @@ export interface CompactionInfo {
|
|
|
109
111
|
tokensBefore: number;
|
|
110
112
|
}
|
|
111
113
|
|
|
112
|
-
|
|
113
|
-
export interface ModelKey {
|
|
114
|
-
provider: string;
|
|
115
|
-
modelId: string;
|
|
116
|
-
}
|
|
114
|
+
|
package/src/ui/agent-widget.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* agent-widget.ts — Persistent widget showing running/completed agents above the editor.
|
|
3
|
-
*
|
|
4
|
-
* Ported from upstream pi-subagents.
|
|
5
|
-
* Import paths use relative imports within our extension.
|
|
6
|
-
* addUsage/getLifetimeTotal/getSessionContextPercent imported from ../usage.js.
|
|
7
3
|
*/
|
|
8
4
|
|
|
9
5
|
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
@@ -62,9 +58,10 @@ const TOOL_DISPLAY: Record<string, string> = {
|
|
|
62
58
|
|
|
63
59
|
// ---- Types ----
|
|
64
60
|
|
|
65
|
-
type Theme = {
|
|
61
|
+
export type Theme = {
|
|
66
62
|
fg(color: string, text: string): string;
|
|
67
63
|
bold(text: string): string;
|
|
64
|
+
italic?: (text: string) => string;
|
|
68
65
|
};
|
|
69
66
|
|
|
70
67
|
export type UICtx = {
|
package/src/utils.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* utils.ts — Security helpers
|
|
2
|
+
* utils.ts — Security helpers and general utilities.
|
|
3
3
|
*
|
|
4
|
-
* Security helpers
|
|
5
|
-
*
|
|
6
|
-
* and tool-execution can use them without circular dependencies.
|
|
4
|
+
* Security helpers (isUnsafeName, isSymlink, safeReadFile) protect against
|
|
5
|
+
* path traversal and symlink attacks in agent/skill name resolution.
|
|
7
6
|
*/
|
|
8
7
|
|
|
9
8
|
import { lstatSync, readFileSync } from "node:fs";
|
|
@@ -35,7 +34,7 @@ export function isSymlink(filePath: string): boolean {
|
|
|
35
34
|
*/
|
|
36
35
|
export function safeReadFile(filePath: string): string | undefined {
|
|
37
36
|
try {
|
|
38
|
-
if (
|
|
37
|
+
if (isSymlink(filePath)) return undefined;
|
|
39
38
|
return readFileSync(filePath, "utf-8");
|
|
40
39
|
} catch {
|
|
41
40
|
return undefined;
|