@narumitw/pi-subagents 0.42.0 → 0.43.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -32
- package/package.json +1 -1
- package/src/agents.ts +49 -8
- package/src/config-ui.ts +223 -28
- package/src/consult-policy.ts +15 -0
- package/src/consult-render.ts +194 -0
- package/src/consult.ts +815 -0
- package/src/cwd-policy.ts +183 -0
- package/src/execution.ts +135 -66
- package/src/in-process-transport.ts +3 -3
- package/src/inspect-render.ts +234 -0
- package/src/inspect.ts +453 -0
- package/src/limits.ts +1 -0
- package/src/params.ts +2 -0
- package/src/persistence.ts +29 -0
- package/src/registry.ts +87 -0
- package/src/render-common.ts +252 -0
- package/src/render.ts +134 -99
- package/src/runner.ts +162 -22
- package/src/safe-text.ts +67 -0
- package/src/settings.ts +199 -12
- package/src/stateful-guidance.ts +35 -0
- package/src/stateful-lifecycle.ts +31 -0
- package/src/stateful-render.ts +249 -0
- package/src/stateful-safety.ts +91 -0
- package/src/stateful.ts +254 -225
- package/src/subagents.ts +100 -19
- package/src/subprocess-transport.ts +19 -2
package/src/subagents.ts
CHANGED
|
@@ -13,20 +13,41 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import type { ExtensionAPI, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
type ConsultationCwdPolicy,
|
|
18
|
+
type ConsultResourcePolicy,
|
|
19
|
+
type DelegationCwdPolicy,
|
|
20
|
+
discoverAgentCatalog,
|
|
21
|
+
formatAgentCatalog,
|
|
22
|
+
type SubagentSettings,
|
|
23
|
+
} from "./agents.js";
|
|
17
24
|
import { registerSubagentConfigCommand } from "./config-ui.js";
|
|
25
|
+
import { registerSubagentConsult } from "./consult.js";
|
|
18
26
|
import { executeSubagent } from "./execution.js";
|
|
27
|
+
import { registerSubagentInspect } from "./inspect.js";
|
|
19
28
|
import { SubagentParams } from "./params.js";
|
|
20
29
|
import { renderSubagentCall, renderSubagentResult } from "./render.js";
|
|
21
30
|
import type { SubagentDetails } from "./runner.js";
|
|
22
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
consumeSubagentSettingsNotice,
|
|
33
|
+
DEFAULT_CONSULT_RESOURCE_POLICY,
|
|
34
|
+
DEFAULT_CONSULTATION_CWD_POLICY,
|
|
35
|
+
DEFAULT_DELEGATION_CWD_POLICY,
|
|
36
|
+
inspectSubagentSettings,
|
|
37
|
+
readSubagentSettings,
|
|
38
|
+
} from "./settings.js";
|
|
23
39
|
import { registerStatefulSubagents } from "./stateful.js";
|
|
24
40
|
|
|
25
41
|
export default function (pi: ExtensionAPI) {
|
|
26
42
|
const settings = readSubagentSettings();
|
|
43
|
+
let currentSettings: SubagentSettings | undefined = settings;
|
|
44
|
+
let currentCatalog = "";
|
|
27
45
|
const blockingEnabled = settings?.blocking?.enabled !== false;
|
|
28
|
-
const refreshBlockingCatalog = blockingEnabled
|
|
46
|
+
const refreshBlockingCatalog = blockingEnabled
|
|
47
|
+
? registerBlockingSubagent(pi, () => currentSettings)
|
|
48
|
+
: () => undefined;
|
|
29
49
|
let refreshStatefulCatalog: (catalog: string) => void = () => undefined;
|
|
50
|
+
let refreshConsultCatalog: (catalog: string) => void = () => undefined;
|
|
30
51
|
|
|
31
52
|
pi.on("session_start", (_event, ctx) => {
|
|
32
53
|
// Preserve a one-shot migration notice from extension load while refreshing
|
|
@@ -34,43 +55,95 @@ export default function (pi: ExtensionAPI) {
|
|
|
34
55
|
const loadNotice = consumeSubagentSettingsNotice();
|
|
35
56
|
const refreshedSettings = readSubagentSettings();
|
|
36
57
|
const refreshedNotice = consumeSubagentSettingsNotice();
|
|
58
|
+
if (!inspectSubagentSettings().error) currentSettings = refreshedSettings;
|
|
37
59
|
const notice = [
|
|
38
60
|
...new Set([loadNotice, refreshedNotice].filter((value) => value !== undefined)),
|
|
39
61
|
].join("\n");
|
|
40
62
|
if (notice) ctx.ui.notify(notice, "warning");
|
|
41
63
|
|
|
42
|
-
|
|
64
|
+
currentCatalog = formatAgentCatalog(
|
|
43
65
|
discoverAgentCatalog(ctx.cwd, ctx.isProjectTrusted(), refreshedSettings),
|
|
44
66
|
).text;
|
|
45
|
-
refreshBlockingCatalog(
|
|
46
|
-
refreshStatefulCatalog(
|
|
67
|
+
refreshBlockingCatalog(currentCatalog);
|
|
68
|
+
refreshStatefulCatalog(currentCatalog);
|
|
69
|
+
refreshConsultCatalog(currentCatalog);
|
|
47
70
|
});
|
|
48
71
|
|
|
49
72
|
const statefulRuntime = registerStatefulSubagents(pi, {
|
|
50
73
|
blockingEnabled,
|
|
51
74
|
settings: settings?.stateful,
|
|
75
|
+
getSettings: () => currentSettings,
|
|
52
76
|
});
|
|
53
77
|
refreshStatefulCatalog = statefulRuntime.setAgentCatalog;
|
|
78
|
+
const getBlockingEnabled = () => blockingEnabled;
|
|
79
|
+
const getConsultResourcePolicy = () =>
|
|
80
|
+
currentSettings?.consult?.resources ?? DEFAULT_CONSULT_RESOURCE_POLICY;
|
|
81
|
+
const getConsultationCwdPolicy = () =>
|
|
82
|
+
currentSettings?.cwdPolicy?.consultation ?? DEFAULT_CONSULTATION_CWD_POLICY;
|
|
83
|
+
const getDelegationCwdPolicy = () =>
|
|
84
|
+
currentSettings?.cwdPolicy?.delegation ?? DEFAULT_DELEGATION_CWD_POLICY;
|
|
85
|
+
registerSubagentInspect(pi, {
|
|
86
|
+
...statefulRuntime,
|
|
87
|
+
getBlockingEnabled,
|
|
88
|
+
getConsultResourcePolicy,
|
|
89
|
+
getConsultationCwdPolicy,
|
|
90
|
+
getDelegationCwdPolicy,
|
|
91
|
+
});
|
|
92
|
+
if (blockingEnabled) {
|
|
93
|
+
refreshConsultCatalog = registerSubagentConsult(pi, {
|
|
94
|
+
getSettings: () => currentSettings,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
54
97
|
registerSubagentConfigCommand(pi, {
|
|
55
98
|
...statefulRuntime,
|
|
56
|
-
getBlockingEnabled
|
|
99
|
+
getBlockingEnabled,
|
|
100
|
+
getConsultResourcePolicy,
|
|
101
|
+
getConsultationCwdPolicy,
|
|
102
|
+
getDelegationCwdPolicy,
|
|
103
|
+
setConsultResourcePolicy(value: ConsultResourcePolicy) {
|
|
104
|
+
currentSettings = {
|
|
105
|
+
...(currentSettings ?? {}),
|
|
106
|
+
consult: { ...(currentSettings?.consult ?? {}), resources: value },
|
|
107
|
+
};
|
|
108
|
+
refreshConsultCatalog(currentCatalog);
|
|
109
|
+
},
|
|
110
|
+
setConsultationCwdPolicy(value: ConsultationCwdPolicy) {
|
|
111
|
+
currentSettings = {
|
|
112
|
+
...(currentSettings ?? {}),
|
|
113
|
+
cwdPolicy: { ...(currentSettings?.cwdPolicy ?? {}), consultation: value },
|
|
114
|
+
};
|
|
115
|
+
refreshConsultCatalog(currentCatalog);
|
|
116
|
+
},
|
|
117
|
+
setDelegationCwdPolicy(value: DelegationCwdPolicy) {
|
|
118
|
+
currentSettings = {
|
|
119
|
+
...(currentSettings ?? {}),
|
|
120
|
+
cwdPolicy: { ...(currentSettings?.cwdPolicy ?? {}), delegation: value },
|
|
121
|
+
};
|
|
122
|
+
refreshBlockingCatalog(currentCatalog);
|
|
123
|
+
statefulRuntime.refreshSettingsGuidance();
|
|
124
|
+
},
|
|
57
125
|
});
|
|
58
126
|
}
|
|
59
127
|
|
|
60
|
-
function registerBlockingSubagent(
|
|
128
|
+
function registerBlockingSubagent(
|
|
129
|
+
pi: ExtensionAPI,
|
|
130
|
+
getSettings: () => SubagentSettings | undefined,
|
|
131
|
+
): (catalog: string) => void {
|
|
61
132
|
let catalog = "";
|
|
62
|
-
const baseDescription =
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
133
|
+
const baseDescription = () =>
|
|
134
|
+
[
|
|
135
|
+
"Run specialized subagents as a blocking operation with isolated contexts.",
|
|
136
|
+
"The call blocks the main agent until every worker and optional aggregator finishes, so queued steering waits.",
|
|
137
|
+
"Modes: single (agent + task), parallel (tasks array), chain (sequential with {previous} placeholder).",
|
|
138
|
+
"Parallel mode may include an aggregator fan-in step that receives all task outputs. Use subagent_consult instead for one synchronous child that must be executor-constrained to read-only tools.",
|
|
139
|
+
'Default agent scope is "user" (from ~/.pi/agent/agents).',
|
|
140
|
+
'To enable project-local agents in .pi/agents, pass agentScope: "both" (or "project") as a top-level argument for that call.',
|
|
141
|
+
`Working-directory target policy: ${getSettings()?.cwdPolicy?.delegation ?? DEFAULT_DELEGATION_CWD_POLICY}. This controls launch targets and protected project resources, not filesystem access or sandboxing.`,
|
|
142
|
+
].join(" ");
|
|
70
143
|
const definition: ToolDefinition<typeof SubagentParams, SubagentDetails> = {
|
|
71
144
|
name: "subagent",
|
|
72
145
|
label: "Blocking Subagent",
|
|
73
|
-
description: appendAgentCatalog(baseDescription, catalog),
|
|
146
|
+
description: appendAgentCatalog(baseDescription(), catalog),
|
|
74
147
|
promptSnippet:
|
|
75
148
|
"Run blocking isolated subagents only when their outputs are required before the main agent can continue.",
|
|
76
149
|
promptGuidelines: [
|
|
@@ -86,7 +159,7 @@ function registerBlockingSubagent(pi: ExtensionAPI): (catalog: string) => void {
|
|
|
86
159
|
parameters: SubagentParams,
|
|
87
160
|
|
|
88
161
|
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
89
|
-
return executeSubagent(toolCallId, params, signal, onUpdate, ctx);
|
|
162
|
+
return executeSubagent(toolCallId, params, signal, onUpdate, ctx, getSettings());
|
|
90
163
|
},
|
|
91
164
|
|
|
92
165
|
renderCall(args, theme) {
|
|
@@ -105,7 +178,7 @@ function registerBlockingSubagent(pi: ExtensionAPI): (catalog: string) => void {
|
|
|
105
178
|
});
|
|
106
179
|
return (nextCatalog: string) => {
|
|
107
180
|
catalog = nextCatalog;
|
|
108
|
-
definition.description = appendAgentCatalog(baseDescription, catalog);
|
|
181
|
+
definition.description = appendAgentCatalog(baseDescription(), catalog);
|
|
109
182
|
pi.registerTool<typeof SubagentParams, SubagentDetails>(definition);
|
|
110
183
|
};
|
|
111
184
|
}
|
|
@@ -118,8 +191,14 @@ export { parsePositiveInteger } from "./execution.js";
|
|
|
118
191
|
export { formatTokens, formatUsageStats } from "./render.js";
|
|
119
192
|
export { buildPiArgs } from "./runner.js";
|
|
120
193
|
export {
|
|
194
|
+
DEFAULT_CONSULT_RESOURCE_POLICY,
|
|
195
|
+
DEFAULT_CONSULTATION_CWD_POLICY,
|
|
196
|
+
DEFAULT_DELEGATION_CWD_POLICY,
|
|
121
197
|
inspectCompletionDeliverySettings,
|
|
198
|
+
inspectConsultResourceSettings,
|
|
199
|
+
inspectCwdPolicySettings,
|
|
122
200
|
inspectDelegationWorkflowSettings,
|
|
201
|
+
inspectSubagentSettings,
|
|
123
202
|
normalizeAgentSettings,
|
|
124
203
|
normalizeSubagentSettings,
|
|
125
204
|
readSubagentSettings,
|
|
@@ -130,5 +209,7 @@ export {
|
|
|
130
209
|
uniqueToolNames,
|
|
131
210
|
updateAgentToolsSetting,
|
|
132
211
|
updateCompletionDeliverySetting,
|
|
212
|
+
updateConsultResourceSetting,
|
|
213
|
+
updateCwdPolicySetting,
|
|
133
214
|
updateDelegationWorkflowSetting,
|
|
134
215
|
} from "./settings.js";
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type AgentConfig,
|
|
3
|
+
discoverAgents,
|
|
4
|
+
type SubagentSettings,
|
|
5
|
+
type SubagentThinkingLevel,
|
|
6
|
+
} from "./agents.js";
|
|
2
7
|
import type { ManagedAgent, TurnOutcome } from "./registry.js";
|
|
3
8
|
import { getResultFinalOutput, runSingleAgent, type SubagentDetails } from "./runner.js";
|
|
4
9
|
import { readSubagentSettings, resolveSubagentThinkingLevel } from "./settings.js";
|
|
@@ -12,11 +17,17 @@ export function resolveStatefulSubprocessThinkingLevel(
|
|
|
12
17
|
return resolveSubagentThinkingLevel(agents, record.agent, record.thinkingLevel);
|
|
13
18
|
}
|
|
14
19
|
|
|
20
|
+
export interface SubprocessTransportOptions {
|
|
21
|
+
getSettings?: () => SubagentSettings | undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
export class SubprocessTransport implements SubagentTransport {
|
|
16
25
|
readonly kind = "subprocess" as const;
|
|
17
26
|
|
|
27
|
+
constructor(private readonly options: SubprocessTransportOptions = {}) {}
|
|
28
|
+
|
|
18
29
|
async runTurn(record: ManagedAgent, task: string, signal: AbortSignal): Promise<TurnOutcome> {
|
|
19
|
-
const settings = readSubagentSettings();
|
|
30
|
+
const settings = this.options.getSettings ? this.options.getSettings() : readSubagentSettings();
|
|
20
31
|
const discovery = discoverAgents(record.cwd, record.agentScope ?? "user", settings);
|
|
21
32
|
const agent = discovery.agents.find((candidate) => candidate.name === record.agent);
|
|
22
33
|
const boundedTask = buildStatefulTurnPrompt(record, task);
|
|
@@ -38,6 +49,12 @@ export class SubprocessTransport implements SubagentTransport {
|
|
|
38
49
|
resolveStatefulTurnTimeout(agent),
|
|
39
50
|
undefined,
|
|
40
51
|
makeDetails,
|
|
52
|
+
undefined,
|
|
53
|
+
{
|
|
54
|
+
projectTrust:
|
|
55
|
+
record.target?.trust.projectTrusted ??
|
|
56
|
+
(record.agentScope === "project" || record.agentScope === "both"),
|
|
57
|
+
},
|
|
41
58
|
);
|
|
42
59
|
return {
|
|
43
60
|
output: getResultFinalOutput(single),
|