@henryqw/pi-subagent 2.3.5 → 2.5.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 CHANGED
@@ -9,7 +9,7 @@ Provide validated user Roles, shared task-model Pi launch policy, generic manage
9
9
  - **Main**: Pi session delegating work.
10
10
  - **Subagent**: isolated Pi child process handling one task.
11
11
  - **Role**: user-owned Markdown profile defining name, description, system instructions, optional exact tool allowlist, extensions, and Skill names.
12
- - **Model Class**: `fast`, `balanced`, or `frontier`, assigned in shared task-model settings or overridden by Main from task complexity.
12
+ - **Model Class**: `fast`, `balanced`, `frontier`, or `fav`, assigned in shared task-model settings or overridden by Main from task complexity.
13
13
  - **Route**: configured model and thinking-level pair selected from a shared Model Class profile; the primary route precedes its optional fallback.
14
14
  - **Delegated Task**: one bounded work request sent from Main to one Role.
15
15
  - **Pi Launch**: reusable `{env,args}` policy for one Role, resolved model route, explicit caller resources, and project trust.
package/README.md CHANGED
@@ -14,16 +14,18 @@ pi install npm:@henryqw/pi-subagent
14
14
 
15
15
  | Package | Why |
16
16
  | --- | --- |
17
- | `@henryqw/pi-task-models` | Required. Shared `fast` / `balanced` / `frontier` routes. |
17
+ | `@henryqw/pi-task-models` | Required. Shared `fast` / `balanced` / `frontier` / `fav` routes. |
18
18
  | `@henryqw/pi-multi-codex` | Required. Child uses Main's active Codex slot. |
19
19
 
20
20
  ## Use
21
21
 
22
22
  | Surface | Type | Purpose |
23
23
  | --- | --- | --- |
24
- | `delegate_task` | tool | Start one isolated child for `role`, `task`, and optional `modelClass`. |
24
+ | `delegate_task` | tool | Start one isolated child for `role`, `task`, and optional `model` or `modelClass`. |
25
25
 
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.
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.
27
+
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.
27
29
 
28
30
  Main splits broad work into independent bounded tasks and keeps integration and cross-cutting decisions. Each `task` states its objective, exact scope and exclusions, relevant context and constraints, expected deliverable, and validation. Each call uses the least capable `modelClass` that can reliably complete its task. Independent sibling calls can run concurrently; concurrent edit tasks must own non-overlapping files. Up to four active ephemeral `delegate_task` children run per Main; excess calls wait FIFO. Queued calls do not start a child or consume child timeout. Managed Herdr workers are unaffected.
29
31
 
@@ -4,7 +4,15 @@ import { basename } from "node:path";
4
4
  import { StringEnum } from "@earendil-works/pi-ai";
5
5
  import { type ExtensionAPI, type ExtensionContext, type Theme } from "@earendil-works/pi-coding-agent";
6
6
  import { type Component, truncateToWidth, type TUI, visibleWidth } from "@earendil-works/pi-tui";
7
- import { modelReference, PROFILE_NAMES, type ProfileName } from "@henryqw/pi-task-models";
7
+ import {
8
+ availableTaskModels,
9
+ modelReference,
10
+ PROFILE_NAMES,
11
+ type ProfileName,
12
+ resolveAvailableModel,
13
+ type ResolvedTaskRoute,
14
+ taskThinkingLevels,
15
+ } from "@henryqw/pi-task-models";
8
16
  import { Type } from "typebox";
9
17
  import { createRoleLaunch, isProfileName, loadRoles, resolveRoleLaunch, resolveTaskRoute } from "@henryqw/pi-subagent";
10
18
 
@@ -415,11 +423,24 @@ const Parameters = Type.Object({
415
423
  task: Type.String({
416
424
  description: "Bounded task packet: objective; exact scope and exclusions; relevant context and constraints; expected deliverable; validation. Never the whole parent request.",
417
425
  }),
426
+ model: Type.Optional(Type.String({
427
+ description: "Designated model as provider/modelId; overrides modelClass. Unknown references reject with the list of available models.",
428
+ })),
418
429
  modelClass: Type.Optional(StringEnum(MODEL_CLASSES, {
419
- 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. Defaults to the shared pi-subagent/delegateTask assignment.",
430
+ 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.",
420
431
  })),
421
432
  });
