@henryqw/pi-subagent 2.9.5 → 2.10.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.
- package/CONTEXT.md +1 -1
- package/README.md +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -5
- package/extensions/subagent.ts +29 -9
- package/package.json +2 -2
package/CONTEXT.md
CHANGED
|
@@ -21,7 +21,7 @@ Provide validated user Roles, shared task-model Pi launch policy, generic manage
|
|
|
21
21
|
- Up to five active ephemeral `delegate_task` children run per Main by default, configurable via `maxSubagents` in `~/.pi/agent/config/pi-subagent.json` or the `PI_SUBAGENT_MAX_SUBAGENTS` environment variable; excess calls wait FIFO. Queued calls do not start a child or consume child timeout. Managed Herdr workers are unaffected.
|
|
22
22
|
- 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.
|
|
23
23
|
- Role Skill names resolve through Main's effective Pi Skill registry; unavailable names warn and skip without blocking delegation.
|
|
24
|
-
- 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
|
+
- Main selects Role and may override Model Class and thinking level per task; omitted class uses shared `pi-subagent/delegateTask` assignment, initially `balanced`. Library callers select Role plus their own shared task ID.
|
|
25
25
|
- 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.
|
|
26
26
|
- Role config lives only in user `config/pi-subagent` directory; model routes live in shared `config/pi-task-models.json`; repository roles do not execute.
|
|
27
27
|
- Numbered Codex routes prefer Main's active account slot and explicitly load the multi-Codex child extension.
|
package/README.md
CHANGED
|
@@ -21,9 +21,9 @@ pi install npm:@henryqw/pi-subagent
|
|
|
21
21
|
|
|
22
22
|
| Surface | Type | Purpose |
|
|
23
23
|
| --- | --- | --- |
|
|
24
|
-
| `delegate_task` | tool | Start one isolated child for `role`, `task`, and optional `model` or `
|
|
24
|
+
| `delegate_task` | tool | Start one isolated child for `role`, `task`, and optional `model`, `modelClass`, or `thinking`. |
|
|
25
25
|
|
|
26
|
-
An explicit `model` (`provider/modelId`) overrides `modelClass` and resolves against the currently available text models; an unknown reference rejects with the list of available models. Thinking level defaults to `medium` when supported, otherwise the highest supported level.
|
|
26
|
+
An explicit `model` (`provider/modelId`) overrides `modelClass` and resolves against the currently available text models; an unknown reference rejects with the list of available models. Thinking level defaults to `medium` when supported, otherwise the highest supported level; a designated model with no usable level rejects before launch. An explicit `thinking` level participates in route resolution itself: routes that cannot honor it are skipped so fallback routes get considered, and delegation rejects when no route supports the level.
|
|
27
27
|
|
|
28
28
|
`modelClass` is `fast`, `balanced`, `frontier`, or `fav`. 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.
|
|
29
29
|
|
package/dist/index.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ export interface ResolvedRoleSkills {
|
|
|
35
35
|
}
|
|
36
36
|
export declare const isProfileName: (value: unknown) => value is ProfileName;
|
|
37
37
|
export declare function loadRoles(agentDir?: string): Role[];
|
|
38
|
-
export declare function resolveTaskRoute(ctx: ExtensionContext, profileName: ProfileName, agentDir?: string): ResolvedTaskRoute;
|
|
38
|
+
export declare function resolveTaskRoute(ctx: ExtensionContext, profileName: ProfileName, agentDir?: string, thinking?: ThinkingLevel): ResolvedTaskRoute;
|
|
39
39
|
export declare function resolveRoleSkills(pi: Pick<ExtensionAPI, "getCommands">, role: Role): ResolvedRoleSkills;
|
|
40
40
|
export declare function createRoleLaunch(pi: Pick<ExtensionAPI, "getCommands">, ctx: Pick<ExtensionContext, "isProjectTrusted">, input: CreateRoleLaunchInput): ResolvedRoleLaunch;
|
|
41
41
|
export declare function resolveRoleLaunch(pi: Pick<ExtensionAPI, "getCommands">, ctx: ExtensionContext, input: ResolveRoleLaunchInput): ResolvedRoleLaunch;
|
package/dist/index.js
CHANGED
|
@@ -81,7 +81,7 @@ export function loadRoles(agentDir = getAgentDir()) {
|
|
|
81
81
|
}
|
|
82
82
|
return roles;
|
|
83
83
|
}
|
|
84
|
-
export function resolveTaskRoute(ctx, profileName, agentDir = getAgentDir()) {
|
|
84
|
+
export function resolveTaskRoute(ctx, profileName, agentDir = getAgentDir(), thinking) {
|
|
85
85
|
let config;
|
|
86
86
|
try {
|
|
87
87
|
config = readTaskModelsConfig(agentDir);
|
|
@@ -89,17 +89,17 @@ export function resolveTaskRoute(ctx, profileName, agentDir = getAgentDir()) {
|
|
|
89
89
|
catch {
|
|
90
90
|
throw new Error("Couldn't read task model config. Run /task-models.");
|
|
91
91
|
}
|
|
92
|
-
return resolveConfiguredRoute(ctx, profileName, config.profiles[profileName], agentDir);
|
|
92
|
+
return resolveConfiguredRoute(ctx, profileName, config.profiles[profileName], agentDir, thinking);
|
|
93
93
|
}
|
|
94
|
-
function resolveConfiguredRoute(ctx, profileName, profile, agentDir = getAgentDir()) {
|
|
94
|
+
function resolveConfiguredRoute(ctx, profileName, profile, agentDir = getAgentDir(), thinking) {
|
|
95
95
|
if (!profile)
|
|
96
96
|
throw new Error(`No ${profileName} task model profile is configured. Run /task-models.`);
|
|
97
97
|
for (const route of orderedProfileRoutes(profile)) {
|
|
98
|
-
const resolved = resolveTaskModelRoute(ctx, route, agentDir);
|
|
98
|
+
const resolved = resolveTaskModelRoute(ctx, route, agentDir, thinking);
|
|
99
99
|
if (resolved)
|
|
100
100
|
return resolved;
|
|
101
101
|
}
|
|
102
|
-
throw new Error(`No usable ${profileName} task model route. Run /task-models.`);
|
|
102
|
+
throw new Error(`No usable ${profileName} task model route${thinking ? ` supporting thinking ${thinking}` : ""}. Run /task-models.`);
|
|
103
103
|
}
|
|
104
104
|
export function resolveRoleSkills(pi, role) {
|
|
105
105
|
const skills = new Map(pi.getCommands()
|
package/extensions/subagent.ts
CHANGED
|
@@ -7,15 +7,18 @@ import { type Component, truncateToWidth, type TUI, visibleWidth } from "@earend
|
|
|
7
7
|
import { readSubagentConfig, type SubagentTimeoutConfig } from "./config.ts";
|
|
8
8
|
import {
|
|
9
9
|
availableTaskModels,
|
|
10
|
+
THINKING_LEVELS,
|
|
11
|
+
type ThinkingLevel,
|
|
10
12
|
modelReference,
|
|
11
13
|
PROFILE_NAMES,
|
|
12
14
|
type ProfileName,
|
|
13
15
|
resolveAvailableModel,
|
|
16
|
+
resolveConfiguredTaskRoute,
|
|
14
17
|
type ResolvedTaskRoute,
|
|
15
18
|
taskThinkingLevels,
|
|
16
19
|
} from "@henryqw/pi-task-models";
|
|
17
20
|
import { Type } from "typebox";
|
|
18
|
-
import { createRoleLaunch, isProfileName, loadRoles,
|
|
21
|
+
import { createRoleLaunch, isProfileName, loadRoles, resolveTaskRoute } from "@henryqw/pi-subagent";
|
|
19
22
|
|
|
20
23
|
const MODEL_CLASSES = PROFILE_NAMES;
|
|
21
24
|
const SUBAGENT_TASK = "pi-subagent/delegateTask";
|
|
@@ -440,17 +443,29 @@ const Parameters = Type.Object({
|
|
|
440
443
|
description: "Classify task complexity: fast for narrow lookups or mechanical edits; balanced for normal bounded work; frontier for ambiguous, cross-cutting, or high-risk reasoning; fav for the user's favorite model when they ask for it. Defaults to the shared pi-subagent/delegateTask assignment.",
|
|
441
444
|
})),
|
|
442
445
|
background: Type.Optional(Type.Boolean({
|
|
443
|
-
description: "Run without blocking: returns a task ID immediately and delivers the outcome as a message when the Subagent settles. Prefer blocking delegation whenever the parent needs the result to continue.",
|
|
446
|
+
description: "Run without blocking: returns a task ID immediately and delivers the outcome as a message when the Subagent settles; the result cannot be waited on. Set only when the user explicitly asks for non-blocking delegation. Prefer blocking delegation whenever the parent needs the result to continue.",
|
|
447
|
+
})),
|
|
448
|
+
thinking: Type.Optional(StringEnum(THINKING_LEVELS, {
|
|
449
|
+
description: "Override the resolved route's thinking level (e.g. when the user asks for deeper or lighter reasoning). Must be supported by the resolved model.",
|
|
444
450
|
})),
|
|
445
451
|
});
|
|
446
452
|
|
|
447
|
-
function resolveDesignatedRoute(ctx: ExtensionContext, reference: string): ResolvedTaskRoute {
|
|
453
|
+
function resolveDesignatedRoute(ctx: ExtensionContext, reference: string, thinking?: ThinkingLevel): ResolvedTaskRoute {
|
|
448
454
|
const models = availableTaskModels(ctx);
|
|
449
455
|
const model = resolveAvailableModel(models, reference, ctx.model?.provider);
|
|
450
456
|
if (!model) {
|
|
451
457
|
throw new Error(`Unknown delegate_task model: ${reference}. Available models: ${models.map((candidate) => modelReference(candidate)).join(", ") || "none"}.`);
|
|
452
458
|
}
|
|
453
459
|
const levels = taskThinkingLevels(ctx, model);
|
|
460
|
+
if (thinking !== undefined) {
|
|
461
|
+
if (!levels.includes(thinking)) {
|
|
462
|
+
throw new Error(`delegate_task thinking ${thinking} is not usable for ${modelReference(model)} in this session. Usable levels here: ${levels.join(", ") || "none"}.`);
|
|
463
|
+
}
|
|
464
|
+
return { model, thinkingLevel: thinking };
|
|
465
|
+
}
|
|
466
|
+
if (!levels.length) {
|
|
467
|
+
throw new Error(`${modelReference(model)} has no usable thinking level in this session; pick another model or adjust the scoped thinking pin.`);
|
|
468
|
+
}
|
|
454
469
|
return { model, thinkingLevel: levels.includes("medium") ? "medium" : levels.at(-1)! };
|
|
455
470
|
}
|
|
456
471
|
|
|
@@ -660,7 +675,7 @@ export default function subagentExtension(
|
|
|
660
675
|
pi.registerTool({
|
|
661
676
|
name: "delegate_task",
|
|
662
677
|
label: "Subagent",
|
|
663
|
-
description: `Delegate one bounded, independently executable task to one isolated Pi Subagent. Roles: ${roleSummary()}
|
|
678
|
+
description: `Delegate one bounded, independently executable task to one isolated Pi Subagent. Roles: ${roleSummary()}.`,
|
|
664
679
|
promptSnippet: "Delegate one bounded, independently executable task to an isolated role",
|
|
665
680
|
promptGuidelines: [
|
|
666
681
|
"Before calling delegate_task, split broad work into the smallest independent bounded tasks; keep integration and cross-cutting decisions in Main.",
|
|
@@ -685,11 +700,16 @@ export default function subagentExtension(
|
|
|
685
700
|
// the cap must pick up model or Codex account changes that happened
|
|
686
701
|
// while it waited.
|
|
687
702
|
const launchCtx = () => latestCtx ?? ctx;
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
703
|
+
// The explicit thinking override participates in route resolution itself:
|
|
704
|
+
// routes that cannot honor it are skipped so fallback routes get considered.
|
|
705
|
+
const resolveLaunch = () => createRoleLaunch(pi, launchCtx(), {
|
|
706
|
+
role,
|
|
707
|
+
route: params.model !== undefined
|
|
708
|
+
? resolveDesignatedRoute(launchCtx(), cleanText(params.model, "model", "delegate_task"), params.thinking)
|
|
709
|
+
: params.modelClass === undefined
|
|
710
|
+
? resolveConfiguredTaskRoute(launchCtx(), SUBAGENT_TASK, undefined, params.thinking)
|
|
711
|
+
: resolveTaskRoute(launchCtx(), params.modelClass, undefined, params.thinking),
|
|
712
|
+
});
|
|
693
713
|
const notifyMissingSkills = (launch: ReturnType<typeof resolveLaunch>) => {
|
|
694
714
|
if (launch.missingSkills.length) {
|
|
695
715
|
ctx.ui.notify(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@henryqw/pi-subagent",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.0",
|
|
4
4
|
"description": "Delegate one task to an isolated Pi role with explicit extensions and skills.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -59,6 +59,6 @@
|
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"@henryqw/pi-herdr": "^0.1.1",
|
|
61
61
|
"@henryqw/pi-multi-codex": "^0.3.8",
|
|
62
|
-
"@henryqw/pi-task-models": "^0.
|
|
62
|
+
"@henryqw/pi-task-models": "^0.6.0"
|
|
63
63
|
}
|
|
64
64
|
}
|