pi-open-agents 0.1.13 → 0.1.14

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.13",
3
+ "version": "0.1.14",
4
4
  "description": "Unified agent and subagent management for pi coding agent, with OpenCode-compatible agent definitions.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -183,6 +183,10 @@ function parseToolsAndPermission(
183
183
  * Convert OpenCode-style tools map to PermissionConfig.
184
184
  *
185
185
  * { "read": true, "write": false } → { "read": "allow", "write": "deny" }
186
+ *
187
+ * `write` and `edit` are kept as separate keys — in pi they are separate
188
+ * tools (create/overwrite vs search-and-replace). Only `apply_patch`
189
+ * (OpenCode-only) is mapped to `edit`.
186
190
  */
187
191
  function toolsMapToPermission(toolsMap: Record<string, unknown>): PermissionConfig {
188
192
  const config: PermissionConfig = {};
@@ -191,8 +195,8 @@ function toolsMapToPermission(toolsMap: Record<string, unknown>): PermissionConf
191
195
  // Skip non-boolean values
192
196
  if (typeof enabled !== "boolean") continue;
193
197
 
194
- // Map edit-like tools to "edit" permission (OpenCode convention)
195
- const permKey = toolName === "write" || toolName === "edit" || toolName === "apply_patch"
198
+ // Map OpenCode-only tools to their pi equivalent
199
+ const permKey = toolName === "apply_patch"
196
200
  ? "edit"
197
201
  : toolName;
198
202
 
@@ -20,15 +20,19 @@ import { matchPattern } from "./matcher.ts";
20
20
  /**
21
21
  * Map a tool name to its permission category.
22
22
  *
23
- * Multiple tools can share a permission category:
24
- * write, edit, apply_patch → "edit"
23
+ * Some tools share a permission category with another tool:
24
+ * apply_patch → "edit" (OpenCode-only tool, functionally equivalent to edit)
25
25
  * list_mcp_resources, read_mcp_resource → "read"
26
26
  *
27
+ * In pi, `write` and `edit` are SEPARATE tools (create/overwrite vs
28
+ * search-and-replace). They must stay as separate permission categories so
29
+ * that `{edit: true, write: false}` works as expected.
30
+ *
27
31
  * Everything else maps by tool name directly.
28
32
  */
29
33
  export function toolToPermission(toolName: string): string {
30
- // File-editing tools all map to "edit"
31
- if (toolName === "write" || toolName === "edit" || toolName === "apply_patch") {
34
+ // OpenCode's apply_patch maps to pi's edit (no apply_patch tool in pi)
35
+ if (toolName === "apply_patch") {
32
36
  return "edit";
33
37
  }
34
38
 
@@ -19,7 +19,6 @@ export type RecursionEnv = Partial<
19
19
  | "PI_OPEN_AGENTS_SYSTEM_PROMPT_MODE"
20
20
  | "PI_OPEN_AGENTS_ALLOWED"
21
21
  | "PI_OPEN_AGENTS_SESSION"
22
- | "PI_OPEN_AGENTS_PERMISSION"
23
22
  | "PI_OPEN_AGENTS_DEBUG",
24
23
  string
25
24
  >
@@ -143,6 +143,41 @@ const defaultExecutorFs: ExecutorFs = {
143
143
 
144
144
  // ─── Helpers ─────────────────────────────────────────────────────────────────
145
145
 
146
+ /**
147
+ * Derive a tools whitelist from the agent definition for the child process.
148
+ *
149
+ * The `--tools` CLI flag is a whitelist (include only these). It works with:
150
+ * - Explicit `agent.tools` array (pi-style CSV)
151
+ * - Simple permission allow-lists (keys with "allow", no wildcard)
152
+ *
153
+ * Does NOT work with:
154
+ * - Wildcard permissions (`*: allow` + specific denies) — can't derive a
155
+ * finite whitelist. The child gets all tools. This is a known limitation
156
+ * of the `--tools` flag (it's a whitelist, not a deny-list).
157
+ * - Pattern-based permissions (`edit: { "*.env": deny }`) — patterns can't
158
+ * be expressed as a flat tool whitelist.
159
+ */
160
+ function deriveToolsWhitelist(agent: AgentDefinition): string[] | undefined {
161
+ // Explicit tools array — use directly
162
+ if (agent.tools && agent.tools.length > 0) {
163
+ return agent.tools;
164
+ }
165
+
166
+ // Derive from permission config
167
+ if (agent.permission) {
168
+ // Can't derive a finite whitelist when wildcard is present
169
+ if ("*" in agent.permission) return undefined;
170
+
171
+ const allowed = Object.entries(agent.permission)
172
+ .filter(([, value]) => value === "allow")
173
+ .map(([key]) => key);
174
+
175
+ return allowed.length > 0 ? allowed : undefined;
176
+ }
177
+
178
+ return undefined;
179
+ }
180
+
146
181
  function emptyUsage(): AgentUsage {
147
182
  return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0 };
148
183
  }
@@ -478,8 +513,9 @@ export async function runSubagent(options: RunSubagentOptions): Promise<AgentRes
478
513
  args.push("--thinking", options.agent.thinking);
479
514
 
480
515
  // Tools whitelist
481
- if (options.agent.tools && options.agent.tools.length > 0) {
482
- args.push("--tools", options.agent.tools.join(","));
516
+ const toolsWhitelist = deriveToolsWhitelist(options.agent);
517
+ if (toolsWhitelist && toolsWhitelist.length > 0) {
518
+ args.push("--tools", toolsWhitelist.join(","));
483
519
  }
484
520
 
485
521
  // System prompt file (append or replace)