pi-open-agents 0.1.2 → 0.1.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-open-agents",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Unified agent and subagent management for pi coding agent, with OpenCode-compatible agent definitions.",
5
5
  "keywords": [
6
6
  "pi-package",
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 { isSubagentReplaceSystemPrompt } from "./subagent/env.ts";
29
+ import { allowedAgentNames, isSubagentProcess } from "./subagent/env.ts";
30
30
 
31
31
  // ─── Settings ────────────────────────────────────────────────────────────────
32
32
 
@@ -98,6 +98,14 @@ export default function piOpenAgents(pi: ExtensionAPI): void {
98
98
  getActiveAgent: () => manager.getActive(),
99
99
  });
100
100
 
101
+ // Subagent child processes are fully configured by the executor via CLI flags
102
+ // (--model, --system-prompt, --tools). Do NOT apply defaultAgent — it would
103
+ // override the executor's model/tools/prompt settings.
104
+ if (isSubagentProcess(process.env)) {
105
+ updateBanner(ctx.ui, undefined, 0);
106
+ return;
107
+ }
108
+
101
109
  // Check for --agent CLI flag first (highest priority)
102
110
  const agentFlag = pi.getFlag("agent");
103
111
  if (typeof agentFlag === "string" && agentFlag) {
@@ -141,33 +149,44 @@ export default function piOpenAgents(pi: ExtensionAPI): void {
141
149
  updateBanner(ctx.ui, manager.getActive(), selectableAgents(manager.getAgents()).length);
142
150
  });
143
151
 
144
- // ── Before Agent Start: Inject system prompt + refresh tools ───────────────
152
+ // ── Before Agent Start: Inject system prompt + subagent guidance ─────────
145
153
 
146
154
  pi.on("before_agent_start", async (event) => {
147
- const active = manager.getActive();
148
- if (!active) return;
155
+ // Skip system prompt modification in subagent child processes
156
+ // (executor sets system prompt via --system-prompt CLI flag)
157
+ if (isSubagentProcess(process.env)) return;
149
158
 
150
- // Re-evaluate tool set (catches async-registered tools like MCP)
151
- refreshAgentTools(active, pi);
159
+ const active = manager.getActive();
160
+ let prompt = event.systemPrompt;
152
161
 
153
- // Inject system prompt
154
- const modifiedPrompt = buildSystemPrompt(active, event.systemPrompt);
155
- if (modifiedPrompt) {
156
- return { systemPrompt: modifiedPrompt };
162
+ // Primary agent: apply system prompt mode + refresh tools
163
+ if (active) {
164
+ refreshAgentTools(active, pi);
165
+ const modified = buildSystemPrompt(active, prompt);
166
+ if (modified !== undefined) prompt = modified;
157
167
  }
158
- });
159
168
 
160
- // ── Before Agent Start (subagent replace mode): Inject available subagents ─
169
+ // Inject subagent delegation guidance (if subagent tool is available)
170
+ const hasSubagentTool = pi.getAllTools().some((t) => t.name === "subagent");
171
+ if (hasSubagentTool) {
172
+ // Determine allowed agents: from active agent config or env vars (subagent process)
173
+ let allowedAgents: string[] | undefined;
174
+ if (active?.allowedAgents && active.allowedAgents.length > 0) {
175
+ allowedAgents = active.allowedAgents;
176
+ } else {
177
+ const envAllowed = allowedAgentNames(process.env);
178
+ if (envAllowed) allowedAgents = [...envAllowed];
179
+ }
161
180
 
162
- pi.on("before_agent_start", async (event) => {
163
- if (!isSubagentReplaceSystemPrompt(process.env)) return;
164
- // In replace mode, append available subagent list to the system prompt
165
- const agents = manager.getAgents();
166
- const spawnable = spawnableAgents(agents);
167
- if (spawnable.length === 0) return;
168
- const names = spawnable.map((a: AgentDefinition) => a.name).sort().join(", ");
169
- const block = `\n\nAvailable subagents:\n- ${names.split(", ").join("\n- ")}`;
170
- return { systemPrompt: event.systemPrompt + block };
181
+ const guidance = buildSubagentGuidance(manager.getAgents(), allowedAgents);
182
+ if (guidance) {
183
+ prompt += guidance;
184
+ }
185
+ }
186
+
187
+ if (prompt !== event.systemPrompt) {
188
+ return { systemPrompt: prompt };
189
+ }
171
190
  });
172
191
 
173
192
  // ── Turn Start: Persist agent state (only on change) ───────────────────────
@@ -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
 
@@ -140,6 +141,41 @@ export function refreshAgentTools(agent: AgentDefinition, pi: ExtensionAPI): voi
140
141
  applyTools(agent, pi);
141
142
  }
142
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
+
143
179
  /**
144
180
  * Build the system prompt injection for the active agent.
145
181
  *