pi-subagents 0.39.0 → 0.40.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/CHANGELOG.md +13 -0
- package/README.md +28 -2
- package/package.json +1 -1
- package/skills/pi-subagents/references/execution-controls.md +4 -0
- package/skills/pi-subagents/references/management-authoring-rpc.md +1 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +15 -2
- package/src/agents/agents.ts +22 -1
- package/src/intercom/result-intercom.ts +24 -2
- package/src/runs/background/result-watcher.ts +7 -2
- package/src/runs/background/subagent-runner.ts +17 -1
- package/src/runs/foreground/execution.ts +3 -0
- package/src/runs/foreground/subagent-executor.ts +3 -0
- package/src/shared/types.ts +7 -0
- package/src/slash/slash-commands.ts +1 -1
- package/src/tui/render.ts +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.40.0] - 2026-08-01
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Documented an optional recommended model-tiering setup in the README: fast workhorse, standard well-scoped, deep-but-bounded, and taste/intent tiers, with cross-provider `fallbackModels` guidance for usage-limit resilience.
|
|
9
|
+
- Added `description` to `subagents.agentOverrides` so deployments can replace the discovered description for builtin and custom agents in list output. Thanks to @chronoAP for #724.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- Refreshed the bundled `pi-subagents` skill for the 0.39 surface: Fleet inspector live controls (`s` steer, `D` stop), the recommended model-tiering recipe, `agentOverrides.description`, `projectRootResolution: "git-root"`, running-card live-detail/model badges, and the newer extension RPC capability projections (`fleetStatus`, `launchResolvedExtensions`, `runtimeAcknowledgedExtensions`, `(runId, index)` correlation). Corrected the stale README "inspection-only" fleet inspector wording.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- Grouped intercom results now report child process status separately from provenance-aware output availability, including salvage guidance when a failed process produced output. Thanks to @youlikemodernart for #727.
|
|
16
|
+
- Collapsed running foreground subagent rows now show the model and thinking level: single-result cards include the effective thinking suffix and parallel/chain rows show the per-child model badge, matching the async widget.
|
|
17
|
+
|
|
5
18
|
## [0.39.0] - 2026-08-01
|
|
6
19
|
|
|
7
20
|
### Added
|
package/README.md
CHANGED
|
@@ -155,6 +155,31 @@ For a persistent override, edit settings. This example pins the reviewer everywh
|
|
|
155
155
|
}
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
+
### Recommended model tiering (optional)
|
|
159
|
+
|
|
160
|
+
A setup that works well in practice is routing agents by task shape instead of running everything on one model. Four tiers:
|
|
161
|
+
|
|
162
|
+
1. **Fast workhorse** — the cheapest capable model at low thinking, for recon, lookups, and mechanical edits. Example: `openai-codex/gpt-5.6-luna:low` on `scout`.
|
|
163
|
+
2. **Standard well-scoped** — a mid-tier model at medium thinking, for most delegations: routine multi-file edits, focused reviews, straightforward implementation. Example: `openai-codex/gpt-5.6-terra:medium` on `worker`, `reviewer`, and a lightweight `delegate` agent.
|
|
164
|
+
3. **Deep but bounded** — a top reasoning model at high thinking, only for hard tasks that arrive with explicit goals and completion criteria. These models tend to loop on vague goals, so keep them off open-ended work. Example: `openai-codex/gpt-5.6-sol:high` on `planner` and oracle-style agents.
|
|
165
|
+
4. **Taste and intent** — a model that reads human intent well and makes judgment calls without looping, for ambiguous work: UX and design decisions, product tradeoffs, planning from vague requirements, writing quality. Example: `anthropic/claude-fable-5` at `low` for lighter passes and `medium` for harder ones.
|
|
166
|
+
|
|
167
|
+
The routing rule: use the capability tiers (1–3) when the task is well-scoped, and the intent tier (4) when scoping or judging is the task itself.
|
|
168
|
+
|
|
169
|
+
Give tier-4 agents cross-provider `fallbackModels` so subscription usage limits degrade gracefully instead of failing the run — fallback triggers on rate-limit and overload errors automatically:
|
|
170
|
+
|
|
171
|
+
```yaml
|
|
172
|
+
---
|
|
173
|
+
name: shaper
|
|
174
|
+
description: Open-ended design/UX/product/planning agent for ambiguous tasks
|
|
175
|
+
model: anthropic/claude-fable-5
|
|
176
|
+
thinking: medium
|
|
177
|
+
fallbackModels: openai-codex/gpt-5.5:high
|
|
178
|
+
---
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
One more interaction worth knowing for tier 4: forked context over an Anthropic parent transcript with signed thinking blocks forces the child's thinking off, so intent-tier agents work best with fresh context.
|
|
182
|
+
|
|
158
183
|
Use `~/.pi/agent/settings.json` for a user override or the project config settings file (`.pi/settings.json` in standard Pi) for a project override. `subagents.defaultModel` applies to builtin, package, user, and project agents that do not set `model` in frontmatter. Per-run model overrides and `agentOverrides.<name>.model` still win, and explicit agent frontmatter still wins over the global default. The same `agentOverrides` block can change `tools`, `skills`, inherited context, prompt text, or disable a builtin. Matching user and project agents also receive override fields that their frontmatter leaves unset, so a shared project config agent can keep the persona while local settings choose the model.
|
|
159
184
|
|
|
160
185
|
By default, project settings resolve from the nearest parent directory that contains `.pi` or `.agents`, preserving existing nested-project behavior. In monorepos or git worktrees where an incidental nested `.pi` directory should not shadow the repository-level config, set this in the repository root `.pi/settings.json`:
|
|
@@ -319,7 +344,7 @@ Foreground runs stream progress in the conversation while they run. They default
|
|
|
319
344
|
|
|
320
345
|
Background runs keep working after control returns to you. Inspect active runs with `subagent({ action: "status" })`, or a specific run with `subagent({ action: "status", id: "..." })`. In the TUI, a persistent FleetView below the editor by default shows `main` plus active children with task, elapsed time, and token totals. Set `fleetViewPlacement` to `"aboveEditor"` to move it above the editor. When the focused editor is empty, press `↓` or `←` to activate FleetView, then use `↑`/`↓` or `j`/`k` to select a child and `Enter` to inspect it; printable navigation keys are never intercepted before activation.
|
|
321
346
|
|
|
322
|
-
`/subagents-fleet` opens the live
|
|
347
|
+
`/subagents-fleet` opens the live fleet inspector with current-session foreground work, recent async children, structured Markdown/tool transcripts, and completed output/session paths. Use `↑`/`↓` or `j`/`k` to select a child, `Shift+K`/`Shift+J` to scroll one line, `PgUp`/`PgDn` to scroll one page, `x`/`Ctrl+O` to toggle tool details, `r` to refresh, and `Esc` to close. For a selected live async child, `s` sends an acknowledged steer message and `D` stops its top-level async run after confirmation. `Ctrl+Alt+F` opens the same inspector even while a foreground turn is active and slash input is queued. Without a TUI, `/subagents-fleet` retains the textual `subagent({ action: "status", view: "fleet" })` fallback, and mutations use explicit commands: run `/subagents-stop` and pick from the selector, or use `/subagents-stop <run-id>` / `subagent({ action: "stop", id: "..." })` when you already know the id. Use `/subagents-detach [run-id]` only for an active foreground single-subagent run you want to leave running without terminating; the eventual result remains available through status/wait. To inspect one background child in text, use `subagent({ action: "status", id: "...", view: "transcript" })`; add `index` for a specific child in a parallel or chain run.
|
|
323
348
|
|
|
324
349
|
FleetView replaces the legacy above-editor async widget by default, while completion notifications remain enabled. Parallel runs show every active child independently. Chains with parallel groups keep their grouped shape in progress and results, so failed or paused agents stay visible next to completed ones. When a child is explicitly allowed to fan out with `tools: subagent`, its nested runs appear under that parent child in the main status tree instead of being hidden inside the child process.
|
|
325
350
|
|
|
@@ -736,6 +761,7 @@ Example:
|
|
|
736
761
|
"subagents": {
|
|
737
762
|
"agentOverrides": {
|
|
738
763
|
"reviewer": {
|
|
764
|
+
"description": "Independent review tier",
|
|
739
765
|
"inheritProjectContext": false
|
|
740
766
|
}
|
|
741
767
|
}
|
|
@@ -743,7 +769,7 @@ Example:
|
|
|
743
769
|
}
|
|
744
770
|
```
|
|
745
771
|
|
|
746
|
-
Supported override fields are `model`, `fallbackModels`, `thinking`, `systemPromptMode`, `inheritProjectContext`, `inheritSkills`, `defaultContext`, `acceptanceRole`, `disabled`, `skills`, `tools`, and `systemPrompt`. Use `defaultContext: false` or `acceptanceRole: false` to clear an inherited override. Project overrides beat user overrides.
|
|
772
|
+
Supported override fields are `description`, `model`, `fallbackModels`, `thinking`, `systemPromptMode`, `inheritProjectContext`, `inheritSkills`, `defaultContext`, `acceptanceRole`, `disabled`, `skills`, `tools`, and `systemPrompt`. `description` replaces the discovered description for builtin and custom agents, which lets list output show deployment-specific routing or model metadata. Use `defaultContext: false` or `acceptanceRole: false` to clear an inherited override. Project overrides beat user overrides.
|
|
747
773
|
|
|
748
774
|
Set `subagents.defaultModel` to give all subagents without an explicit model their own default model, separate from the parent session model. Per-agent model overrides and agent frontmatter still win.
|
|
749
775
|
|
package/package.json
CHANGED
|
@@ -20,6 +20,8 @@ Precedence is by parsed runtime name:
|
|
|
20
20
|
2. user scope
|
|
21
21
|
3. builtin agents
|
|
22
22
|
|
|
23
|
+
Project settings resolve from the nearest parent directory containing `.pi` or `.agents` by default. In monorepos or git worktrees where an incidental nested `.pi` directory should not shadow the repository config, set `subagents.projectRootResolution: "git-root"` in the repository root `.pi/settings.json`; a nested project can opt back with `"nearest"` in its own settings.
|
|
24
|
+
|
|
23
25
|
## Running Subagents
|
|
24
26
|
|
|
25
27
|
### Single agent
|
|
@@ -149,6 +151,8 @@ const run = subagent({
|
|
|
149
151
|
// Continue local inspection, then later call status with the returned id.
|
|
150
152
|
```
|
|
151
153
|
|
|
154
|
+
While children run, the persistent FleetView and the collapsed foreground tool-result card show live per-child detail: resolved model and thinking level, `[fresh]`/`[fork]` context, tool/token/elapsed counters, and current activity. The collapsed running card also prints the configured expand-key hint ("Press … for live detail"); expanding it shows nested children, recent tools, and recent output. Model badges appear once the child's model resolves at first attempt start. `/subagents-fleet` opens the live fleet inspector, which also has per-child controls (`s` steer, `D` stop with confirmation).
|
|
155
|
+
|
|
152
156
|
Inspect async runs with `subagent({ action: "status", id: "..." })` or `subagent({ action: "status" })` for active runs. Use `subagent({ action: "status", view: "fleet" })` when supervising several active foreground/background runs and `subagent({ action: "status", id: "...", view: "transcript", index: 0 })` when you need the latest child output without digging through artifacts. If a delegated fanout child launches nested runs, the parent status view shows them as a tree and you can target a nested run directly with its nested id.
|
|
153
157
|
|
|
154
158
|
Stop a current-session top-level async run with `stop` (or `/subagents-stop`). Stopped runs finish as `stopped`/cancelled and are not resumable. For an active foreground single-subagent run, `/subagents-detach [run-id]` leaves the child running without terminating it and returns the eventual result through status/wait. Append one more step to the tail of a still-running async chain with `append-step` (`chain` must contain exactly one step). Use checkpoint steps for planned human gates; they pause without launching a child and are approved or rejected through current-session control actions:
|
|
@@ -141,4 +141,4 @@ Additional user prompt templates can delegate into `pi-subagents` through the na
|
|
|
141
141
|
|
|
142
142
|
Other Pi extensions can call `pi-subagents` through the in-process event bus. The stable v1 channels are `subagents:rpc:v1:ready`, `subagents:rpc:v1:request`, and per-request replies at `subagents:rpc:v1:reply:<requestId>`. Envelopes use `{ version: 1, requestId, method, params }`, and replies use `{ version: 1, requestId, success, data | error }`. `ping` advertises the exact process-local async completion event as `events.asyncComplete` for RPC-spawn consumers.
|
|
143
143
|
|
|
144
|
-
Methods: `ping`, `status`, `spawn`, `steer`, `interrupt`, `resume`, and `stop`. `spawn` is async-only and rejects management actions, `async: false`, or `clarify: true`; it reuses the normal executor, so discovery, validation, session attribution, configured spawn caps, child-safety depth, artifacts, and async status are shared with the `subagent` tool. `status`, acknowledged async `steer`, and `interrupt` map to the normal control actions. RPC steer disables pause-and-revive recovery and advertises `capabilities.nonRecoveringSteer`, preserving the caller's authority over the exact spawned child. `resume` requires a target plus non-empty message and delegates to the package-owned revival path; it may set a caller-owned `file-only` output path but cannot override the persisted child model, tools, budgets, session ownership, or exclusive session lease. `stop` targets running async runs through the existing timeout control channel. `pi.events` is process-local, so separate Pi processes and child subagents need lifecycle artifact files or `pi-intercom` instead.
|
|
144
|
+
Methods: `ping`, `status`, `spawn`, `steer`, `interrupt`, `resume`, and `stop`. `ping` capability metadata advertises optional projections: `capabilities.fleetStatus: { version: 1 }` adds bounded current-session `data.fleet` records (opaque reconciliation `key`, resolved `agent`, optional `role`, `model`, `effort`, caller-facing `goal`, `startedAt`, split `{ input, output, total }` tokens, plus `totalActive`/`omitted` overflow counts) to successful `status` replies; `capabilities.launchResolvedExtensions` advertises parent-resolved opaque launch-extension identifiers in status details; `capabilities.runtimeAcknowledgedExtensions` advertises the best-effort child-runtime acknowledgement projection fed by cooperating extensions emitting `subagent:acknowledge-extension`. Foreground `details.results[]` rows carry a stable numeric `index`; correlate children by `(runId, index)` rather than row position. Consumers should read status/result artifacts and RPC projections instead of scraping terminal output and must ignore unknown fields. `spawn` is async-only and rejects management actions, `async: false`, or `clarify: true`; it reuses the normal executor, so discovery, validation, session attribution, configured spawn caps, child-safety depth, artifacts, and async status are shared with the `subagent` tool. `status`, acknowledged async `steer`, and `interrupt` map to the normal control actions. RPC steer disables pause-and-revive recovery and advertises `capabilities.nonRecoveringSteer`, preserving the caller's authority over the exact spawned child. `resume` requires a target plus non-empty message and delegates to the package-owned revival path; it may set a caller-owned `file-only` output path but cannot override the persisted child model, tools, budgets, session ownership, or exclusive session lease. `stop` targets running async runs through the existing timeout control channel. `pi.events` is process-local, so separate Pi processes and child subagents need lifecycle artifact files or `pi-intercom` instead.
|
|
@@ -31,7 +31,7 @@ Humans often use the slash-command layer instead:
|
|
|
31
31
|
- `/subagents-stop [run-id]` — stop a current-session top-level async run; opens a selector when no id is given
|
|
32
32
|
- `/subagents-detach [run-id]` — detach an active foreground single-subagent run without terminating its child
|
|
33
33
|
- `/subagent-cost` — show parent plus child token usage and cost for the session
|
|
34
|
-
- `/subagents-fleet` — open the live
|
|
34
|
+
- `/subagents-fleet` — open the live fleet inspector with per-child controls; `Ctrl+Alt+F` opens it during an active foreground turn, `↑↓`/`jk` selects children, `PgUp`/`PgDn` scrolls transcript detail, `s` steers the selected live async child, and `D` stops its top-level async run after confirmation
|
|
35
35
|
- `/subagents-watchdog` — inspect or configure the opt-in adversarial change watchdog (model, on/off, recommend-model, check)
|
|
36
36
|
- `/subagents-doctor` — diagnose setup, discovery, async paths, and intercom bridge state
|
|
37
37
|
- `/subagents-models [agent]` — show the live runtime-loaded builtin model mapping
|
|
@@ -253,12 +253,25 @@ Direct settings example:
|
|
|
253
253
|
}
|
|
254
254
|
```
|
|
255
255
|
|
|
256
|
-
Useful override fields: `model`, `fallbackModels`, `thinking`,
|
|
256
|
+
Useful override fields: `description`, `model`, `fallbackModels`, `thinking`,
|
|
257
257
|
`systemPromptMode`, `inheritProjectContext`, `inheritSkills`, `defaultContext`,
|
|
258
258
|
`acceptanceRole`, `disabled`, `skills`, `tools`, `extensions`, and `systemPrompt`.
|
|
259
|
+
`description` replaces the discovered description for builtin and custom agents
|
|
260
|
+
in `list` output, which is useful for deployment-specific routing notes.
|
|
259
261
|
Use `acceptanceRole: false` to clear an override. Create a user or project
|
|
260
262
|
agent with the same name only when you want a substantially different agent.
|
|
261
263
|
|
|
264
|
+
### Recommended model tiering (optional)
|
|
265
|
+
|
|
266
|
+
When several providers are available, route agents by task shape instead of one model for everything:
|
|
267
|
+
|
|
268
|
+
1. **Fast workhorse** — cheapest capable model at low thinking for recon, lookups, and mechanical edits (for example on `scout`).
|
|
269
|
+
2. **Standard well-scoped** — mid-tier model at medium thinking for most delegations: routine multi-file edits, focused reviews, straightforward implementation (for example on `worker`, `reviewer`, `delegate`).
|
|
270
|
+
3. **Deep but bounded** — top reasoning model at high thinking only for hard tasks that arrive with explicit goals and completion criteria; these models loop on vague goals (for example on `planner` and oracle-style agents).
|
|
271
|
+
4. **Taste and intent** — a model that reads human intent well for ambiguous work: UX/design judgment, product tradeoffs, planning from vague requirements, writing quality.
|
|
272
|
+
|
|
273
|
+
Routing rule: use tiers 1–3 when the task is well-scoped; use tier 4 when scoping or judging is the task itself. Give tier-4 agents cross-provider `fallbackModels` so subscription usage limits degrade gracefully; fallback triggers automatically on rate-limit and overload errors. Note that forked context over an Anthropic parent transcript with signed thinking blocks forces the child's thinking off, so intent-tier agents work best with fresh context.
|
|
274
|
+
|
|
262
275
|
If a provider rejects model IDs with thinking suffixes, use
|
|
263
276
|
`subagents.disableThinking: true` in user or project settings to clear bundled
|
|
264
277
|
builtin thinking defaults globally. A higher-precedence per-agent `thinking`
|
package/src/agents/agents.ts
CHANGED
|
@@ -59,6 +59,7 @@ export function defaultInheritSkills(): boolean {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
export interface BuiltinAgentOverrideBase {
|
|
62
|
+
description?: string;
|
|
62
63
|
model?: string;
|
|
63
64
|
fallbackModels?: string[];
|
|
64
65
|
thinking?: string | false;
|
|
@@ -80,6 +81,7 @@ export interface BuiltinAgentOverrideBase {
|
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
interface BuiltinAgentOverrideConfig {
|
|
84
|
+
description?: string;
|
|
83
85
|
model?: string | false;
|
|
84
86
|
fallbackModels?: string[] | false;
|
|
85
87
|
thinking?: string | false;
|
|
@@ -545,6 +547,7 @@ function arraysEqual(a: string[] | undefined, b: string[] | undefined): boolean
|
|
|
545
547
|
|
|
546
548
|
function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
|
|
547
549
|
return {
|
|
550
|
+
description: agent.description,
|
|
548
551
|
model: agent.model,
|
|
549
552
|
fallbackModels: agent.fallbackModels ? [...agent.fallbackModels] : undefined,
|
|
550
553
|
thinking: agent.thinking,
|
|
@@ -568,6 +571,7 @@ function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
|
|
|
568
571
|
|
|
569
572
|
function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentOverrideConfig {
|
|
570
573
|
return {
|
|
574
|
+
...(override.description !== undefined ? { description: override.description } : {}),
|
|
571
575
|
...(override.model !== undefined ? { model: override.model } : {}),
|
|
572
576
|
...(override.fallbackModels !== undefined
|
|
573
577
|
? { fallbackModels: override.fallbackModels === false ? false : [...override.fallbackModels] }
|
|
@@ -720,6 +724,14 @@ function parseBuiltinOverrideEntry(
|
|
|
720
724
|
const input = value as Record<string, unknown>;
|
|
721
725
|
const override: BuiltinAgentOverrideConfig = {};
|
|
722
726
|
|
|
727
|
+
if ("description" in input) {
|
|
728
|
+
if (typeof input.description === "string" && input.description.trim()) {
|
|
729
|
+
override.description = input.description.trim();
|
|
730
|
+
} else {
|
|
731
|
+
throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'description'; expected a non-empty string.`);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
723
735
|
if ("model" in input) {
|
|
724
736
|
if (typeof input.model === "string" || input.model === false) override.model = input.model;
|
|
725
737
|
else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'model'; expected a string or false.`);
|
|
@@ -967,6 +979,7 @@ function applyBuiltinOverride(
|
|
|
967
979
|
override: { ...meta, base: cloneOverrideBase(agent) },
|
|
968
980
|
};
|
|
969
981
|
|
|
982
|
+
if (override.description !== undefined) next.description = override.description;
|
|
970
983
|
if (override.model !== undefined) next.model = override.model === false ? undefined : override.model;
|
|
971
984
|
if (override.fallbackModels !== undefined) {
|
|
972
985
|
next.fallbackModels = override.fallbackModels === false ? undefined : [...override.fallbackModels];
|
|
@@ -1087,6 +1100,10 @@ function applyCustomAgentOverride(
|
|
|
1087
1100
|
anyFilled = true;
|
|
1088
1101
|
};
|
|
1089
1102
|
|
|
1103
|
+
if (override.description !== undefined) {
|
|
1104
|
+
mutable().description = override.description;
|
|
1105
|
+
anyFilled = true;
|
|
1106
|
+
}
|
|
1090
1107
|
if (override.model !== undefined) {
|
|
1091
1108
|
fill("model", ["model"], override.model === false ? undefined : override.model);
|
|
1092
1109
|
}
|
|
@@ -1177,10 +1194,14 @@ function applyCustomAgentOverrides(
|
|
|
1177
1194
|
|
|
1178
1195
|
export function buildBuiltinOverrideConfig(
|
|
1179
1196
|
base: BuiltinAgentOverrideBase,
|
|
1180
|
-
draft: Pick<AgentConfig, "model" | "fallbackModels" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritSkills" | "defaultContext" | "acceptanceRole" | "disabled" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools" | "extensions" | "subagentOnlyExtensions" | "completionGuard" | "toolBudget"
|
|
1197
|
+
draft: Pick<AgentConfig, "model" | "fallbackModels" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritSkills" | "defaultContext" | "acceptanceRole" | "disabled" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools" | "extensions" | "subagentOnlyExtensions" | "completionGuard" | "toolBudget"> & Partial<Pick<AgentConfig, "description">>,
|
|
1181
1198
|
): BuiltinAgentOverrideConfig | undefined {
|
|
1182
1199
|
const override: BuiltinAgentOverrideConfig = {};
|
|
1183
1200
|
|
|
1201
|
+
if (draft.description !== undefined) {
|
|
1202
|
+
const description = draft.description.trim();
|
|
1203
|
+
if (description && description !== base.description) override.description = description;
|
|
1204
|
+
}
|
|
1184
1205
|
if (draft.model !== base.model) override.model = draft.model ?? false;
|
|
1185
1206
|
if (!arraysEqual(draft.fallbackModels, base.fallbackModels)) override.fallbackModels = draft.fallbackModels ? [...draft.fallbackModels] : false;
|
|
1186
1207
|
if (draft.thinking !== base.thinking) override.thinking = draft.thinking ?? false;
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
type SubagentResultIntercomChild,
|
|
12
12
|
type SubagentResultIntercomPayload,
|
|
13
13
|
type SubagentResultStatus,
|
|
14
|
+
type SubagentOutputState,
|
|
14
15
|
type SubagentRunMode,
|
|
15
16
|
SUBAGENT_RESULT_INTERCOM_DELIVERY_EVENT,
|
|
16
17
|
SUBAGENT_RESULT_INTERCOM_EVENT,
|
|
@@ -64,6 +65,21 @@ function formatStatusCounts(counts: Record<SubagentResultStatus, number>): strin
|
|
|
64
65
|
return parts.length ? parts.join(", ") : "0 results";
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
function countOutputStates(children: SubagentResultIntercomChild[]): Record<SubagentOutputState, number> {
|
|
69
|
+
const counts: Record<SubagentOutputState, number> = { present: 0, absent: 0, unknown: 0 };
|
|
70
|
+
for (const child of children) counts[child.outputState ?? "unknown"] += 1;
|
|
71
|
+
return counts;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function formatOutputCounts(counts: Record<SubagentOutputState, number>): string {
|
|
75
|
+
const parts = [
|
|
76
|
+
counts.present ? `${counts.present} present` : undefined,
|
|
77
|
+
counts.absent ? `${counts.absent} absent` : undefined,
|
|
78
|
+
counts.unknown ? `${counts.unknown} unknown` : undefined,
|
|
79
|
+
].filter((part): part is string => Boolean(part));
|
|
80
|
+
return parts.length ? parts.join(", ") : "0 outputs";
|
|
81
|
+
}
|
|
82
|
+
|
|
67
83
|
function resolveGroupedStatus(children: SubagentResultIntercomChild[]): SubagentResultStatus {
|
|
68
84
|
const counts = countStatuses(children);
|
|
69
85
|
if (counts.failed > 0) return "failed";
|
|
@@ -219,14 +235,19 @@ function formatSubagentResultIntercomMessage(input: {
|
|
|
219
235
|
parallelHandoff?: ParallelHandoffReference;
|
|
220
236
|
}): string {
|
|
221
237
|
const counts = countStatuses(input.children);
|
|
238
|
+
const outputCounts = countOutputStates(input.children);
|
|
222
239
|
const lines: string[] = [
|
|
223
240
|
"subagent results",
|
|
224
241
|
"",
|
|
225
242
|
`Run: ${input.runId}`,
|
|
226
243
|
`Mode: ${input.mode}`,
|
|
227
|
-
`
|
|
244
|
+
`Process status: ${input.status}`,
|
|
228
245
|
`Children: ${formatStatusCounts(counts)}`,
|
|
246
|
+
`Outputs: ${formatOutputCounts(outputCounts)} (semantic adequacy unassessed)`,
|
|
229
247
|
];
|
|
248
|
+
if (input.children.some((child) => child.status === "failed" && child.outputState === "present")) {
|
|
249
|
+
lines.push("Recovery: At least one failed process produced output. Inspect that output before retrying; output presence does not establish task completion.");
|
|
250
|
+
}
|
|
230
251
|
if (input.mode === "chain" && typeof input.chainSteps === "number") {
|
|
231
252
|
lines.push(`Chain steps: ${input.chainSteps}`);
|
|
232
253
|
}
|
|
@@ -245,7 +266,7 @@ function formatSubagentResultIntercomMessage(input: {
|
|
|
245
266
|
for (let index = 0; index < input.children.length; index++) {
|
|
246
267
|
const child = input.children[index]!;
|
|
247
268
|
lines.push("");
|
|
248
|
-
lines.push(`${index + 1}. ${child.agent} — ${child.status}`);
|
|
269
|
+
lines.push(`${index + 1}. ${child.agent} — process ${child.status} · output ${child.outputState ?? "unknown"}`);
|
|
249
270
|
if (child.intercomTarget) lines.push(`${input.source === "async" ? "Previous intercom target" : "Run intercom target"}: ${child.intercomTarget}`);
|
|
250
271
|
if (child.artifactPath) lines.push(`Output artifact: ${child.artifactPath}`);
|
|
251
272
|
if (child.sessionPath) lines.push(`Session: ${child.sessionPath}`);
|
|
@@ -260,6 +281,7 @@ function formatSubagentResultIntercomMessage(input: {
|
|
|
260
281
|
export function buildSubagentResultIntercomPayload(input: GroupedResultIntercomMessageInput): SubagentResultIntercomPayload {
|
|
261
282
|
const children = input.children.map((child) => ({
|
|
262
283
|
...child,
|
|
284
|
+
outputState: child.outputState ?? "unknown",
|
|
263
285
|
summary: child.summary.trim() || "(no output)",
|
|
264
286
|
children: compactNestedResultChildren(child.children),
|
|
265
287
|
}));
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type NestedRunSummary,
|
|
9
9
|
type ParallelHandoffReference,
|
|
10
10
|
type SubagentResultIntercomChild,
|
|
11
|
+
type SubagentOutputState,
|
|
11
12
|
type SubagentState,
|
|
12
13
|
} from "../../shared/types.ts";
|
|
13
14
|
import {
|
|
@@ -45,6 +46,7 @@ type ResultWatcherDeps = {
|
|
|
45
46
|
type ResultFileChild = {
|
|
46
47
|
agent?: string;
|
|
47
48
|
output?: string;
|
|
49
|
+
outputState?: SubagentOutputState;
|
|
48
50
|
error?: string;
|
|
49
51
|
success?: boolean;
|
|
50
52
|
state?: string;
|
|
@@ -178,9 +180,9 @@ export function createResultWatcher(
|
|
|
178
180
|
const hasResultChildren = Array.isArray(data.results) && data.results.length > 0;
|
|
179
181
|
const resultChildren: ResultFileChild[] = hasResultChildren
|
|
180
182
|
? data.results!
|
|
181
|
-
: [{ agent: data.agent ?? undefined, output: data.summary, success: data.success }];
|
|
183
|
+
: [{ agent: data.agent ?? undefined, output: data.summary, outputState: "unknown", success: data.success }];
|
|
182
184
|
const normalizedChildren = attachNestedChildrenToResultChildren(runId, resultChildren.map((result = {}, index): SubagentResultIntercomChild => {
|
|
183
|
-
const baseOutput = result.output ?? data.summary;
|
|
185
|
+
const baseOutput = hasResultChildren ? result.output : result.output ?? data.summary;
|
|
184
186
|
const hasRealOutput = typeof baseOutput === "string" && baseOutput.trim().length > 0;
|
|
185
187
|
const output = hasRealOutput ? baseOutput : "(no output)";
|
|
186
188
|
const summary = result.success === false && result.error
|
|
@@ -206,6 +208,9 @@ export function createResultWatcher(
|
|
|
206
208
|
turnBudgetExceeded: result.turnBudgetExceeded,
|
|
207
209
|
processSignal: result.processSignal,
|
|
208
210
|
}),
|
|
211
|
+
outputState: result.outputState === "present" || result.outputState === "absent" || result.outputState === "unknown"
|
|
212
|
+
? result.outputState
|
|
213
|
+
: "unknown",
|
|
209
214
|
summary,
|
|
210
215
|
index,
|
|
211
216
|
artifactPath: result.artifactPaths?.outputPath,
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
type ResolvedTurnBudget,
|
|
28
28
|
type ResolvedToolBudget,
|
|
29
29
|
type SubagentRunMode,
|
|
30
|
+
type SubagentOutputState,
|
|
30
31
|
type UsageBudgetConfig,
|
|
31
32
|
type ToolBudgetState,
|
|
32
33
|
type TurnBudgetState,
|
|
@@ -184,6 +185,7 @@ interface StepResult {
|
|
|
184
185
|
launchResolvedExtensions?: LaunchResolvedChildExtensionsV1;
|
|
185
186
|
runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensionsV1;
|
|
186
187
|
output: string;
|
|
188
|
+
outputState?: SubagentOutputState;
|
|
187
189
|
error?: string;
|
|
188
190
|
protocolError?: ProtocolOutputLimit;
|
|
189
191
|
success: boolean;
|
|
@@ -422,6 +424,7 @@ interface RunPiStreamingResult {
|
|
|
422
424
|
error?: string;
|
|
423
425
|
protocolError?: ProtocolOutputLimit;
|
|
424
426
|
finalOutput: string;
|
|
427
|
+
outputState: SubagentOutputState;
|
|
425
428
|
interrupted?: boolean;
|
|
426
429
|
timedOut?: boolean;
|
|
427
430
|
stopped?: boolean;
|
|
@@ -837,6 +840,7 @@ function runPiStreaming(
|
|
|
837
840
|
error: stopped ? (stopMessage ?? "Subagent stopped by user.") : timedOut ? (timeoutMessage ?? "Subagent timed out.") : turnBudgetExceeded ? turnBudgetMessage : interrupted || forcedDrainAfterFinalSuccess ? undefined : finalError ?? signalError,
|
|
838
841
|
protocolError,
|
|
839
842
|
finalOutput: (timedOut || stopped) && !finalOutput.trim() ? (stopped ? stopMessage ?? "Subagent stopped by user." : timeoutMessage ?? "Subagent timed out.") : finalOutput,
|
|
843
|
+
outputState: finalOutput.trim() ? "present" : "absent",
|
|
840
844
|
interrupted,
|
|
841
845
|
timedOut,
|
|
842
846
|
stopped,
|
|
@@ -870,7 +874,7 @@ function runPiStreaming(
|
|
|
870
874
|
const stderr = stderrTail.text();
|
|
871
875
|
const finalOutput = getFinalOutput(messages) || rawStdoutTail.text().trim();
|
|
872
876
|
const spawnErrorMessage = spawnError instanceof Error ? spawnError.message : String(spawnError);
|
|
873
|
-
resolve({ stderr, exitCode: 1, messages, usage, toolCount, durationMs: Date.now() - startedAt, model, error: stopped ? (stopMessage ?? "Subagent stopped by user.") : timedOut ? (timeoutMessage ?? "Subagent timed out.") : turnBudgetExceeded ? turnBudgetMessage : error ?? assistantError ?? spawnErrorMessage, protocolError, finalOutput: (timedOut || stopped) && !finalOutput.trim() ? (stopped ? stopMessage ?? "Subagent stopped by user." : timeoutMessage ?? "Subagent timed out.") : finalOutput, timedOut, stopped, turnBudget, turnBudgetExceeded, wrapUpRequested: turnBudget?.outcome === "wrap-up-requested" || turnBudget?.outcome === "termination-deferred" || turnBudgetExceeded || undefined, observedMutationAttempt, watchdog: childWatchdogState, processInstanceId });
|
|
877
|
+
resolve({ stderr, exitCode: 1, messages, usage, toolCount, durationMs: Date.now() - startedAt, model, error: stopped ? (stopMessage ?? "Subagent stopped by user.") : timedOut ? (timeoutMessage ?? "Subagent timed out.") : turnBudgetExceeded ? turnBudgetMessage : error ?? assistantError ?? spawnErrorMessage, protocolError, finalOutput: (timedOut || stopped) && !finalOutput.trim() ? (stopped ? stopMessage ?? "Subagent stopped by user." : timeoutMessage ?? "Subagent timed out.") : finalOutput, outputState: finalOutput.trim() ? "present" : "absent", timedOut, stopped, turnBudget, turnBudgetExceeded, wrapUpRequested: turnBudget?.outcome === "wrap-up-requested" || turnBudget?.outcome === "termination-deferred" || turnBudgetExceeded || undefined, observedMutationAttempt, watchdog: childWatchdogState, processInstanceId });
|
|
874
878
|
});
|
|
875
879
|
});
|
|
876
880
|
}
|
|
@@ -1496,6 +1500,13 @@ async function runSingleStep(
|
|
|
1496
1500
|
const childWrittenOutput = step.outputPath
|
|
1497
1501
|
? extractChildWrittenOutput(finalResult?.messages, step.outputPath, step.cwd ?? ctx.cwd)
|
|
1498
1502
|
: undefined;
|
|
1503
|
+
const outputState: SubagentOutputState = finalResult?.outputState === "present"
|
|
1504
|
+
|| (finalResult as (RunPiStreamingResult & { structuredOutput?: unknown }) | undefined)?.structuredOutput !== undefined
|
|
1505
|
+
|| Boolean(childWrittenOutput?.trim())
|
|
1506
|
+
? "present"
|
|
1507
|
+
: resolvedOutput.savedPath
|
|
1508
|
+
? "unknown"
|
|
1509
|
+
: finalResult?.outputState ?? "unknown";
|
|
1499
1510
|
const finalizedOutput = finalizeSingleOutput({
|
|
1500
1511
|
fullOutput: outputForSummary,
|
|
1501
1512
|
outputPath: step.outputPath,
|
|
@@ -1586,6 +1597,7 @@ async function runSingleStep(
|
|
|
1586
1597
|
...(step.agentContract ? { agentContract: step.agentContract } : {}),
|
|
1587
1598
|
launchContractDigest: actualLaunchContractDigest,
|
|
1588
1599
|
output: outputForSummary,
|
|
1600
|
+
outputState,
|
|
1589
1601
|
exitCode: effectiveFinalExitCode,
|
|
1590
1602
|
error: effectiveFinalError,
|
|
1591
1603
|
protocolError: finalResult?.protocolError,
|
|
@@ -3376,6 +3388,7 @@ async function runSubagent(
|
|
|
3376
3388
|
launchResolvedExtensions: pr.launchResolvedExtensions,
|
|
3377
3389
|
runtimeAcknowledgedExtensions: pr.runtimeAcknowledgedExtensions,
|
|
3378
3390
|
output: pr.output,
|
|
3391
|
+
outputState: pr.outputState,
|
|
3379
3392
|
error: pr.error,
|
|
3380
3393
|
protocolError: pr.protocolError,
|
|
3381
3394
|
success: pr.stopped !== true && pr.interrupted !== true && pr.exitCode === 0,
|
|
@@ -3784,6 +3797,7 @@ async function runSubagent(
|
|
|
3784
3797
|
launchContractDigest: pr.launchContractDigest,
|
|
3785
3798
|
launchResolvedExtensions: pr.launchResolvedExtensions,
|
|
3786
3799
|
output: pr.output,
|
|
3800
|
+
outputState: pr.outputState,
|
|
3787
3801
|
error: pr.error,
|
|
3788
3802
|
protocolError: pr.protocolError,
|
|
3789
3803
|
success: pr.stopped !== true && pr.interrupted !== true && pr.exitCode === 0,
|
|
@@ -3970,6 +3984,7 @@ async function runSubagent(
|
|
|
3970
3984
|
launchResolvedExtensions: singleResult.launchResolvedExtensions,
|
|
3971
3985
|
runtimeAcknowledgedExtensions: singleResult.runtimeAcknowledgedExtensions,
|
|
3972
3986
|
output: stopped || childStopped ? stopMessage : timedOut ? (timeoutMessage ?? "Subagent timed out.") : singleResult.output,
|
|
3987
|
+
outputState: singleResult.outputState,
|
|
3973
3988
|
error: stopped || childStopped ? stopMessage : timedOut ? (timeoutMessage ?? "Subagent timed out.") : singleResult.error,
|
|
3974
3989
|
protocolError: singleResult.protocolError,
|
|
3975
3990
|
success: !stopped && !childStopped && !timedOut && singleResult.interrupted !== true && singleResult.exitCode === 0,
|
|
@@ -4297,6 +4312,7 @@ async function runSubagent(
|
|
|
4297
4312
|
agent: r.agent,
|
|
4298
4313
|
context: r.context,
|
|
4299
4314
|
output: r.output,
|
|
4315
|
+
outputState: r.outputState,
|
|
4300
4316
|
error: r.error,
|
|
4301
4317
|
protocolError: r.protocolError,
|
|
4302
4318
|
success: r.success,
|
|
@@ -379,6 +379,7 @@ async function runSingleAttempt(
|
|
|
379
379
|
launchContractDigest,
|
|
380
380
|
launchResolvedExtensions,
|
|
381
381
|
exitCode: 0,
|
|
382
|
+
outputState: "absent",
|
|
382
383
|
messages: [],
|
|
383
384
|
usage: emptyUsage(),
|
|
384
385
|
model: modelArg,
|
|
@@ -1228,6 +1229,7 @@ async function runSingleAttempt(
|
|
|
1228
1229
|
|
|
1229
1230
|
const acceptanceOutput = getFinalOutput(result.messages ?? []);
|
|
1230
1231
|
let fullOutput = stripAcceptanceReport(acceptanceOutput);
|
|
1232
|
+
result.outputState = fullOutput.trim() || result.structuredOutput !== undefined ? "present" : "absent";
|
|
1231
1233
|
if (result.timedOut) {
|
|
1232
1234
|
const timeoutMessage = formatTimeoutMessage(options.timeoutMs ?? 0);
|
|
1233
1235
|
fullOutput = fullOutput.trim()
|
|
@@ -1287,6 +1289,7 @@ async function runSingleAttempt(
|
|
|
1287
1289
|
result.outputSaveError = resolvedOutput.saveError;
|
|
1288
1290
|
if (resolvedOutput.savedPath) {
|
|
1289
1291
|
result.outputReference = formatSavedOutputReference(resolvedOutput.savedPath, fullOutput);
|
|
1292
|
+
if (result.outputState === "absent") result.outputState = "unknown";
|
|
1290
1293
|
}
|
|
1291
1294
|
}
|
|
1292
1295
|
artifactOutputByResult.set(result, fullOutput);
|
|
@@ -443,6 +443,7 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
|
|
|
443
443
|
...(result.exitCode !== undefined ? { exitCode: result.exitCode } : {}),
|
|
444
444
|
...(result.error ? { error: result.error } : {}),
|
|
445
445
|
...(result.finalOutput ? { finalOutput: result.finalOutput } : {}),
|
|
446
|
+
...(result.outputState ? { outputState: result.outputState } : {}),
|
|
446
447
|
...(result.outputMode ? { outputMode: result.outputMode } : {}),
|
|
447
448
|
...(result.savedOutputPath ? { savedOutputPath: result.savedOutputPath } : {}),
|
|
448
449
|
...(result.outputSaveError ? { outputSaveError: result.outputSaveError } : {}),
|
|
@@ -521,6 +522,7 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
521
522
|
...(input.result.exitCode !== undefined ? { exitCode: input.result.exitCode } : {}),
|
|
522
523
|
...(input.result.error ? { error: input.result.error } : {}),
|
|
523
524
|
...(input.result.finalOutput ? { finalOutput: input.result.finalOutput } : {}),
|
|
525
|
+
outputState: input.result.outputState,
|
|
524
526
|
outputMode: input.result.outputMode,
|
|
525
527
|
savedOutputPath: input.result.savedOutputPath,
|
|
526
528
|
outputSaveError: input.result.outputSaveError,
|
|
@@ -1487,6 +1489,7 @@ async function emitForegroundResultIntercom(input: {
|
|
|
1487
1489
|
stopped: result.stopped,
|
|
1488
1490
|
turnBudgetExceeded: result.turnBudgetExceeded,
|
|
1489
1491
|
}),
|
|
1492
|
+
outputState: result.outputState ?? "unknown",
|
|
1490
1493
|
summary: resultSummaryForIntercom(result),
|
|
1491
1494
|
index,
|
|
1492
1495
|
artifactPath: result.artifactPaths?.outputPath,
|
package/src/shared/types.ts
CHANGED
|
@@ -225,6 +225,7 @@ export interface ControlEvent {
|
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
export type SubagentResultStatus = "completed" | "failed" | "paused" | "stopped" | "detached";
|
|
228
|
+
export type SubagentOutputState = "present" | "absent" | "unknown";
|
|
228
229
|
export type SubagentRunMode = "single" | "parallel" | "chain";
|
|
229
230
|
|
|
230
231
|
export interface ParallelHandoffPatch {
|
|
@@ -517,7 +518,10 @@ export type PublicNestedRunSummary = Pick<
|
|
|
517
518
|
|
|
518
519
|
export interface SubagentResultIntercomChild {
|
|
519
520
|
agent: string;
|
|
521
|
+
/** Process/lifecycle status. It does not establish semantic task completion. */
|
|
520
522
|
status: SubagentResultStatus;
|
|
523
|
+
/** Whether the child produced substantive output before its process ended. */
|
|
524
|
+
outputState?: SubagentOutputState;
|
|
521
525
|
summary: string;
|
|
522
526
|
index?: number;
|
|
523
527
|
artifactPath?: string;
|
|
@@ -862,6 +866,8 @@ export interface SingleResult {
|
|
|
862
866
|
artifactPaths?: ArtifactPaths;
|
|
863
867
|
truncation?: TruncationResult;
|
|
864
868
|
finalOutput?: string;
|
|
869
|
+
/** Provenance-aware state for substantive child output, excluding synthetic lifecycle messages. */
|
|
870
|
+
outputState?: SubagentOutputState;
|
|
865
871
|
outputMode?: OutputMode;
|
|
866
872
|
savedOutputPath?: string;
|
|
867
873
|
outputReference?: SavedOutputReference;
|
|
@@ -1319,6 +1325,7 @@ export interface ForegroundResumeChild {
|
|
|
1319
1325
|
exitCode?: number;
|
|
1320
1326
|
error?: string;
|
|
1321
1327
|
finalOutput?: string;
|
|
1328
|
+
outputState?: SubagentOutputState;
|
|
1322
1329
|
outputMode?: OutputMode;
|
|
1323
1330
|
savedOutputPath?: string;
|
|
1324
1331
|
outputSaveError?: string;
|
|
@@ -1298,7 +1298,7 @@ export function registerSlashCommands(
|
|
|
1298
1298
|
});
|
|
1299
1299
|
|
|
1300
1300
|
pi.registerCommand("subagents-fleet", {
|
|
1301
|
-
description: "Open the live
|
|
1301
|
+
description: "Open the live subagent fleet inspector",
|
|
1302
1302
|
handler: async (_args, ctx) => showFleet(ctx),
|
|
1303
1303
|
});
|
|
1304
1304
|
|
package/src/tui/render.ts
CHANGED
|
@@ -1318,7 +1318,7 @@ function renderSingleCompact(d: Details, r: Details["results"][number], theme: T
|
|
|
1318
1318
|
]);
|
|
1319
1319
|
const c = new Container();
|
|
1320
1320
|
const width = getTermWidth() - 4;
|
|
1321
|
-
const modelDisplay = modelThinkingBadge(theme, r.model);
|
|
1321
|
+
const modelDisplay = modelThinkingBadge(theme, r.model ?? r.progress?.model, r.thinking ?? r.progress?.thinking);
|
|
1322
1322
|
c.addChild(new Text(truncLine(`${resultGlyph(r, output, theme, isRunning, undefined, frame)} ${theme.fg("toolTitle", theme.bold(r.agent))}${modelDisplay}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`, width), 0, 0));
|
|
1323
1323
|
|
|
1324
1324
|
if (isRunning && r.progress) {
|
|
@@ -1428,7 +1428,9 @@ function renderMultiCompact(d: Details, theme: Theme, frame?: number): Component
|
|
|
1428
1428
|
const glyph = rPending ? theme.fg("dim", "◦") : resultGlyph(r, output, theme, rRunning, progressRunningSeed(rProg), frame);
|
|
1429
1429
|
const pendingLabel = rPending ? ` ${theme.fg("dim", "· pending")}` : "";
|
|
1430
1430
|
const stepLabel = entry.rowLabel ?? resultRowLabel(multiLabel, i, stepNumber);
|
|
1431
|
-
const
|
|
1431
|
+
const rowProgressModel = rProg && "status" in rProg ? rProg : undefined;
|
|
1432
|
+
const rowModelDisplay = modelThinkingBadge(theme, r.model ?? rowProgressModel?.model, r.thinking ?? rowProgressModel?.thinking);
|
|
1433
|
+
const line = `${glyph} ${stepLabel}: ${themeBold(theme, agentName)}${contextModeBadge(theme, r.context)}${rowModelDisplay}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}${pendingLabel}`;
|
|
1432
1434
|
c.addChild(new Text(truncLine(` ${line}`, width), 0, 0));
|
|
1433
1435
|
if (rRunning && rProg && "status" in rProg) {
|
|
1434
1436
|
const activity = compactCurrentActivity(rProg);
|