@narumitw/pi-subagents 0.17.2 → 0.20.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/README.md CHANGED
@@ -67,7 +67,7 @@ Common controls:
67
67
 
68
68
  - `cwd` — run a job from a different working directory.
69
69
  - `timeoutMs` — set a hard subprocess timeout.
70
- - `thinkingLevel` — request `off`, `minimal`, `low`, `medium`, `high`, or `xhigh` thinking for the spawned Pi process.
70
+ - `thinkingLevel` — request `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max` thinking for the spawned Pi process.
71
71
 
72
72
  ## 🧭 Proactive use
73
73
 
@@ -381,7 +381,7 @@ Each subprocess has a hard timeout to avoid runaway workers.
381
381
  - Set `timeoutMs` on a task, chain step, or aggregator to override it locally.
382
382
  - If omitted, the default is `PI_SUBAGENT_TIMEOUT_MS`, or `600000` milliseconds (10 minutes) when unset.
383
383
 
384
- Set `thinkingLevel` to pass Pi's `--thinking <level>` to a subprocess. Supported values are `off`, `minimal`, `low`, `medium`, `high`, and `xhigh`.
384
+ Set `thinkingLevel` to pass Pi's `--thinking <level>` to a subprocess. Supported values are `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`.
385
385
 
386
386
  Thinking-level precedence is: task/chain step/aggregator `thinkingLevel` → top-level `thinkingLevel` → agent default from config or frontmatter → Pi subprocess default. Omit `thinkingLevel` to preserve existing behavior. Pi still owns model capability clamping, so unsupported thinking levels are handled by the spawned Pi process.
387
387
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narumitw/pi-subagents",
3
- "version": "0.17.2",
3
+ "version": "0.20.0",
4
4
  "description": "Pi extension for delegating work to specialized isolated subagents.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -39,11 +39,11 @@
39
39
  },
40
40
  "devDependencies": {
41
41
  "@biomejs/biome": "2.5.3",
42
- "@earendil-works/pi-agent-core": "0.80.6",
43
- "@earendil-works/pi-ai": "0.80.3",
44
- "@earendil-works/pi-coding-agent": "0.80.3",
45
- "@earendil-works/pi-tui": "0.80.3",
46
- "typescript": "6.0.3"
42
+ "@earendil-works/pi-agent-core": "0.80.10",
43
+ "@earendil-works/pi-ai": "0.80.10",
44
+ "@earendil-works/pi-coding-agent": "0.80.10",
45
+ "@earendil-works/pi-tui": "0.80.10",
46
+ "typescript": "7.0.2"
47
47
  },
