pi-open-agents 0.1.1 → 0.1.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/package.json +1 -1
- package/src/index.ts +29 -22
- package/src/primary/executor.ts +46 -6
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -21,12 +21,12 @@ import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
|
21
21
|
import type { AgentDefinition, ThinkingLevel } from "./types.ts";
|
|
22
22
|
import { loadAgents, selectableAgents, spawnableAgents } from "./discovery/loader.ts";
|
|
23
23
|
import { AgentManager } from "./primary/manager.ts";
|
|
24
|
-
import { refreshAgentTools, buildSystemPrompt } from "./primary/executor.ts";
|
|
24
|
+
import { refreshAgentTools, buildSystemPrompt, buildSubagentGuidance } from "./primary/executor.ts";
|
|
25
25
|
import { updateBanner } from "./tui/banner.ts";
|
|
26
26
|
import { registerTuiControls } from "./tui/selector.ts";
|
|
27
27
|
import { registerAgentTools } from "./tui/tools.ts";
|
|
28
28
|
import { registerSubagentTool } from "./subagent/tool.ts";
|
|
29
|
-
import {
|
|
29
|
+
import { allowedAgentNames } from "./subagent/env.ts";
|
|
30
30
|
|
|
31
31
|
// ─── Settings ────────────────────────────────────────────────────────────────
|
|
32
32
|
|
|
@@ -141,33 +141,40 @@ export default function piOpenAgents(pi: ExtensionAPI): void {
|
|
|
141
141
|
updateBanner(ctx.ui, manager.getActive(), selectableAgents(manager.getAgents()).length);
|
|
142
142
|
});
|
|
143
143
|
|
|
144
|
-
// ── Before Agent Start: Inject system prompt +
|
|
144
|
+
// ── Before Agent Start: Inject system prompt + subagent guidance ─────────
|
|
145
145
|
|
|
146
146
|
pi.on("before_agent_start", async (event) => {
|
|
147
147
|
const active = manager.getActive();
|
|
148
|
-
|
|
148
|
+
let prompt = event.systemPrompt;
|
|
149
149
|
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (modifiedPrompt) {
|
|
156
|
-
return { systemPrompt: modifiedPrompt };
|
|
150
|
+
// Primary agent: apply system prompt mode + refresh tools
|
|
151
|
+
if (active) {
|
|
152
|
+
refreshAgentTools(active, pi);
|
|
153
|
+
const modified = buildSystemPrompt(active, prompt);
|
|
154
|
+
if (modified !== undefined) prompt = modified;
|
|
157
155
|
}
|
|
158
|
-
});
|
|
159
156
|
|
|
160
|
-
|
|
157
|
+
// Inject subagent delegation guidance (if subagent tool is available)
|
|
158
|
+
const hasSubagentTool = pi.getAllTools().some((t) => t.name === "subagent");
|
|
159
|
+
if (hasSubagentTool) {
|
|
160
|
+
// Determine allowed agents: from active agent config or env vars (subagent process)
|
|
161
|
+
let allowedAgents: string[] | undefined;
|
|
162
|
+
if (active?.allowedAgents && active.allowedAgents.length > 0) {
|
|
163
|
+
allowedAgents = active.allowedAgents;
|
|
164
|
+
} else {
|
|
165
|
+
const envAllowed = allowedAgentNames(process.env);
|
|
166
|
+
if (envAllowed) allowedAgents = [...envAllowed];
|
|
167
|
+
}
|
|
161
168
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
const guidance = buildSubagentGuidance(manager.getAgents(), allowedAgents);
|
|
170
|
+
if (guidance) {
|
|
171
|
+
prompt += guidance;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (prompt !== event.systemPrompt) {
|
|
176
|
+
return { systemPrompt: prompt };
|
|
177
|
+
}
|
|
171
178
|
});
|
|
172
179
|
|
|
173
180
|
// ── Turn Start: Persist agent state (only on change) ───────────────────────
|
package/src/primary/executor.ts
CHANGED
|
@@ -13,6 +13,7 @@ import type { Model } from "@earendil-works/pi-ai";
|
|
|
13
13
|
import type { AgentDefinition, ModelRef, PermissionRule } from "../types.ts";
|
|
14
14
|
import { parsePermissionRules } from "../permission/parser.ts";
|
|
15
15
|
import { getDisabledTools } from "../permission/evaluator.ts";
|
|
16
|
+
import { spawnableAgents } from "../discovery/loader.ts";
|
|
16
17
|
|
|
17
18
|
// ─── Model Resolution ────────────────────────────────────────────────────────
|
|
18
19
|
|
|
@@ -38,7 +39,14 @@ async function applyModel(agent: AgentDefinition, pi: ExtensionAPI, ctx: Extensi
|
|
|
38
39
|
if (!ref) return;
|
|
39
40
|
|
|
40
41
|
const model = ctx.modelRegistry.find(ref.provider, ref.modelId);
|
|
41
|
-
if (!model)
|
|
42
|
+
if (!model) {
|
|
43
|
+
const available = ctx.modelRegistry.getAvailable().map((m: Model<any>) => `${m.provider}/${m.id}`).join(", ");
|
|
44
|
+
ctx.ui.notify(
|
|
45
|
+
`Agent "${agent.name}": Model ${ref.provider}/${ref.modelId} not found in registry. Available: ${available}`,
|
|
46
|
+
"warning",
|
|
47
|
+
);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
42
50
|
|
|
43
51
|
const success = await pi.setModel(model);
|
|
44
52
|
if (!success) {
|
|
@@ -133,6 +141,41 @@ export function refreshAgentTools(agent: AgentDefinition, pi: ExtensionAPI): voi
|
|
|
133
141
|
applyTools(agent, pi);
|
|
134
142
|
}
|
|
135
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Build subagent delegation guidance to inject into the system prompt.
|
|
146
|
+
*
|
|
147
|
+
* Teaches the model HOW to delegate — specifically that subagent names
|
|
148
|
+
* are NOT CLI commands and must be invoked via the `subagent` tool.
|
|
149
|
+
* This is injected automatically, so agent authors don't need to explain
|
|
150
|
+
* the delegation mechanism in their prompts.
|
|
151
|
+
*
|
|
152
|
+
* Works for both primary agents and subagent child processes.
|
|
153
|
+
*/
|
|
154
|
+
export function buildSubagentGuidance(
|
|
155
|
+
agents: AgentDefinition[],
|
|
156
|
+
allowedAgents?: string[],
|
|
157
|
+
): string | undefined {
|
|
158
|
+
const allSpawnable = spawnableAgents(agents);
|
|
159
|
+
|
|
160
|
+
// Filter by allowedAgents whitelist if specified
|
|
161
|
+
let available: AgentDefinition[];
|
|
162
|
+
if (allowedAgents && allowedAgents.length > 0) {
|
|
163
|
+
const allowed = new Set(allowedAgents);
|
|
164
|
+
available = allSpawnable.filter((a) => allowed.has(a.name));
|
|
165
|
+
} else {
|
|
166
|
+
available = allSpawnable;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (available.length === 0) return undefined;
|
|
170
|
+
|
|
171
|
+
const list = available
|
|
172
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
173
|
+
.map((a) => `- ${a.name}: ${a.description ?? "Subagent for delegated tasks."}`)
|
|
174
|
+
.join("\n");
|
|
175
|
+
|
|
176
|
+
return `\n\n## Subagent Delegation\n\nYou have ${available.length} subagent(s) available. Delegate isolated tasks using the \`subagent\` tool:\n\nsubagent({ agent: "<name>", task: "<task description>" })\n\nAvailable subagents:\n${list}\n\nIMPORTANT: Subagent names are NOT bash commands. Always use the \`subagent\` tool to delegate — never write scripts that call agent names as CLI commands.`;
|
|
177
|
+
}
|
|
178
|
+
|
|
136
179
|
/**
|
|
137
180
|
* Build the system prompt injection for the active agent.
|
|
138
181
|
*
|
|
@@ -151,11 +194,8 @@ export function buildSystemPrompt(
|
|
|
151
194
|
return `${agent.prompt}\n\n${currentPrompt}`;
|
|
152
195
|
|
|
153
196
|
case "replace":
|
|
154
|
-
// Replace the provider prompt
|
|
155
|
-
|
|
156
|
-
// the subprocess executor (subagent path)
|
|
157
|
-
// For primary agents, "replace" behaves like append for now
|
|
158
|
-
return `${agent.prompt}\n\n${currentPrompt}`;
|
|
197
|
+
// Replace the provider/system prompt entirely with the agent body
|
|
198
|
+
return agent.prompt;
|
|
159
199
|
|
|
160
200
|
case "replace-all":
|
|
161
201
|
// Full replacement — only agent body
|