pi-herdr-agents 1.2.1 → 1.2.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/CHANGELOG.md
CHANGED
|
@@ -7,11 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
9
9
|
|
|
10
|
-
## [v1.2.
|
|
10
|
+
## [v1.2.3](https://github.com/giuseppecrj/pi-herdr-agents/compare/v1.2.2...v1.2.3)
|
|
11
|
+
|
|
12
|
+
### Commits
|
|
13
|
+
|
|
14
|
+
- fix: shorten worktree labels [`4caeabb`](https://github.com/giuseppecrj/pi-herdr-agents/commit/4caeabb4db088b8c60d0caf701ec49fe538f8df2)
|
|
15
|
+
- docs: add release workflow skill [`9937f34`](https://github.com/giuseppecrj/pi-herdr-agents/commit/9937f3402ab672023525c19a50dee3cbd7d579bb)
|
|
16
|
+
|
|
17
|
+
## [v1.2.2](https://github.com/giuseppecrj/pi-herdr-agents/compare/v1.2.1...v1.2.2) - 2026-08-18
|
|
18
|
+
|
|
19
|
+
### Commits
|
|
20
|
+
|
|
21
|
+
- fix: hide completion tool from auto-exit children [`e4cad6b`](https://github.com/giuseppecrj/pi-herdr-agents/commit/e4cad6bdbe5d1952f5bb838640493c20ab332dbd)
|
|
22
|
+
- chore: release v1.2.2 [`ab7843b`](https://github.com/giuseppecrj/pi-herdr-agents/commit/ab7843b4919574789029c985f27509b968242b05)
|
|
23
|
+
|
|
24
|
+
## [v1.2.1](https://github.com/giuseppecrj/pi-herdr-agents/compare/v1.2.0...v1.2.1) - 2026-08-13
|
|
11
25
|
|
|
12
26
|
### Commits
|
|
13
27
|
|
|
14
28
|
- docs: polish package readme and artwork [`908dd76`](https://github.com/giuseppecrj/pi-herdr-agents/commit/908dd76875c91871b8d91113e6d1633d5112d5b3)
|
|
29
|
+
- chore: release v1.2.1 [`df87f12`](https://github.com/giuseppecrj/pi-herdr-agents/commit/df87f126ec5cad4735c441752471aa8bd724f2e9)
|
|
15
30
|
|
|
16
31
|
## [v1.2.0](https://github.com/giuseppecrj/pi-herdr-agents/compare/v1.1.0...v1.2.0) - 2026-08-13
|
|
17
32
|
|
package/package.json
CHANGED
|
@@ -117,6 +117,7 @@ import {
|
|
|
117
117
|
persistWorktreeResult,
|
|
118
118
|
runSubagentScript,
|
|
119
119
|
writeWorktreeManifest,
|
|
120
|
+
buildSubagentToolAllowlist,
|
|
120
121
|
type WorktreeHandoff,
|
|
121
122
|
type WorktreeLaunch,
|
|
122
123
|
} from "./launch.ts";
|
|
@@ -231,8 +232,7 @@ const SubagentParams = Type.Object({
|
|
|
231
232
|
Type.Object({
|
|
232
233
|
branch: Type.String({
|
|
233
234
|
minLength: 1,
|
|
234
|
-
description:
|
|
235
|
-
"New branch name for an isolated Herdr-managed Git worktree",
|
|
235
|
+
description: "New branch name for an isolated Herdr-managed Git worktree",
|
|
236
236
|
}),
|
|
237
237
|
base: Type.Optional(
|
|
238
238
|
Type.String({
|
|
@@ -360,7 +360,7 @@ function getFrontmatterValue(
|
|
|
360
360
|
}
|
|
361
361
|
|
|
362
362
|
function parseOptionalBoolean(value: string | undefined): boolean | undefined {
|
|
363
|
-
return value
|
|
363
|
+
return value == null ? undefined : value === "true";
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
function parseSessionMode(
|
|
@@ -400,12 +400,8 @@ function parseAgentDefinition(
|
|
|
400
400
|
getFrontmatterValue(frontmatter, "skill"),
|
|
401
401
|
thinking: thinking && isThinkingLevel(thinking) ? thinking : undefined,
|
|
402
402
|
denyTools: getFrontmatterValue(frontmatter, "deny-tools"),
|
|
403
|
-
spawning: parseOptionalBoolean(
|
|
404
|
-
|
|
405
|
-
),
|
|
406
|
-
autoExit: parseOptionalBoolean(
|
|
407
|
-
getFrontmatterValue(frontmatter, "auto-exit"),
|
|
408
|
-
),
|
|
403
|
+
spawning: parseOptionalBoolean(getFrontmatterValue(frontmatter, "spawning")),
|
|
404
|
+
autoExit: parseOptionalBoolean(getFrontmatterValue(frontmatter, "auto-exit")),
|
|
409
405
|
interactive: parseOptionalBoolean(
|
|
410
406
|
getFrontmatterValue(frontmatter, "interactive"),
|
|
411
407
|
),
|
|
@@ -461,8 +457,7 @@ function findPackageMetadata(path: string): {
|
|
|
461
457
|
const pkg = JSON.parse(readFileSync(packagePath, "utf8"));
|
|
462
458
|
return {
|
|
463
459
|
provider: typeof pkg.name === "string" ? pkg.name : undefined,
|
|
464
|
-
providerVersion:
|
|
465
|
-
typeof pkg.version === "string" ? pkg.version : undefined,
|
|
460
|
+
providerVersion: typeof pkg.version === "string" ? pkg.version : undefined,
|
|
466
461
|
};
|
|
467
462
|
} catch {
|
|
468
463
|
return {};
|
|
@@ -489,8 +484,7 @@ function discoverRolePackPaths(pi?: Pick<ExtensionAPI, "events">): {
|
|
|
489
484
|
if (typeof path !== "string" || !isAbsolute(path)) {
|
|
490
485
|
diagnostics.push({
|
|
491
486
|
code: "invalid-role-pack-path",
|
|
492
|
-
message:
|
|
493
|
-
"Role packs must register an absolute file or directory path.",
|
|
487
|
+
message: "Role packs must register an absolute file or directory path.",
|
|
494
488
|
});
|
|
495
489
|
return;
|
|
496
490
|
}
|
|
@@ -527,8 +521,7 @@ function discoverAgentCatalog(pi?: Pick<ExtensionAPI, "events">): AgentCatalog {
|
|
|
527
521
|
continue;
|
|
528
522
|
}
|
|
529
523
|
const parsed = parseAgentDefinition(content, fallbackName);
|
|
530
|
-
if (parsed)
|
|
531
|
-
agents.set(parsed.name, { ...parsed, source, path: filePath });
|
|
524
|
+
if (parsed) agents.set(parsed.name, { ...parsed, source, path: filePath });
|
|
532
525
|
}
|
|
533
526
|
};
|
|
534
527
|
|
|
@@ -1029,9 +1022,9 @@ function resolveResultPresentation(
|
|
|
1029
1022
|
`subagent or resume the session with subagent_resume.`;
|
|
1030
1023
|
} else {
|
|
1031
1024
|
body =
|
|
1032
|
-
result.exitCode
|
|
1033
|
-
? `Sub-agent "${name}"
|
|
1034
|
-
: `Sub-agent "${name}"
|
|
1025
|
+
result.exitCode === 0
|
|
1026
|
+
? `Sub-agent "${name}" completed (${formatElapsed(result.elapsed)}).\n\n${result.summary}`
|
|
1027
|
+
: `Sub-agent "${name}" failed (exit code ${result.exitCode}).\n\n${result.summary}`;
|
|
1035
1028
|
}
|
|
1036
1029
|
|
|
1037
1030
|
if (result.fallbackAttempts && result.fallbackAttempts.length > 1) {
|
|
@@ -1404,32 +1397,6 @@ function updateWidget() {
|
|
|
1404
1397
|
* first positional message so that /skill: args land in messages[1..] and arrive
|
|
1405
1398
|
* as standalone prompts in the child session.
|
|
1406
1399
|
*/
|
|
1407
|
-
const SUBAGENT_CONTROL_TOOLS = ["caller_ping", "subagent_done"] as const;
|
|
1408
|
-
|
|
1409
|
-
/**
|
|
1410
|
-
* Build the child --tools allowlist.
|
|
1411
|
-
*
|
|
1412
|
-
* Pi 0.70+ applies --tools to built-in, extension, and custom tools. If a
|
|
1413
|
-
* subagent definition restricts tools to e.g. "read,bash,write", the child
|
|
1414
|
-
* control tools from subagent-done.ts would otherwise be hidden, leaving a
|
|
1415
|
-
* manually resumed or user-touched subagent unable to call subagent_done.
|
|
1416
|
-
*/
|
|
1417
|
-
function buildSubagentToolAllowlist(effectiveTools?: string): string | null {
|
|
1418
|
-
const requested = (effectiveTools ?? "")
|
|
1419
|
-
.split(",")
|
|
1420
|
-
.map((tool) => tool.trim())
|
|
1421
|
-
.filter(Boolean);
|
|
1422
|
-
|
|
1423
|
-
if (requested.length === 0) return null;
|
|
1424
|
-
|
|
1425
|
-
const allow = new Set(requested);
|
|
1426
|
-
for (const tool of SUBAGENT_CONTROL_TOOLS) {
|
|
1427
|
-
allow.add(tool);
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
return [...allow].join(",");
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
1400
|
function buildPiPromptArgs(params: {
|
|
1434
1401
|
effectiveSkills?: string;
|
|
1435
1402
|
taskDelivery: "direct" | "artifact";
|
|
@@ -1488,15 +1455,13 @@ function ensureLifecycle(running: RunningSubagent): SubagentLifecycle {
|
|
|
1488
1455
|
turnActive: state.phase === "active",
|
|
1489
1456
|
providerActive: false,
|
|
1490
1457
|
toolActive: state.activeScope === "tool",
|
|
1491
|
-
...(state.activeScope
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
? { waitingSince: state.waitingSinceMs }
|
|
1499
|
-
: {}),
|
|
1458
|
+
...(state.activeScope ? { activeScope: state.activeScope as any } : {}),
|
|
1459
|
+
...(state.activeSinceMs == null
|
|
1460
|
+
? {}
|
|
1461
|
+
: { activeSince: state.activeSinceMs }),
|
|
1462
|
+
...(state.waitingSinceMs == null
|
|
1463
|
+
? {}
|
|
1464
|
+
: { waitingSince: state.waitingSinceMs }),
|
|
1500
1465
|
...(state.activityLabel && state.activeScope === "tool"
|
|
1501
1466
|
? { toolName: state.activityLabel }
|
|
1502
1467
|
: {}),
|
|
@@ -1692,10 +1657,7 @@ function startStatusRefresh(pi: ExtensionAPI) {
|
|
|
1692
1657
|
pi.sendMessage(
|
|
1693
1658
|
{
|
|
1694
1659
|
customType: "subagent_status",
|
|
1695
|
-
content: formatStatusAggregate(
|
|
1696
|
-
transitionLines,
|
|
1697
|
-
statusConfig.lineLimit,
|
|
1698
|
-
),
|
|
1660
|
+
content: formatStatusAggregate(transitionLines, statusConfig.lineLimit),
|
|
1699
1661
|
display: true,
|
|
1700
1662
|
details: { lines: capped.visibleLines, overflow: capped.overflow },
|
|
1701
1663
|
},
|
|
@@ -1965,9 +1927,7 @@ function resolveSubagentRuntimePlans(
|
|
|
1965
1927
|
wrapPiModelRegistry(ctx.modelRegistry),
|
|
1966
1928
|
);
|
|
1967
1929
|
if (params.worktree && plans.length > 1) {
|
|
1968
|
-
throw new Error(
|
|
1969
|
-
"Model fallbacks are not supported for worktree subagents.",
|
|
1970
|
-
);
|
|
1930
|
+
throw new Error("Model fallbacks are not supported for worktree subagents.");
|
|
1971
1931
|
}
|
|
1972
1932
|
return plans;
|
|
1973
1933
|
}
|
|
@@ -2050,9 +2010,9 @@ async function watchSubagent(
|
|
|
2050
2010
|
? observed.thinking
|
|
2051
2011
|
: undefined;
|
|
2052
2012
|
const mismatch =
|
|
2053
|
-
observedModel
|
|
2054
|
-
?
|
|
2055
|
-
:
|
|
2013
|
+
observedModel === running.runtimePlan.model
|
|
2014
|
+
? undefined
|
|
2015
|
+
: `Resolved model ${running.runtimePlan.model} but child reported ${observedModel}`;
|
|
2056
2016
|
running.runtimePlan = {
|
|
2057
2017
|
...running.runtimePlan,
|
|
2058
2018
|
...(observedThinking ? { thinking: observedThinking } : {}),
|
|
@@ -2067,15 +2027,15 @@ async function watchSubagent(
|
|
|
2067
2027
|
findLastAssistantMessage(allEntries) ??
|
|
2068
2028
|
(result.errorMessage
|
|
2069
2029
|
? `Subagent error: ${result.errorMessage}`
|
|
2070
|
-
: result.exitCode
|
|
2071
|
-
?
|
|
2072
|
-
:
|
|
2030
|
+
: result.exitCode === 0
|
|
2031
|
+
? "Sub-agent exited without output"
|
|
2032
|
+
: `Sub-agent exited with code ${result.exitCode}`);
|
|
2073
2033
|
} else {
|
|
2074
2034
|
summary = result.errorMessage
|
|
2075
2035
|
? `Subagent error: ${result.errorMessage}`
|
|
2076
|
-
: result.exitCode
|
|
2077
|
-
?
|
|
2078
|
-
:
|
|
2036
|
+
: result.exitCode === 0
|
|
2037
|
+
? "Sub-agent exited without output"
|
|
2038
|
+
: `Sub-agent exited with code ${result.exitCode}`;
|
|
2079
2039
|
}
|
|
2080
2040
|
|
|
2081
2041
|
const worktreeHandoff = finalizeSubagentSurface(
|
|
@@ -2383,11 +2343,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
2383
2343
|
);
|
|
2384
2344
|
}
|
|
2385
2345
|
const id = `workflow-${candidate.runId}-${Math.random().toString(16).slice(2, 10)}`;
|
|
2386
|
-
const sessionFile = join(
|
|
2387
|
-
dirname(candidate.path),
|
|
2388
|
-
"sessions",
|
|
2389
|
-
`${id}.jsonl`,
|
|
2390
|
-
);
|
|
2346
|
+
const sessionFile = join(dirname(candidate.path), "sessions", `${id}.jsonl`);
|
|
2391
2347
|
let surface: string | undefined;
|
|
2392
2348
|
let launched = false;
|
|
2393
2349
|
const childController = new AbortController();
|
|
@@ -2497,10 +2453,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
2497
2453
|
if (childController.signal.aborted)
|
|
2498
2454
|
return workflowFailure("cancelled", "Workflow cancelled.");
|
|
2499
2455
|
const message = error instanceof Error ? error.message : String(error);
|
|
2500
|
-
return workflowFailure(
|
|
2501
|
-
launched ? "child_error" : "launch_error",
|
|
2502
|
-
message,
|
|
2503
|
-
);
|
|
2456
|
+
return workflowFailure(launched ? "child_error" : "launch_error", message);
|
|
2504
2457
|
} finally {
|
|
2505
2458
|
owner.controller.signal.removeEventListener("abort", onOwnerAbort);
|
|
2506
2459
|
owner.children.delete(id);
|
|
@@ -2525,7 +2478,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
2525
2478
|
const envelope = {
|
|
2526
2479
|
runId: candidate.runId,
|
|
2527
2480
|
state: outcome.state,
|
|
2528
|
-
...(outcome.result
|
|
2481
|
+
...(outcome.result === undefined ? {} : { result: outcome.result }),
|
|
2529
2482
|
...(outcome.error ? { error: outcome.error } : {}),
|
|
2530
2483
|
...(checkoutResult ? { checkout: checkoutResult } : {}),
|
|
2531
2484
|
};
|
|
@@ -2567,9 +2520,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
2567
2520
|
) => {
|
|
2568
2521
|
if (!claimWorkflowTerminal(owner.gate, outcome)) {
|
|
2569
2522
|
return (
|
|
2570
|
-
owner.gate.outcome ??
|
|
2571
|
-
runtime.workflowOutcomes.get(owner.runId) ??
|
|
2572
|
-
outcome
|
|
2523
|
+
owner.gate.outcome ?? runtime.workflowOutcomes.get(owner.runId) ?? outcome
|
|
2573
2524
|
);
|
|
2574
2525
|
}
|
|
2575
2526
|
runtime.workflowOutcomes.set(owner.runId, outcome);
|
|
@@ -2848,7 +2799,8 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
2848
2799
|
content: [
|
|
2849
2800
|
{
|
|
2850
2801
|
type: "text",
|
|
2851
|
-
text:
|
|
2802
|
+
text:
|
|
2803
|
+
"Error: start pi with a persistent session before preparing a workflow.",
|
|
2852
2804
|
},
|
|
2853
2805
|
],
|
|
2854
2806
|
details: { error: "workflow_persistent_session_required" },
|
|
@@ -2863,9 +2815,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
2863
2815
|
});
|
|
2864
2816
|
runtime.pendingWorkflow = candidate;
|
|
2865
2817
|
return {
|
|
2866
|
-
content: [
|
|
2867
|
-
{ type: "text", text: formatApprovalPacket(candidate) },
|
|
2868
|
-
],
|
|
2818
|
+
content: [{ type: "text", text: formatApprovalPacket(candidate) }],
|
|
2869
2819
|
details: {
|
|
2870
2820
|
runId: candidate.runId,
|
|
2871
2821
|
scriptHash: candidate.scriptHash,
|
|
@@ -3003,13 +2953,9 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3003
2953
|
}
|
|
3004
2954
|
try {
|
|
3005
2955
|
const root = realpathSync(
|
|
3006
|
-
execFileSync(
|
|
3007
|
-
"
|
|
3008
|
-
|
|
3009
|
-
{
|
|
3010
|
-
encoding: "utf8",
|
|
3011
|
-
},
|
|
3012
|
-
).trim(),
|
|
2956
|
+
execFileSync("git", ["-C", ctx.cwd, "rev-parse", "--show-toplevel"], {
|
|
2957
|
+
encoding: "utf8",
|
|
2958
|
+
}).trim(),
|
|
3013
2959
|
);
|
|
3014
2960
|
const commonDir = realpathSync(
|
|
3015
2961
|
execFileSync(
|
|
@@ -3032,7 +2978,8 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3032
2978
|
content: [
|
|
3033
2979
|
{
|
|
3034
2980
|
type: "text",
|
|
3035
|
-
text:
|
|
2981
|
+
text:
|
|
2982
|
+
"Error: workflow cancellation must use the approved repository identity.",
|
|
3036
2983
|
},
|
|
3037
2984
|
],
|
|
3038
2985
|
details: { error: "workflow_cancel_identity_mismatch" },
|
|
@@ -3064,9 +3011,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3064
3011
|
};
|
|
3065
3012
|
}
|
|
3066
3013
|
return {
|
|
3067
|
-
content: [
|
|
3068
|
-
{ type: "text", text: "Error: unsupported workflow action." },
|
|
3069
|
-
],
|
|
3014
|
+
content: [{ type: "text", text: "Error: unsupported workflow action." }],
|
|
3070
3015
|
details: { error: "workflow_action_unavailable" },
|
|
3071
3016
|
};
|
|
3072
3017
|
},
|
|
@@ -3139,7 +3084,8 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3139
3084
|
content: [
|
|
3140
3085
|
{
|
|
3141
3086
|
type: "text",
|
|
3142
|
-
text:
|
|
3087
|
+
text:
|
|
3088
|
+
"Error: no session file. Start pi with a persistent session to use subagents.",
|
|
3143
3089
|
},
|
|
3144
3090
|
],
|
|
3145
3091
|
details: { error: "no session file" },
|
|
@@ -3157,9 +3103,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3157
3103
|
parentThinking !== "xhigh" &&
|
|
3158
3104
|
parentThinking !== "max"
|
|
3159
3105
|
) {
|
|
3160
|
-
throw new Error(
|
|
3161
|
-
`Unsupported parent thinking level: ${parentThinking}`,
|
|
3162
|
-
);
|
|
3106
|
+
throw new Error(`Unsupported parent thinking level: ${parentThinking}`);
|
|
3163
3107
|
}
|
|
3164
3108
|
const runtimePlans = resolveSubagentRuntimePlans(
|
|
3165
3109
|
params,
|
|
@@ -3252,9 +3196,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3252
3196
|
exitCode: result.exitCode,
|
|
3253
3197
|
elapsed: result.elapsed,
|
|
3254
3198
|
sessionFile: result.sessionFile,
|
|
3255
|
-
...(result.errorMessage
|
|
3256
|
-
? { errorMessage: result.errorMessage }
|
|
3257
|
-
: {}),
|
|
3199
|
+
...(result.errorMessage ? { errorMessage: result.errorMessage } : {}),
|
|
3258
3200
|
...(result.fallbackAttempts
|
|
3259
3201
|
? { fallbackAttempts: result.fallbackAttempts }
|
|
3260
3202
|
: {}),
|
|
@@ -3301,9 +3243,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3301
3243
|
(running.worktree
|
|
3302
3244
|
? ` in worktree ${running.worktree.path} on branch ${running.worktree.branch}. `
|
|
3303
3245
|
: ". ") +
|
|
3304
|
-
(worktreeLaunchWarning
|
|
3305
|
-
? `Warning: ${worktreeLaunchWarning} `
|
|
3306
|
-
: "") +
|
|
3246
|
+
(worktreeLaunchWarning ? `Warning: ${worktreeLaunchWarning} ` : "") +
|
|
3307
3247
|
`Do NOT generate or assume any results — you have no idea what the sub-agent will do or produce. ` +
|
|
3308
3248
|
`The results will be delivered to you automatically as a steer message when the sub-agent finishes. ` +
|
|
3309
3249
|
`Until then, move on to other work or tell the user you're waiting.`,
|
|
@@ -3320,9 +3260,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3320
3260
|
thinking: running.runtimePlan?.thinking,
|
|
3321
3261
|
runtimePlan: running.runtimePlan,
|
|
3322
3262
|
...(running.worktree ? { worktree: running.worktree } : {}),
|
|
3323
|
-
...(worktreeLaunchWarning
|
|
3324
|
-
? { warning: worktreeLaunchWarning }
|
|
3325
|
-
: {}),
|
|
3263
|
+
...(worktreeLaunchWarning ? { warning: worktreeLaunchWarning } : {}),
|
|
3326
3264
|
status: "started",
|
|
3327
3265
|
},
|
|
3328
3266
|
};
|
|
@@ -3334,8 +3272,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3334
3272
|
typeof partialArgs.name === "string" && partialArgs.name
|
|
3335
3273
|
? partialArgs.name
|
|
3336
3274
|
: "(unnamed)";
|
|
3337
|
-
const task =
|
|
3338
|
-
typeof partialArgs.task === "string" ? partialArgs.task : "";
|
|
3275
|
+
const task = typeof partialArgs.task === "string" ? partialArgs.task : "";
|
|
3339
3276
|
const agent =
|
|
3340
3277
|
typeof partialArgs.agent === "string" && partialArgs.agent
|
|
3341
3278
|
? theme.fg("dim", ` (${partialArgs.agent})`)
|
|
@@ -3344,9 +3281,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3344
3281
|
typeof partialArgs.cwd === "string" && partialArgs.cwd
|
|
3345
3282
|
? theme.fg("dim", ` in ${partialArgs.cwd}`)
|
|
3346
3283
|
: "";
|
|
3347
|
-
const worktree = partialArgs.worktree as
|
|
3348
|
-
| { branch?: unknown }
|
|
3349
|
-
| undefined;
|
|
3284
|
+
const worktree = partialArgs.worktree as { branch?: unknown } | undefined;
|
|
3350
3285
|
const worktreeHint =
|
|
3351
3286
|
typeof worktree?.branch === "string"
|
|
3352
3287
|
? theme.fg("dim", ` on ${worktree.branch} (worktree)`)
|
|
@@ -3362,8 +3297,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3362
3297
|
// LLM generates tool arguments, so args.task grows token by token.
|
|
3363
3298
|
// We keep it compact here — Ctrl+O on renderResult expands the full content.
|
|
3364
3299
|
if (task) {
|
|
3365
|
-
const firstLine =
|
|
3366
|
-
task.split("\n").find((l: string) => l.trim()) ?? "";
|
|
3300
|
+
const firstLine = task.split("\n").find((l: string) => l.trim()) ?? "";
|
|
3367
3301
|
const preview =
|
|
3368
3302
|
firstLine.length > 100 ? firstLine.slice(0, 100) + "…" : firstLine;
|
|
3369
3303
|
if (preview) {
|
|
@@ -3502,21 +3436,13 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3502
3436
|
const agents = details?.agents ?? [];
|
|
3503
3437
|
const diagnostics = details?.diagnostics ?? [];
|
|
3504
3438
|
if (agents.length === 0 && diagnostics.length === 0) {
|
|
3505
|
-
return new Text(
|
|
3506
|
-
theme.fg("dim", "No subagent definitions found."),
|
|
3507
|
-
0,
|
|
3508
|
-
0,
|
|
3509
|
-
);
|
|
3439
|
+
return new Text(theme.fg("dim", "No subagent definitions found."), 0, 0);
|
|
3510
3440
|
}
|
|
3511
3441
|
const lines = agents.map((a: any) => {
|
|
3512
3442
|
const source =
|
|
3513
|
-
a.source === "package" && a.provider
|
|
3514
|
-
? `package:${a.provider}`
|
|
3515
|
-
: a.source;
|
|
3443
|
+
a.source === "package" && a.provider ? `package:${a.provider}` : a.source;
|
|
3516
3444
|
const badge = theme.fg("accent", ` (${source})`);
|
|
3517
|
-
const desc = a.description
|
|
3518
|
-
? theme.fg("dim", ` — ${a.description}`)
|
|
3519
|
-
: "";
|
|
3445
|
+
const desc = a.description ? theme.fg("dim", ` — ${a.description}`) : "";
|
|
3520
3446
|
const model = a.model ? theme.fg("dim", ` [${a.model}]`) : "";
|
|
3521
3447
|
return ` ${theme.fg("toolTitle", theme.bold(a.name))}${badge}${model}${desc}`;
|
|
3522
3448
|
});
|
|
@@ -3673,17 +3599,14 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3673
3599
|
return;
|
|
3674
3600
|
}
|
|
3675
3601
|
|
|
3676
|
-
const allEntries = getNewEntries(
|
|
3677
|
-
params.sessionPath,
|
|
3678
|
-
entryCountBefore,
|
|
3679
|
-
);
|
|
3602
|
+
const allEntries = getNewEntries(params.sessionPath, entryCountBefore);
|
|
3680
3603
|
const summary =
|
|
3681
3604
|
findLastAssistantMessage(allEntries) ??
|
|
3682
3605
|
(result.errorMessage
|
|
3683
3606
|
? `Subagent error: ${result.errorMessage}`
|
|
3684
|
-
: result.exitCode
|
|
3685
|
-
?
|
|
3686
|
-
:
|
|
3607
|
+
: result.exitCode === 0
|
|
3608
|
+
? "Resumed session exited without new output"
|
|
3609
|
+
: `Resumed session exited with code ${result.exitCode}`);
|
|
3687
3610
|
const presentation = resolveResultPresentation(
|
|
3688
3611
|
{ ...result, summary, sessionFile: params.sessionPath },
|
|
3689
3612
|
name,
|
|
@@ -3696,12 +3619,8 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3696
3619
|
exitCode: result.exitCode,
|
|
3697
3620
|
elapsed: result.elapsed,
|
|
3698
3621
|
sessionFile: params.sessionPath,
|
|
3699
|
-
...(result.errorMessage
|
|
3700
|
-
|
|
3701
|
-
: {}),
|
|
3702
|
-
...(running.runtimePlan
|
|
3703
|
-
? { runtimePlan: running.runtimePlan }
|
|
3704
|
-
: {}),
|
|
3622
|
+
...(result.errorMessage ? { errorMessage: result.errorMessage } : {}),
|
|
3623
|
+
...(running.runtimePlan ? { runtimePlan: running.runtimePlan } : {}),
|
|
3705
3624
|
});
|
|
3706
3625
|
})
|
|
3707
3626
|
.catch((err) => {
|
|
@@ -3872,10 +3791,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3872
3791
|
|
|
3873
3792
|
const branch = parts.shift();
|
|
3874
3793
|
if (!branch || branch === "list") {
|
|
3875
|
-
ctx.ui.notify(
|
|
3876
|
-
"Usage: /worktree <name> [task] | /worktree list",
|
|
3877
|
-
"warning",
|
|
3878
|
-
);
|
|
3794
|
+
ctx.ui.notify("Usage: /worktree <name> [task] | /worktree list", "warning");
|
|
3879
3795
|
return;
|
|
3880
3796
|
}
|
|
3881
3797
|
if (!isTerminalAvailable()) {
|
|
@@ -3911,7 +3827,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3911
3827
|
);
|
|
3912
3828
|
const result = await launchPiWorktreeHandoff({
|
|
3913
3829
|
kind: "fresh",
|
|
3914
|
-
name: `
|
|
3830
|
+
name: `wt: ${branch}`,
|
|
3915
3831
|
task,
|
|
3916
3832
|
cwd: ctx.cwd,
|
|
3917
3833
|
worktree: { branch },
|
|
@@ -3976,10 +3892,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3976
3892
|
...formatVisibleAgentDefinitions(catalog.agents),
|
|
3977
3893
|
...formatAgentDiagnostics(catalog.diagnostics),
|
|
3978
3894
|
];
|
|
3979
|
-
ctx.ui.notify(
|
|
3980
|
-
lines.join("\n") || "No subagent definitions found.",
|
|
3981
|
-
"info",
|
|
3982
|
-
);
|
|
3895
|
+
ctx.ui.notify(lines.join("\n") || "No subagent definitions found.", "info");
|
|
3983
3896
|
return;
|
|
3984
3897
|
}
|
|
3985
3898
|
if (!trimmed) {
|
|
@@ -4029,7 +3942,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
4029
3942
|
typeof details.errorMessage === "string" ? details.errorMessage : "";
|
|
4030
3943
|
const failed = exitCode !== 0 || !!errorMessage;
|
|
4031
3944
|
const elapsed =
|
|
4032
|
-
details.elapsed
|
|
3945
|
+
details.elapsed == null ? "?" : formatElapsed(details.elapsed);
|
|
4033
3946
|
const bgFn = failed
|
|
4034
3947
|
? (text: string) => theme.bg("toolErrorBg", text)
|
|
4035
3948
|
: (text: string) => theme.bg("toolSuccessBg", text);
|
|
@@ -4055,10 +3968,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
4055
3968
|
const summary = rawContent
|
|
4056
3969
|
.replace(/\n\nSession: .+\nResume: .+$/, "")
|
|
4057
3970
|
.replace(`Sub-agent "${name}" completed (${elapsed}).\n\n`, "")
|
|
4058
|
-
.replace(
|
|
4059
|
-
`Sub-agent "${name}" failed (exit code ${exitCode}).\n\n`,
|
|
4060
|
-
"",
|
|
4061
|
-
)
|
|
3971
|
+
.replace(`Sub-agent "${name}" failed (exit code ${exitCode}).\n\n`, "")
|
|
4062
3972
|
.replace(
|
|
4063
3973
|
new RegExp(
|
|
4064
3974
|
`^Sub-agent "${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}" failed after ${elapsed} \\(provider/agent error — auto-retry exhausted\\)\\.\\n\\n`,
|
|
@@ -4078,9 +3988,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
4078
3988
|
}
|
|
4079
3989
|
if (details.sessionFile) {
|
|
4080
3990
|
contentLines.push("");
|
|
4081
|
-
contentLines.push(
|
|
4082
|
-
theme.fg("dim", `Session: ${details.sessionFile}`),
|
|
4083
|
-
);
|
|
3991
|
+
contentLines.push(theme.fg("dim", `Session: ${details.sessionFile}`));
|
|
4084
3992
|
contentLines.push(
|
|
4085
3993
|
theme.fg("dim", `Resume: pi --session ${details.sessionFile}`),
|
|
4086
3994
|
);
|
|
@@ -4094,9 +4002,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
4094
4002
|
}
|
|
4095
4003
|
const totalLines = summary.split("\n").length;
|
|
4096
4004
|
if (totalLines > 5) {
|
|
4097
|
-
contentLines.push(
|
|
4098
|
-
theme.fg("muted", `… ${totalLines - 5} more lines`),
|
|
4099
|
-
);
|
|
4005
|
+
contentLines.push(theme.fg("muted", `… ${totalLines - 5} more lines`));
|
|
4100
4006
|
}
|
|
4101
4007
|
}
|
|
4102
4008
|
contentLines.push(
|
|
@@ -4116,8 +4022,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
4116
4022
|
pi.registerMessageRenderer("subagent_status", (message, options, theme) => {
|
|
4117
4023
|
const details = message.details as any;
|
|
4118
4024
|
const lines = Array.isArray(details?.lines) ? details.lines : [];
|
|
4119
|
-
const overflow =
|
|
4120
|
-
typeof details?.overflow === "number" ? details.overflow : 0;
|
|
4025
|
+
const overflow = typeof details?.overflow === "number" ? details.overflow : 0;
|
|
4121
4026
|
if (lines.length === 0 && overflow === 0) return undefined;
|
|
4122
4027
|
|
|
4123
4028
|
return {
|
|
@@ -4173,9 +4078,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
4173
4078
|
contentLines.push(details.message ?? "");
|
|
4174
4079
|
if (details.sessionFile) {
|
|
4175
4080
|
contentLines.push("");
|
|
4176
|
-
contentLines.push(
|
|
4177
|
-
theme.fg("dim", `Session: ${details.sessionFile}`),
|
|
4178
|
-
);
|
|
4081
|
+
contentLines.push(theme.fg("dim", `Session: ${details.sessionFile}`));
|
|
4179
4082
|
}
|
|
4180
4083
|
} else {
|
|
4181
4084
|
const preview = (details.message ?? "")
|
|
@@ -28,7 +28,6 @@ import {
|
|
|
28
28
|
type HerdrWorktreeSurface,
|
|
29
29
|
} from "./terminal.ts";
|
|
30
30
|
|
|
31
|
-
const SUBAGENT_CONTROL_TOOLS = ["caller_ping", "subagent_done"] as const;
|
|
32
31
|
const SUBAGENTS_DIR = dirname(fileURLToPath(import.meta.url));
|
|
33
32
|
|
|
34
33
|
type SubagentSessionMode = "standalone" | "lineage-only" | "fork";
|
|
@@ -550,7 +549,10 @@ function buildPiCommand(
|
|
|
550
549
|
shellQuote(artifacts.systemPromptFile),
|
|
551
550
|
);
|
|
552
551
|
}
|
|
553
|
-
const toolAllowlist =
|
|
552
|
+
const toolAllowlist = buildSubagentToolAllowlist(
|
|
553
|
+
request.behavior.tools,
|
|
554
|
+
request.behavior.autoExit,
|
|
555
|
+
);
|
|
554
556
|
if (toolAllowlist) parts.push("--tools", shellQuote(toolAllowlist));
|
|
555
557
|
if (!request.handoff) {
|
|
556
558
|
for (const prompt of buildPromptArgs(
|
|
@@ -579,7 +581,9 @@ function buildPiCommand(
|
|
|
579
581
|
env.push(`PI_SUBAGENT_NAME=${shellQuote(request.name)}`);
|
|
580
582
|
if (request.agent)
|
|
581
583
|
env.push(`PI_SUBAGENT_AGENT=${shellQuote(request.agent)}`);
|
|
582
|
-
|
|
584
|
+
env.push(
|
|
585
|
+
`PI_SUBAGENT_AUTO_EXIT=${request.behavior.autoExit ? "1" : "0"}`,
|
|
586
|
+
);
|
|
583
587
|
env.push(`PI_SUBAGENT_SESSION=${shellQuote(artifacts.sessionFile)}`);
|
|
584
588
|
env.push(`PI_SUBAGENT_ID=${shellQuote(resolved.id)}`);
|
|
585
589
|
env.push(`PI_SUBAGENT_ACTIVITY_FILE=${shellQuote(artifacts.activityFile)}`);
|
|
@@ -678,7 +682,7 @@ async function launchResumedPiSubagent(
|
|
|
678
682
|
`PI_SUBAGENT_SESSION=${shellQuote(request.sessionFile)}`,
|
|
679
683
|
`PI_SUBAGENT_ID=${shellQuote(id)}`,
|
|
680
684
|
`PI_SUBAGENT_ACTIVITY_FILE=${shellQuote(activityFile)}`,
|
|
681
|
-
|
|
685
|
+
`PI_SUBAGENT_AUTO_EXIT=${autoExit ? "1" : "0"}`,
|
|
682
686
|
];
|
|
683
687
|
const command = [
|
|
684
688
|
...env,
|
|
@@ -722,14 +726,19 @@ async function launchResumedPiSubagent(
|
|
|
722
726
|
};
|
|
723
727
|
}
|
|
724
728
|
|
|
725
|
-
function
|
|
729
|
+
export function buildSubagentToolAllowlist(
|
|
730
|
+
tools?: string,
|
|
731
|
+
autoExit = false,
|
|
732
|
+
): string | null {
|
|
726
733
|
const requested = (tools ?? "")
|
|
727
734
|
.split(",")
|
|
728
735
|
.map((tool) => tool.trim())
|
|
729
736
|
.filter(Boolean);
|
|
730
737
|
if (requested.length === 0) return null;
|
|
731
738
|
const allow = new Set(requested);
|
|
732
|
-
|
|
739
|
+
allow.delete("subagent_done");
|
|
740
|
+
allow.add("caller_ping");
|
|
741
|
+
if (!autoExit) allow.add("subagent_done");
|
|
733
742
|
return [...allow].join(",");
|
|
734
743
|
}
|
|
735
744
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Extension loaded into sub-agents.
|
|
3
3
|
* - Shows agent identity + available tools as a styled widget above the editor (toggle with Ctrl+J)
|
|
4
|
-
* - Provides a `subagent_done` tool for
|
|
4
|
+
* - Provides a `subagent_done` tool for interactive agents to self-terminate
|
|
5
5
|
*/
|
|
6
6
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
7
7
|
import { Box, Text } from "@earendil-works/pi-tui";
|
|
@@ -302,6 +302,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
302
302
|
},
|
|
303
303
|
});
|
|
304
304
|
|
|
305
|
+
if (autoExit) return;
|
|
306
|
+
|
|
305
307
|
pi.registerTool({
|
|
306
308
|
name: "subagent_done",
|
|
307
309
|
label: "Subagent Done",
|