@narumitw/pi-subagents 2.0.0 → 2.0.2
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 +21 -8
- package/dist/chunks/auto-transport-SY2VHUFH.ts +109 -0
- package/dist/chunks/auto-transport-SY2VHUFH.ts.map +7 -0
- package/dist/chunks/capability-grant-CGEWOEKE.ts +19 -0
- package/dist/chunks/capability-grant-CGEWOEKE.ts.map +7 -0
- package/dist/chunks/chunk-434NII74.ts +282 -0
- package/dist/chunks/chunk-434NII74.ts.map +7 -0
- package/dist/chunks/chunk-47SK2QYC.ts +56 -0
- package/dist/chunks/chunk-47SK2QYC.ts.map +7 -0
- package/dist/chunks/chunk-6KJ34M6S.ts +159 -0
- package/dist/chunks/chunk-6KJ34M6S.ts.map +7 -0
- package/dist/chunks/chunk-7OBINKFM.ts +80 -0
- package/dist/chunks/chunk-7OBINKFM.ts.map +7 -0
- package/dist/chunks/chunk-7QPPBBXZ.ts +335 -0
- package/dist/chunks/chunk-7QPPBBXZ.ts.map +7 -0
- package/dist/chunks/chunk-AEUYS7JC.ts +73 -0
- package/dist/chunks/chunk-AEUYS7JC.ts.map +7 -0
- package/dist/chunks/chunk-BFK2Z4ZF.ts +189 -0
- package/dist/chunks/chunk-BFK2Z4ZF.ts.map +7 -0
- package/dist/chunks/chunk-BOAXY55Y.ts +153 -0
- package/dist/chunks/chunk-BOAXY55Y.ts.map +7 -0
- package/dist/chunks/chunk-C4L6P266.ts +22 -0
- package/dist/chunks/chunk-C4L6P266.ts.map +7 -0
- package/dist/chunks/chunk-DPPVEQAM.ts +29 -0
- package/dist/chunks/chunk-DPPVEQAM.ts.map +7 -0
- package/dist/chunks/chunk-DQMN4OYM.ts +913 -0
- package/dist/chunks/chunk-DQMN4OYM.ts.map +7 -0
- package/dist/chunks/chunk-DSOOH73Y.ts +238 -0
- package/dist/chunks/chunk-DSOOH73Y.ts.map +7 -0
- package/dist/chunks/chunk-EFBPISNT.ts +421 -0
- package/dist/chunks/chunk-EFBPISNT.ts.map +7 -0
- package/dist/chunks/chunk-EWWXFU7N.ts +150 -0
- package/dist/chunks/chunk-EWWXFU7N.ts.map +7 -0
- package/dist/chunks/chunk-F4L2UVSK.ts +380 -0
- package/dist/chunks/chunk-F4L2UVSK.ts.map +7 -0
- package/dist/chunks/chunk-F5YWVHQX.ts +1543 -0
- package/dist/chunks/chunk-F5YWVHQX.ts.map +7 -0
- package/dist/chunks/chunk-G3RSMSXJ.ts +63 -0
- package/dist/chunks/chunk-G3RSMSXJ.ts.map +7 -0
- package/dist/chunks/chunk-G6VZTCFH.ts +70 -0
- package/dist/chunks/chunk-G6VZTCFH.ts.map +7 -0
- package/dist/chunks/chunk-H3FP6DLR.ts +160 -0
- package/dist/chunks/chunk-H3FP6DLR.ts.map +7 -0
- package/dist/chunks/chunk-HA36LPCO.ts +130 -0
- package/dist/chunks/chunk-HA36LPCO.ts.map +7 -0
- package/dist/chunks/chunk-HKC4ES4B.ts +65 -0
- package/dist/chunks/chunk-HKC4ES4B.ts.map +7 -0
- package/dist/chunks/chunk-HOP5FXDT.ts +27 -0
- package/dist/chunks/chunk-HOP5FXDT.ts.map +7 -0
- package/dist/chunks/chunk-I2FAK44T.ts +324 -0
- package/dist/chunks/chunk-I2FAK44T.ts.map +7 -0
- package/dist/chunks/chunk-IGAMVWFN.ts +139 -0
- package/dist/chunks/chunk-IGAMVWFN.ts.map +7 -0
- package/dist/chunks/chunk-KMGKCEO4.ts +38 -0
- package/dist/chunks/chunk-KMGKCEO4.ts.map +7 -0
- package/dist/chunks/chunk-N7BLVXKK.ts +73 -0
- package/dist/chunks/chunk-N7BLVXKK.ts.map +7 -0
- package/dist/chunks/chunk-NIF42QMF.ts +297 -0
- package/dist/chunks/chunk-NIF42QMF.ts.map +7 -0
- package/dist/chunks/chunk-O4VO6JUJ.ts +52 -0
- package/dist/chunks/chunk-O4VO6JUJ.ts.map +7 -0
- package/dist/chunks/chunk-ONDTY4EL.ts +91 -0
- package/dist/chunks/chunk-ONDTY4EL.ts.map +7 -0
- package/dist/chunks/chunk-OVPHFGKO.ts +1769 -0
- package/dist/chunks/chunk-OVPHFGKO.ts.map +7 -0
- package/dist/chunks/chunk-P7OH4XMF.ts +60 -0
- package/dist/chunks/chunk-P7OH4XMF.ts.map +7 -0
- package/dist/chunks/chunk-PSK43Y6A.ts +339 -0
- package/dist/chunks/chunk-PSK43Y6A.ts.map +7 -0
- package/dist/chunks/chunk-QBORTI4W.ts +468 -0
- package/dist/chunks/chunk-QBORTI4W.ts.map +7 -0
- package/dist/chunks/chunk-QDVGH2X7.ts +171 -0
- package/dist/chunks/chunk-QDVGH2X7.ts.map +7 -0
- package/dist/chunks/chunk-RHRQMT7Q.ts +490 -0
- package/dist/chunks/chunk-RHRQMT7Q.ts.map +7 -0
- package/dist/chunks/chunk-RRR66UWR.ts +28 -0
- package/dist/chunks/chunk-RRR66UWR.ts.map +7 -0
- package/dist/chunks/chunk-RSUXZD6S.ts +275 -0
- package/dist/chunks/chunk-RSUXZD6S.ts.map +7 -0
- package/dist/chunks/chunk-RTCVYIZA.ts +221 -0
- package/dist/chunks/chunk-RTCVYIZA.ts.map +7 -0
- package/dist/chunks/chunk-S2IWVK3J.ts +44 -0
- package/dist/chunks/chunk-S2IWVK3J.ts.map +7 -0
- package/dist/chunks/chunk-SCZ33MYW.ts +52 -0
- package/dist/chunks/chunk-SCZ33MYW.ts.map +7 -0
- package/dist/chunks/chunk-SWGQLFSD.ts +60 -0
- package/dist/chunks/chunk-SWGQLFSD.ts.map +7 -0
- package/dist/chunks/chunk-UMBPMVJW.ts +22 -0
- package/dist/chunks/chunk-UMBPMVJW.ts.map +7 -0
- package/dist/chunks/chunk-UVFVHIOP.ts +735 -0
- package/dist/chunks/chunk-UVFVHIOP.ts.map +7 -0
- package/dist/chunks/completion-delivery-YPOWSSV3.ts +16 -0
- package/dist/chunks/completion-delivery-YPOWSSV3.ts.map +7 -0
- package/dist/chunks/config-status-J4GPWP46.ts +34 -0
- package/dist/chunks/config-status-J4GPWP46.ts.map +7 -0
- package/dist/chunks/config-ui-3IBMHTQB.ts +1146 -0
- package/dist/chunks/config-ui-3IBMHTQB.ts.map +7 -0
- package/dist/chunks/consult-NYXVDN5G.ts +518 -0
- package/dist/chunks/consult-NYXVDN5G.ts.map +7 -0
- package/dist/chunks/context-QDKLVQXE.ts +11 -0
- package/dist/chunks/context-QDKLVQXE.ts.map +7 -0
- package/dist/chunks/create-stateful-transport-4WKZHRSI.ts +130 -0
- package/dist/chunks/create-stateful-transport-4WKZHRSI.ts.map +7 -0
- package/dist/chunks/cwd-policy-NB6XZ5GF.ts +17 -0
- package/dist/chunks/cwd-policy-NB6XZ5GF.ts.map +7 -0
- package/dist/chunks/delegation-contract-WJPT4FVS.ts +24 -0
- package/dist/chunks/delegation-contract-WJPT4FVS.ts.map +7 -0
- package/dist/chunks/discovery-K25T3SH4.ts +12 -0
- package/dist/chunks/discovery-K25T3SH4.ts.map +7 -0
- package/dist/chunks/execution-DS2DEIQZ.ts +4034 -0
- package/dist/chunks/execution-DS2DEIQZ.ts.map +7 -0
- package/dist/chunks/in-process-transport-TEYAWSEC.ts +38 -0
- package/dist/chunks/in-process-transport-TEYAWSEC.ts.map +7 -0
- package/dist/chunks/inspect-UOQQ3KNL.ts +619 -0
- package/dist/chunks/inspect-UOQQ3KNL.ts.map +7 -0
- package/dist/chunks/peer-communication-KL36Z6CO.ts +294 -0
- package/dist/chunks/peer-communication-KL36Z6CO.ts.map +7 -0
- package/dist/chunks/persistence-VSGXAW4P.ts +344 -0
- package/dist/chunks/persistence-VSGXAW4P.ts.map +7 -0
- package/dist/chunks/registry-E6XPJB7L.ts +1324 -0
- package/dist/chunks/registry-E6XPJB7L.ts.map +7 -0
- package/dist/chunks/retained-semantic-state-7WXSGRJU.ts +81 -0
- package/dist/chunks/retained-semantic-state-7WXSGRJU.ts.map +7 -0
- package/dist/chunks/rpc-transport-Y6LA5BOP.ts +1154 -0
- package/dist/chunks/rpc-transport-Y6LA5BOP.ts.map +7 -0
- package/dist/chunks/runtime-policy-5YELCOVC.ts +15 -0
- package/dist/chunks/runtime-policy-5YELCOVC.ts.map +7 -0
- package/dist/chunks/semantic-snapshot-WPJ3UAFW.ts +19 -0
- package/dist/chunks/semantic-snapshot-WPJ3UAFW.ts.map +7 -0
- package/dist/chunks/spawn-idempotency-QGHO2WKC.ts +13 -0
- package/dist/chunks/spawn-idempotency-QGHO2WKC.ts.map +7 -0
- package/dist/chunks/stateful-lifecycle-JAR6K55H.ts +15 -0
- package/dist/chunks/stateful-lifecycle-JAR6K55H.ts.map +7 -0
- package/dist/chunks/subprocess-transport-C2XVR25R.ts +158 -0
- package/dist/chunks/subprocess-transport-C2XVR25R.ts.map +7 -0
- package/dist/chunks/workspace-WDKLN72J.ts +11 -0
- package/dist/chunks/workspace-WDKLN72J.ts.map +7 -0
- package/dist/index.ts +1057 -0
- package/dist/index.ts.map +7 -0
- package/package.json +8 -4
- package/src/index.ts +1 -1
- package/src/settings-reader.ts +208 -0
- package/src/settings.ts +22 -187
- package/src/stateful-registration.ts +1230 -0
- package/src/stateful-safety.ts +1 -1
- package/src/stateful.ts +2 -1234
- package/src/subagents-extension.ts +307 -0
- package/src/subagents.ts +1 -308
package/dist/index.ts
ADDED
|
@@ -0,0 +1,1057 @@
|
|
|
1
|
+
// @generated by scripts/build-runtime.mjs; do not edit.
|
|
2
|
+
// @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
|
|
3
|
+
import {
|
|
4
|
+
registerSubagentInspect
|
|
5
|
+
} from "./chunks/chunk-F4L2UVSK.js";
|
|
6
|
+
import {
|
|
7
|
+
SubagentParams,
|
|
8
|
+
hasUsableAggregator
|
|
9
|
+
} from "./chunks/chunk-NIF42QMF.js";
|
|
10
|
+
import {
|
|
11
|
+
registerStatefulSubagents
|
|
12
|
+
} from "./chunks/chunk-F5YWVHQX.js";
|
|
13
|
+
import {
|
|
14
|
+
discoverAgentCatalog,
|
|
15
|
+
formatAgentCatalog,
|
|
16
|
+
formatUsageStats,
|
|
17
|
+
registerSubagentConsult
|
|
18
|
+
} from "./chunks/chunk-RHRQMT7Q.js";
|
|
19
|
+
import "./chunks/chunk-P7OH4XMF.js";
|
|
20
|
+
import "./chunks/chunk-N7BLVXKK.js";
|
|
21
|
+
import {
|
|
22
|
+
getResultFinalOutput,
|
|
23
|
+
isResultError
|
|
24
|
+
} from "./chunks/chunk-AEUYS7JC.js";
|
|
25
|
+
import "./chunks/chunk-RTCVYIZA.js";
|
|
26
|
+
import "./chunks/chunk-ONDTY4EL.js";
|
|
27
|
+
import "./chunks/chunk-O4VO6JUJ.js";
|
|
28
|
+
import {
|
|
29
|
+
cachedModuleLoader,
|
|
30
|
+
throwIfAborted
|
|
31
|
+
} from "./chunks/chunk-HOP5FXDT.js";
|
|
32
|
+
import "./chunks/chunk-PSK43Y6A.js";
|
|
33
|
+
import "./chunks/chunk-RSUXZD6S.js";
|
|
34
|
+
import {
|
|
35
|
+
DEFAULT_CONSULTATION_CWD_POLICY,
|
|
36
|
+
DEFAULT_CONSULT_RESOURCE_POLICY,
|
|
37
|
+
DEFAULT_DELEGATION_CWD_POLICY,
|
|
38
|
+
consumeSubagentSettingsNotice,
|
|
39
|
+
inspectSubagentSettings,
|
|
40
|
+
readSubagentSettings,
|
|
41
|
+
resolveBlockingMaxParallelTasks
|
|
42
|
+
} from "./chunks/chunk-QBORTI4W.js";
|
|
43
|
+
import "./chunks/chunk-7OBINKFM.js";
|
|
44
|
+
import "./chunks/chunk-DPPVEQAM.js";
|
|
45
|
+
import {
|
|
46
|
+
SUBAGENT_COMPLETION_MESSAGE_TYPE,
|
|
47
|
+
renderCompletionMessage
|
|
48
|
+
} from "./chunks/chunk-QDVGH2X7.js";
|
|
49
|
+
import {
|
|
50
|
+
expansionHint,
|
|
51
|
+
formatToolActivity,
|
|
52
|
+
safeBlock,
|
|
53
|
+
safeLine
|
|
54
|
+
} from "./chunks/chunk-BFK2Z4ZF.js";
|
|
55
|
+
import "./chunks/chunk-SWGQLFSD.js";
|
|
56
|
+
import "./chunks/chunk-I2FAK44T.js";
|
|
57
|
+
import {
|
|
58
|
+
MAX_BLOCKING_PARALLEL_CONCURRENCY
|
|
59
|
+
} from "./chunks/chunk-H3FP6DLR.js";
|
|
60
|
+
import "./chunks/chunk-S2IWVK3J.js";
|
|
61
|
+
|
|
62
|
+
// src/subagents-extension.ts
|
|
63
|
+
import {
|
|
64
|
+
CONFIG_DIR_NAME
|
|
65
|
+
} from "@earendil-works/pi-coding-agent";
|
|
66
|
+
|
|
67
|
+
// src/config-registration.ts
|
|
68
|
+
var SUBCOMMANDS = [
|
|
69
|
+
{ value: "settings", label: "settings", description: "Configure subagent user settings" },
|
|
70
|
+
{ value: "status", label: "status", description: "Show effective subagent settings" },
|
|
71
|
+
{ value: "help", label: "help", description: "Show subagent settings help" }
|
|
72
|
+
];
|
|
73
|
+
function registerSubagentConfigLifecycle(pi) {
|
|
74
|
+
const owner = { generation: 0, controller: new AbortController() };
|
|
75
|
+
pi.on("session_start", () => {
|
|
76
|
+
owner.generation += 1;
|
|
77
|
+
owner.controller.abort(new DOMException("Subagent session replaced", "AbortError"));
|
|
78
|
+
owner.controller = new AbortController();
|
|
79
|
+
});
|
|
80
|
+
pi.on("session_shutdown", () => {
|
|
81
|
+
owner.generation += 1;
|
|
82
|
+
owner.controller.abort(new DOMException("Subagent session shut down", "AbortError"));
|
|
83
|
+
});
|
|
84
|
+
return owner;
|
|
85
|
+
}
|
|
86
|
+
function registerSubagentConfigCommand(pi, runtime, owner = registerSubagentConfigLifecycle(pi), dependencies = {}) {
|
|
87
|
+
const loadConfigUi = cachedModuleLoader(
|
|
88
|
+
dependencies.loadConfigUi ?? (() => import("./chunks/config-ui-3IBMHTQB.js"))
|
|
89
|
+
);
|
|
90
|
+
const loadConfigStatus = cachedModuleLoader(
|
|
91
|
+
dependencies.loadConfigStatus ?? (() => import("./chunks/config-status-J4GPWP46.js"))
|
|
92
|
+
);
|
|
93
|
+
pi.registerCommand("subagents", {
|
|
94
|
+
description: "Manage current-session subagents and user settings",
|
|
95
|
+
getArgumentCompletions(prefix) {
|
|
96
|
+
const normalized = prefix.trim().toLowerCase();
|
|
97
|
+
const matches = SUBCOMMANDS.filter((item) => item.value.startsWith(normalized));
|
|
98
|
+
return matches.length > 0 ? matches : null;
|
|
99
|
+
},
|
|
100
|
+
async handler(args, ctx) {
|
|
101
|
+
const subcommand = args.trim().toLowerCase();
|
|
102
|
+
const runStatusCommand = async (show) => {
|
|
103
|
+
const generation = owner.generation;
|
|
104
|
+
const controller = owner.controller;
|
|
105
|
+
const isCurrent = () => generation === owner.generation && controller === owner.controller && !controller.signal.aborted;
|
|
106
|
+
let status;
|
|
107
|
+
try {
|
|
108
|
+
status = await loadConfigStatus();
|
|
109
|
+
} catch (error) {
|
|
110
|
+
if (!isCurrent()) return;
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
if (!isCurrent()) return;
|
|
114
|
+
show(status);
|
|
115
|
+
};
|
|
116
|
+
if (!subcommand && ctx.mode !== "tui") {
|
|
117
|
+
await runStatusCommand((status) => status.showSubagentStatus(ctx, runtime));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (subcommand === "status") {
|
|
121
|
+
await runStatusCommand((status) => status.showSubagentStatus(ctx, runtime));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (subcommand === "help") {
|
|
125
|
+
await runStatusCommand((status) => status.showSubagentHelp(ctx, runtime));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (!subcommand || subcommand === "settings") {
|
|
129
|
+
const generation = owner.generation;
|
|
130
|
+
const controller = owner.controller;
|
|
131
|
+
const isCurrent = () => generation === owner.generation && controller === owner.controller && !controller.signal.aborted;
|
|
132
|
+
let configUi;
|
|
133
|
+
try {
|
|
134
|
+
configUi = await loadConfigUi();
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (!isCurrent()) return;
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
if (!isCurrent()) return;
|
|
140
|
+
if (!subcommand) await configUi.showSubagentManager(pi, ctx, runtime, owner);
|
|
141
|
+
else await configUi.showSubagentSettings(ctx, runtime, owner);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (ctx.mode === "tui" || ctx.hasUI) {
|
|
145
|
+
ctx.ui.notify(`Unknown /subagents subcommand: ${subcommand}`, "warning");
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// src/render.ts
|
|
152
|
+
import {
|
|
153
|
+
getMarkdownTheme
|
|
154
|
+
} from "@earendil-works/pi-coding-agent";
|
|
155
|
+
import { Container, Markdown, Spacer, Text as Text2 } from "@earendil-works/pi-tui";
|
|
156
|
+
|
|
157
|
+
// src/panel-render.ts
|
|
158
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
159
|
+
function renderPanelCall(args, theme) {
|
|
160
|
+
const panel = args.panel;
|
|
161
|
+
if (!panel) return void 0;
|
|
162
|
+
const scope = args.agentScope ?? "user";
|
|
163
|
+
let text = theme.fg("toolTitle", theme.bold("subagent ")) + theme.fg("accent", `panel ${panel.preset ?? "custom"} (${panel.reviewers.length} reviewers)`) + theme.fg("muted", ` [${scope}]`);
|
|
164
|
+
text += `
|
|
165
|
+
${theme.fg("dim", preview(panel.task, 80))}`;
|
|
166
|
+
for (const reviewer of panel.reviewers.slice(0, 3)) {
|
|
167
|
+
text += `
|
|
168
|
+
${theme.fg("muted", `${safeLine(reviewer.id, "reviewer", 256)}:`)} ${theme.fg("accent", safeLine(reviewer.agent, "agent", 256))}`;
|
|
169
|
+
if (reviewer.focus) text += theme.fg("dim", ` ${preview(reviewer.focus, 40)}`);
|
|
170
|
+
}
|
|
171
|
+
if (panel.reviewers.length > 3) {
|
|
172
|
+
text += `
|
|
173
|
+
${theme.fg("muted", `... +${panel.reviewers.length - 3} more`)}`;
|
|
174
|
+
}
|
|
175
|
+
text += `
|
|
176
|
+
${theme.fg("muted", "synthesis \u2192 ")}${theme.fg("accent", safeLine(panel.synthesizer.agent, "agent", 256))}`;
|
|
177
|
+
return new Text(text, 0, 0);
|
|
178
|
+
}
|
|
179
|
+
function renderPanelResult(details, expanded, isPartial, theme) {
|
|
180
|
+
const panel = details.panel;
|
|
181
|
+
if (details.mode !== "panel" || !panel) return void 0;
|
|
182
|
+
const running = isPartial || panel.state === "running";
|
|
183
|
+
const status = running ? "running" : panel.state;
|
|
184
|
+
const color = status === "completed" ? "success" : status === "running" || status === "degraded" ? "warning" : "error";
|
|
185
|
+
let text = `${theme.fg(color, status)} ${theme.fg("accent", safeLine(panel.preset, "panel", 128))}`;
|
|
186
|
+
text += theme.fg(
|
|
187
|
+
"muted",
|
|
188
|
+
` \xB7 valid ${panel.validReviewCount}/${panel.reviewerIds.length} \xB7 failed ${panel.failedReviewCount} \xB7 blockers ${panel.blockingObjectionCount} \xB7 dissent ${panel.dissentCount}`
|
|
189
|
+
);
|
|
190
|
+
if (panel.synthesis) {
|
|
191
|
+
text += `
|
|
192
|
+
${theme.fg("toolOutput", safeBlock(panel.synthesis.summary, "", 8 * 1024))}`;
|
|
193
|
+
} else if (panel.state === "insufficient-panel") {
|
|
194
|
+
text += `
|
|
195
|
+
${theme.fg("warning", "Synthesis skipped because too few valid reviews remained.")}`;
|
|
196
|
+
}
|
|
197
|
+
if (!expanded) return new Text(text, 0, 0);
|
|
198
|
+
text += `
|
|
199
|
+
|
|
200
|
+
${theme.fg("muted", "Shared task: ")}${theme.fg("dim", safeBlock(panel.sharedTaskPreview, "", 2 * 1024))}`;
|
|
201
|
+
text += `
|
|
202
|
+
|
|
203
|
+
${theme.fg("muted", "Reviewers:")}`;
|
|
204
|
+
for (const reviewerId of panel.reviewerIds) {
|
|
205
|
+
const artifact = panel.evidence.find((candidate) => candidate.reviewerId === reviewerId);
|
|
206
|
+
const failure = panel.failures.find((candidate) => candidate.reviewerId === reviewerId);
|
|
207
|
+
const result = details.results.find(
|
|
208
|
+
(candidate) => candidate.step === panel.reviewerIds.indexOf(reviewerId) + 1
|
|
209
|
+
);
|
|
210
|
+
const model = result?.actualModel ?? result?.model;
|
|
211
|
+
text += `
|
|
212
|
+
${theme.fg("accent", safeLine(reviewerId, "reviewer", 256))}`;
|
|
213
|
+
if (model) text += theme.fg("muted", ` \xB7 ${safeLine(model, "", 256)}`);
|
|
214
|
+
if (artifact) {
|
|
215
|
+
text += theme.fg(
|
|
216
|
+
"muted",
|
|
217
|
+
` \xB7 ${artifact.review.disposition} \xB7 revision ${artifact.revision}`
|
|
218
|
+
);
|
|
219
|
+
for (const finding of artifact.review.findings) {
|
|
220
|
+
text += `
|
|
221
|
+
${theme.fg(finding.severity === "critical" || finding.severity === "high" ? "error" : "warning", `[${finding.severity}]`)} ${safeLine(finding.title, "finding", 512)}`;
|
|
222
|
+
}
|
|
223
|
+
} else if (failure) {
|
|
224
|
+
text += theme.fg("error", ` \xB7 ${safeLine(failure.kind, "failed", 128)}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (panel.synthesis?.disagreements.length) {
|
|
228
|
+
text += `
|
|
229
|
+
|
|
230
|
+
${theme.fg("muted", "Disagreements:")}`;
|
|
231
|
+
for (const disagreement of panel.synthesis.disagreements) {
|
|
232
|
+
text += `
|
|
233
|
+
${safeLine(disagreement.summary, "disagreement", 1024)}`;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (panel.synthesis?.objections.length) {
|
|
237
|
+
text += `
|
|
238
|
+
|
|
239
|
+
${theme.fg("muted", "Blocking objections:")}`;
|
|
240
|
+
for (const objection of panel.synthesis.objections) {
|
|
241
|
+
text += `
|
|
242
|
+
${safeLine(`${objection.reviewerId}/${objection.findingId}: ${objection.resolution}`, "objection", 1024)}`;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const limitations = panel.synthesis?.limitations ?? [];
|
|
246
|
+
if (limitations.length) {
|
|
247
|
+
text += `
|
|
248
|
+
|
|
249
|
+
${theme.fg("muted", "Limitations:")}`;
|
|
250
|
+
for (const limitation of limitations) text += `
|
|
251
|
+
${safeLine(limitation, "", 1024)}`;
|
|
252
|
+
}
|
|
253
|
+
text += `
|
|
254
|
+
|
|
255
|
+
${theme.fg("muted", `Budget: review ${panel.budgets.reviewMs}ms \xB7 finalization ${panel.budgets.finalizationMs}ms \xB7 synthesis ${panel.budgets.synthesisMs}ms \xB7 cleanup ${panel.budgets.cleanupMs}ms`)}`;
|
|
256
|
+
text += `
|
|
257
|
+
${theme.fg("muted", `Cleanup: ${panel.cleanupComplete ? "complete" : "pending"}`)}`;
|
|
258
|
+
return new Text(text, 0, 0);
|
|
259
|
+
}
|
|
260
|
+
function preview(value, maxLength) {
|
|
261
|
+
const text = safeLine(value, "", 2 * 1024);
|
|
262
|
+
return text.length > maxLength ? `${text.slice(0, maxLength)}...` : text;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// src/render.ts
|
|
266
|
+
var COLLAPSED_ITEM_COUNT = 5;
|
|
267
|
+
function previewTask(task, maxLength = 40) {
|
|
268
|
+
const safe = safeLine(task, "...", 2 * 1024);
|
|
269
|
+
return safe.length > maxLength ? `${safe.slice(0, maxLength)}...` : safe;
|
|
270
|
+
}
|
|
271
|
+
function previewAgent(agent) {
|
|
272
|
+
return safeLine(agent, "...", 256);
|
|
273
|
+
}
|
|
274
|
+
function formatResultUsageStats(result) {
|
|
275
|
+
return formatUsageStats(
|
|
276
|
+
result.usage,
|
|
277
|
+
result.model,
|
|
278
|
+
result.thinkingLevel,
|
|
279
|
+
result.actualProvider,
|
|
280
|
+
result.actualModel
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
function formatToolCall(toolName, args, themeFg) {
|
|
284
|
+
return formatToolActivity(toolName, args, themeFg);
|
|
285
|
+
}
|
|
286
|
+
function getDisplayItems(messages) {
|
|
287
|
+
const items = [];
|
|
288
|
+
for (const msg of messages) {
|
|
289
|
+
if (msg.role === "assistant") {
|
|
290
|
+
for (const part of msg.content) {
|
|
291
|
+
if (part.type === "text") {
|
|
292
|
+
const text = safeBlock(part.text, "", 8 * 1024).trim();
|
|
293
|
+
if (text) items.push({ type: "text", text });
|
|
294
|
+
} else if (part.type === "toolCall") {
|
|
295
|
+
items.push({
|
|
296
|
+
type: "toolCall",
|
|
297
|
+
name: safeLine(part.name, "tool", 256),
|
|
298
|
+
args: part.arguments
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return items;
|
|
305
|
+
}
|
|
306
|
+
function getCollapsedDisplayItems(result) {
|
|
307
|
+
if (result.recentActivity && result.recentActivity.length > 0) {
|
|
308
|
+
return {
|
|
309
|
+
items: result.recentActivity,
|
|
310
|
+
total: Math.max(result.recentActivity.length, result.recentActivityTotal ?? 0)
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
const items = getDisplayItems(result.messages);
|
|
314
|
+
return { items, total: items.length };
|
|
315
|
+
}
|
|
316
|
+
function sanitizeSingleResultForRender(result) {
|
|
317
|
+
return {
|
|
318
|
+
...result,
|
|
319
|
+
agent: safeLine(result.agent, "subagent", 256),
|
|
320
|
+
task: safeBlock(result.task, "", 50 * 1024),
|
|
321
|
+
stderr: safeBlock(result.stderr, "", 8 * 1024),
|
|
322
|
+
model: result.model ? safeLine(result.model, "", 256) : void 0,
|
|
323
|
+
actualProvider: result.actualProvider ? safeLine(result.actualProvider, "", 256) : void 0,
|
|
324
|
+
actualModel: result.actualModel ? safeLine(result.actualModel, "", 256) : void 0,
|
|
325
|
+
stopReason: result.stopReason ? safeLine(result.stopReason, "", 256) : void 0,
|
|
326
|
+
errorMessage: result.errorMessage ? safeBlock(result.errorMessage, "", 8 * 1024) : void 0,
|
|
327
|
+
finalOutput: result.finalOutput ? safeBlock(result.finalOutput, "", 50 * 1024) : void 0
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function renderResultStatus(result, isPartial) {
|
|
331
|
+
if (result.outcome && !["completed", "partial"].includes(result.outcome.status)) {
|
|
332
|
+
return result.outcome.status.split("-").map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`).join(" ");
|
|
333
|
+
}
|
|
334
|
+
if (isResultError(result)) {
|
|
335
|
+
return result.stopReason === "aborted" || result.aborted ? "Cancelled" : "Failed";
|
|
336
|
+
}
|
|
337
|
+
return isPartial || result.exitCode === -1 ? "Running" : "Completed";
|
|
338
|
+
}
|
|
339
|
+
function coloredStatus(theme, status) {
|
|
340
|
+
const color = status === "Failed" ? "error" : status === "Completed" ? "success" : "warning";
|
|
341
|
+
return theme.fg(color, status);
|
|
342
|
+
}
|
|
343
|
+
function coloredResultStatus(theme, result, isPartial) {
|
|
344
|
+
return coloredStatus(theme, renderResultStatus(result, isPartial));
|
|
345
|
+
}
|
|
346
|
+
function formatResultIntelligence(result) {
|
|
347
|
+
const structured = result.structuredResult?.version === "pi-subagents:result:v2" ? result.structuredResult : void 0;
|
|
348
|
+
return [
|
|
349
|
+
result.contract ? `contract: ${result.contract.level} \xB7 ${result.contract.taskId}` : void 0,
|
|
350
|
+
result.outcome ? `outcome: ${result.outcome.status}${result.outcome.reasonCode ? ` \xB7 ${result.outcome.reasonCode}` : ""}` : void 0,
|
|
351
|
+
structured ? `evidence: ${structured.claims.length} claims \xB7 ${structured.artifacts.length} artifacts \xB7 ${structured.verification.length} verification items \xB7 ${structured.limitations.length} limitations` : void 0,
|
|
352
|
+
result.resultContractInvalid ? "contract warning: invalid structured result" : void 0,
|
|
353
|
+
result.executionPlan ? `plan: ${result.executionPlan.id.slice(0, 12)} \xB7 generation ${result.executionPlan.taskGeneration} \xB7 admission ${result.executionPlan.admission.recommendation}` : void 0,
|
|
354
|
+
result.capabilityGrant ? `grant: ${result.capabilityGrant.state} \xB7 expires ${result.capabilityGrant.expiresAt}` : void 0
|
|
355
|
+
].filter((line) => Boolean(line)).join("\n");
|
|
356
|
+
}
|
|
357
|
+
function formatResultPolicy(result) {
|
|
358
|
+
if (!result.policy) return "";
|
|
359
|
+
return [
|
|
360
|
+
`inherited: ${result.policy.inherited.map((tool) => safeLine(tool, "", 256)).join(", ") || "none"}`,
|
|
361
|
+
`overridden: ${result.policy.overridden.map((tool) => safeLine(tool, "", 256)).join(", ") || "none"}`,
|
|
362
|
+
`unsupported: ${result.policy.unsupported.map((tool) => safeLine(tool, "", 256)).join(", ") || "none"}`
|
|
363
|
+
].join("\n");
|
|
364
|
+
}
|
|
365
|
+
function renderSubagentCall(args, theme) {
|
|
366
|
+
const scope = args.agentScope ?? "user";
|
|
367
|
+
const limits = [
|
|
368
|
+
typeof args.timeoutMs === "number" ? `timeout:${args.timeoutMs}ms` : void 0,
|
|
369
|
+
typeof args.totalTimeoutMs === "number" ? `total:${args.totalTimeoutMs}ms` : void 0,
|
|
370
|
+
typeof args.idleTimeoutMs === "number" ? `idle:${args.idleTimeoutMs}ms` : void 0,
|
|
371
|
+
typeof args.maxTurns === "number" ? `turns:${args.maxTurns}` : void 0,
|
|
372
|
+
typeof args.maxToolCalls === "number" ? `tools:${args.maxToolCalls}` : void 0
|
|
373
|
+
].filter((value) => Boolean(value));
|
|
374
|
+
const limitText = limits.length > 0 ? ` \xB7 ${limits.join(" \xB7 ")}` : "";
|
|
375
|
+
const panelCall = renderPanelCall(args, theme);
|
|
376
|
+
if (panelCall) return panelCall;
|
|
377
|
+
if (args.workflow && args.workflow.tasks.length > 0) {
|
|
378
|
+
let text2 = theme.fg("toolTitle", theme.bold("subagent ")) + theme.fg("accent", `workflow (${args.workflow.tasks.length} tasks)`) + theme.fg("muted", ` [${scope}]${limitText}`);
|
|
379
|
+
for (const task of args.workflow.tasks.slice(0, 3)) {
|
|
380
|
+
text2 += `
|
|
381
|
+
${theme.fg("muted", `${task.id}:`)} ${theme.fg("accent", previewAgent(task.agent))}${theme.fg("dim", ` ${previewTask(task.task)}`)}`;
|
|
382
|
+
}
|
|
383
|
+
if (args.workflow.tasks.length > 3) {
|
|
384
|
+
text2 += `
|
|
385
|
+
${theme.fg("muted", `... +${args.workflow.tasks.length - 3} more`)}`;
|
|
386
|
+
}
|
|
387
|
+
return new Text2(text2, 0, 0);
|
|
388
|
+
}
|
|
389
|
+
if (args.chain && args.chain.length > 0) {
|
|
390
|
+
let text2 = theme.fg("toolTitle", theme.bold("subagent ")) + theme.fg("accent", `chain (${args.chain.length} steps)`) + theme.fg("muted", ` [${scope}]${limitText}`);
|
|
391
|
+
for (let i = 0; i < Math.min(args.chain.length, 3); i++) {
|
|
392
|
+
const step = args.chain[i];
|
|
393
|
+
const cleanTask = typeof step?.task === "string" ? step.task.replace(/\{previous\}/g, "").trim() : void 0;
|
|
394
|
+
text2 += "\n " + theme.fg("muted", `${i + 1}.`) + " " + theme.fg("accent", previewAgent(step?.agent)) + theme.fg("dim", ` ${previewTask(cleanTask)}`);
|
|
395
|
+
}
|
|
396
|
+
if (args.chain.length > 3)
|
|
397
|
+
text2 += `
|
|
398
|
+
${theme.fg("muted", `... +${args.chain.length - 3} more`)}`;
|
|
399
|
+
return new Text2(text2, 0, 0);
|
|
400
|
+
}
|
|
401
|
+
if (args.tasks && args.tasks.length > 0) {
|
|
402
|
+
let text2 = theme.fg("toolTitle", theme.bold("subagent ")) + theme.fg("accent", `parallel (${args.tasks.length} tasks)`) + theme.fg("muted", ` [${scope}]${limitText}`);
|
|
403
|
+
for (const task of args.tasks.slice(0, 3)) {
|
|
404
|
+
const item = task;
|
|
405
|
+
text2 += `
|
|
406
|
+
${theme.fg("accent", previewAgent(item?.agent))}${theme.fg("dim", ` ${previewTask(item?.task)}`)}`;
|
|
407
|
+
}
|
|
408
|
+
if (args.tasks.length > 3)
|
|
409
|
+
text2 += `
|
|
410
|
+
${theme.fg("muted", `... +${args.tasks.length - 3} more`)}`;
|
|
411
|
+
if (hasUsableAggregator(args.aggregator)) {
|
|
412
|
+
const aggregator = args.aggregator;
|
|
413
|
+
text2 += `
|
|
414
|
+
${theme.fg("muted", "fan-in \u2192 ")}${theme.fg("accent", previewAgent(aggregator.agent))}${theme.fg(
|
|
415
|
+
"dim",
|
|
416
|
+
` ${previewTask(aggregator.task)}`
|
|
417
|
+
)}`;
|
|
418
|
+
}
|
|
419
|
+
return new Text2(text2, 0, 0);
|
|
420
|
+
}
|
|
421
|
+
const agentName = previewAgent(args.agent);
|
|
422
|
+
const preview2 = previewTask(args.task, 60);
|
|
423
|
+
let text = theme.fg("toolTitle", theme.bold("subagent ")) + theme.fg("accent", agentName) + theme.fg("muted", ` [${scope}]${limitText}`);
|
|
424
|
+
text += `
|
|
425
|
+
${theme.fg("dim", preview2)}`;
|
|
426
|
+
return new Text2(text, 0, 0);
|
|
427
|
+
}
|
|
428
|
+
function renderSubagentResult(result, { expanded, isPartial }, theme) {
|
|
429
|
+
const rawDetails = result.details;
|
|
430
|
+
if (rawDetails?.mode === "panel") {
|
|
431
|
+
const panelResult = renderPanelResult(rawDetails, expanded, isPartial, theme);
|
|
432
|
+
if (panelResult) return panelResult;
|
|
433
|
+
}
|
|
434
|
+
if (!rawDetails || rawDetails.results.length === 0) {
|
|
435
|
+
const text2 = result.content[0];
|
|
436
|
+
return new Text2(
|
|
437
|
+
text2?.type === "text" ? safeBlock(text2.text, "(no output)", 8 * 1024) : "(no output)",
|
|
438
|
+
0,
|
|
439
|
+
0
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
const details = {
|
|
443
|
+
...rawDetails,
|
|
444
|
+
results: rawDetails.results.map(sanitizeSingleResultForRender),
|
|
445
|
+
aggregator: rawDetails.aggregator ? sanitizeSingleResultForRender(rawDetails.aggregator) : void 0
|
|
446
|
+
};
|
|
447
|
+
const mdTheme = getMarkdownTheme();
|
|
448
|
+
const renderDisplayItems = (items, limit, total = items.length) => {
|
|
449
|
+
const toShow = limit ? items.slice(-limit) : items;
|
|
450
|
+
const skipped = Math.max(0, total - toShow.length);
|
|
451
|
+
let text2 = "";
|
|
452
|
+
if (skipped > 0) text2 += theme.fg("muted", `... ${skipped} earlier items
|
|
453
|
+
`);
|
|
454
|
+
for (const item of toShow) {
|
|
455
|
+
if (item.type === "text") {
|
|
456
|
+
const safeText = safeBlock(item.text, "", 8 * 1024);
|
|
457
|
+
const preview2 = expanded ? safeText : safeText.split("\n").slice(0, 3).join("\n");
|
|
458
|
+
text2 += `${theme.fg("toolOutput", preview2)}
|
|
459
|
+
`;
|
|
460
|
+
} else {
|
|
461
|
+
text2 += `${theme.fg("muted", "\u2192 ") + formatToolCall(item.name, item.args, theme.fg.bind(theme))}
|
|
462
|
+
`;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return text2.trimEnd();
|
|
466
|
+
};
|
|
467
|
+
if (details.mode === "single" && details.results.length === 1) {
|
|
468
|
+
const r = details.results[0];
|
|
469
|
+
const isError = isResultError(r);
|
|
470
|
+
const icon = isError ? theme.fg("error", "\u2717") : isPartial ? theme.fg("warning", "\u23F3") : theme.fg("success", "\u2713");
|
|
471
|
+
const displayItems = getDisplayItems(r.messages);
|
|
472
|
+
const finalOutput = getResultFinalOutput(r);
|
|
473
|
+
if (expanded) {
|
|
474
|
+
const container = new Container();
|
|
475
|
+
let header = `${icon} ${theme.fg("toolTitle", theme.bold(r.agent))}${theme.fg("muted", ` (${safeLine(r.agentSource, "unknown", 128)})`)} \xB7 ${coloredResultStatus(theme, r, isPartial)}`;
|
|
476
|
+
if (isError && r.stopReason) header += ` ${theme.fg("error", `[${r.stopReason}]`)}`;
|
|
477
|
+
container.addChild(new Text2(header, 0, 0));
|
|
478
|
+
if (isError && r.errorMessage)
|
|
479
|
+
container.addChild(new Text2(theme.fg("error", `Error: ${r.errorMessage}`), 0, 0));
|
|
480
|
+
container.addChild(new Spacer(1));
|
|
481
|
+
container.addChild(new Text2(theme.fg("muted", "\u2500\u2500\u2500 Task \u2500\u2500\u2500"), 0, 0));
|
|
482
|
+
container.addChild(new Text2(theme.fg("dim", r.task), 0, 0));
|
|
483
|
+
const intelligence = formatResultIntelligence(r);
|
|
484
|
+
if (intelligence) {
|
|
485
|
+
container.addChild(new Spacer(1));
|
|
486
|
+
container.addChild(new Text2(theme.fg("muted", "\u2500\u2500\u2500 Delegation \u2500\u2500\u2500"), 0, 0));
|
|
487
|
+
container.addChild(new Text2(theme.fg("dim", intelligence), 0, 0));
|
|
488
|
+
}
|
|
489
|
+
const policy = formatResultPolicy(r);
|
|
490
|
+
if (policy) {
|
|
491
|
+
container.addChild(new Spacer(1));
|
|
492
|
+
container.addChild(new Text2(theme.fg("muted", "\u2500\u2500\u2500 Policy \u2500\u2500\u2500"), 0, 0));
|
|
493
|
+
container.addChild(new Text2(theme.fg("dim", policy), 0, 0));
|
|
494
|
+
}
|
|
495
|
+
container.addChild(new Spacer(1));
|
|
496
|
+
container.addChild(new Text2(theme.fg("muted", "\u2500\u2500\u2500 Output \u2500\u2500\u2500"), 0, 0));
|
|
497
|
+
if (displayItems.length === 0 && !finalOutput) {
|
|
498
|
+
container.addChild(new Text2(theme.fg("muted", "(no output)"), 0, 0));
|
|
499
|
+
} else {
|
|
500
|
+
for (const item of displayItems) {
|
|
501
|
+
if (item.type === "toolCall")
|
|
502
|
+
container.addChild(
|
|
503
|
+
new Text2(
|
|
504
|
+
theme.fg("muted", "\u2192 ") + formatToolCall(item.name, item.args, theme.fg.bind(theme)),
|
|
505
|
+
0,
|
|
506
|
+
0
|
|
507
|
+
)
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
if (finalOutput) {
|
|
511
|
+
container.addChild(new Spacer(1));
|
|
512
|
+
container.addChild(new Markdown(finalOutput.trim(), 0, 0, mdTheme));
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
const usageStr2 = formatResultUsageStats(r);
|
|
516
|
+
if (usageStr2) {
|
|
517
|
+
container.addChild(new Spacer(1));
|
|
518
|
+
container.addChild(new Text2(theme.fg("dim", usageStr2), 0, 0));
|
|
519
|
+
}
|
|
520
|
+
return container;
|
|
521
|
+
}
|
|
522
|
+
const collapsed = getCollapsedDisplayItems(r);
|
|
523
|
+
let text2 = `${icon} ${theme.fg("toolTitle", theme.bold(r.agent))}${theme.fg("muted", ` (${safeLine(r.agentSource, "unknown", 128)})`)} \xB7 ${coloredResultStatus(theme, r, isPartial)}`;
|
|
524
|
+
if (isError && r.stopReason) text2 += ` ${theme.fg("error", `[${r.stopReason}]`)}`;
|
|
525
|
+
if (isError && r.errorMessage) text2 += `
|
|
526
|
+
${theme.fg("error", `Error: ${r.errorMessage}`)}`;
|
|
527
|
+
if (collapsed.items.length > 0) {
|
|
528
|
+
text2 += `
|
|
529
|
+
${renderDisplayItems(collapsed.items, COLLAPSED_ITEM_COUNT, collapsed.total)}`;
|
|
530
|
+
if (collapsed.total > COLLAPSED_ITEM_COUNT) text2 += `
|
|
531
|
+
${expansionHint()}`;
|
|
532
|
+
} else if (finalOutput.trim()) {
|
|
533
|
+
const outputLines = finalOutput.trim().split("\n");
|
|
534
|
+
text2 += `
|
|
535
|
+
${theme.fg("toolOutput", outputLines.slice(0, 3).join("\n"))}`;
|
|
536
|
+
if (outputLines.length > 3) text2 += `
|
|
537
|
+
${expansionHint()}`;
|
|
538
|
+
} else if (!isError || !r.errorMessage) {
|
|
539
|
+
text2 += `
|
|
540
|
+
${theme.fg("muted", isPartial && !isError ? "(running...)" : "(no output)")}`;
|
|
541
|
+
}
|
|
542
|
+
const usageStr = formatResultUsageStats(r);
|
|
543
|
+
if (usageStr) text2 += `
|
|
544
|
+
${theme.fg("dim", usageStr)}`;
|
|
545
|
+
return new Text2(text2, 0, 0);
|
|
546
|
+
}
|
|
547
|
+
const aggregateUsage = (results) => {
|
|
548
|
+
const total = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
|
|
549
|
+
for (const r of results) {
|
|
550
|
+
total.input += r.usage.input;
|
|
551
|
+
total.output += r.usage.output;
|
|
552
|
+
total.cacheRead += r.usage.cacheRead;
|
|
553
|
+
total.cacheWrite += r.usage.cacheWrite;
|
|
554
|
+
total.cost += r.usage.cost;
|
|
555
|
+
total.turns += r.usage.turns;
|
|
556
|
+
}
|
|
557
|
+
return total;
|
|
558
|
+
};
|
|
559
|
+
if (details.mode === "chain") {
|
|
560
|
+
const currentResult = details.results.at(-1);
|
|
561
|
+
const currentIsRunning = isPartial && currentResult !== void 0 && !isResultError(currentResult);
|
|
562
|
+
const successCount = details.results.filter(
|
|
563
|
+
(result2) => !isResultError(result2) && (!currentIsRunning || result2 !== currentResult)
|
|
564
|
+
).length;
|
|
565
|
+
const icon = currentIsRunning ? theme.fg("warning", "\u23F3") : successCount === details.results.length ? theme.fg("success", "\u2713") : theme.fg("error", "\u2717");
|
|
566
|
+
const overallStatus = currentIsRunning ? "Running" : successCount === details.results.length ? "Completed" : "Failed";
|
|
567
|
+
if (expanded) {
|
|
568
|
+
const container = new Container();
|
|
569
|
+
container.addChild(
|
|
570
|
+
new Text2(
|
|
571
|
+
icon + " " + theme.fg("toolTitle", theme.bold("chain ")) + theme.fg("accent", `${successCount}/${details.results.length} steps`) + ` \xB7 ${coloredStatus(theme, overallStatus)}`,
|
|
572
|
+
0,
|
|
573
|
+
0
|
|
574
|
+
)
|
|
575
|
+
);
|
|
576
|
+
for (const r of details.results) {
|
|
577
|
+
const rFailed = isResultError(r);
|
|
578
|
+
const rIcon = rFailed ? theme.fg("error", "\u2717") : currentIsRunning && r === currentResult ? theme.fg("warning", "\u23F3") : theme.fg("success", "\u2713");
|
|
579
|
+
const displayItems = getDisplayItems(r.messages);
|
|
580
|
+
const finalOutput = getResultFinalOutput(r);
|
|
581
|
+
container.addChild(new Spacer(1));
|
|
582
|
+
container.addChild(
|
|
583
|
+
new Text2(
|
|
584
|
+
`${theme.fg("muted", `\u2500\u2500\u2500 Step ${r.step}: `) + theme.fg("accent", r.agent)} ${rIcon} ${coloredResultStatus(theme, r, currentIsRunning && r === currentResult)}`,
|
|
585
|
+
0,
|
|
586
|
+
0
|
|
587
|
+
)
|
|
588
|
+
);
|
|
589
|
+
container.addChild(new Text2(theme.fg("muted", "Task: ") + theme.fg("dim", r.task), 0, 0));
|
|
590
|
+
const policy = formatResultPolicy(r);
|
|
591
|
+
if (policy)
|
|
592
|
+
container.addChild(
|
|
593
|
+
new Text2(theme.fg("muted", "Policy:\n") + theme.fg("dim", policy), 0, 0)
|
|
594
|
+
);
|
|
595
|
+
if (rFailed && r.errorMessage)
|
|
596
|
+
container.addChild(new Text2(theme.fg("error", `Error: ${r.errorMessage}`), 0, 0));
|
|
597
|
+
for (const item of displayItems) {
|
|
598
|
+
if (item.type === "toolCall") {
|
|
599
|
+
container.addChild(
|
|
600
|
+
new Text2(
|
|
601
|
+
theme.fg("muted", "\u2192 ") + formatToolCall(item.name, item.args, theme.fg.bind(theme)),
|
|
602
|
+
0,
|
|
603
|
+
0
|
|
604
|
+
)
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
if (finalOutput) {
|
|
609
|
+
container.addChild(new Spacer(1));
|
|
610
|
+
container.addChild(new Markdown(finalOutput.trim(), 0, 0, mdTheme));
|
|
611
|
+
}
|
|
612
|
+
const stepUsage = formatResultUsageStats(r);
|
|
613
|
+
if (stepUsage) container.addChild(new Text2(theme.fg("dim", stepUsage), 0, 0));
|
|
614
|
+
}
|
|
615
|
+
const usageStr2 = formatUsageStats(aggregateUsage(details.results));
|
|
616
|
+
if (usageStr2) {
|
|
617
|
+
container.addChild(new Spacer(1));
|
|
618
|
+
container.addChild(new Text2(theme.fg("dim", `Total: ${usageStr2}`), 0, 0));
|
|
619
|
+
}
|
|
620
|
+
return container;
|
|
621
|
+
}
|
|
622
|
+
let text2 = icon + " " + theme.fg("toolTitle", theme.bold("chain ")) + theme.fg("accent", `${successCount}/${details.results.length} steps`) + ` \xB7 ${coloredStatus(theme, overallStatus)}`;
|
|
623
|
+
for (const r of details.results) {
|
|
624
|
+
const rFailed = isResultError(r);
|
|
625
|
+
const rIcon = rFailed ? theme.fg("error", "\u2717") : currentIsRunning && r === currentResult ? theme.fg("warning", "\u23F3") : theme.fg("success", "\u2713");
|
|
626
|
+
const collapsed = getCollapsedDisplayItems(r);
|
|
627
|
+
const finalOutput = getResultFinalOutput(r).trim();
|
|
628
|
+
text2 += `
|
|
629
|
+
|
|
630
|
+
${theme.fg("muted", `\u2500\u2500\u2500 Step ${r.step}: `)}${theme.fg("accent", r.agent)} ${rIcon} ${coloredResultStatus(theme, r, currentIsRunning && r === currentResult)}`;
|
|
631
|
+
if (rFailed && r.errorMessage) text2 += `
|
|
632
|
+
${theme.fg("error", `Error: ${r.errorMessage}`)}`;
|
|
633
|
+
if (collapsed.items.length > 0)
|
|
634
|
+
text2 += `
|
|
635
|
+
${renderDisplayItems(collapsed.items, 5, collapsed.total)}`;
|
|
636
|
+
else if (currentIsRunning && r === currentResult)
|
|
637
|
+
text2 += `
|
|
638
|
+
${theme.fg("muted", "(running...)")}`;
|
|
639
|
+
else if (finalOutput)
|
|
640
|
+
text2 += `
|
|
641
|
+
${theme.fg("toolOutput", finalOutput.split("\n").slice(0, 3).join("\n"))}`;
|
|
642
|
+
else if (!rFailed || !r.errorMessage) text2 += `
|
|
643
|
+
${theme.fg("muted", "(no output)")}`;
|
|
644
|
+
}
|
|
645
|
+
const usageStr = formatUsageStats(aggregateUsage(details.results));
|
|
646
|
+
if (usageStr) text2 += `
|
|
647
|
+
|
|
648
|
+
${theme.fg("dim", `Total: ${usageStr}`)}`;
|
|
649
|
+
text2 += `
|
|
650
|
+
${expansionHint()}`;
|
|
651
|
+
return new Text2(text2, 0, 0);
|
|
652
|
+
}
|
|
653
|
+
if (details.mode === "parallel" || details.mode === "workflow") {
|
|
654
|
+
const modeLabel = details.mode === "workflow" ? "workflow" : "parallel";
|
|
655
|
+
const resultIsRunning = (result2) => result2.exitCode === -1 && !isResultError(result2);
|
|
656
|
+
const running = details.results.filter(resultIsRunning).length;
|
|
657
|
+
const successCount = details.results.filter(
|
|
658
|
+
(result2) => result2.exitCode !== -1 && !isResultError(result2)
|
|
659
|
+
).length;
|
|
660
|
+
const failCount = details.results.filter(isResultError).length;
|
|
661
|
+
const aggregator = details.aggregator;
|
|
662
|
+
const aggregatorFailed = aggregator ? isResultError(aggregator) : false;
|
|
663
|
+
const aggregatorRunning = aggregator ? !aggregatorFailed && (isPartial || aggregator.exitCode === -1) : false;
|
|
664
|
+
const pendingSuccessfulSettlement = isPartial && !aggregator && running === 0 && failCount === 0;
|
|
665
|
+
const isRunning = running > 0 || aggregatorRunning || pendingSuccessfulSettlement;
|
|
666
|
+
const icon = isRunning ? theme.fg("warning", "\u23F3") : failCount > 0 || aggregatorFailed ? theme.fg("warning", "\u25D0") : theme.fg("success", "\u2713");
|
|
667
|
+
const status = isRunning ? aggregatorRunning ? `${successCount + failCount}/${details.results.length} done, fan-in running` : running > 0 ? `${successCount + failCount}/${details.results.length} done, ${running} running` : `${successCount + failCount}/${details.results.length} done, running` : aggregator ? `${successCount}/${details.results.length} tasks + fan-in` : `${successCount}/${details.results.length} tasks`;
|
|
668
|
+
const overallStatus = isRunning ? "Running" : failCount > 0 || aggregatorFailed ? "Failed" : "Completed";
|
|
669
|
+
if (expanded && !isRunning) {
|
|
670
|
+
const container = new Container();
|
|
671
|
+
container.addChild(
|
|
672
|
+
new Text2(
|
|
673
|
+
`${icon} ${theme.fg("toolTitle", theme.bold(`${modeLabel} `))}${theme.fg("accent", status)} \xB7 ${coloredStatus(theme, overallStatus)}`,
|
|
674
|
+
0,
|
|
675
|
+
0
|
|
676
|
+
)
|
|
677
|
+
);
|
|
678
|
+
for (const r of details.results) {
|
|
679
|
+
const rFailed = isResultError(r);
|
|
680
|
+
const rIcon = rFailed ? theme.fg("error", "\u2717") : resultIsRunning(r) ? theme.fg("warning", "\u23F3") : theme.fg("success", "\u2713");
|
|
681
|
+
const displayItems = getDisplayItems(r.messages);
|
|
682
|
+
const finalOutput = getResultFinalOutput(r);
|
|
683
|
+
container.addChild(new Spacer(1));
|
|
684
|
+
container.addChild(
|
|
685
|
+
new Text2(
|
|
686
|
+
`${theme.fg("muted", "\u2500\u2500\u2500 ") + theme.fg("accent", r.agent)} ${rIcon} ${coloredResultStatus(theme, r, resultIsRunning(r))}`,
|
|
687
|
+
0,
|
|
688
|
+
0
|
|
689
|
+
)
|
|
690
|
+
);
|
|
691
|
+
container.addChild(new Text2(theme.fg("muted", "Task: ") + theme.fg("dim", r.task), 0, 0));
|
|
692
|
+
const policy = formatResultPolicy(r);
|
|
693
|
+
if (policy)
|
|
694
|
+
container.addChild(
|
|
695
|
+
new Text2(theme.fg("muted", "Policy:\n") + theme.fg("dim", policy), 0, 0)
|
|
696
|
+
);
|
|
697
|
+
if (rFailed && r.errorMessage)
|
|
698
|
+
container.addChild(new Text2(theme.fg("error", `Error: ${r.errorMessage}`), 0, 0));
|
|
699
|
+
for (const item of displayItems) {
|
|
700
|
+
if (item.type === "toolCall") {
|
|
701
|
+
container.addChild(
|
|
702
|
+
new Text2(
|
|
703
|
+
theme.fg("muted", "\u2192 ") + formatToolCall(item.name, item.args, theme.fg.bind(theme)),
|
|
704
|
+
0,
|
|
705
|
+
0
|
|
706
|
+
)
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
if (finalOutput) {
|
|
711
|
+
container.addChild(new Spacer(1));
|
|
712
|
+
container.addChild(new Markdown(finalOutput.trim(), 0, 0, mdTheme));
|
|
713
|
+
}
|
|
714
|
+
const taskUsage = formatResultUsageStats(r);
|
|
715
|
+
if (taskUsage) container.addChild(new Text2(theme.fg("dim", taskUsage), 0, 0));
|
|
716
|
+
}
|
|
717
|
+
if (aggregator) {
|
|
718
|
+
const rIcon = aggregatorFailed ? theme.fg("error", "\u2717") : aggregatorRunning ? theme.fg("warning", "\u23F3") : theme.fg("success", "\u2713");
|
|
719
|
+
const displayItems = getDisplayItems(aggregator.messages);
|
|
720
|
+
const finalOutput = getResultFinalOutput(aggregator);
|
|
721
|
+
container.addChild(new Spacer(1));
|
|
722
|
+
container.addChild(
|
|
723
|
+
new Text2(
|
|
724
|
+
`${theme.fg("muted", "\u2500\u2500\u2500 fan-in \u2192 ") + theme.fg("accent", aggregator.agent)} ${rIcon} ${coloredResultStatus(theme, aggregator, aggregatorRunning)}`,
|
|
725
|
+
0,
|
|
726
|
+
0
|
|
727
|
+
)
|
|
728
|
+
);
|
|
729
|
+
container.addChild(
|
|
730
|
+
new Text2(theme.fg("muted", "Task: ") + theme.fg("dim", aggregator.task), 0, 0)
|
|
731
|
+
);
|
|
732
|
+
const aggregatorPolicy = formatResultPolicy(aggregator);
|
|
733
|
+
if (aggregatorPolicy)
|
|
734
|
+
container.addChild(
|
|
735
|
+
new Text2(theme.fg("muted", "Policy:\n") + theme.fg("dim", aggregatorPolicy), 0, 0)
|
|
736
|
+
);
|
|
737
|
+
if (aggregatorFailed && aggregator.errorMessage)
|
|
738
|
+
container.addChild(
|
|
739
|
+
new Text2(theme.fg("error", `Error: ${aggregator.errorMessage}`), 0, 0)
|
|
740
|
+
);
|
|
741
|
+
for (const item of displayItems) {
|
|
742
|
+
if (item.type === "toolCall") {
|
|
743
|
+
container.addChild(
|
|
744
|
+
new Text2(
|
|
745
|
+
theme.fg("muted", "\u2192 ") + formatToolCall(item.name, item.args, theme.fg.bind(theme)),
|
|
746
|
+
0,
|
|
747
|
+
0
|
|
748
|
+
)
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
if (finalOutput) {
|
|
753
|
+
container.addChild(new Spacer(1));
|
|
754
|
+
container.addChild(new Markdown(finalOutput.trim(), 0, 0, mdTheme));
|
|
755
|
+
}
|
|
756
|
+
const fanInUsage = formatResultUsageStats(aggregator);
|
|
757
|
+
if (fanInUsage) container.addChild(new Text2(theme.fg("dim", fanInUsage), 0, 0));
|
|
758
|
+
}
|
|
759
|
+
const usageResults = aggregator ? [...details.results, aggregator] : details.results;
|
|
760
|
+
const usageStr = formatUsageStats(aggregateUsage(usageResults));
|
|
761
|
+
if (usageStr) {
|
|
762
|
+
container.addChild(new Spacer(1));
|
|
763
|
+
container.addChild(new Text2(theme.fg("dim", `Total: ${usageStr}`), 0, 0));
|
|
764
|
+
}
|
|
765
|
+
return container;
|
|
766
|
+
}
|
|
767
|
+
let text2 = `${icon} ${theme.fg("toolTitle", theme.bold(`${modeLabel} `))}${theme.fg("accent", status)} \xB7 ${coloredStatus(theme, overallStatus)}`;
|
|
768
|
+
for (const r of details.results) {
|
|
769
|
+
const rFailed = isResultError(r);
|
|
770
|
+
const rRunning = resultIsRunning(r);
|
|
771
|
+
const rIcon = rFailed ? theme.fg("error", "\u2717") : rRunning ? theme.fg("warning", "\u23F3") : theme.fg("success", "\u2713");
|
|
772
|
+
const collapsed = getCollapsedDisplayItems(r);
|
|
773
|
+
const finalOutput = getResultFinalOutput(r).trim();
|
|
774
|
+
text2 += `
|
|
775
|
+
|
|
776
|
+
${theme.fg("muted", "\u2500\u2500\u2500 ")}${theme.fg("accent", r.agent)} ${rIcon} ${coloredResultStatus(theme, r, rRunning)}`;
|
|
777
|
+
if (rFailed && r.errorMessage) text2 += `
|
|
778
|
+
${theme.fg("error", `Error: ${r.errorMessage}`)}`;
|
|
779
|
+
if (collapsed.items.length > 0)
|
|
780
|
+
text2 += `
|
|
781
|
+
${renderDisplayItems(collapsed.items, 5, collapsed.total)}`;
|
|
782
|
+
else if (rRunning) text2 += `
|
|
783
|
+
${theme.fg("muted", "(running...)")}`;
|
|
784
|
+
else if (finalOutput)
|
|
785
|
+
text2 += `
|
|
786
|
+
${theme.fg("toolOutput", finalOutput.split("\n").slice(0, 3).join("\n"))}`;
|
|
787
|
+
else if (!rFailed || !r.errorMessage) text2 += `
|
|
788
|
+
${theme.fg("muted", "(no output)")}`;
|
|
789
|
+
}
|
|
790
|
+
if (aggregator) {
|
|
791
|
+
const rIcon = aggregatorFailed ? theme.fg("error", "\u2717") : aggregatorRunning ? theme.fg("warning", "\u23F3") : theme.fg("success", "\u2713");
|
|
792
|
+
const collapsed = getCollapsedDisplayItems(aggregator);
|
|
793
|
+
const finalOutput = getResultFinalOutput(aggregator).trim();
|
|
794
|
+
text2 += `
|
|
795
|
+
|
|
796
|
+
${theme.fg("muted", "\u2500\u2500\u2500 fan-in \u2192 ")}${theme.fg("accent", aggregator.agent)} ${rIcon} ${coloredResultStatus(theme, aggregator, aggregatorRunning)}`;
|
|
797
|
+
if (aggregatorFailed && aggregator.errorMessage)
|
|
798
|
+
text2 += `
|
|
799
|
+
${theme.fg("error", `Error: ${aggregator.errorMessage}`)}`;
|
|
800
|
+
if (collapsed.items.length > 0)
|
|
801
|
+
text2 += `
|
|
802
|
+
${renderDisplayItems(collapsed.items, 5, collapsed.total)}`;
|
|
803
|
+
else if (aggregatorRunning) text2 += `
|
|
804
|
+
${theme.fg("muted", "(running...)")}`;
|
|
805
|
+
else if (finalOutput)
|
|
806
|
+
text2 += `
|
|
807
|
+
${theme.fg("toolOutput", finalOutput.split("\n").slice(0, 3).join("\n"))}`;
|
|
808
|
+
else if (!aggregatorFailed || !aggregator.errorMessage)
|
|
809
|
+
text2 += `
|
|
810
|
+
${theme.fg("muted", "(no output)")}`;
|
|
811
|
+
}
|
|
812
|
+
if (!isRunning) {
|
|
813
|
+
const usageResults = aggregator ? [...details.results, aggregator] : details.results;
|
|
814
|
+
const usageStr = formatUsageStats(aggregateUsage(usageResults));
|
|
815
|
+
if (usageStr) text2 += `
|
|
816
|
+
|
|
817
|
+
${theme.fg("dim", `Total: ${usageStr}`)}`;
|
|
818
|
+
}
|
|
819
|
+
if (!expanded) text2 += `
|
|
820
|
+
${expansionHint()}`;
|
|
821
|
+
return new Text2(text2, 0, 0);
|
|
822
|
+
}
|
|
823
|
+
const text = result.content[0];
|
|
824
|
+
return new Text2(
|
|
825
|
+
text?.type === "text" ? safeBlock(text.text, "(no output)", 8 * 1024) : "(no output)",
|
|
826
|
+
0,
|
|
827
|
+
0
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// src/subagents-extension.ts
|
|
832
|
+
function subagents_extension_default(pi, dependencies = {}) {
|
|
833
|
+
pi.registerMessageRenderer(SUBAGENT_COMPLETION_MESSAGE_TYPE, renderCompletionMessage);
|
|
834
|
+
const loadBlockingExecution = cachedModuleLoader(
|
|
835
|
+
dependencies.loadBlockingExecution ?? (() => import("./chunks/execution-DS2DEIQZ.js"))
|
|
836
|
+
);
|
|
837
|
+
const configOwner = registerSubagentConfigLifecycle(pi);
|
|
838
|
+
const settings = readSubagentSettings();
|
|
839
|
+
let currentSettings = settings;
|
|
840
|
+
let currentCatalog = "";
|
|
841
|
+
const blockingEnabled = settings?.blocking?.enabled !== false;
|
|
842
|
+
const refreshBlockingCatalog = blockingEnabled ? registerBlockingSubagent(pi, () => currentSettings, loadBlockingExecution) : () => void 0;
|
|
843
|
+
let refreshStatefulCatalog = () => void 0;
|
|
844
|
+
let refreshConsultCatalog = () => void 0;
|
|
845
|
+
pi.on("session_start", (_event, ctx) => {
|
|
846
|
+
const loadNotice = consumeSubagentSettingsNotice();
|
|
847
|
+
const refreshedSettings = readSubagentSettings();
|
|
848
|
+
const refreshedNotice = consumeSubagentSettingsNotice();
|
|
849
|
+
if (!inspectSubagentSettings().error) currentSettings = refreshedSettings;
|
|
850
|
+
const notice = [
|
|
851
|
+
...new Set([loadNotice, refreshedNotice].filter((value) => value !== void 0))
|
|
852
|
+
].join("\n");
|
|
853
|
+
if (notice) ctx.ui.notify(notice, "warning");
|
|
854
|
+
currentCatalog = formatAgentCatalog(
|
|
855
|
+
discoverAgentCatalog(ctx.cwd, ctx.isProjectTrusted(), refreshedSettings)
|
|
856
|
+
).text;
|
|
857
|
+
refreshBlockingCatalog(currentCatalog);
|
|
858
|
+
refreshStatefulCatalog(currentCatalog);
|
|
859
|
+
refreshConsultCatalog(currentCatalog);
|
|
860
|
+
});
|
|
861
|
+
const statefulRuntime = registerStatefulSubagents(pi, {
|
|
862
|
+
blockingEnabled,
|
|
863
|
+
settings: settings?.stateful,
|
|
864
|
+
getSettings: () => currentSettings,
|
|
865
|
+
loadTransport: dependencies.loadStatefulTransport
|
|
866
|
+
});
|
|
867
|
+
refreshStatefulCatalog = statefulRuntime.setAgentCatalog;
|
|
868
|
+
const getBlockingEnabled = () => blockingEnabled;
|
|
869
|
+
const getMaxParallelTasks = () => resolveBlockingMaxParallelTasks(currentSettings);
|
|
870
|
+
const getConsultResourcePolicy = () => currentSettings?.consult?.resources ?? DEFAULT_CONSULT_RESOURCE_POLICY;
|
|
871
|
+
const getConsultationCwdPolicy = () => currentSettings?.cwdPolicy?.consultation ?? DEFAULT_CONSULTATION_CWD_POLICY;
|
|
872
|
+
const getDelegationCwdPolicy = () => currentSettings?.cwdPolicy?.delegation ?? DEFAULT_DELEGATION_CWD_POLICY;
|
|
873
|
+
registerSubagentInspect(
|
|
874
|
+
pi,
|
|
875
|
+
{
|
|
876
|
+
...statefulRuntime,
|
|
877
|
+
getBlockingEnabled,
|
|
878
|
+
getMaxParallelTasks,
|
|
879
|
+
getConsultResourcePolicy,
|
|
880
|
+
getConsultationCwdPolicy,
|
|
881
|
+
getDelegationCwdPolicy
|
|
882
|
+
},
|
|
883
|
+
dependencies.inspect
|
|
884
|
+
);
|
|
885
|
+
if (blockingEnabled) {
|
|
886
|
+
refreshConsultCatalog = registerSubagentConsult(
|
|
887
|
+
pi,
|
|
888
|
+
{ getSettings: () => currentSettings },
|
|
889
|
+
dependencies.consult
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
registerSubagentConfigCommand(
|
|
893
|
+
pi,
|
|
894
|
+
{
|
|
895
|
+
...statefulRuntime,
|
|
896
|
+
getBlockingEnabled,
|
|
897
|
+
getMaxParallelTasks,
|
|
898
|
+
getConsultResourcePolicy,
|
|
899
|
+
getConsultationCwdPolicy,
|
|
900
|
+
getDelegationCwdPolicy,
|
|
901
|
+
setMaxParallelTasks(value) {
|
|
902
|
+
const previousSettings = currentSettings;
|
|
903
|
+
currentSettings = {
|
|
904
|
+
...currentSettings ?? {},
|
|
905
|
+
blocking: { ...currentSettings?.blocking ?? {}, maxParallelTasks: value }
|
|
906
|
+
};
|
|
907
|
+
try {
|
|
908
|
+
refreshBlockingCatalog(currentCatalog);
|
|
909
|
+
} catch (applyError) {
|
|
910
|
+
currentSettings = previousSettings;
|
|
911
|
+
try {
|
|
912
|
+
refreshBlockingCatalog(currentCatalog);
|
|
913
|
+
} catch (rollbackError) {
|
|
914
|
+
throw new AggregateError(
|
|
915
|
+
[applyError, rollbackError],
|
|
916
|
+
"Failed to apply and roll back the parallel-worker limit"
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
throw applyError;
|
|
920
|
+
}
|
|
921
|
+
},
|
|
922
|
+
setConsultResourcePolicy(value) {
|
|
923
|
+
currentSettings = {
|
|
924
|
+
...currentSettings ?? {},
|
|
925
|
+
consult: { ...currentSettings?.consult ?? {}, resources: value }
|
|
926
|
+
};
|
|
927
|
+
refreshConsultCatalog(currentCatalog);
|
|
928
|
+
},
|
|
929
|
+
setConsultationCwdPolicy(value) {
|
|
930
|
+
currentSettings = {
|
|
931
|
+
...currentSettings ?? {},
|
|
932
|
+
cwdPolicy: { ...currentSettings?.cwdPolicy ?? {}, consultation: value }
|
|
933
|
+
};
|
|
934
|
+
refreshConsultCatalog(currentCatalog);
|
|
935
|
+
},
|
|
936
|
+
setDelegationCwdPolicy(value) {
|
|
937
|
+
currentSettings = {
|
|
938
|
+
...currentSettings ?? {},
|
|
939
|
+
cwdPolicy: { ...currentSettings?.cwdPolicy ?? {}, delegation: value }
|
|
940
|
+
};
|
|
941
|
+
refreshBlockingCatalog(currentCatalog);
|
|
942
|
+
statefulRuntime.refreshSettingsGuidance();
|
|
943
|
+
}
|
|
944
|
+
},
|
|
945
|
+
configOwner,
|
|
946
|
+
dependencies.config
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
function registerBlockingSubagent(pi, getSettings, loadExecution) {
|
|
950
|
+
let catalog = "";
|
|
951
|
+
const activeControllers = /* @__PURE__ */ new Set();
|
|
952
|
+
const activeWork = /* @__PURE__ */ new Set();
|
|
953
|
+
const cancelAndWaitForWork = async (reason) => {
|
|
954
|
+
for (const controller of activeControllers) {
|
|
955
|
+
controller.abort(new DOMException(reason, "AbortError"));
|
|
956
|
+
}
|
|
957
|
+
await Promise.allSettled([...activeWork]);
|
|
958
|
+
};
|
|
959
|
+
pi.on("session_start", () => cancelAndWaitForWork("Blocking subagent session replaced"));
|
|
960
|
+
pi.on("session_shutdown", () => cancelAndWaitForWork("Blocking subagent session shut down"));
|
|
961
|
+
const baseDescription = () => [
|
|
962
|
+
"Run specialized subagents as a blocking operation with isolated contexts.",
|
|
963
|
+
"The call blocks the main agent until every worker and optional aggregator finishes, so queued steering waits.",
|
|
964
|
+
"Modes: single (agent + task), parallel (tasks array), chain (sequential with {previous} placeholder), workflow (named dependency tasks with optional capability routing), or panel (independent reviewers plus evidence-preserving synthesis).",
|
|
965
|
+
"Parallel mode may include an aggregator fan-in step; workflow mode validates dependencies, authority, artifacts, scope conflicts, retries, and hedging before scheduling. Use subagent_consult instead for one synchronous child that must be executor-constrained to read-only tools.",
|
|
966
|
+
'Default agent scope is "user" (from ~/.pi/agent/agents).',
|
|
967
|
+
`To enable project-local agents in ${CONFIG_DIR_NAME}/agents, pass agentScope: "both" (or "project") as a top-level argument for that call.`,
|
|
968
|
+
`Maximum parallel worker tasks per call: ${resolveBlockingMaxParallelTasks(getSettings())}. Parallel execution starts at most ${MAX_BLOCKING_PARALLEL_CONCURRENCY} workers at once.`,
|
|
969
|
+
`Working-directory target policy: ${getSettings()?.cwdPolicy?.delegation ?? DEFAULT_DELEGATION_CWD_POLICY}. This controls launch targets and protected project resources, not filesystem access or sandboxing.`
|
|
970
|
+
].join(" ");
|
|
971
|
+
const promptGuidelines = () => [
|
|
972
|
+
"Use subagent only when delegation fits; the main agent should decide how many subagents to spawn from task shape instead of waiting for the user to specify a count.",
|
|
973
|
+
"The main agent retains overall planning, immediate critical-path work, integration, final verification, and the final answer.",
|
|
974
|
+
"Use no subagent for simple answers, quick targeted edits, latency-sensitive one-step work, tasks requiring frequent user back-and-forth, or critical-path work the main agent can perform directly.",
|
|
975
|
+
"One ordinary implementation worker should not replace work the main agent can perform directly; use a blocking single only when intentional synchronous isolation or a user-requested specialist justifies waiting.",
|
|
976
|
+
"Keep ordinary planning in the main agent, or use explicit workflow mode when a genuine dependency graph requires caller-authored orchestration.",
|
|
977
|
+
"Keep ordinary review in the main agent with a review skill and deterministic checks; reserve panel mode or custom verifier agents for consequential independent verification.",
|
|
978
|
+
"Use the blocking subagent tool only when delegated outputs are required before the main agent's next action and waiting is intentional; the main agent cannot process queued steering until the call returns.",
|
|
979
|
+
"Use a blocking subagent single, parallel, chain, workflow, panel, or fan-in call only when synchronous context or output isolation is worth making the main agent unavailable while it runs.",
|
|
980
|
+
`If a blocking parallel subagent call is genuinely required, keep tasks independent, stay within the configured max ${resolveBlockingMaxParallelTasks(getSettings())}, and avoid write-heavy implementation touching the same files or shared state.`,
|
|
981
|
+
"For parallel subagent calls, omit the aggregator key entirely unless a fan-in step is required; do not send null, empty strings, or an empty object for unused optional fields.",
|
|
982
|
+
"Use workflow mode for explicit dependencies or capability routing; declare read/write or ownership scopes, require structured-v2 artifacts when downstream tasks consume them, and use retry or hedging only with the required side-effect contract.",
|
|
983
|
+
"Use panel mode only for consequential review or research that benefits from at least two independent reviewers and one bounded synthesis; agreement is not proof, dissent and blocking objections remain visible, and simple or latency-sensitive work should not use a panel.",
|
|
984
|
+
'Do not use subagent with project-local agents unless the user explicitly wants project agents or sets agentScope to "project" or "both"; keep confirmation enabled for untrusted repositories.',
|
|
985
|
+
"When using subagent, write self-contained tasks with file paths, context, expected output, and whether the subagent may edit files.",
|
|
986
|
+
"Set subagent timeoutMs to the shortest realistic work deadline for the task difficulty, just as thinkingLevel should match reasoning difficulty; split oversized tasks instead of extending the deadline merely to compensate for broad scope. Use totalTimeoutMs to cap an entire blocking workflow, idleTimeoutMs for stalled work, and maxTurns or maxToolCalls to stop repeated work without progress. Every budget stop preserves a bounded checkpoint and may make one separately bounded summary attempt."
|
|
987
|
+
];
|
|
988
|
+
const definition = {
|
|
989
|
+
name: "subagent",
|
|
990
|
+
label: "Blocking Subagent",
|
|
991
|
+
description: appendAgentCatalog(baseDescription(), catalog),
|
|
992
|
+
promptSnippet: "Run blocking isolated subagents only when their outputs are required before the main agent can continue.",
|
|
993
|
+
promptGuidelines: promptGuidelines(),
|
|
994
|
+
parameters: SubagentParams,
|
|
995
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
996
|
+
const lifecycleController = new AbortController();
|
|
997
|
+
activeControllers.add(lifecycleController);
|
|
998
|
+
const effectiveSignal = signal ? AbortSignal.any([signal, lifecycleController.signal]) : lifecycleController.signal;
|
|
999
|
+
const work = (async () => {
|
|
1000
|
+
throwIfAborted(effectiveSignal, "Blocking subagent execution was cancelled");
|
|
1001
|
+
let executionModule;
|
|
1002
|
+
try {
|
|
1003
|
+
executionModule = await loadExecution();
|
|
1004
|
+
} catch (error) {
|
|
1005
|
+
throwIfAborted(
|
|
1006
|
+
effectiveSignal,
|
|
1007
|
+
"Blocking subagent execution was cancelled while loading"
|
|
1008
|
+
);
|
|
1009
|
+
throw error;
|
|
1010
|
+
}
|
|
1011
|
+
throwIfAborted(effectiveSignal, "Blocking subagent execution was cancelled while loading");
|
|
1012
|
+
return executionModule.executeSubagent(
|
|
1013
|
+
toolCallId,
|
|
1014
|
+
params,
|
|
1015
|
+
effectiveSignal,
|
|
1016
|
+
onUpdate,
|
|
1017
|
+
ctx,
|
|
1018
|
+
getSettings()
|
|
1019
|
+
);
|
|
1020
|
+
})();
|
|
1021
|
+
activeWork.add(work);
|
|
1022
|
+
try {
|
|
1023
|
+
return await work;
|
|
1024
|
+
} finally {
|
|
1025
|
+
activeControllers.delete(lifecycleController);
|
|
1026
|
+
activeWork.delete(work);
|
|
1027
|
+
}
|
|
1028
|
+
},
|
|
1029
|
+
renderCall(args, theme) {
|
|
1030
|
+
return renderSubagentCall(args, theme);
|
|
1031
|
+
},
|
|
1032
|
+
renderResult(result, options, theme) {
|
|
1033
|
+
return renderSubagentResult(result, options, theme);
|
|
1034
|
+
}
|
|
1035
|
+
};
|
|
1036
|
+
pi.registerTool(definition);
|
|
1037
|
+
pi.on("tool_result", (event) => {
|
|
1038
|
+
if (event.toolName !== "subagent") return;
|
|
1039
|
+
if (event.details?.isError)
|
|
1040
|
+
return { isError: true };
|
|
1041
|
+
});
|
|
1042
|
+
return (nextCatalog) => {
|
|
1043
|
+
catalog = nextCatalog;
|
|
1044
|
+
definition.description = appendAgentCatalog(baseDescription(), catalog);
|
|
1045
|
+
definition.promptGuidelines = promptGuidelines();
|
|
1046
|
+
pi.registerTool(definition);
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
function appendAgentCatalog(baseDescription, catalog) {
|
|
1050
|
+
return catalog ? `${baseDescription}
|
|
1051
|
+
|
|
1052
|
+
${catalog}` : baseDescription;
|
|
1053
|
+
}
|
|
1054
|
+
export {
|
|
1055
|
+
subagents_extension_default as default
|
|
1056
|
+
};
|
|
1057
|
+
//# sourceMappingURL=index.ts.map
|