pi-fast-subagent 0.9.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 CHANGED
@@ -14,6 +14,7 @@ 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
17
18
  - Chronological expanded view (Ctrl+O): subagent tool calls and response text interleaved in execution order
18
19
  - Collapsed view shows response + trailing tool calls as an indented tree
19
20
 
package/index.ts CHANGED
@@ -26,7 +26,7 @@ import {
26
26
  summarizeTask,
27
27
  } from "./format.js";
28
28
  import { defaultLoaderPool } from "./loader-pool.js";
29
- import { renderSubagentResult } from "./render.js";
29
+ import { renderSubagentCall, renderSubagentResult } from "./render.js";
30
30
  import { getCurrentDepth, mapConcurrent, runAgent } from "./runner.js";
31
31
  import { SubagentParams } from "./schemas.js";
32
32
  import type { AgentRowStatus, OnUpdate, RunResult, SubagentDetails, ToolCallEntry } from "./types.js";
@@ -317,6 +317,10 @@ export default function (pi: ExtensionAPI) {
317
317
  ].join(" "),
318
318
  parameters: SubagentParams,
319
319
 
320
+ renderCall(args, theme, context) {
321
+ return renderSubagentCall(args, theme, context);
322
+ },
323
+
320
324
  renderResult(result: AgentToolResult<unknown>, opts: ToolRenderResultOptions, theme: Theme) {
321
325
  return renderSubagentResult(result, opts, theme);
322
326
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-fast-subagent",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "In-process subagent delegation for pi with single, parallel, and background modes",
5
5
  "type": "module",
6
6
  "keywords": [
package/render.ts CHANGED
@@ -8,6 +8,7 @@ import { join } from "node:path";
8
8
  import { getAgentDir, Theme, truncateToVisualLines, keyHint } from "@mariozechner/pi-coding-agent";
9
9
  import type { AgentToolResult, ToolRenderResultOptions } from "@mariozechner/pi-coding-agent";
10
10
  import { truncateToWidth, wrapTextWithAnsi } from "@mariozechner/pi-tui";
11
+ import type { Component } from "@mariozechner/pi-tui";
11
12
 
12
13
  import { formatDuration, formatUsage } from "./format.js";
13
14
  import type { SubagentDetails, ToolCallEntry } from "./types.js";
@@ -73,6 +74,116 @@ function readPreviewSettings(): { previewLines: number; promptPreviewLines: numb
73
74
  return _settingsCache;
74
75
  }
75
76
 
77
+ interface SubagentCallArgs {
78
+ agent?: unknown;
79
+ task?: unknown;
80
+ tasks?: unknown;
81
+ action?: unknown;
82
+ background?: unknown;
83
+ }
84
+
85
+ interface SubagentRenderCallContext {
86
+ state: Record<string, unknown>;
87
+ executionStarted: boolean;
88
+ argsComplete: boolean;
89
+ }
90
+
91
+ function asString(v: unknown): string | undefined {
92
+ return typeof v === "string" ? v : undefined;
93
+ }
94
+
95
+ function taskPreviewLines(task: string, width: number, maxLines: number): { lines: string[]; skipped: number } {
96
+ const innerWidth = Math.max(1, width - 2);
97
+ const visual: string[] = [];
98
+ for (const raw of task.split("\n")) {
99
+ try {
100
+ for (const w of wrapTextWithAnsi(raw, innerWidth)) visual.push(w);
101
+ } catch {
102
+ visual.push(truncateToWidth(raw, innerWidth, "..."));
103
+ }
104
+ }
105
+ const lines = visual.slice(0, maxLines).map((line) => truncateToWidth(` ${line}`, width, "..."));
106
+ return { lines, skipped: Math.max(0, visual.length - lines.length) };
107
+ }
108
+
109
+ /**
110
+ * Render tool-call args while provider is still streaming them. This makes long
111
+ * subagent prompt generation visible before execute() can start.
112
+ */
113
+ export function renderSubagentCall(
114
+ args: SubagentCallArgs,
115
+ theme: Theme,
116
+ context: SubagentRenderCallContext,
117
+ ): Component {
118
+ const cache = context.state as {
119
+ callWidth?: number;
120
+ callLines?: string[];
121
+ callKey?: string;
122
+ };
123
+
124
+ return {
125
+ invalidate() {
126
+ cache.callWidth = undefined;
127
+ cache.callLines = undefined;
128
+ cache.callKey = undefined;
129
+ },
130
+ render(width: number): string[] {
131
+ const key = JSON.stringify({ args, executionStarted: context.executionStarted, argsComplete: context.argsComplete, width });
132
+ if (cache.callWidth === width && cache.callKey === key && cache.callLines) return cache.callLines;
133
+
134
+ const out: string[] = [];
135
+ const agent = asString(args.agent);
136
+ const action = asString(args.action);
137
+ const task = asString(args.task);
138
+ const tasks = Array.isArray(args.tasks) ? args.tasks as Array<Record<string, unknown>> : undefined;
139
+ const isParallel = !!tasks?.length;
140
+ const status = context.executionStarted
141
+ ? "running"
142
+ : context.argsComplete
143
+ ? "starting"
144
+ : task || isParallel
145
+ ? "writing prompt"
146
+ : "waiting for prompt";
147
+ const mode = isParallel ? `Parallel (${tasks!.length})` : "Subagent";
148
+ const bg = args.background === true ? " · background" : "";
149
+ const target = agent ? ` ${agent}` : action ? ` ${action}` : "";
150
+ out.push(truncateToWidth(`${theme.fg("toolTitle", mode)}${target}${bg} · ${theme.fg("dim", status)}`, width, "..."));
151
+
152
+ // Once execution starts, result renderer owns prompt display. Keep call row compact.
153
+ if (context.executionStarted) {
154
+ cache.callWidth = width;
155
+ cache.callKey = key;
156
+ cache.callLines = out;
157
+ return out;
158
+ }
159
+
160
+ const maxLines = readPreviewSettings().promptPreviewLines;
161
+ if (task) {
162
+ out.push(truncateToWidth("Prompt:", width, "..."));
163
+ const preview = taskPreviewLines(task, width, maxLines);
164
+ out.push(...preview.lines);
165
+ if (preview.skipped > 0) out.push(truncateToWidth(theme.fg("muted", ` … (${preview.skipped} more lines)`), width, "..."));
166
+ } else if (tasks?.length) {
167
+ const maxRows = Math.max(1, Math.min(maxLines, tasks.length));
168
+ for (let i = 0; i < maxRows; i++) {
169
+ const t = tasks[i]!;
170
+ const rowAgent = asString(t.agent) ?? "?";
171
+ const rowTask = asString(t.task) ?? "";
172
+ out.push(truncateToWidth(` [${rowAgent}] ${rowTask || theme.fg("dim", "writing prompt...")}`, width, "..."));
173
+ }
174
+ if (tasks.length > maxRows) out.push(truncateToWidth(theme.fg("muted", ` … (${tasks.length - maxRows} more task${tasks.length - maxRows === 1 ? "" : "s"})`), width, "..."));
175
+ } else {
176
+ out.push(truncateToWidth(theme.fg("dim", " waiting for streamed tool arguments..."), width, "..."));
177
+ }
178
+
179
+ cache.callWidth = width;
180
+ cache.callKey = key;
181
+ cache.callLines = out;
182
+ return out;
183
+ },
184
+ };
185
+ }
186
+
76
187
  export function renderSubagentResult(
77
188
  result: AgentToolResult<unknown>,
78
189
  { isPartial, expanded }: ToolRenderResultOptions,