422
433
 
434
+ function resolveDesignatedRoute(ctx: ExtensionContext, reference: string): ResolvedTaskRoute {
435
+ const models = availableTaskModels(ctx);
436
+ const model = resolveAvailableModel(models, reference, ctx.model?.provider);
437
+ if (!model) {
438
+ throw new Error(`Unknown delegate_task model: ${reference}. Available models: ${models.map((candidate) => modelReference(candidate)).join(", ") || "none"}.`);
439
+ }
440
+ const levels = taskThinkingLevels(ctx, model);
441
+ return { model, thinkingLevel: levels.includes("medium") ? "medium" : levels.at(-1)! };
442
+ }
443
+
423
444
  const roleSummary = (): string => {
424
445
  try {
425
446
  const roles = loadRoles();
@@ -553,12 +574,12 @@ export default function subagentExtension(
553
574
  pi.registerTool({
554
575
  name: "delegate_task",
555
576
  label: "Subagent",
556
- description: `Delegate one bounded, independently executable task to one isolated Pi Subagent. Roles: ${roleSummary()}. Choose fast for narrow work, balanced for normal work, or frontier for ambiguous and high-risk work; omit modelClass to use shared task-model settings.`,
577
+ description: `Delegate one bounded, independently executable task to one isolated Pi Subagent. Roles: ${roleSummary()}. Choose fast for narrow work, balanced for normal work, frontier for ambiguous and high-risk work, or fav when the user asks for their favorite model; omit modelClass to use shared task-model settings. When the user designates a specific model, pass it as provider/modelId in model.`,
557
578
  promptSnippet: "Delegate one bounded, independently executable task to an isolated role",
558
579
  promptGuidelines: [
559
580
  "Before calling delegate_task, split broad work into the smallest independent bounded tasks; keep integration and cross-cutting decisions in Main.",
560
581
  "Each delegate_task task must state its objective, exact scope and exclusions, relevant context and constraints, expected deliverable, and validation; never pass the parent request unchanged.",
561
- "For each delegate_task call, choose the least capable modelClass that can reliably complete the task: fast for narrow work, balanced for normal work, and frontier only for ambiguous, cross-cutting, or high-risk work.",
582
+ "Use fav when the user explicitly asks for their favorite model. Otherwise, choose the least capable modelClass that can reliably complete the task: fast for narrow work, balanced for normal work, and frontier only for ambiguous, cross-cutting, or high-risk work.",
562
583
  "Submit independent delegate_task calls together for parallel execution. Parallel edits must own non-overlapping files; otherwise sequence them. Use the minimum number of Subagents needed.",
563
584
  ],
564
585
  parameters: Parameters,
@@ -571,11 +592,13 @@ export default function subagentExtension(
571
592
  }
572
593
 
573
594
  if (params.modelClass !== undefined && !isModelClass(params.modelClass)) {
574
- throw new Error("delegate_task modelClass must be fast, balanced, or frontier.");
595
+ throw new Error("delegate_task modelClass must be fast, balanced, frontier, or fav.");
575
596
  }
576
- const launch = params.modelClass === undefined
577
- ? resolveRoleLaunch(pi, ctx, { role, taskId: SUBAGENT_TASK })
578
- : createRoleLaunch(pi, ctx, { role, route: resolveTaskRoute(ctx, params.modelClass) });
597
+ const launch = params.model !== undefined
598
+ ? createRoleLaunch(pi, ctx, { role, route: resolveDesignatedRoute(ctx, cleanText(params.model, "model", "delegate_task")) })
599
+ : params.modelClass === undefined
600
+ ? resolveRoleLaunch(pi, ctx, { role, taskId: SUBAGENT_TASK })
601
+ : createRoleLaunch(pi, ctx, { role, route: resolveTaskRoute(ctx, params.modelClass) });
579
602
  const modelReferenceValue = modelReference(launch.model);
580
603
  const thinkingLevel = launch.thinkingLevel;
581
604
  if (launch.missingSkills.length) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henryqw/pi-subagent",
3
- "version": "2.3.5",
3
+ "version": "2.5.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.3.0"
62
+ "@henryqw/pi-task-models": "^0.4.0"
63
63
  }
64
64
  }