pi-fast-subagent 0.9.1 → 0.9.3
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 +2 -2
- package/agents/scout.md +4 -3
- package/index.ts +14 -3
- package/package.json +1 -1
- package/render.ts +83 -12
- package/runner.ts +6 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pi-fast-subagent
|
|
2
2
|
|
|
3
|
-
In-process subagent delegation for [pi](https://github.com/badlogic/pi-mono).
|
|
3
|
+
In-process subagent delegation for [pi](https://github.com/badlogic/pi-mono) with max visibility.
|
|
4
4
|
|
|
5
5
|
Runs subagents with `createAgentSession()` in same process instead of spawning `pi` subprocesses. This removes subprocess cold-start and reuses pi auth/model registry.
|
|
6
6
|
|
|
@@ -289,7 +289,7 @@ Goal: keep this extension **small and focused** — aligned with pi's philosophy
|
|
|
289
289
|
|
|
290
290
|
- Async/background isolation not supported in-process
|
|
291
291
|
- Git worktree isolation not supported
|
|
292
|
-
- Nested subagent
|
|
292
|
+
- Nested subagent spawning disabled by default (`maxDepth: 0`); opt in per agent via frontmatter
|
|
293
293
|
|
|
294
294
|
## Tool Reference
|
|
295
295
|
|
package/agents/scout.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: scout
|
|
3
3
|
description: Explores codebases, maps structure, traces data flow, answers how things work across many files
|
|
4
|
-
model:
|
|
4
|
+
model: openai-codex/gpt-5.4-mini
|
|
5
5
|
|
|
6
6
|
# tools: which tools this agent can use.
|
|
7
7
|
# (omit) → all tools: builtins + every parent extension (default)
|
|
@@ -36,5 +36,6 @@ Output style:
|
|
|
36
36
|
- use sections
|
|
37
37
|
- include file paths
|
|
38
38
|
- include short bullets
|
|
39
|
-
-
|
|
40
|
-
- do not
|
|
39
|
+
- describe what code does, not whether it is good or bad
|
|
40
|
+
- do not rate, review, or analyze quality
|
|
41
|
+
- do not propose code changes
|
package/index.ts
CHANGED
|
@@ -49,6 +49,7 @@ function refreshBgStatus(): void {
|
|
|
49
49
|
_setBgStatus?.(running.length > 0 ? `⧗ ${running.length} bg agent${running.length > 1 ? "s" : ""}` : undefined);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
|
|
52
53
|
// ─── Foreground detach registry ─────────────────────────────────────────────
|
|
53
54
|
|
|
54
55
|
interface ForegroundDetachEntry {
|
|
@@ -417,11 +418,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
417
418
|
const { agent, error } = findAgent(params.agent);
|
|
418
419
|
if (error || !agent) return { content: [{ type: "text", text: error ?? "Not found" }] };
|
|
419
420
|
|
|
421
|
+
const effectiveModel = params.model;
|
|
422
|
+
|
|
420
423
|
if (params.background) {
|
|
421
424
|
const bgAbort = new AbortController();
|
|
422
425
|
const handle: BackgroundHandleLike = { abort: () => bgAbort.abort() };
|
|
423
426
|
const resultPromise: Promise<BackgroundJobResult> = runAgent(
|
|
424
|
-
agent, params.task, cwd,
|
|
427
|
+
agent, params.task, cwd, effectiveModel, bgAbort.signal, undefined,
|
|
425
428
|
).then((r) => ({ summary: r.output, exitCode: r.exitCode, error: r.error, model: r.model }));
|
|
426
429
|
const jobId = getBgManager().adoptHandle(agent.name, params.task, cwd, handle, resultPromise);
|
|
427
430
|
return { content: [{ type: "text", text: `Background job started: ${jobId}\nTo check status, ask me to poll job ${jobId}.` }] };
|
|
@@ -441,7 +444,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
441
444
|
: undefined;
|
|
442
445
|
|
|
443
446
|
const agentRunPromise: Promise<RunResult> = runAgent(
|
|
444
|
-
agent, params.task, cwd,
|
|
447
|
+
agent, params.task, cwd, effectiveModel, agentAbort.signal, wrappedOnUpdate,
|
|
445
448
|
);
|
|
446
449
|
|
|
447
450
|
const bgResultPromise: Promise<BackgroundJobResult> = agentRunPromise
|
|
@@ -546,7 +549,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
546
549
|
parallelAgents[i]!.responseText = (partial.content?.[0] as any)?.text || parallelAgents[i]!.responseText;
|
|
547
550
|
emitParallel(true);
|
|
548
551
|
};
|
|
549
|
-
const result = await runAgent(
|
|
552
|
+
const result = await runAgent(
|
|
553
|
+
agent,
|
|
554
|
+
t.task,
|
|
555
|
+
t.cwd ?? cwd,
|
|
556
|
+
t.model,
|
|
557
|
+
signal,
|
|
558
|
+
agentOnUpdate,
|
|
559
|
+
parentDepth,
|
|
560
|
+
);
|
|
550
561
|
parallelAgents[i]!.status = result.exitCode === 0 ? "done" : "error";
|
|
551
562
|
parallelAgents[i]!.durMs = Date.now() - agentStart;
|
|
552
563
|
parallelAgents[i]!.toolCalls = result.toolCalls;
|
package/package.json
CHANGED
package/render.ts
CHANGED
|
@@ -302,6 +302,16 @@ export function renderSubagentResult(
|
|
|
302
302
|
promptSkipped?: number;
|
|
303
303
|
responseLines?: string[];
|
|
304
304
|
skipped?: number;
|
|
305
|
+
expandedWidth?: number;
|
|
306
|
+
expandedEventsLen?: number;
|
|
307
|
+
expandedLastEventTs?: number;
|
|
308
|
+
expandedTask?: string;
|
|
309
|
+
expandedAgentName?: string;
|
|
310
|
+
expandedToolCallsLen?: number;
|
|
311
|
+
expandedAgentTextLen?: number;
|
|
312
|
+
expandedBodyLines?: string[];
|
|
313
|
+
expandedFooterKey?: string;
|
|
314
|
+
expandedOutputLines?: string[];
|
|
305
315
|
} = {};
|
|
306
316
|
|
|
307
317
|
function renderExpandedChronological(width: number): string[] {
|
|
@@ -334,11 +344,22 @@ export function renderSubagentResult(
|
|
|
334
344
|
}
|
|
335
345
|
}
|
|
336
346
|
} else {
|
|
337
|
-
// Render events chronologically
|
|
347
|
+
// Render events chronologically. Coalesce text deltas to avoid one-line-per-token output.
|
|
338
348
|
let lastWasText = false;
|
|
349
|
+
let textBuffer = "";
|
|
339
350
|
const agentLabel = `${details.agentName ?? "Agent"}:`;
|
|
351
|
+
|
|
352
|
+
const flushTextBuffer = () => {
|
|
353
|
+
if (!textBuffer) return;
|
|
354
|
+
for (const line of textBuffer.split("\n")) {
|
|
355
|
+
for (const w of wrapLine(indent + line, width)) out.push(w);
|
|
356
|
+
}
|
|
357
|
+
textBuffer = "";
|
|
358
|
+
};
|
|
359
|
+
|
|
340
360
|
for (const evt of events) {
|
|
341
361
|
if (evt.type === "tool_start") {
|
|
362
|
+
if (lastWasText) flushTextBuffer();
|
|
342
363
|
lastWasText = false;
|
|
343
364
|
const call = `${evt.toolName}(${evt.argSummary})`;
|
|
344
365
|
toolLineMap.set(evt.toolCallId, out.length);
|
|
@@ -348,10 +369,9 @@ export function renderSubagentResult(
|
|
|
348
369
|
out.push(truncateToWidth(theme.fg("toolTitle", agentLabel), width, "..."));
|
|
349
370
|
lastWasText = true;
|
|
350
371
|
}
|
|
351
|
-
|
|
352
|
-
for (const w of wrapLine(indent + line, width)) out.push(w);
|
|
353
|
-
}
|
|
372
|
+
textBuffer += evt.text;
|
|
354
373
|
} else if (evt.type === "tool_end") {
|
|
374
|
+
if (lastWasText) flushTextBuffer();
|
|
355
375
|
lastWasText = false;
|
|
356
376
|
const toolLineIdx = toolLineMap.get(evt.toolCallId);
|
|
357
377
|
const dur = evt.durMs != null
|
|
@@ -368,13 +388,27 @@ export function renderSubagentResult(
|
|
|
368
388
|
}
|
|
369
389
|
}
|
|
370
390
|
}
|
|
391
|
+
|
|
392
|
+
if (lastWasText) flushTextBuffer();
|
|
371
393
|
}
|
|
372
394
|
|
|
373
395
|
return out;
|
|
374
396
|
}
|
|
375
397
|
|
|
376
398
|
return {
|
|
377
|
-
invalidate() {
|
|
399
|
+
invalidate() {
|
|
400
|
+
cache.width = undefined;
|
|
401
|
+
cache.expandedWidth = undefined;
|
|
402
|
+
cache.expandedEventsLen = undefined;
|
|
403
|
+
cache.expandedLastEventTs = undefined;
|
|
404
|
+
cache.expandedTask = undefined;
|
|
405
|
+
cache.expandedAgentName = undefined;
|
|
406
|
+
cache.expandedToolCallsLen = undefined;
|
|
407
|
+
cache.expandedAgentTextLen = undefined;
|
|
408
|
+
cache.expandedBodyLines = undefined;
|
|
409
|
+
cache.expandedFooterKey = undefined;
|
|
410
|
+
cache.expandedOutputLines = undefined;
|
|
411
|
+
},
|
|
378
412
|
render(width: number): string[] {
|
|
379
413
|
const out: string[] = [];
|
|
380
414
|
const indent = " ";
|
|
@@ -382,15 +416,52 @@ export function renderSubagentResult(
|
|
|
382
416
|
theme.fg("muted", `${indent}… (${count} more line${count === 1 ? "" : "s"})`);
|
|
383
417
|
|
|
384
418
|
if (expanded) {
|
|
385
|
-
|
|
386
|
-
const
|
|
387
|
-
|
|
419
|
+
const events = details.executionEvents || [];
|
|
420
|
+
const lastEventTs = events.length ? events[events.length - 1]!.timestamp : undefined;
|
|
421
|
+
const taskKey = details.task ?? "";
|
|
422
|
+
const agentKey = details.agentName ?? "";
|
|
423
|
+
const bodyCacheHit =
|
|
424
|
+
cache.expandedWidth === width
|
|
425
|
+
&& cache.expandedEventsLen === events.length
|
|
426
|
+
&& cache.expandedLastEventTs === lastEventTs
|
|
427
|
+
&& cache.expandedTask === taskKey
|
|
428
|
+
&& cache.expandedAgentName === agentKey
|
|
429
|
+
&& cache.expandedToolCallsLen === toolCalls.length
|
|
430
|
+
&& cache.expandedAgentTextLen === agentText.length
|
|
431
|
+
&& Array.isArray(cache.expandedBodyLines);
|
|
432
|
+
|
|
433
|
+
let bodyLines: string[];
|
|
434
|
+
if (bodyCacheHit) {
|
|
435
|
+
bodyLines = cache.expandedBodyLines!;
|
|
436
|
+
} else {
|
|
437
|
+
bodyLines = renderExpandedChronological(width);
|
|
438
|
+
cache.expandedWidth = width;
|
|
439
|
+
cache.expandedEventsLen = events.length;
|
|
440
|
+
cache.expandedLastEventTs = lastEventTs;
|
|
441
|
+
cache.expandedTask = taskKey;
|
|
442
|
+
cache.expandedAgentName = agentKey;
|
|
443
|
+
cache.expandedToolCallsLen = toolCalls.length;
|
|
444
|
+
cache.expandedAgentTextLen = agentText.length;
|
|
445
|
+
cache.expandedBodyLines = bodyLines;
|
|
446
|
+
cache.expandedFooterKey = undefined;
|
|
447
|
+
cache.expandedOutputLines = undefined;
|
|
448
|
+
}
|
|
449
|
+
|
|
388
450
|
const status = statusLine();
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
451
|
+
const bgHint = details.running && !details.backgroundJobId
|
|
452
|
+
? truncateToWidth(theme.fg("dim", "Ctrl+Shift+B: move to background"), width, "...")
|
|
453
|
+
: "";
|
|
454
|
+
const footerKey = `${status}__${bgHint}`;
|
|
455
|
+
|
|
456
|
+
if (cache.expandedFooterKey !== footerKey || !Array.isArray(cache.expandedOutputLines)) {
|
|
457
|
+
const expandedOut = [...bodyLines, ""];
|
|
458
|
+
if (status) expandedOut.push(truncateToWidth(status, width, "..."));
|
|
459
|
+
if (bgHint) expandedOut.push(bgHint);
|
|
460
|
+
cache.expandedFooterKey = footerKey;
|
|
461
|
+
cache.expandedOutputLines = expandedOut;
|
|
392
462
|
}
|
|
393
|
-
|
|
463
|
+
|
|
464
|
+
return cache.expandedOutputLines!;
|
|
394
465
|
}
|
|
395
466
|
|
|
396
467
|
// Collapsed view
|
package/runner.ts
CHANGED
|
@@ -114,6 +114,8 @@ export async function runAgent(
|
|
|
114
114
|
});
|
|
115
115
|
await allowUiPaint(coldLoader);
|
|
116
116
|
|
|
117
|
+
const createPrevEnvDepth = process.env[DEPTH_ENV];
|
|
118
|
+
process.env[DEPTH_ENV] = String(depth + 1);
|
|
117
119
|
const loaderLease = await pool.acquire(
|
|
118
120
|
cwd,
|
|
119
121
|
agentDir,
|
|
@@ -134,6 +136,8 @@ export async function runAgent(
|
|
|
134
136
|
session = created.session;
|
|
135
137
|
} catch (e) {
|
|
136
138
|
loaderLease.release();
|
|
139
|
+
if (createPrevEnvDepth === undefined) delete process.env[DEPTH_ENV];
|
|
140
|
+
else process.env[DEPTH_ENV] = createPrevEnvDepth;
|
|
137
141
|
return {
|
|
138
142
|
output: "",
|
|
139
143
|
exitCode: 1,
|
|
@@ -142,6 +146,8 @@ export async function runAgent(
|
|
|
142
146
|
usage: { input: 0, output: 0, cost: 0, turns: 0 },
|
|
143
147
|
};
|
|
144
148
|
}
|
|
149
|
+
if (createPrevEnvDepth === undefined) delete process.env[DEPTH_ENV];
|
|
150
|
+
else process.env[DEPTH_ENV] = createPrevEnvDepth;
|
|
145
151
|
|
|
146
152
|
// Resolve and apply model
|
|
147
153
|
const modelStr = modelOverride ?? agent.model;
|