48
48
  "repository": {
49
49
  "type": "git",
package/src/agents.ts CHANGED
@@ -4,10 +4,17 @@
4
4
 
5
5
  import * as fs from "node:fs";
6
6
  import * as path from "node:path";
7
- import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
8
7
  import { getAgentDir, parseFrontmatter } from "@earendil-works/pi-coding-agent";
9
8
 
10
- export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"] as const satisfies readonly ThinkingLevel[];
9
+ export const THINKING_LEVELS = [
10
+ "off",
11
+ "minimal",
12
+ "low",
13
+ "medium",
14
+ "high",
15
+ "xhigh",
16
+ "max",
17
+ ] as const;
11
18
 
12
19
  export type SubagentThinkingLevel = (typeof THINKING_LEVELS)[number];
13
20
 
package/src/config-ui.ts CHANGED
@@ -4,8 +4,8 @@ import {
4
4
  Container,
5
5
  Key,
6
6
  matchesKey,
7
- SelectList,
8
7
  type SelectItem,
8
+ SelectList,
9
9
  Spacer,
10
10
  Text,
11
11
  truncateToWidth,
@@ -20,7 +20,7 @@ import {
20
20
  uniqueToolNames,
21
21
  } from "./settings.js";
22
22
 
23
- class ToolToggleList {
23
+ export class ToolToggleList {
24
24
  private items: { name: string; selected: boolean }[];
25
25
  private cursor = 0;
26
26
  private cachedWidth?: number;
@@ -98,6 +98,7 @@ export function registerSubagentConfigCommand(pi: ExtensionAPI) {
98
98
  }
99
99
 
100
100
  // Loop: agent selection → tool toggle (Esc in tools returns here)
101
+ let selectedAgentIndex = 0;
101
102
  while (true) {
102
103
  // Step 1: pick an agent to configure
103
104
  const agentItems: SelectItem[] = agents.map((a) => {
@@ -133,7 +134,20 @@ export function registerSubagentConfigCommand(pi: ExtensionAPI) {
133
134
  scrollInfo: (t: string) => theme.fg("dim", t),
134
135
  noMatch: (t: string) => theme.fg("warning", t),
135
136
  });
136
- selectList.onSelect = (item) => done(item.value);
137
+ selectList.setSelectedIndex(selectedAgentIndex);
138
+ selectList.onSelectionChange = (item) => {
139
+ selectedAgentIndex = Math.max(
140
+ 0,
141
+ agentItems.findIndex((candidate) => candidate.value === item.value),
142
+ );
143
+ };
144
+ selectList.onSelect = (item) => {
145
+ selectedAgentIndex = Math.max(
146
+ 0,
147
+ agentItems.findIndex((candidate) => candidate.value === item.value),
148
+ );
149
+ done(item.value);
150
+ };
137
151
  selectList.onCancel = () => done(null);
138
152
  container.addChild(selectList);
139
153
  container.addChild(
@@ -193,7 +207,11 @@ export function registerSubagentConfigCommand(pi: ExtensionAPI) {
193
207
  );
194
208
  container.addChild(new Spacer(1));
195
209
  container.addChild(
196
- new Text(theme.fg("muted", "Toggle tools with Enter/Space. S to save, Esc to cancel."), 1, 0),
210
+ new Text(
211
+ theme.fg("muted", "Toggle tools with Enter/Space. S to save, Esc to cancel."),
212
+ 1,
213
+ 0,
214
+ ),
197
215
  );
198
216
  container.addChild(new Spacer(1));
199
217
 
@@ -206,7 +224,11 @@ export function registerSubagentConfigCommand(pi: ExtensionAPI) {
206
224
 
207
225
  container.addChild(new Spacer(1));
208
226
  container.addChild(
209
- new Text(theme.fg("dim", "↑↓ navigate · enter/space toggle · S save · esc cancel"), 1, 0),
227
+ new Text(
228
+ theme.fg("dim", "↑↓ navigate · enter/space toggle · S save · esc cancel"),
229
+ 1,
230
+ 0,
231
+ ),
210
232
  );
211
233
  container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
212
234
 
@@ -1,3 +1,4 @@
1
+ import { join } from "node:path";
1
2
  import type { Api, Model } from "@earendil-works/pi-ai";
2
3
  import {
3
4
  createAgentSession,
@@ -7,7 +8,12 @@ import {
7
8
  SessionManager,
8
9
  SettingsManager,
9
10
  } from "@earendil-works/pi-coding-agent";
10
- import { type AgentConfig, discoverAgents, type SubagentThinkingLevel } from "./agents.js";
11
+ import {
12
+ type AgentConfig,
13
+ discoverAgents,
14
+ isThinkingLevel,
15
+ type SubagentThinkingLevel,
16
+ } from "./agents.js";
11
17
  import { redactPrivateText } from "./context.js";
12
18
  import { resolveDefaultSubagentTimeoutMs } from "./execution.js";
13
19
  import { DEFAULT_MAX_CONTEXT_BYTES, DEFAULT_MAX_OUTPUT_BYTES, truncateUtf8 } from "./limits.js";
@@ -18,6 +24,25 @@ import type { SubagentTransport } from "./transport.js";
18
24
  const BUILT_IN_TOOL_NAMES = new Set(["read", "bash", "edit", "write", "grep", "find", "ls"]);
19
25
  const DEFAULT_ABORT_GRACE_MS = 5_000;
20
26
 
27
+ interface ChildModelRuntime {
28
+ getModel(provider: string, modelId: string): Model<Api> | undefined;
29
+ registerProvider(provider: string, config: unknown): void;
30
+ registerNativeProvider?(provider: unknown): void;
31
+ setRuntimeApiKey(provider: string, apiKey: string): Promise<void> | void;
32
+ }
33
+
34
+ interface CodingAgentRuntimeModule {
35
+ ModelRuntime?: {
36
+ create(options?: { authPath?: string; modelsPath?: string | null }): Promise<ChildModelRuntime>;
37
+ };
38
+ }
39
+
40
+ interface RegisteredProviderRegistry {
41
+ getRegisteredProviderConfig?(provider: string): unknown;
42
+ getRegisteredProviderIds?(): readonly string[];
43
+ getRegisteredNativeProvider?(provider: string): unknown;
44
+ }
45
+
21
46
  export interface ParentRuntimeSnapshot {
22
47
  model: Model<Api> | undefined;
23
48
  thinkingLevel: SubagentThinkingLevel;
@@ -316,20 +341,31 @@ export async function createSdkChildSession(
316
341
  options.agent.agentScope === "project" || options.agent.agentScope === "both",
317
342
  );
318
343
  const resolved = await resolveChildModel(options);
344
+ const modelRuntime = await createChildModelRuntime(
345
+ options.modelRegistry,
346
+ resolved.model,
347
+ agentDir,
348
+ );
349
+ const model =
350
+ modelRuntime?.getModel(resolved.model.provider, resolved.model.id) ?? resolved.model;
319
351
  const sessionManager = SessionManager.inMemory(options.agent.cwd);
320
- seedChildSessionManager(sessionManager, options, resolved.model);
321
- const created = await createAgentSession({
352
+ seedChildSessionManager(sessionManager, options, model);
353
+ const sessionOptions: Record<string, unknown> = {
322
354
  cwd: options.agent.cwd,
323
355
  agentDir,
324
- model: resolved.model,
356
+ model,
325
357
  thinkingLevel: resolved.thinkingLevel,
326
- modelRegistry: options.modelRegistry,
327
358
  resourceLoader,
328
359
  settingsManager,
329
360
  sessionManager,
330
361
  tools: options.tools,
331
362
  noTools: options.tools?.length === 0 ? "all" : undefined,
332
- });
363
+ };
364
+ if (modelRuntime) sessionOptions.modelRuntime = modelRuntime;
365
+ else sessionOptions.modelRegistry = options.modelRegistry;
366
+ const created = await createAgentSession(
367
+ sessionOptions as NonNullable<Parameters<typeof createAgentSession>[0]>,
368
+ );
333
369
  const session = created.session;
334
370
  if (options.tools !== undefined) {
335
371
  const active = session.getActiveToolNames();
@@ -359,6 +395,42 @@ export async function createSdkChildSession(
359
395
  };
360
396
  }
361
397
 
398
+ async function createChildModelRuntime(
399
+ modelRegistry: ModelRegistry,
400
+ model: Model<Api>,
401
+ agentDir: string,
402
+ ): Promise<ChildModelRuntime | undefined> {
403
+ const codingAgentModule = (await import(
404
+ "@earendil-works/pi-coding-agent"
405
+ )) as unknown as CodingAgentRuntimeModule;
406
+ if (!codingAgentModule.ModelRuntime) return undefined;
407
+
408
+ const modelRuntime = await codingAgentModule.ModelRuntime.create({
409
+ authPath: join(agentDir, "auth.json"),
410
+ modelsPath: join(agentDir, "models.json"),
411
+ });
412
+ const registeredProviders = modelRegistry as unknown as RegisteredProviderRegistry;
413
+ copyRegisteredProviders(registeredProviders, modelRuntime);
414
+ const auth = await modelRegistry.getApiKeyAndHeaders(model);
415
+ if (auth.ok && auth.apiKey) await modelRuntime.setRuntimeApiKey(model.provider, auth.apiKey);
416
+ return modelRuntime;
417
+ }
418
+
419
+ export function copyRegisteredProviders(
420
+ registeredProviders: RegisteredProviderRegistry,
421
+ modelRuntime: ChildModelRuntime,
422
+ ): void {
423
+ for (const provider of registeredProviders.getRegisteredProviderIds?.() ?? []) {
424
+ const config = registeredProviders.getRegisteredProviderConfig?.(provider);
425
+ if (config !== undefined) {
426
+ modelRuntime.registerProvider(provider, config);
427
+ continue;
428
+ }
429
+ const nativeProvider = registeredProviders.getRegisteredNativeProvider?.(provider);
430
+ if (nativeProvider) modelRuntime.registerNativeProvider?.(nativeProvider);
431
+ }
432
+ }
433
+
362
434
  export async function resolveChildModel(options: ChildSessionCreateOptions): Promise<{
363
435
  model: Model<Api>;
364
436
  thinkingLevel: SubagentThinkingLevel;
@@ -390,14 +462,7 @@ function parseModelRequest(value: string): {
390
462
  const separator = requested.lastIndexOf(":");
391
463
  if (separator > 0) {
392
464
  const suffix = requested.slice(separator + 1);
393
- if (
394
- suffix === "off" ||
395
- suffix === "minimal" ||
396
- suffix === "low" ||
397
- suffix === "medium" ||
398
- suffix === "high" ||
399
- suffix === "xhigh"
400
- ) {
465
+ if (isThinkingLevel(suffix)) {
401
466
  return { model: requested.slice(0, separator), thinkingLevel: suffix };
402
467
  }
403
468
  }
package/src/params.ts CHANGED
@@ -9,7 +9,8 @@ const TimeoutMs = Type.Number({
9
9
  });
10
10
 
11
11
  const ThinkingLevelSchema = StringEnum(THINKING_LEVELS, {
12
- description: "Pi thinking level for the subagent process: off, minimal, low, medium, high, or xhigh.",
12
+ description:
13
+ "Pi thinking level for the subagent process: off, minimal, low, medium, high, xhigh, or max.",
13
14
  });
14
15
 
15
16
  const TaskItem = Type.Object({
package/src/stateful.ts CHANGED
@@ -3,7 +3,7 @@ import * as path from "node:path";
3
3
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
4
4
  import { StringEnum } from "@earendil-works/pi-ai";
5
5
  import { Type } from "typebox";
6
- import { discoverAgents, type AgentScope } from "./agents.js";
6
+ import { discoverAgents, type AgentScope, isThinkingLevel } from "./agents.js";
7
7
  import { buildContextSnapshot, type ContextMode, redactPrivateText } from "./context.js";
8
8
  import { assertSubagentDepthAllowed } from "./execution.js";
9
9
  import { DEFAULT_MAX_CONTEXT_BYTES, truncateUtf8 } from "./limits.js";
@@ -830,10 +830,7 @@ export function resolveStatefulTransportKind(
830
830
  }
831
831
 
832
832
  function normalizeRuntimeThinkingLevel(value: string): ParentRuntimeSnapshot["thinkingLevel"] {
833
- if (value === "off" || value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh") {
834
- return value;
835
- }
836
- return value === "max" ? "xhigh" : "off";
833
+ return isThinkingLevel(value) ? value : "off";
837
834
  }
838
835
 
839
836
  export {