@arhen/pi-core-subagent 1.3.57 → 1.3.58

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arhen/pi-core-subagent",
3
- "version": "1.3.57",
3
+ "version": "1.3.58",
4
4
  "type": "module",
5
5
  "description": "pi extension: fast in-process subagents with a dependency-graph scheduler (needs edges gate tasks and carry upstream output into dependent prompts), plus background runs, intercom and agent-to-agent mailbox. Leader defines agents inline.",
6
6
  "license": "MIT",
package/src/format.ts CHANGED
@@ -111,10 +111,17 @@ export function isTalking(task: TaskSnapshot): boolean {
111
111
  return TALK_TOOLS.some((t) => a.startsWith(t));
112
112
  }
113
113
  let pulsePhase = 0;
114
+ const NOTE_CAP = 240;
115
+ function noteSnippet(note: string): string {
116
+ return note.length > NOTE_CAP ? `${note.slice(0, NOTE_CAP)}…` : note;
117
+ }
114
118
  export function compactLines(run: RunSnapshot): string[] {
115
119
  const lines: string[] = [];
116
120
  for (const task of run.tasks.slice(0, MAX_TASKS)) {
117
121
  lines.push(taskLine(task));
122
+ // a swapped model or toolset changes what the task did, so it cannot stay out of the status view
123
+ if (task.modelNote) lines.push(` ↳ Model: ${noteSnippet(task.modelNote)}`);
124
+ if (task.toolsNote) lines.push(` ↳ Tools: ${noteSnippet(task.toolsNote)}`);
118
125
  }
119
126
  if (run.tasks.length > MAX_TASKS) lines.push(`… +${run.tasks.length - MAX_TASKS} more`);
120
127
  return lines;
package/src/index.ts CHANGED
@@ -299,7 +299,8 @@ export default function (pi: ExtensionAPI) {
299
299
  ? `\nApplied IN PLACE (no branch) — ${t.isolationReason ?? "worktree unavailable"}. The changes are already in your working tree.`
300
300
  : "";
301
301
  const wtErr = t.worktreeError ? `\nWorktree: ${t.worktreeError}` : "";
302
- return `\n## ${t.agent} ${statusIcon(t.status)}\nGoal: ${truncateText(t.task, 300)}\n${t.error ? `Error: ${t.error}` : t.finalText || "(no output yet)"}${wt}${wtErr}\n${formatUsage(t.usage)}`;
302
+ const modelNote = t.modelNote ? `\nModel: ${t.modelNote}` : "";
303
+ return `\n## ${t.agent} ${statusIcon(t.status)}\nGoal: ${truncateText(t.task, 300)}\n${t.error ? `Error: ${t.error}` : t.finalText || "(no output yet)"}${wt}${wtErr}${modelNote}\n${formatUsage(t.usage)}`;
303
304
  }),
304
305
  ].join("\n");
305
306
  return { content: [{ type: "text", text: truncateText(text) }], details: { run: cloneRun(run) } };
package/src/manager.ts CHANGED
@@ -2,7 +2,13 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, rmSync
2
2
  import { rename, rm, writeFile } from "node:fs/promises";
3
3
  import { basename, dirname, join, relative, sep } from "node:path";
4
4
  import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
