pi-subagents-router 1.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/LICENSE +21 -0
- package/README.md +119 -0
- package/dist/AgentConfig.d.ts +52 -0
- package/dist/AgentConfig.js +7 -0
- package/dist/BuiltinAgents.d.ts +10 -0
- package/dist/BuiltinAgents.js +156 -0
- package/dist/Supvisor.d.ts +33 -0
- package/dist/Supvisor.js +310 -0
- package/dist/agent-router.d.ts +16 -0
- package/dist/agent-router.js +209 -0
- package/package.json +54 -0
- package/src/AgentConfig.ts +59 -0
- package/src/BuiltinAgents.ts +165 -0
- package/src/Supvisor.ts +462 -0
- package/src/agent-router.ts +243 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi-subagents — Agent Router Extension
|
|
3
|
+
*
|
|
4
|
+
* v2.0: Supervisor-based routing (refactored from monolithic agent-router.ts)
|
|
5
|
+
*
|
|
6
|
+
* Entry point that wires the Supervisor into the VS Code extension host.
|
|
7
|
+
* Exposes:
|
|
8
|
+
* - "route_agent" tool (single-agent, same behavior as v1)
|
|
9
|
+
* - "plan_and_route" tool (multi-agent workflow, NEW)
|
|
10
|
+
* - "/assign-agent" command (backward-compatible shortcut)
|
|
11
|
+
* - "list_agents" tool (list all available agents)
|
|
12
|
+
*
|
|
13
|
+
* @see https://github.com/tintinweb/pi-subagents
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
17
|
+
import { Type } from "typebox";
|
|
18
|
+
|
|
19
|
+
import { Supervisor } from "./Supvisor.ts";
|
|
20
|
+
import { BUILTIN_AGENTS } from "./BuiltinAgents.ts";
|
|
21
|
+
|
|
22
|
+
const PI_SUBAGENTS_REPO = "https://github.com/tintinweb/pi-subagents";
|
|
23
|
+
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Entry point
|
|
26
|
+
// ============================================================================
|
|
27
|
+
|
|
28
|
+
export default async function(pi: ExtensionAPI) {
|
|
29
|
+
// Build the supervisor with builtin agents
|
|
30
|
+
const supervisor = new Supervisor([...BUILTIN_AGENTS], pi);
|
|
31
|
+
|
|
32
|
+
// ---- Register route_agent tool (UNCHANGED behavior) ------------------
|
|
33
|
+
pi.registerTool({
|
|
34
|
+
name: "route_agent",
|
|
35
|
+
label: "Route Agent",
|
|
36
|
+
description: `Route tasks to appropriate sub-agents via pi-subagents (${PI_SUBAGENTS_REPO})`,
|
|
37
|
+
parameters: Type.Object({
|
|
38
|
+
taskDescription: Type.String({ description: "Description of the task to be performed" }),
|
|
39
|
+
forceAgent: Type.Optional(Type.String({ description: "Optionally force a specific agent type" })),
|
|
40
|
+
runInBackground: Type.Optional(Type.Boolean({ description: "Run the agent in background (non-blocking)" })),
|
|
41
|
+
thinking: Type.Optional(Type.String({ description: "Thinking level" })),
|
|
42
|
+
model: Type.Optional(Type.String({ description: "Model to use" })),
|
|
43
|
+
}),
|
|
44
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx: any) {
|
|
45
|
+
const { taskDescription, forceAgent, runInBackground, thinking, model } = params;
|
|
46
|
+
|
|
47
|
+
// If forceAgent is specified, use it directly
|
|
48
|
+
if (forceAgent) {
|
|
49
|
+
const result = await supervisor.routeAgent(
|
|
50
|
+
taskDescription,
|
|
51
|
+
forceAgent,
|
|
52
|
+
runInBackground,
|
|
53
|
+
thinking,
|
|
54
|
+
model
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
if (result.success) {
|
|
58
|
+
const agent = BUILTIN_AGENTS.find((a) => a.name === result.agentType);
|
|
59
|
+
return {
|
|
60
|
+
content: [{
|
|
61
|
+
type: "text",
|
|
62
|
+
text: `Routed to agent: ${result.agentType} via pi-subagents\n\n${agent?.description ?? ""}\n\nAgent ID: ${result.agentId ?? "foreground"}\n\nExecuted by: ${PI_SUBAGENTS_REPO}`,
|
|
63
|
+
}],
|
|
64
|
+
details: {
|
|
65
|
+
agentType: result.agentType,
|
|
66
|
+
reason: result.reason ?? "Forced by user",
|
|
67
|
+
subagentRepo: PI_SUBAGENTS_REPO,
|
|
68
|
+
agentId: result.agentId,
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
} else {
|
|
72
|
+
return {
|
|
73
|
+
content: [{
|
|
74
|
+
type: "text",
|
|
75
|
+
text: `Failed to spawn agent '${forceAgent}': ${result.error}`,
|
|
76
|
+
}],
|
|
77
|
+
details: { error: result.error },
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Otherwise, analyze and route normally
|
|
83
|
+
const result = await supervisor.routeAgent(
|
|
84
|
+
taskDescription,
|
|
85
|
+
undefined,
|
|
86
|
+
runInBackground,
|
|
87
|
+
thinking,
|
|
88
|
+
model
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (result.success) {
|
|
92
|
+
const agent = BUILTIN_AGENTS.find((a) => a.name === result.agentType);
|
|
93
|
+
return {
|
|
94
|
+
content: [{
|
|
95
|
+
type: "text",
|
|
96
|
+
text: `Routed to agent: ${result.agentType} via pi-subagents\n\nDescription: ${agent?.description ?? ""}\n\nReason: ${result.reason ?? ""}\n\nAgent ID: ${result.agentId ?? "foreground"}\n\nExecuted by: ${PI_SUBAGENTS_REPO}`,
|
|
97
|
+
}],
|
|
98
|
+
details: {
|
|
99
|
+
agentType: result.agentType,
|
|
100
|
+
reason: result.reason,
|
|
101
|
+
subagentRepo: PI_SUBAGENTS_REPO,
|
|
102
|
+
agentId: result.agentId,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
} else {
|
|
106
|
+
return {
|
|
107
|
+
content: [{
|
|
108
|
+
type: "text",
|
|
109
|
+
text: `Failed to spawn agent: ${result.error}`,
|
|
110
|
+
}],
|
|
111
|
+
details: { error: result.error },
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// ---- Register plan_and_route tool (NEW — multi-agent workflow) --------
|
|
118
|
+
pi.registerTool({
|
|
119
|
+
name: "plan_and_route",
|
|
120
|
+
label: "Plan and Route",
|
|
121
|
+
description: `Plan a multi-agent workflow via pi-subagents (${PI_SUBAGENTS_REPO}). Launches multiple agents in parallel based on goal keywords.`,
|
|
122
|
+
parameters: Type.Object({
|
|
123
|
+
goal: Type.String({ description: "The goal description for multi-agent execution" }),
|
|
124
|
+
forceAgents: Type.Optional(Type.Array(Type.String({ description: "Agent names to force" }))),
|
|
125
|
+
runInBackground: Type.Optional(Type.Boolean({ description: "Run agents in background" })),
|
|
126
|
+
}),
|
|
127
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx: any) {
|
|
128
|
+
const { goal, forceAgents, runInBackground } = params;
|
|
129
|
+
|
|
130
|
+
const result = await supervisor.planAndRoute(goal, { forceAgents, background: runInBackground });
|
|
131
|
+
|
|
132
|
+
if (result.success) {
|
|
133
|
+
return {
|
|
134
|
+
content: [{
|
|
135
|
+
type: "text",
|
|
136
|
+
text: `Multi-agent workflow launched successfully!\n\nAgents: ${(result.agents ?? []).join(", ")}\nSteps:\n${(result.steps ?? []).join("\n")}`,
|
|
137
|
+
}],
|
|
138
|
+
details: {
|
|
139
|
+
agents: result.agents,
|
|
140
|
+
results: result.results,
|
|
141
|
+
subagentRepo: PI_SUBAGENTS_REPO,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
} else {
|
|
145
|
+
return {
|
|
146
|
+
content: [{
|
|
147
|
+
type: "text",
|
|
148
|
+
text: `Failed to plan workflow: ${result.error}`,
|
|
149
|
+
}],
|
|
150
|
+
details: { error: result.error },
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// ---- Register /assign-agent command (UNCHANGED behavior) --------------
|
|
157
|
+
pi.registerCommand("assign-agent", {
|
|
158
|
+
description: `Route tasks to appropriate sub-agents via pi-subagents (${PI_SUBAGENTS_REPO})`,
|
|
159
|
+
handler: async (input: string, ctx: any) => {
|
|
160
|
+
if (!input) {
|
|
161
|
+
ctx.ui.notify("Please provide a task description", "error");
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const result = await supervisor.routeAgent(input, undefined, false);
|
|
166
|
+
|
|
167
|
+
if (result.success) {
|
|
168
|
+
ctx.ui.notify(`Routed to ${result.agentType}: ${result.reason ?? ""} (via pi-subagents)`, "info");
|
|
169
|
+
} else {
|
|
170
|
+
ctx.ui.notify(`Failed to spawn agent: ${result.error}`, "error");
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// ---- Register list_agents tool (UNCHANGED behavior) ------------------
|
|
176
|
+
pi.registerTool({
|
|
177
|
+
name: "list_agents",
|
|
178
|
+
label: "List Agents",
|
|
179
|
+
description: `List all available agents and their routing criteria (${PI_SUBAGENTS_REPO})`,
|
|
180
|
+
parameters: Type.Object({}),
|
|
181
|
+
async execute() {
|
|
182
|
+
const agents = [
|
|
183
|
+
...BUILTIN_AGENTS,
|
|
184
|
+
// TODO: add filesystem agents here when implemented
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
let response = `Available Agents (routed via pi-subagents):\n\n`;
|
|
188
|
+
agents.forEach((agent, index) => {
|
|
189
|
+
response += `${index + 1}. **${agent.name}**\n`;
|
|
190
|
+
response += ` Description: ${agent.description}\n`;
|
|
191
|
+
response += ` Thinking: ${agent.thinking ?? "medium"}\n`;
|
|
192
|
+
response += ` Triggers: ${agent.triggers?.join(", ") ?? "none"}\n`;
|
|
193
|
+
response += ` Use When: ${agent.useWhen?.join(", ") ?? "any"}\n`;
|
|
194
|
+
response += ` Avoid When: ${agent.avoidWhen?.join(", ") ?? "none"}\n`;
|
|
195
|
+
response += ` Tools: ${agent.tools?.join(", ") ?? "default"}\n\n`;
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
content: [{ type: "text", text: response }],
|
|
200
|
+
details: {
|
|
201
|
+
agents: agents.map((a) => ({
|
|
202
|
+
name: a.name,
|
|
203
|
+
description: a.description,
|
|
204
|
+
useWhen: a.useWhen,
|
|
205
|
+
avoidWhen: a.avoidWhen,
|
|
206
|
+
})),
|
|
207
|
+
subagentRepo: PI_SUBAGENTS_REPO,
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// ---- Intercept input (UNCHANGED behavior from old code) --------------
|
|
214
|
+
pi.on("input", async (event: any, ctx: any) => {
|
|
215
|
+
// Skip commands / special prefixes
|
|
216
|
+
if (event.text.startsWith("/") || event.text.startsWith("!")) {
|
|
217
|
+
return { action: "continue" };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Analyze and route
|
|
221
|
+
const result = await supervisor.routeAgent(event.text, undefined, true);
|
|
222
|
+
|
|
223
|
+
if (result.success) {
|
|
224
|
+
ctx.ui.notify(
|
|
225
|
+
`Auto-routed to ${result.agentType}: ${result.reason ?? ""} (via pi-subagents)`,
|
|
226
|
+
"info"
|
|
227
|
+
);
|
|
228
|
+
return { action: "handled" };
|
|
229
|
+
} else {
|
|
230
|
+
// Fallback to text transformation instead of suppressing
|
|
231
|
+
ctx.ui.notify(`Auto-routing failed: ${result.error}`, "warning");
|
|
232
|
+
return {
|
|
233
|
+
action: "transform",
|
|
234
|
+
text: `[ROUTED] ${event.text}`,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// ---- Session start notification (UNCHANGED) --------------------------
|
|
240
|
+
pi.on("session_start", async (_event: any, ctx: any) => {
|
|
241
|
+
ctx.ui.notify(`${PI_SUBAGENTS_REPO} engine loaded — route_agent and plan_and_route available`, "info");
|
|
242
|
+
});
|
|
243
|
+
}
|