@gotgenes/pi-subagents 7.8.1 → 9.0.0

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.
@@ -58,8 +58,7 @@ function loadFromDir(dir: string, agents: Map<string, AgentConfig>, source: "pro
58
58
  displayName: str(fm.display_name),
59
59
  description: str(fm.description) ?? name,
60
60
  builtinToolNames: csvList(fm.tools, BUILTIN_TOOL_NAMES),
61
- disallowedTools: csvListOptional(fm.disallowed_tools),
62
- extensions: inheritField(fm.extensions ?? fm.inherit_extensions),
61
+ extensions: resolveBoolExtensions(fm.extensions ?? fm.inherit_extensions),
63
62
  skills: inheritField(fm.skills ?? fm.inherit_skills),
64
63
  model: str(fm.model),
65
64
  thinking: str(fm.thinking) as ThinkingLevel | undefined,
@@ -111,11 +110,19 @@ function csvList(val: unknown, defaults: string[]): string[] {
111
110
  }
112
111
 
113
112
  /**
114
- * Parse an optional comma-separated list field.
115
- * omitted undefined; "none"/empty undefined; csv listed items.
113
+ * Resolve the `extensions` field to a boolean.
114
+ * CSV/array values (legacy allowlist syntax) are coerced to `true` with a warning.
116
115
  */
117
- function csvListOptional(val: unknown): string[] | undefined {
118
- return parseCsvField(val);
116
+ function resolveBoolExtensions(val: unknown): boolean {
117
+ const result = inheritField(val);
118
+ if (Array.isArray(result)) {
119
+ console.warn(
120
+ "[pi-subagents] extensions allowlist syntax is deprecated — treating as \"true\" (inherit all).\n" +
121
+ "Use \"permission:\" frontmatter in pi-permission-system for per-tool access control.",
122
+ );
123
+ return true;
124
+ }
125
+ return result;
119
126
  }
120
127
 
121
128
  /**
@@ -22,7 +22,7 @@ import type { ShellExec, SubagentType, ThinkingLevel } from "#src/types";
22
22
  const EXCLUDED_TOOL_NAMES = ["Agent", "get_subagent_result", "steer_subagent"];
23
23
 
24
24
  /**
25
- * Filter the session's active tool names according to extension/denylist rules.
25
+ * Filter the session's active tool names according to extension rules.
26
26
  *
27
27
  * Run twice - once before `bindExtensions` (filters built-in tools) and once after
28
28
  * (filters extension-registered tools, which only join the active set during
@@ -36,20 +36,14 @@ function filterActiveTools(
36
36
  activeTools: string[],
37
37
  config: ToolFilterConfig,
38
38
  ): string[] {
39
- const { toolNames, extensions, disallowedSet } = config;
40
- if (extensions === false) {
41
- // Extensions disabled: only apply the denylist to built-in tools.
42
- if (!disallowedSet) return activeTools;
43
- return activeTools.filter((t) => !disallowedSet.has(t));
39
+ const { toolNames, extensions } = config;
40
+ if (!extensions) {
41
+ return activeTools;
44
42
  }
45
43
  const builtinToolNameSet = new Set(toolNames);
46
44
  return activeTools.filter((t) => {
47
45
  if (EXCLUDED_TOOL_NAMES.includes(t)) return false;
48
- if (disallowedSet?.has(t)) return false;
49
46
  if (builtinToolNameSet.has(t)) return true;
50
- if (Array.isArray(extensions)) {
51
- return extensions.some((ext) => t.startsWith(ext) || t.includes(ext));
52
- }
53
47
  return true;
54
48
  });
55
49
  }
@@ -302,7 +296,7 @@ export async function runAgent(
302
296
 
303
297
  const agentDir = io.getAgentDir();
304
298
 
305
- // Load extensions/skills: true or string[] → load; false → don't.
299
+ // Load extensions/skills: true → load; false → don't.
306
300
  // Suppress AGENTS.md/CLAUDE.md and APPEND_SYSTEM.md - upstream's
307
301
  // buildSystemPrompt() re-appends both AFTER systemPromptOverride, which
308
302
  // would defeat prompt_mode: replace and isolated: true. Parent context, if
@@ -311,7 +305,7 @@ export async function runAgent(
311
305
  const loader = io.createResourceLoader({
312
306
  cwd: cfg.effectiveCwd,
313
307
  agentDir,
314
- noExtensions: cfg.toolFilter.extensions === false,
308
+ noExtensions: !cfg.toolFilter.extensions,
315
309
  noSkills: cfg.noSkills,
316
310
  noPromptTemplates: true,
317
311
  noThemes: true,
@@ -340,10 +334,9 @@ export async function runAgent(
340
334
  thinkingLevel: cfg.thinkingLevel,
341
335
  });
342
336
 
343
- // Filter active tools: remove our own tools to prevent nesting,
344
- // apply extension allowlist if specified, and apply disallowedTools denylist.
337
+ // Filter active tools: remove our own tools to prevent nesting.
345
338
  // First pass - over built-in tools, before bindExtensions registers extension tools.
346
- if (cfg.toolFilter.extensions !== false || cfg.toolFilter.disallowedSet) {
339
+ if (cfg.toolFilter.extensions) {
347
340
  const filtered = filterActiveTools(session.getActiveToolNames(), cfg.toolFilter);
348
341
  session.setActiveToolsByName(filtered);
349
342
  }
@@ -366,10 +359,9 @@ export async function runAgent(
366
359
  // Patch 2 (RepOne #443): re-filter active tools after bindExtensions.
367
360
  // Extension-registered tools (added during bindExtensions) are not in the
368
361
  // session's active set when the first filter pass runs above. Without this
369
- // re-filter, the `extensions: string[]` allowlist branch never matches any
370
- // extension tools and `extensions: true` lets non-allowlisted denylist
371
- // entries slip in. Run the same filter against the post-bind active set.
372
- if (cfg.toolFilter.extensions !== false || cfg.toolFilter.disallowedSet) {
362
+ // re-filter, EXCLUDED_TOOL_NAMES would not be applied to extension-registered
363
+ // tools. Run the same filter against the post-bind active set.
364
+ if (cfg.toolFilter.extensions) {
373
365
  const refiltered = filterActiveTools(session.getActiveToolNames(), cfg.toolFilter);
374
366
  session.setActiveToolsByName(refiltered);
375
367
  }
@@ -21,16 +21,14 @@ import type { AgentPromptConfig, SubagentType, ThinkingLevel } from "#src/types"
21
21
  /**
22
22
  * Tool filtering configuration — consumed by `filterActiveTools` in `agent-runner.ts`.
23
23
  *
24
- * Groups the three fields that travel together through the assembly→runner boundary:
25
- * the built-in tool allowlist, the optional denylist, and the extensions setting.
24
+ * Groups the two fields that travel together through the assembly→runner boundary:
25
+ * the built-in tool allowlist and the extensions setting.
26
26
  */
27
27
  export interface ToolFilterConfig {
28
28
  /** Built-in tool name allowlist for this agent type. */
29
29
  toolNames: string[];
30
- /** Disallowed tool set from agentConfig. undefined when empty. */
31
- disallowedSet: Set<string> | undefined;
32
- /** Resolved extensions setting: false | true | string[] allowlist. */
33
- extensions: boolean | string[];
30
+ /** Resolved extensions setting: true = inherit all, false = none. */
31
+ extensions: boolean;
34
32
  }
35
33
 
36
34
  /**
@@ -210,11 +208,6 @@ export function assembleSessionConfig(
210
208
  // tell the resource loader not to load them again.
211
209
  const noSkills = skills === false || Array.isArray(skills);
212
210
 
213
- // Disallowed tools set (for filterActiveTools in runAgent)
214
- const disallowedSet = agentConfig.disallowedTools
215
- ? new Set(agentConfig.disallowedTools)
216
- : undefined;
217
-
218
211
  // Model resolution: explicit option > config model string > parent model
219
212
  const model =
220
213
  options.model ??
@@ -229,7 +222,7 @@ export function assembleSessionConfig(
229
222
  return {
230
223
  effectiveCwd,
231
224
  systemPrompt,
232
- toolFilter: { toolNames, disallowedSet, extensions },
225
+ toolFilter: { toolNames, extensions },
233
226
  model,
234
227
  thinkingLevel,
235
228
  noSkills,
package/src/types.ts CHANGED
@@ -42,10 +42,8 @@ export interface AgentPromptConfig {
42
42
  /** Unified agent configuration — used for both default and user-defined agents. */
43
43
  export interface AgentConfig extends AgentIdentity, AgentPromptConfig {
44
44
  builtinToolNames?: string[];
45
- /** Tool denylist these tools are removed even if `builtinToolNames` or extensions include them. */
46
- disallowedTools?: string[];
47
- /** true = inherit all, string[] = only listed, false = none */
48
- extensions: true | string[] | false;
45
+ /** true = inherit all extensions, false = none */
46
+ extensions: boolean;
49
47
  /** true = inherit all, string[] = only listed, false = none */
50
48
  skills: true | string[] | false;
51
49
  model?: string;
@@ -47,14 +47,10 @@ export function buildEjectContent(cfg: AgentConfig): string {
47
47
  if (cfg.thinking) fmFields.push(`thinking: ${cfg.thinking}`);
48
48
  if (cfg.maxTurns) fmFields.push(`max_turns: ${cfg.maxTurns}`);
49
49
  fmFields.push(`prompt_mode: ${cfg.promptMode}`);
50
- if (cfg.extensions === false) fmFields.push("extensions: false");
51
- else if (Array.isArray(cfg.extensions))
52
- fmFields.push(`extensions: ${cfg.extensions.join(", ")}`);
50
+ if (!cfg.extensions) fmFields.push("extensions: false");
53
51
  if (cfg.skills === false) fmFields.push("skills: false");
54
52
  else if (Array.isArray(cfg.skills))
55
53
  fmFields.push(`skills: ${cfg.skills.join(", ")}`);
56
- if (cfg.disallowedTools?.length)
57
- fmFields.push(`disallowed_tools: ${cfg.disallowedTools.join(", ")}`);
58
54
  if (cfg.inheritContext) fmFields.push("inherit_context: true");
59
55
  if (cfg.runInBackground) fmFields.push("run_in_background: true");
60
56
  if (cfg.isolated) fmFields.push("isolated: true");
@@ -104,9 +104,8 @@ model: <optional model as "provider/modelId", e.g. "anthropic/claude-haiku-4-5-2
104
104
  thinking: <optional thinking level: off, minimal, low, medium, high, xhigh. Omit to inherit>
105
105
  max_turns: <optional max agentic turns. 0 or omit for unlimited (default)>
106
106
  prompt_mode: <"replace" (body IS the full system prompt) or "append" (body is appended to default prompt). Default: replace>
107
- extensions: <true (inherit all MCP/extension tools), false (none), or comma-separated names. Default: true>
107
+ extensions: <true (inherit all MCP/extension tools) or false (none). Default: true>
108
108
  skills: <true (inherit all), false (none), or comma-separated skill names to preload into prompt. Default: true>
109
- disallowed_tools: <comma-separated tool names to block, even if otherwise available. Omit for none>
110
109
  inherit_context: <true to fork parent conversation into agent so it sees chat history. Default: false>
111
110
  run_in_background: <true to run in background by default. Default: false>
112
111
  isolated: <true for no extension/MCP tools, only built-in tools. Default: false>