pi-open-agents 0.1.29 → 0.1.30
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/README.md +11 -1
- package/package.json +1 -1
- package/src/primary/executor.ts +52 -25
package/README.md
CHANGED
|
@@ -167,6 +167,15 @@ active agent's rules *before it runs* — not just used to filter the toolset:
|
|
|
167
167
|
> blocks matching *individual calls* (patterns included), not just statically
|
|
168
168
|
> hiding whole tools. (#20)
|
|
169
169
|
|
|
170
|
+
> **⚠️ Behavior change (v0.1.30):** primary-agent static filtering now covers
|
|
171
|
+
> **all tool sources**, not just builtins (#18). If an agent declares
|
|
172
|
+
> `permission` or `tools:`, extension tools (web, LSP, MCP, …) not matched
|
|
173
|
+
> are hidden from the model — previously they were always kept. Only blanket
|
|
174
|
+
> `deny` rules hide a tool; argument-pattern denies and `ask` stay enforced
|
|
175
|
+
> by the runtime gate. If your allowlist drops `subagent`, the agent cannot
|
|
176
|
+
> delegate anymore — apply warns you explicitly. No restrictions = everything
|
|
177
|
+
> stays active, exactly as before.
|
|
178
|
+
|
|
170
179
|
**Inheritance down the delegation chain (#8).** A subagent can never exceed
|
|
171
180
|
the authority of the agent that delegated to it — in both directions of the
|
|
172
181
|
rule language:
|
|
@@ -387,6 +396,7 @@ six sessions, zero chaos: that's the exact workstation setup documented in
|
|
|
387
396
|
- ~~**Parent permission inheritance** for subagents~~ — ✅ shipped in [0.1.27](https://github.com/andrea-tomassi/pi-open-agents/releases/tag/v0.1.27) (#8)
|
|
388
397
|
- ~~**Agent hot-reload**~~ — ✅ works via pi's built-in `/reload`, shipped in [0.1.27](https://github.com/andrea-tomassi/pi-open-agents/releases/tag/v0.1.27) (#9)
|
|
389
398
|
- ~~**Per-call model override** in `subagent()`~~ — ✅ shipped in [0.1.19](https://github.com/andrea-tomassi/pi-open-agents/releases/tag/v0.1.19) (#10)
|
|
399
|
+
- ~~**Primary tool filters incl. extension tools**~~ — ✅ shipped in [0.1.30](https://github.com/andrea-tomassi/pi-open-agents/releases/tag/v0.1.30) (#18)
|
|
390
400
|
- **Parallel subagent limit** — #11
|
|
391
401
|
|
|
392
402
|
---
|
|
@@ -398,7 +408,7 @@ opening an issue or pull request.
|
|
|
398
408
|
|
|
399
409
|
```bash
|
|
400
410
|
npm install
|
|
401
|
-
npm test #
|
|
411
|
+
npm test # 207 tests
|
|
402
412
|
npm run typecheck # tsc --noEmit
|
|
403
413
|
```
|
|
404
414
|
|
package/package.json
CHANGED
package/src/primary/executor.ts
CHANGED
|
@@ -75,47 +75,72 @@ function applyThinking(agent: AgentDefinition, pi: ExtensionAPI): void {
|
|
|
75
75
|
*
|
|
76
76
|
* Logic:
|
|
77
77
|
* 1. If agent has permission rules → filter via permission engine
|
|
78
|
-
* 2. Else if agent has tools whitelist → filter to only those
|
|
78
|
+
* 2. Else if agent has tools whitelist → filter to only those
|
|
79
79
|
* 3. Otherwise → all tools active
|
|
80
80
|
*
|
|
81
|
-
*
|
|
81
|
+
* Filtering covers ALL registered tool sources (builtin, extension, MCP,
|
|
82
|
+
* SDK): a restricted profile can hide extension tools (web, LSP, …) from
|
|
83
|
+
* the model, not just block their calls. Only blanket `deny` rules hide a
|
|
84
|
+
* tool statically — argument-pattern denies and `ask` stay enforced by the
|
|
85
|
+
* runtime gate (#20), which also remains the backstop for tools registered
|
|
86
|
+
* after the agent started (issue #18).
|
|
87
|
+
*
|
|
88
|
+
* @returns The applied active tool names (for the subagent-hidden warning).
|
|
82
89
|
*/
|
|
83
|
-
function
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
90
|
+
export function computeActiveTools(
|
|
91
|
+
agent: AgentDefinition,
|
|
92
|
+
allTools: Array<{ name: string }>,
|
|
93
|
+
): string[] {
|
|
94
|
+
// Case 1: Permission-based filtering (all sources)
|
|
87
95
|
if (agent.permission) {
|
|
88
96
|
const rules = parsePermissionRules(agent.permission);
|
|
89
97
|
const disabled = getDisabledTools(
|
|
90
98
|
allTools.map((t) => t.name),
|
|
91
99
|
rules,
|
|
92
100
|
);
|
|
93
|
-
|
|
94
|
-
.filter((t) => {
|
|
95
|
-
// Always keep non-builtin tools (extensions, MCP)
|
|
96
|
-
if (t.sourceInfo?.source !== "builtin") return true;
|
|
97
|
-
return !disabled.has(t.name);
|
|
98
|
-
})
|
|
99
|
-
.map((t) => t.name);
|
|
100
|
-
pi.setActiveTools(filtered);
|
|
101
|
-
return;
|
|
101
|
+
return allTools.filter((t) => !disabled.has(t.name)).map((t) => t.name);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
// Case 2: Simple tools whitelist (
|
|
104
|
+
// Case 2: Simple tools whitelist (all sources)
|
|
105
105
|
if (agent.tools && agent.tools.length > 0) {
|
|
106
106
|
const whitelist = new Set(agent.tools.map((t) => t.toLowerCase()));
|
|
107
|
-
|
|
108
|
-
.filter((t) =>
|
|
109
|
-
if (t.sourceInfo?.source !== "builtin") return true;
|
|
110
|
-
return whitelist.has(t.name.toLowerCase());
|
|
111
|
-
})
|
|
107
|
+
return allTools
|
|
108
|
+
.filter((t) => whitelist.has(t.name.toLowerCase()))
|
|
112
109
|
.map((t) => t.name);
|
|
113
|
-
pi.setActiveTools(filtered);
|
|
114
|
-
return;
|
|
115
110
|
}
|
|
116
111
|
|
|
117
112
|
// Case 3: No restrictions — keep all tools
|
|
118
|
-
|
|
113
|
+
return allTools.map((t) => t.name);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function applyTools(agent: AgentDefinition, pi: ExtensionAPI): string[] {
|
|
117
|
+
const active = computeActiveTools(agent, pi.getAllTools());
|
|
118
|
+
pi.setActiveTools(active);
|
|
119
|
+
return active;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Warn when an agent's static restrictions hide the `subagent` tool (#18).
|
|
124
|
+
*
|
|
125
|
+
* Without it the model cannot delegate at all — that may be exactly what a
|
|
126
|
+
* locked-down profile wants, but it must be a deliberate choice, never a
|
|
127
|
+
* surprise from an allowlist that simply forgot to name it.
|
|
128
|
+
*/
|
|
129
|
+
export function warnIfHidingSubagent(
|
|
130
|
+
agent: AgentDefinition,
|
|
131
|
+
allNames: string[],
|
|
132
|
+
activeNames: string[],
|
|
133
|
+
ui: ExtensionContext["ui"],
|
|
134
|
+
): void {
|
|
135
|
+
const restricted = agent.permission !== undefined || (agent.tools?.length ?? 0) > 0;
|
|
136
|
+
if (!restricted) return;
|
|
137
|
+
if (!allNames.includes("subagent")) return;
|
|
138
|
+
if (activeNames.includes("subagent")) return;
|
|
139
|
+
ui.notify(
|
|
140
|
+
`Agent "${agent.name}": tool restrictions hide the "subagent" tool — this agent cannot delegate. ` +
|
|
141
|
+
`Add "subagent" to the allowlist if delegation is intended.`,
|
|
142
|
+
"warning",
|
|
143
|
+
);
|
|
119
144
|
}
|
|
120
145
|
|
|
121
146
|
// ─── Main Entry Point ────────────────────────────────────────────────────────
|
|
@@ -131,7 +156,9 @@ export async function applyAgentConfig(
|
|
|
131
156
|
): Promise<void> {
|
|
132
157
|
await applyModel(agent, pi, ctx);
|
|
133
158
|
applyThinking(agent, pi);
|
|
134
|
-
|
|
159
|
+
const before = pi.getAllTools().map((t) => t.name);
|
|
160
|
+
const active = applyTools(agent, pi);
|
|
161
|
+
warnIfHidingSubagent(agent, before, active, ctx.ui);
|
|
135
162
|
}
|
|
136
163
|
|
|
137
164
|
/**
|