@bacnh85/pi-subagent 0.8.0 → 0.8.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/CHANGELOG.md +6 -0
- package/README.md +2 -0
- package/agents/reviewer.md +4 -2
- package/extensions/index.ts +30 -18
- package/extensions/runner.ts +6 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.1 (2026-07-15)
|
|
4
|
+
|
|
5
|
+
### Review handoff
|
|
6
|
+
|
|
7
|
+
- Reviewer findings now require reproduction or evidence, expected behavior, and acceptance criteria so implementation agents receive self-contained actionable issues.
|
|
8
|
+
|
|
3
9
|
## 0.6.0 (2026-07-12)
|
|
4
10
|
|
|
5
11
|
### Security (breaking changes)
|
package/README.md
CHANGED
|
@@ -87,6 +87,7 @@ Every child execution receives a timeout:
|
|
|
87
87
|
- **Maximum:** 60 minutes (`MAX_TIMEOUT_MS`)
|
|
88
88
|
- Timeout values must be positive integers within the allowed range.
|
|
89
89
|
- Timeout errors are distinguishable from parent cancellation.
|
|
90
|
+
- Progress heartbeats keep the parent transport active during quiet model work; explicit timeouts remain hard deadlines.
|
|
90
91
|
- Parallel tasks and chain steps may have per-item timeouts.
|
|
91
92
|
|
|
92
93
|
### Output safety
|
|
@@ -158,6 +159,7 @@ The raw `stopReason` from the Pi SDK is preserved in the result.
|
|
|
158
159
|
- **Parent cancellation:** Aborting the parent tool call cancels all children.
|
|
159
160
|
- **Sibling cancellation:** In parallel mode with `abortOnFailure: true`, the first failed task cancels running siblings.
|
|
160
161
|
- **Timeout vs. abort:** Timeout errors set `status: "timeout"` and `stopReason: "timeout"`; parent cancellation sets `status: "aborted"`.
|
|
162
|
+
- **Transport idle:** The parent tool receives periodic progress heartbeats while a child runs; these do not extend its timeout.
|
|
161
163
|
|
|
162
164
|
## Extension contract
|
|
163
165
|
|
package/agents/reviewer.md
CHANGED
|
@@ -9,7 +9,7 @@ sandbox: read-only
|
|
|
9
9
|
|
|
10
10
|
You are an independent senior code reviewer. Inspect the requested Git scope with read-only tools.
|
|
11
11
|
|
|
12
|
-
Focus only on actionable issues introduced by the reviewed change:
|
|
12
|
+
Focus only on actionable issues introduced by the reviewed change. Return each confirmed finding as one self-contained issue another agent can fix without redoing the review:
|
|
13
13
|
1. Correctness and edge cases
|
|
14
14
|
2. Security and data loss
|
|
15
15
|
3. Regressions and API compatibility
|
|
@@ -27,8 +27,10 @@ Return JSON only:
|
|
|
27
27
|
"file": "relative/path",
|
|
28
28
|
"line": 1,
|
|
29
29
|
"issue": "what is wrong and why it matters",
|
|
30
|
-
"evidence": "specific inspected code evidence",
|
|
30
|
+
"evidence": "reproduction steps or specific inspected code evidence",
|
|
31
|
+
"expectedBehavior": "what should happen instead",
|
|
31
32
|
"suggestedFix": "smallest safe fix",
|
|
33
|
+
"acceptanceCriteria": "observable pass conditions and exact verification checks",
|
|
32
34
|
"blocking": true
|
|
33
35
|
}
|
|
34
36
|
]
|
package/extensions/index.ts
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
isFailedResult,
|
|
39
39
|
mapWithConcurrencyLimit,
|
|
40
40
|
runSubAgent,
|
|
41
|
+
startHeartbeat,
|
|
41
42
|
} from "./runner.ts";
|
|
42
43
|
import {
|
|
43
44
|
normalizeTimeout,
|
|
@@ -515,6 +516,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
515
516
|
parentSignal?: AbortSignal,
|
|
516
517
|
timeoutMs?: number,
|
|
517
518
|
onProgress?: (partial: SubAgentResult) => void,
|
|
519
|
+
heartbeatDetails?: () => SubagentDetails,
|
|
518
520
|
isReadOnly?: boolean,
|
|
519
521
|
): Promise<SubAgentResult> {
|
|
520
522
|
const agent = agents.find((a) => a.name === agentName);
|
|
@@ -576,23 +578,30 @@ export default function (pi: ExtensionAPI) {
|
|
|
576
578
|
};
|
|
577
579
|
}
|
|
578
580
|
|
|
579
|
-
const
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
581
|
+
const stopHeartbeat = onUpdate ? startHeartbeat(() => onUpdate({
|
|
582
|
+
content: [{ type: "text", text: `Subagent ${agentName} is still running…` }],
|
|
583
|
+
details: heartbeatDetails?.() ?? makeDetails("single")([]),
|
|
584
|
+
})) : undefined;
|
|
585
|
+
try {
|
|
586
|
+
return await runSubAgent({
|
|
587
|
+
cwd: safeCwd,
|
|
588
|
+
systemPrompt: params.instructions
|
|
589
|
+
? `${agent.systemPrompt}\n\n## Task Contract\n${params.instructions.slice(0, MAX_INSTRUCTIONS_LENGTH)}`
|
|
590
|
+
: agent.systemPrompt,
|
|
591
|
+
task,
|
|
592
|
+
tools,
|
|
593
|
+
model: resolved.model,
|
|
594
|
+
authStorage,
|
|
595
|
+
modelRegistry,
|
|
596
|
+
signal: parentSignal,
|
|
597
|
+
timeoutMs: effectiveTimeoutMs,
|
|
598
|
+
agentName,
|
|
599
|
+
thinkingLevel: agent.thinking,
|
|
600
|
+
onMessage: onProgress,
|
|
601
|
+
});
|
|
602
|
+
} finally {
|
|
603
|
+
stopHeartbeat?.();
|
|
604
|
+
}
|
|
596
605
|
}
|
|
597
606
|
|
|
598
607
|
// --- Chain mode ---
|
|
@@ -615,6 +624,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
615
624
|
step.agent, taskWithContext, step.cwd,
|
|
616
625
|
signal, step.timeout ?? params.timeout,
|
|
617
626
|
(partial) => threadStore.updateThread(thread.id, { result: partial }),
|
|
627
|
+
() => makeDetails("chain")(results),
|
|
618
628
|
);
|
|
619
629
|
threadStore.updateThread(thread.id, {
|
|
620
630
|
status: isFailedResult(result) ? (result.stopReason === "aborted" ? "aborted" : "failed") : "completed",
|
|
@@ -767,7 +777,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
767
777
|
const result = await runOne(
|
|
768
778
|
t.agent, t.task, t.cwd,
|
|
769
779
|
parallelController.signal, t.timeout ?? params.timeout,
|
|
770
|
-
|
|
780
|
+
(partial) => threadStore.updateThread(parallelThreads[index].id, { result: partial }),
|
|
781
|
+
() => makeDetails("parallel")([...allResults]),
|
|
771
782
|
);
|
|
772
783
|
allResults[index] = result;
|
|
773
784
|
threadStore.updateThread(parallelThreads[index].id, {
|
|
@@ -823,6 +834,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
823
834
|
params.agent, params.task, params.cwd,
|
|
824
835
|
signal, params.timeout,
|
|
825
836
|
(partial) => threadStore.updateThread(thread.id, { result: partial }),
|
|
837
|
+
() => makeDetails("single")([]),
|
|
826
838
|
);
|
|
827
839
|
threadStore.updateThread(thread.id, {
|
|
828
840
|
status: isFailedResult(result) ? (result.stopReason === "aborted" ? "aborted" : "failed") : "completed",
|
package/extensions/runner.ts
CHANGED
|
@@ -63,6 +63,12 @@ export interface SubAgentResult {
|
|
|
63
63
|
// Public API
|
|
64
64
|
// ---------------------------------------------------------------------------
|
|
65
65
|
|
|
66
|
+
export function startHeartbeat(onHeartbeat: () => void, intervalMs = 30_000): () => void {
|
|
67
|
+
const timer = setInterval(onHeartbeat, intervalMs);
|
|
68
|
+
timer.unref?.();
|
|
69
|
+
return () => clearInterval(timer);
|
|
70
|
+
}
|
|
71
|
+
|
|
66
72
|
export async function runSubAgent(options: {
|
|
67
73
|
cwd: string;
|
|
68
74
|
systemPrompt: string;
|
package/package.json
CHANGED