@henryqw/pi-subagent 2.2.0 → 2.3.1
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/CONTEXT.md +1 -1
- package/README.md +5 -5
- package/dist/index.js +8 -9
- package/extensions/role-tools.ts +33 -0
- package/package.json +1 -1
package/CONTEXT.md
CHANGED
|
@@ -18,7 +18,7 @@ Provide validated user Roles, shared task-model Pi launch policy, generic manage
|
|
|
18
18
|
## Invariants
|
|
19
19
|
|
|
20
20
|
- One Delegated Task creates one ephemeral child process and no saved session.
|
|
21
|
-
- Ambient child extensions and Skills stay disabled; Role explicitly selects
|
|
21
|
+
- Ambient child extensions and Skills stay disabled; Role explicitly selects extension sources and named Skills. Pi loads Skills supplied by those extension packages or their resource discovery. Omitted Role tools use Pi's effective `defaultTools`; an explicit list sets base tools while loaded extension tools activate automatically.
|
|
22
22
|
- Role Skill names resolve through Main's effective Pi Skill registry; unavailable names warn and skip without blocking delegation.
|
|
23
23
|
- Main selects Role and may override Model Class per task; omitted class uses shared `pi-subagent/delegateTask` assignment, initially `balanced`. Library callers select Role plus their own shared task ID.
|
|
24
24
|
- The selected profile resolves primary then fallback only before launch when a route, model, or thinking level is unavailable. If neither route is usable, launch rejects with `Run /task-models`; a started child is never retried by this package.
|
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ pi install npm:@henryqw/pi-subagent
|
|
|
25
25
|
|
|
26
26
|
`modelClass` is `fast`, `balanced`, or `frontier`. Omitted class uses the shared `pi-subagent/delegateTask` assignment, which defaults to `balanced`. Primary route is resolved against current scoped text models; fallback is tried only before launch. If no route is usable, delegation rejects with `Run /task-models`. A started child is never retried.
|
|
27
27
|
|
|
28
|
-
Each call starts one isolated child (`pi --mode json -p --no-session`). Ambient extensions and
|
|
28
|
+
Each call starts one isolated child (`pi --mode json -p --no-session`). Ambient extensions and Skills are off. Role/caller extensions load; those packages' tools and Skills auto-load, plus any extra `skills` names. Child uses the delegated working directory and Main's project approval. Abort kills the child process group. Streaming output is capped at 50 KiB. Unused JSON event types are discarded before payload buffering; consumed or unclassifiable events above 1 MiB fail delegation.
|
|
29
29
|
|
|
30
30
|
TUI shows one row per Subagent with role, route, task, tokens, and elapsed time. Terminal rows drop after one second.
|
|
31
31
|
|
|
@@ -53,9 +53,9 @@ Do not edit files.
|
|
|
53
53
|
| --- | --- | --- |
|
|
54
54
|
| `name` | yes | Role selected by Main |
|
|
55
55
|
| `description` | yes | Tells Main when to use the role |
|
|
56
|
-
| `tools` | no | Omit for Pi
|
|
57
|
-
| `extensions` | no | Absolute/`~/` paths or package sources. Repository-relative paths are rejected. |
|
|
58
|
-
| `skills` | no |
|
|
56
|
+
| `tools` | no | Omit for Pi defaults; when present, base tools are listed and every loaded Role/caller extension tool is added automatically. `[]` leaves extension tools only. |
|
|
57
|
+
| `extensions` | no | Absolute/`~/` paths or package sources. Package-declared Skills and Pi `resources_discover` Skill paths load automatically. Repository-relative paths are rejected. |
|
|
58
|
+
| `skills` | no | Additional effective Pi Skill names, resolved from Main's registry |
|
|
59
59
|
| Markdown body | yes | Role system instructions |
|
|
60
60
|
|
|
61
61
|
Missing skills warn and skip; they do not block delegation. No repo-controlled `.pi/agents` roles. No package-local model picker.
|
|
@@ -86,7 +86,7 @@ const tab = await reconcileManagedSubagentTab(host, { cwd: worktree, launch, lab
|
|
|
86
86
|
await startManagedSubagent(host, agentName, tab.paneId, launch, { execute });
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
`resolveRoleLaunch` uses shared task assignment and effective Pi registries. Caller tools extend
|
|
89
|
+
`resolveRoleLaunch` uses shared task assignment and effective Pi registries. Caller tools extend Role base tools; Role and caller extension tools activate automatically. Omitted Role `tools` preserves Pi defaults. Generic host APIs contain no workflow prompts or durable state.
|
|
90
90
|
|
|
91
91
|
## Remove
|
|
92
92
|
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,8 @@ import { createHerdrClient, herdrCommandFailure, hasHerdrErrorCode } from "@henr
|
|
|
7
7
|
import { modelReference, orderedProfileRoutes, PROFILE_NAMES, readTaskModelsConfig, resolveConfiguredTaskRoute, resolveTaskModelRoute, } from "@henryqw/pi-task-models";
|
|
8
8
|
const CODEX_ALIAS = /^openai-codex-(?:[2-9]|[1-9]\d+)$/;
|
|
9
9
|
const MULTI_CODEX_EXTENSION = fileURLToPath(import.meta.resolve("@henryqw/pi-multi-codex/extensions/multi-codex.ts"));
|
|
10
|
+
const ROLE_TOOLS_EXTENSION = fileURLToPath(new URL("../extensions/role-tools.ts", import.meta.url));
|
|
11
|
+
const ROLE_TOOL_POLICY_FLAG = "pi-subagent-role-tools";
|
|
10
12
|
export const isProfileName = (value) => typeof value === "string" && PROFILE_NAMES.includes(value);
|
|
11
13
|
const cleanText = (value, field, source) => {
|
|
12
14
|
if (typeof value !== "string" || !value.trim() || value.includes("\0")) {
|
|
@@ -117,14 +119,15 @@ export function resolveRoleSkills(pi, role) {
|
|
|
117
119
|
export function createRoleLaunch(pi, ctx, input) {
|
|
118
120
|
const role = input.role;
|
|
119
121
|
const skills = resolveRoleSkills(pi, role);
|
|
122
|
+
const tools = role.tools === undefined
|
|
123
|
+
? undefined
|
|
124
|
+
: [...new Set([...role.tools, ...(input.tools ?? [])].map((tool) => cleanText(tool, "tool", `Role ${role.name}`)))];
|
|
120
125
|
const extensions = [
|
|
121
126
|
...role.extensions,
|
|
122
127
|
...(input.extensions ?? []),
|
|
123
128
|
...(CODEX_ALIAS.test(input.route.model.provider) ? [MULTI_CODEX_EXTENSION] : []),
|
|
129
|
+
...(tools === undefined ? [] : [ROLE_TOOLS_EXTENSION]),
|
|
124
130
|
].map((extension) => validateExtension(extension, `Role ${role.name}`));
|
|
125
|
-
const tools = role.tools === undefined
|
|
126
|
-
? undefined
|
|
127
|
-
: [...new Set([...role.tools, ...(input.tools ?? [])].map((tool) => cleanText(tool, "tool", `Role ${role.name}`)))];
|
|
128
131
|
const env = Object.fromEntries(Object.entries(input.env ?? {}).map(([key, value]) => {
|
|
129
132
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
|
|
130
133
|
throw new Error(`Invalid launch environment name: ${key}`);
|
|
@@ -137,12 +140,8 @@ export function createRoleLaunch(pi, ctx, input) {
|
|
|
137
140
|
args.push("--extension", extension);
|
|
138
141
|
for (const skill of skills.paths)
|
|
139
142
|
args.push("--skill", skill);
|
|
140
|
-
if (tools !== undefined)
|
|
141
|
-
|
|
142
|
-
args.push("--tools", tools.join(","));
|
|
143
|
-
else
|
|
144
|
-
args.push("--no-tools");
|
|
145
|
-
}
|
|
143
|
+
if (tools !== undefined)
|
|
144
|
+
args.push(`--${ROLE_TOOL_POLICY_FLAG}`, JSON.stringify(tools));
|
|
146
145
|
args.push("--model", modelReference(input.route.model));
|
|
147
146
|
if (input.route.thinkingLevel)
|
|
148
147
|
args.push("--thinking", input.route.thinkingLevel);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
export const ROLE_TOOL_POLICY_FLAG = "pi-subagent-role-tools";
|
|
4
|
+
|
|
5
|
+
function configuredTools(value: unknown): string[] | undefined {
|
|
6
|
+
if (value === undefined) return;
|
|
7
|
+
if (typeof value !== "string") throw new Error(`${ROLE_TOOL_POLICY_FLAG} must be JSON tool names.`);
|
|
8
|
+
let parsed: unknown;
|
|
9
|
+
try {
|
|
10
|
+
parsed = JSON.parse(value);
|
|
11
|
+
} catch {
|
|
12
|
+
throw new Error(`${ROLE_TOOL_POLICY_FLAG} must be JSON tool names.`);
|
|
13
|
+
}
|
|
14
|
+
if (!Array.isArray(parsed) || parsed.some((name) => typeof name !== "string" || !name.trim() || name.includes("\0"))) {
|
|
15
|
+
throw new Error(`${ROLE_TOOL_POLICY_FLAG} must be JSON tool names.`);
|
|
16
|
+
}
|
|
17
|
+
return [...new Set(parsed.map((name) => name.trim()))];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default function roleTools(pi: ExtensionAPI): void {
|
|
21
|
+
pi.registerFlag(ROLE_TOOL_POLICY_FLAG, {
|
|
22
|
+
description: "Internal Pi Subagent Role tool policy",
|
|
23
|
+
type: "string",
|
|
24
|
+
});
|
|
25
|
+
pi.on("session_start", () => {
|
|
26
|
+
const selected = configuredTools(pi.getFlag(ROLE_TOOL_POLICY_FLAG));
|
|
27
|
+
if (!selected) return;
|
|
28
|
+
const extensionTools = pi.getAllTools()
|
|
29
|
+
.filter((tool) => !["builtin", "sdk", "inline"].includes(tool.sourceInfo.source))
|
|
30
|
+
.map((tool) => tool.name);
|
|
31
|
+
pi.setActiveTools([...new Set([...selected, ...extensionTools])]);
|
|
32
|
+
});
|
|
33
|
+
}
|