5
- import type { Api, AssistantMessage, Model } from "@earendil-works/pi-ai";
5
+ import {
6
+ type Api,
7
+ type AssistantMessage,
8
+ clampThinkingLevel,
9
+ getSupportedThinkingLevels,
10
+ type Model,
11
+ } from "@earendil-works/pi-ai";
6
12
  import {
7
13
  type AgentSessionEvent,
8
14
  createAgentSession,
@@ -160,21 +166,19 @@ export function resolveChildModel(ctx: ExtensionContext, explicit: string | unde
160
166
  const PROBE_THINKING_LEVELS: ThinkingLevel[] = ["low", "minimal", "medium", "high", "xhigh", "max"];
161
167
 
162
168
  /**
163
- * Thinking level for the usability probe: the one the task asked for, else the cheapest
164
- * the model accepts. Providers that require adaptive thinking reject a probe without one
165
- * (9router claude models answer `thinking.type.disabled is not supported`), which used to
166
- * make every preflight fail and fall back to the session model.
169
+ * Thinking level for the usability probe: exactly what the child session will send — the clamped
170
+ * requested level, or the cheapest the model accepts when none was requested. Probing without a
171
+ * level makes adaptive-thinking providers reject the request (9router claude models answer
172
+ * "thinking.type.disabled is not supported"), which used to make every preflight fail and fall
173
+ * back to the session model.
167
174
  */
168
175
  function probeThinking(model: Model<Api>, thinking?: string): ThinkingLevel | undefined {
169
- if (thinking && thinking !== "off") return thinking as ThinkingLevel;
170
176
  if (!model.reasoning) return undefined;
171
- const map = model.thinkingLevelMap;
172
- if (!map) return "low";
173
- for (const level of PROBE_THINKING_LEVELS) {
174
- const mapped = map[level as keyof typeof map];
175
- if (mapped !== null && mapped !== undefined) return level;
176
- }
177
- return undefined;
177
+ const supported = getSupportedThinkingLevels(model);
178
+ if (thinking && thinking !== "off") return clampThinkingLevel(model, thinking as ThinkingLevel);
179
+ // the child clamps an unsupported "off" up to its cheapest level, so probe that instead
180
+ if (thinking === "off") return supported.includes("off") ? undefined : supported[0];
181
+ return PROBE_THINKING_LEVELS.find((level) => supported.includes(level));
178
182
  }
179
183
 
180
184
  async function probeModel(
@@ -325,10 +329,10 @@ export class SubagentManager {
325
329
  return false;
326
330
  }
327
331
 
328
- clearWidget(ctx: ExtensionContext): void {
332
+ clearWidget(ctx?: ExtensionContext): void {
329
333
  this.widgetRuns = [];
330
334
  this.widgetTui = null;
331
- if (ctx.hasUI) {
335
+ if (ctx?.hasUI) {
332
336
  try {
333
337
  ctx.ui.setWidget("subagents", undefined);
334
338
  } catch {}
@@ -506,8 +510,14 @@ export class SubagentManager {
506
510
  }
507
511
 
508
512
  private widgetTui: TUI | null = null;
513
+
514
+ /** Settled runs are history (subagent_status/result still reach them) — the widget shows live work only. */
515
+ private pruneSettledWidget(): void {
516
+ this.widgetRuns = this.widgetRuns.filter((r) => !TERMINAL.includes(r.status));
517
+ }
509
518
  private upsertWidgetRun(run: RunSnapshot | undefined): void {
510
- if (!run) return;
519
+ this.pruneSettledWidget();
520
+ if (!run || TERMINAL.includes(run.status)) return;
511
521
  const idx = this.widgetRuns.findIndex((r) => r.id === run.id);
512
522
  if (idx >= 0) this.widgetRuns[idx] = run;
513
523
  else this.widgetRuns.push(run);
@@ -547,12 +557,17 @@ export class SubagentManager {
547
557
  this.widgetTimers.delete(run.id);
548
558
  }
549
559
  }
550
- if (!run || this.widgetRuns.length === 0) return;
551
- if (ctx?.hasUI) {
552
- this.ensureWidget(ctx);
553
- this.widgetTui?.requestRender();
560
+ this.pruneSettledWidget();
561
+ if (this.widgetRuns.length === 0) {
562
+ if (this.widgetTui) this.clearWidget(ctx);
563
+ } else {
564
+ if (ctx?.hasUI) {
565
+ this.ensureWidget(ctx);
566
+ this.widgetTui?.requestRender();
567
+ }
568
+ this.maybePulse(ctx);
554
569
  }
555
- this.maybePulse(ctx);
570
+ if (!run) return;
556
571
 
557
572
  onUpdate?.({
558
573
  content: [