pi-fast-subagent 0.8.0 → 0.9.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/README.md +19 -0
- package/agents.ts +2 -2
- package/format.ts +119 -0
- package/index.ts +58 -896
- package/loader-pool.ts +175 -0
- package/package.json +11 -1
- package/render.ts +459 -0
- package/runner.ts +371 -0
- package/schemas.ts +59 -0
- package/types.ts +55 -0
package/README.md
CHANGED
|
@@ -14,6 +14,25 @@ Runs subagents with `createAgentSession()` in same process instead of spawning `
|
|
|
14
14
|
- User + project agent discovery
|
|
15
15
|
- Project agents override user agents
|
|
16
16
|
- Max nesting depth guard
|
|
17
|
+
- Streamed prompt preview while the parent LLM is writing the subagent task
|
|
18
|
+
- Chronological expanded view (Ctrl+O): subagent tool calls and response text interleaved in execution order
|
|
19
|
+
- Collapsed view shows response + trailing tool calls as an indented tree
|
|
20
|
+
|
|
21
|
+
## Settings
|
|
22
|
+
|
|
23
|
+
Configure preview sizes in `~/.pi/agent/settings.json` or `.pi/settings.json`:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"fastSubagent": {
|
|
28
|
+
"previewLines": 12,
|
|
29
|
+
"promptPreviewLines": 12
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- `previewLines` — response text preview lines in collapsed view (default 12)
|
|
35
|
+
- `promptPreviewLines` — task/prompt preview lines in collapsed view (default 12)
|
|
17
36
|
|
|
18
37
|
## Install
|
|
19
38
|
|
package/agents.ts
CHANGED
|
@@ -48,7 +48,7 @@ export function agentNeedsExtensions(tools: AgentTools): boolean {
|
|
|
48
48
|
|
|
49
49
|
// Default: all tools, matching pi-subagents behavior. Agents opt into lean mode
|
|
50
50
|
// with `tools: builtins` or explicit built-in allowlists.
|
|
51
|
-
function parseToolsField(raw: unknown): AgentTools {
|
|
51
|
+
export function parseToolsField(raw: unknown): AgentTools {
|
|
52
52
|
if (raw === undefined || raw === null) return "all";
|
|
53
53
|
const str = String(raw).trim();
|
|
54
54
|
if (!str) return "all";
|
|
@@ -60,7 +60,7 @@ function parseToolsField(raw: unknown): AgentTools {
|
|
|
60
60
|
return list.length ? list : "all";
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
function parseMaxDepthField(raw: unknown): number {
|
|
63
|
+
export function parseMaxDepthField(raw: unknown): number {
|
|
64
64
|
if (raw === undefined || raw === null || raw === "") return 0;
|
|
65
65
|
const n = Number(raw);
|
|
66
66
|
if (!Number.isFinite(n) || n < 0) return 0;
|
package/format.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure formatting helpers used by runner + render + command layers.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { AgentConfig } from "./agents.js";
|
|
6
|
+
import type { BackgroundSubagentJob } from "./background-types.js";
|
|
7
|
+
import type { RunResult } from "./types.js";
|
|
8
|
+
|
|
9
|
+
export function formatTools(tools: AgentConfig["tools"]): string {
|
|
10
|
+
if (tools === "all") return "all";
|
|
11
|
+
if (tools === "builtins") return "builtins (default)";
|
|
12
|
+
if (tools === "none") return "none";
|
|
13
|
+
return tools.join(", ");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function shortPath(p: unknown): string {
|
|
17
|
+
if (typeof p !== "string") return "";
|
|
18
|
+
const cwd = process.cwd();
|
|
19
|
+
if (p.startsWith(cwd + "/")) return p.slice(cwd.length + 1);
|
|
20
|
+
return p.replace(/^\/Users\/[^/]+\/[^/]+\//, "");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function summarizeToolArgs(toolName: unknown, toolInput: unknown): string {
|
|
24
|
+
const name = String(toolName ?? "");
|
|
25
|
+
const input =
|
|
26
|
+
toolInput && typeof toolInput === "object" ? (toolInput as Record<string, unknown>) : {};
|
|
27
|
+
const filePath = (): string => shortPath(input.path ?? input.file_path) || "";
|
|
28
|
+
switch (name) {
|
|
29
|
+
case "Read":
|
|
30
|
+
case "read":
|
|
31
|
+
case "Write":
|
|
32
|
+
case "write":
|
|
33
|
+
case "Edit":
|
|
34
|
+
case "edit":
|
|
35
|
+
return filePath();
|
|
36
|
+
case "Bash":
|
|
37
|
+
case "bash": {
|
|
38
|
+
const cmd = String(input.command ?? "");
|
|
39
|
+
return cmd.length > 80 ? cmd.slice(0, 77) + "..." : cmd;
|
|
40
|
+
}
|
|
41
|
+
case "Glob":
|
|
42
|
+
case "glob":
|
|
43
|
+
return String(input.pattern ?? "");
|
|
44
|
+
case "find": {
|
|
45
|
+
const pat = String(input.pattern ?? "");
|
|
46
|
+
const p = shortPath(input.path);
|
|
47
|
+
return p ? `${pat} in ${p}` : pat;
|
|
48
|
+
}
|
|
49
|
+
case "Grep":
|
|
50
|
+
case "grep": {
|
|
51
|
+
const pat = String(input.pattern ?? "");
|
|
52
|
+
const g = input.glob ? ` ${input.glob}` : "";
|
|
53
|
+
return `${pat}${g}`;
|
|
54
|
+
}
|
|
55
|
+
case "ls":
|
|
56
|
+
return shortPath(input.path) || "";
|
|
57
|
+
case "subagent": {
|
|
58
|
+
const agent = String(input.agent ?? "");
|
|
59
|
+
const t = String(input.task ?? "");
|
|
60
|
+
const summary = t.length > 50 ? t.slice(0, 47) + "..." : t;
|
|
61
|
+
return agent ? `${agent}: ${summary}` : summary;
|
|
62
|
+
}
|
|
63
|
+
default: {
|
|
64
|
+
for (const v of Object.values(input)) {
|
|
65
|
+
if (typeof v === "string" && v.length > 0)
|
|
66
|
+
return v.length > 60 ? v.slice(0, 57) + "..." : v;
|
|
67
|
+
}
|
|
68
|
+
return "";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function formatDuration(ms: number): string {
|
|
74
|
+
const s = Math.max(0, Math.floor(ms / 1000));
|
|
75
|
+
const m = Math.floor(s / 60);
|
|
76
|
+
const rem = s % 60;
|
|
77
|
+
return m > 0 ? `${m}m ${rem}s` : `${rem}s`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function summarizeTask(task: string, max = 60): string {
|
|
81
|
+
return task.length > max ? task.slice(0, max - 3) + "..." : task;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function formatTokens(n: number): string {
|
|
85
|
+
if (n < 1000) return String(n);
|
|
86
|
+
if (n < 10000) return `${(n / 1000).toFixed(1)}k`;
|
|
87
|
+
return `${Math.round(n / 1000)}k`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function formatUsage(usage: RunResult["usage"], model?: string): string {
|
|
91
|
+
const parts: string[] = [];
|
|
92
|
+
if (usage.turns) parts.push(`${usage.turns} turn${usage.turns > 1 ? "s" : ""}`);
|
|
93
|
+
if (usage.input) parts.push(`↑${formatTokens(usage.input)}`);
|
|
94
|
+
if (usage.output) parts.push(`↓${formatTokens(usage.output)}`);
|
|
95
|
+
if (usage.cost) parts.push(`$${usage.cost.toFixed(4)}`);
|
|
96
|
+
if (model) parts.push(model);
|
|
97
|
+
return parts.join(" ");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function getFinalText(r: RunResult): string {
|
|
101
|
+
if (r.exitCode !== 0) return `Error: ${r.error ?? r.output ?? "(no output)"}`;
|
|
102
|
+
return r.output || "(no output)";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function formatBgJobSummary(job: BackgroundSubagentJob, now = Date.now()): string {
|
|
106
|
+
const dur = job.completedAt ? formatDuration(job.completedAt - job.startedAt) : formatDuration(now - job.startedAt);
|
|
107
|
+
return `${job.id} [${job.status}] ${job.agentName} · ${dur} · ${summarizeTask(job.task)}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function formatBgJobDetails(job: BackgroundSubagentJob, now = Date.now()): string {
|
|
111
|
+
const dur = job.completedAt ? formatDuration(job.completedAt - job.startedAt) : formatDuration(now - job.startedAt);
|
|
112
|
+
const lines = [`${job.id} [${job.status}] ${job.agentName} · ${dur}`, `Task: ${job.task}`];
|
|
113
|
+
if (job.model) lines.push(`Model: ${job.model}`);
|
|
114
|
+
if (job.status === "completed") lines.push(`\nResult:\n${job.resultSummary ?? "(no output)"}`);
|
|
115
|
+
if (job.status === "failed") lines.push(`\nError: ${job.error ?? "(unknown)"}`);
|
|
116
|
+
if (job.status === "cancelled") lines.push("\nCancelled.");
|
|
117
|
+
if (job.status === "running") lines.push("\nStill running.");
|
|
118
|
+
return lines.join("\n");
|
|
119
|
+
}
|