pi-subagents 0.24.2 → 0.24.3

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 CHANGED
@@ -1,7 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.24.3] - 2026-05-14
4
+
5
+ ### Added
6
+ - Show provider-free model and thinking labels in async subagent widgets and status views.
7
+ - Added a packaged `/review-loop` prompt for parent-controlled worker, fresh-reviewer, and fix-worker cycles that can run as an initial async chain or as follow-up subagent runs after async worker completions, stopping when reviewers find no fixes worth doing now or the review-round cap is reached.
8
+
9
+ ### Fixed
10
+ - Let `async: true` chain tool calls run in the background when `clarify` is omitted, and avoid showing the async badge for explicit foreground clarify runs.
11
+
3
12
  ## [Unreleased]
4
13
 
14
+ ### Added
15
+
16
+ ### Fixed
17
+
5
18
  ## [0.24.2] - 2026-05-10
6
19
 
7
20
  ### Fixed
package/README.md CHANGED
@@ -70,6 +70,10 @@ Run parallel reviewers on this diff. I want one focused on correctness, one on t
70
70
  Have worker implement this approved plan. Afterward, run parallel reviewers, summarize their feedback, and apply the fixes that make sense.
71
71
  ```
72
72
 
73
+ ```text
74
+ Run a review loop on this change until reviewers stop finding fixes worth doing, with a max of 3 rounds.
75
+ ```
76
+
73
77
  ```text
74
78
  Use scout to understand the auth flow, then have planner turn that into an implementation plan.
75
79
  ```
@@ -85,6 +89,7 @@ Those are ordinary Pi requests. Pi decides whether to call `subagent`, which age
85
89
  | Review a diff | “Use reviewer to review this diff.” |
86
90
  | Run parallel reviewers | “Run reviewers for correctness, tests, and cleanup.” |
87
91
  | Implement then review | “Implement this, then review it.” |
92
+ | Review until clean | “Run a review loop on this change with a max of 3 rounds.” |
88
93
  | Execute a plan carefully | “Have worker implement this approved plan, then run reviewers and apply the feedback.” |
89
94
  | Scout before planning | “Use scout to inspect the auth flow before planning.” |
90
95
  | Run in the background | “Run this in the background.” |
@@ -185,6 +190,7 @@ The package includes reusable prompt templates for common workflows. You do not
185
190
  | Prompt | Use it for |
186
191
  |--------|------------|
187
192
  | `/parallel-review` | Launch fresh-context reviewers with distinct angles, then synthesize what to fix. |
193
+ | `/review-loop` | Run parent-controlled worker, reviewer, and fix-worker cycles until clean or capped. |
188
194
  | `/parallel-research` | Combine `researcher` and `scout` for external evidence, local code context, and practical tradeoffs. |
189
195
  | `/parallel-context-build` | Run `context-builder` agents in parallel to produce planning handoff context and meta-prompts. |
190
196
  | `/parallel-handoff-plan` | Combine external research and `context-builder` passes into an implementation handoff plan and meta-prompt. |
@@ -583,7 +589,7 @@ The package bundles a `pi-subagents` skill that is automatically available to th
583
589
 
584
590
  What the bundled skill covers:
585
591
  - **Delegation patterns**: when to launch which agent, whether to use single, parallel, chain, or async mode, and whether to use fresh or forked context
586
- - **Prompt workflow recipes**: how to apply the packaged techniques directly with `subagent(...)` when the user describes the workflow in natural language instead of invoking a slash command. This includes parallel review, parallel research, parallel context-build, parallel handoff-plan, gather-context-and-clarify, and parallel cleanup
592
+ - **Prompt workflow recipes**: how to apply the packaged techniques directly with `subagent(...)` when the user describes the workflow in natural language instead of invoking a slash command. This includes parallel review, review-loop, parallel research, parallel context-build, parallel handoff-plan, gather-context-and-clarify, and parallel cleanup
587
593
  - **Role-agent prompting guidance**: compact contract prompts instead of long scripts, what to include in role-specific meta prompts, and retrieval budgets for researchers
588
594
  - **Safety boundaries**: child agents must not run subagents, must not invent intercom targets, and must escalate unapproved decisions
589
595
  - **Intercom conventions**: when to ask vs send, and how parent-side result delivery works with `pi-intercom`
@@ -620,8 +626,8 @@ These are the parameters the LLM passes when it calls the `subagent` tool. Most
620
626
  { agent: "reviewer" }
621
627
  ]}
622
628
 
623
- // Chain without TUI, suitable for background execution
624
- { chain: [...], clarify: false, async: true }
629
+ // Chain in the background, suitable for unblocking the main chat
630
+ { chain: [...], async: true }
625
631
 
626
632
  // Chain with fan-out/fan-in
627
633
  { chain: [
@@ -710,7 +716,7 @@ Agent definitions are not loaded into context by default. Management actions let
710
716
  | `chainDir` | string | temp chain dir | Persistent directory for chain artifacts. |
711
717
  | `clarify` | boolean | true for chains | Show TUI preview/edit flow. |
712
718
  | `agentScope` | `user \| project \| both` | `both` | Agent discovery scope. Project wins on collisions. |
713
- | `async` | boolean | false | Background execution. Chains require `clarify: false`. |
719
+ | `async` | boolean | false | Background execution. For chains, `clarify: true` explicitly keeps the run foreground for the clarify UI. |
714
720
  | `cwd` | string | runtime cwd | Override working directory. |
715
721
  | `maxOutput` | object | 200KB, 5000 lines | Final output truncation limits. |
716
722
  | `artifacts` | boolean | true | Write debug artifacts. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-subagents",
3
- "version": "0.24.2",
3
+ "version": "0.24.3",
4
4
  "description": "Pi extension for delegating tasks to subagents with chains, parallel execution, and TUI clarification",
5
5
  "author": "Nico Bailon",
6
6
  "license": "MIT",
@@ -0,0 +1,41 @@
1
+ ---
2
+ description: Review/fix loop until clean
3
+ ---
4
+
5
+ Run a parent-orchestrated review loop for the requested work.
6
+
7
+ Use the `subagent` tool. Keep the parent session as the loop controller and final decision-maker. Child subagents must receive concrete role-specific tasks; they must not run subagents or manage the loop themselves.
8
+
9
+ Default to a maximum of 3 review rounds unless I specify a different cap. Count a review round each time fresh-context reviewers inspect the current diff after a worker pass. Stop early when reviewers find no blockers or fixes worth doing now.
10
+
11
+ If the invocation includes an implementation request, first launch one async `worker` to implement the approved scope. If the current diff is already the target, start with review. The sequence can be launched up front as an async/background chain when the workflow is already clear, or continued as follow-up subagent runs after each async completion. For an initial chain, pass `async: true` so the main chat is unblocked; do not set `clarify: true` unless I explicitly want the foreground clarify UI. Use only one writer against the active worktree at a time unless I explicitly ask for isolated worktrees.
12
+
13
+ For each review round, launch fresh-context `reviewer` agents in parallel. Reviewers must inspect the repository, relevant instructions, and current diff directly from files and commands. They must not rely on the main conversation history and must not edit files.
14
+
15
+ Choose review angles from the actual change. Common angles are correctness/regressions, tests/validation, and simplicity/maintainability. Add security, performance, docs/API contracts, or user-flow validation when the work calls for it. Prefer three strong reviewers over many vague reviewers.
16
+
17
+ After reviewers return, synthesize their feedback into:
18
+ - blockers or scope/product/architecture decisions that need user approval;
19
+ - fixes worth doing now;
20
+ - optional improvements;
21
+ - feedback to ignore or defer, with a short reason.
22
+
23
+ Do not blindly apply every reviewer suggestion. If reviewers surface an unapproved product, scope, or architecture decision, pause and ask me before launching a fix worker.
24
+
25
+ When an async implementation worker completes, treat its handoff as the transition into review, not as final completion, unless I explicitly asked for worker-only work, review-only output, or to stop after implementation.
26
+
27
+ When there are fixes worth doing now and the workflow is implementation-authorized, launch one async forked `worker` to apply only those synthesized fixes. Ask it to preserve the approved scope, run focused validation, and report changed files, commands run with exit codes, validation evidence, surprises, and anything left undone.
28
+
29
+ After a fix worker returns, run another review round only when it made material changes or addressed non-trivial findings. Do not keep looping for optional polish, speculative improvements, or findings already deferred by the parent.
30
+
31
+ Stop and summarize when one of these is true:
32
+ - reviewers find no blockers or fixes worth doing now;
33
+ - remaining feedback is optional, speculative, or intentionally deferred;
34
+ - reviewers surface an unapproved decision that needs me;
35
+ - the max review-round cap is reached.
36
+
37
+ On completion, inspect the final diff yourself, run or confirm focused validation where appropriate, and summarize the loop: rounds run, fixes applied, validation, remaining deferred items, and why the loop stopped.
38
+
39
+ Additional target, implementation request, max-iteration cap, or review focus from the slash command invocation:
40
+
41
+ $@
@@ -40,6 +40,7 @@ you are guiding a human through an interactive flow.
40
40
 
41
41
  Packaged prompt shortcuts are also available for repeatable workflows. Treat them as reusable orchestration recipes, not just human slash commands. When the user asks for one of these shapes, or when the workflow clearly fits, apply the same pattern directly with `subagent(...)` and other tools:
42
42
  - `/parallel-review` — fresh-context reviewers with distinct review angles, then synthesis
43
+ - `/review-loop` — parent-orchestrated worker, fresh-reviewer, and fix-worker cycles until clean or capped
43
44
  - `/parallel-research` — combine `researcher` and `scout` for external evidence plus local code context
44
45
  - `/parallel-context-build` — parallel `context-builder` passes that produce planning handoff context and meta-prompts
45
46
  - `/parallel-handoff-plan` — external-reference research plus local `context-builder` passes, followed by a synthesis handoff plan and implementation-ready meta-prompt
@@ -54,6 +55,10 @@ The prompt templates in `prompts/` encode workflows the parent agent can run on
54
55
 
55
56
  Use this when the user wants adversarial review of a diff, plan, issue, file, or implemented work. Launch fresh-context `reviewer` agents with distinct angles generated from the actual target. Common angles are correctness/regressions, tests/validation, and simplicity/maintainability; adapt for TypeScript, UI, security, docs, or large structural changes. Reviewers should inspect files and diffs directly, return concise evidence-backed findings with file/line references, and avoid edits unless the user explicitly asks for a writer pass. The parent synthesizes fixes worth doing now, optional improvements, and feedback to ignore/defer before applying anything.
56
57
 
58
+ ### Review-loop technique
59
+
60
+ Use this when the user wants implementation or current diff review to continue until reviewers stop finding fixes worth doing now. Keep the loop in the parent session: one async `worker` implements or fixes, fresh-context `reviewer` agents inspect the actual repo and diff, the parent synthesizes accepted fixes, and one async forked `worker` applies them. The parent can express the sequence up front as an async/background chain when the workflow is known, or continue with explicit follow-up subagent runs after each async completion. For an initial chain, pass `async: true` so the main chat is unblocked; do not set `clarify: true` unless the user explicitly wants the foreground clarify UI. Treat an async implementation worker handoff as an intermediate state, not final completion, unless the user explicitly asked for worker-only work, review-only output, or to stop after implementation. Stop when reviewers find no blockers or fixes worth doing now, remaining feedback is optional or deferred, an unapproved product/scope/architecture decision appears, or the max review-round cap is reached. Default to 3 review rounds unless the user sets a different cap. Do not loop for optional polish, and do not let children launch subagents or decide the loop outcome.
61
+
57
62
  ### Parallel research technique
58
63
 
59
64
  Use this when the question needs both external evidence and local implications. Combine `researcher` for official docs, specs, ecosystem behavior, recent changes, benchmarks, and primary sources with `scout` for repository files, patterns, constraints, tests, and likely integration points. Give each child a distinct angle: external evidence, local code context, and practical tradeoffs. Ask for source links or file ranges, confidence level, gaps, and decision implications. Do not ask these children to edit unless implementation was explicitly requested.
@@ -260,9 +265,11 @@ without forcing each step to rediscover everything.
260
265
 
261
266
  ### Async/background
262
267
 
263
- Use async mode whenever the parent agent should keep working while a child runs. A normal foreground `subagent(...)` call blocks the parent until the child completes; it is appropriate when the next parent step depends on the child result. If you say you will "ask a reviewer while I continue auditing" or otherwise run local work in parallel with a child, launch with `async: true`.
268
+ Prefer async mode for every subagent launch. Set `async: true` no matter the task unless there is a specific reason to opt into a foreground/blocking run. This applies to scouts, researchers, workers, reviewers, validators, oracle checks, one-off delegates, chains, and parallel groups. Keep the write path single-threaded even when the run is async.
264
269
 
265
- Do not end your turn immediately after launching an async child if you promised to keep working. Continue the local inspection or other independent work, then check the async run when its result is needed. If there is no independent work left and you would only be running `sleep` or status polling commands to wait, end your turn instead. Pi will deliver the async completion when it arrives.
270
+ Async does not mean parallel writes. Do not edit the same active worktree while an async worker is changing it. Parent-side overlap should be reading, validation prep, synthesis, command planning, or review of unaffected context unless the writer is isolated in a separate worktree.
271
+
272
+ Do not end your turn immediately after launching an async child if you promised to keep working. Continue the local inspection, synthesis, or validation prep, then check the async run when its result is needed. If there is no independent work left and you would only be running `sleep` or status polling commands to wait, end your turn instead. Pi will deliver the async completion when it arrives.
266
273
 
267
274
  ```typescript
268
275
  subagent({
@@ -360,7 +367,7 @@ subagent({
360
367
  ```
361
368
 
362
369
  Chains default to clarify mode; set `clarify: false` to skip it. Clarify edits affect only the next run; use management actions, settings, or markdown files for persistent changes.
363
- For programmatic background launches, use `clarify: false, async: true`.
370
+ For programmatic background launches, use `async: true`. Set `clarify: false` when you want to bypass chain clarification explicitly; `clarify: true` keeps the run foreground for the clarify UI.
364
371
 
365
372
 
366
373
  ## Worktree Isolation
@@ -410,6 +417,8 @@ subagent({
410
417
  a forked advisory thread that inherits the parent session history and uses that
411
418
  history as a baseline contract.
412
419
 
420
+ Use `oracle` as a smart-friend escalation when the parent needs help with trajectory rather than diff inspection: architectural boundaries, model capability routing, merge conflicts, reviewer disagreement, context drift after long work, a worker about to invent a pattern, or fixes that require product/scope tradeoffs. Ask broad questions when the right concern is unclear, and let `oracle` point out missing context or files the parent should inspect before asking again. Keep `oracle` advisory unless it has been explicitly assigned the single writer role.
421
+
413
422
  ## Subagent + Intercom Coordination
414
423
 
415
424
  `pi-subagents` works without `pi-intercom`. When `pi-intercom` is installed and enabled, the intercom bridge can automatically give child agents a private coordination channel back to the parent session.
@@ -539,9 +548,10 @@ copying a full builtin file.
539
548
  ## Prompt Template Integration
540
549
 
541
550
  The package includes prompt shortcuts for common workflows: `/parallel-review`,
542
- `/parallel-research`, `/parallel-context-build`, `/parallel-handoff-plan`,
543
- `/gather-context-and-clarify`, and `/parallel-cleanup`. Use them when the user
544
- wants repeatable review, research, context handoff, implementation handoff,
551
+ `/review-loop`, `/parallel-research`, `/parallel-context-build`,
552
+ `/parallel-handoff-plan`, `/gather-context-and-clarify`, and
553
+ `/parallel-cleanup`. Use them when the user wants repeatable review,
554
+ review/fix loops, research, context handoff, implementation handoff,
545
555
  clarification, or cleanup-review patterns. `/parallel-review autofix` and
546
556
  `/parallel-cleanup autofix` synthesize reviewer feedback and then apply only the
547
557
  fixes worth doing now. Parent agents can also apply the same recipes directly
@@ -570,10 +580,13 @@ particular agent or with forked context.
570
580
 
571
581
  ## Best Practices
572
582
 
583
+ ### Prefer async orchestration
584
+
585
+ Launch every subagent asynchronously by default. Use `async: true` for scouts, researchers, workers, reviewers, validators, oracle checks, one-off delegates, chains, and parallel groups unless you intentionally need a foreground/blocking run. The parent should keep moving: inspect code while scouts run, prepare validation while a worker implements, do a local diff pass while reviewers review, and synthesize or verify while a fix worker applies accepted feedback. Async is the default orchestration posture; foreground runs are the explicit opt-out.
586
+
573
587
  ### Keep writes single-threaded by default
574
588
 
575
- A strong pattern is one main decision-maker plus advisory/research/review
576
- subagents around it. Use `oracle` for advice and `worker` for the actual write path.
589
+ A strong pattern is one main decision-maker plus advisory/research/review/validation subagents around it. Use `oracle` for advice and `worker` for the actual write path. Parallelize reading, review, validation, and synthesis support, not normal writes, unless you deliberately isolate writers with worktrees. A child that writes should report what changed, what was left undone, commands run with exit codes, validation evidence, surprises, and any decisions that need parent approval.
577
590
 
578
591
  ### Use fork for branched advisory or execution threads
579
592
 
@@ -625,6 +638,7 @@ When the user approves launching a subagent to carry out a plan or workflow, tre
625
638
 
626
639
  - `/gather-context-and-clarify` maps to: launch `scout` and, when needed, `researcher`; synthesize findings; then use `interview` to ask every clarification question needed for shared understanding.
627
640
  - `/parallel-review` maps to: launch fresh-context `reviewer` agents with distinct review angles; synthesize the feedback before applying anything.
641
+ - `/review-loop` maps to: keep the parent in charge of worker → fresh reviewers → synthesized fix worker cycles until no fixes worth doing now remain, an unapproved decision appears, or the review-round cap is reached.
628
642
  - `/parallel-research` maps to: combine local `scout` context with external `researcher` evidence when current docs, ecosystem behavior, or API details matter.
629
643
  - `/parallel-context-build` maps to: run a chain-mode parallel group of `context-builder` agents with distinct temp output paths, then synthesize their context and meta-prompt sections.
630
644
  - `/parallel-handoff-plan` maps to: run external `researcher` plus local/strategy `context-builder` passes, then a synthesis `context-builder` that writes an implementation handoff plan and implementation-ready meta-prompt.
@@ -633,26 +647,36 @@ When the user approves launching a subagent to carry out a plan or workflow, tre
633
647
  For feature work, use this sequence as scaffolding for parent-agent behavior:
634
648
 
635
649
  ```text
636
- clarify → planner → worker → parallel fresh-context reviewers → worker
650
+ clarify → validation contract → planner → async worker → parallel async fresh-context reviewers/validatorsasync fix worker → follow-up review when warranted → parent review
637
651
  ```
638
652
 
639
- The first `worker` implements the approved plan. The parallel reviewers inspect the resulting diff from fresh context. The final `worker` applies synthesized review fixes in forked context. Do not stop after parallel review unless the user explicitly asked for review-only output or the review surfaced a decision that needs approval first.
653
+ The validation contract defines what done means before code is written: expected behavior, acceptance checks, commands or user flows to exercise, and evidence the worker should return. Keep it lightweight for small tasks, but make it explicit enough that reviewers and validators are checking the intended outcome rather than the worker’s own assumptions.
654
+
655
+ The first `worker` implements the approved plan. The parent continues with independent inspection or validation prep while it runs, not parallel edits to the same worktree. When the async worker completes, treat its handoff as the transition into review, not as final completion, unless the user explicitly asked for worker-only work, review-only output, or to stop after implementation. Parallel reviewers inspect the resulting diff from fresh context. Validators check behavior with the best available evidence: commands, tests, browser/CLI interaction, screenshots, logs, or manual reproduction notes. The final `worker` applies synthesized review fixes in forked context, then the parent looks over the final diff before completing. The parent may launch these steps as an initial async chain when the workflow is already clear, or as follow-up subagent runs after each async completion. Initial chains should pass `async: true` so the main chat is unblocked; avoid `clarify: true` unless the user asked for foreground clarification. Do not stop after parallel review unless the user explicitly asked for review-only output or the review surfaced a decision that needs approval first.
656
+
657
+ For complex work, risky changes, broad refactors, or many changed lines, increase review and validation fanout rather than trusting one reviewer. Use distinct angles such as correctness/regressions, tests/validation, simplicity/maintainability, security/privacy, performance, docs/API contracts, and user-flow behavior. When reviewers find non-trivial issues or the fix worker touches many lines, run another focused review round before final validation.
658
+
659
+ For very large work, split into serial milestones instead of launching a swarm of writers. Each milestone gets one writer, a validation contract, fresh-context review/validation, a fix pass, and parent acceptance before the next milestone starts. Use parallel subagents inside a milestone for read-only context, research, review, and validation only.
640
660
 
641
661
  Keep orchestration authority in the parent session. Child subagents should not launch more subagents, read this skill, or run their own orchestration loops. Spawned subagents do not receive the `pi-subagents` skill, parent-only status/control/slash messages, prior parent `subagent` tool-call/tool-result artifacts, or the `subagent` extension tool. Child context filtering also strips old hidden orchestration-instruction messages when they appear in inherited history. Every child also receives a boundary instruction that says the parent owns orchestration, the child must not propose or run subagents, and implementation children must call real edit/write tools instead of printing pseudo tool calls. Pass children concrete role-specific work instead.
642
662
 
643
663
  1. Clarify first. This is mandatory. Gather code context with `scout` or `context-builder`, add `researcher` only when external evidence matters, then ask the user clarifying questions with `interview` until scope, acceptance criteria, constraints, and non-goals are clear.
644
- 2. Plan when useful. For complex work, call `planner` or write a plan doc yourself and get approval before implementation. For simple work, confirm shared understanding and explicitly note why planning is skipped.
645
- 3. Implement with one writer. After approval, launch `worker` with a proper meta prompt that includes clarified requirements, relevant context, plan path or summary, acceptance criteria, and validation expectations. Packaged `worker` defaults to forked context; pass `context: "fresh"` only when you intentionally want a fresh child.
646
- 4. Review after implementation. After the worker completes, launch parallel fresh-context `reviewer` agents for correctness/regressions, tests/validation, and simplicity/maintainability. Use `output: false` unless review artifacts are explicitly needed.
647
- 5. Synthesize, then run the fix worker. Separate blockers, fixes worth doing now, optional improvements, and feedback to ignore/defer, then launch a forked `worker` to apply fixes worth doing now when the workflow is implementation-authorized. If reviewers found scope/product/architecture choices that were not approved, ask the user first instead of applying them.
648
- 6. Validate and complete. After the fix worker returns, run or confirm focused validation, update docs/changelog when relevant, and summarize what changed and why.
664
+ 2. Define the validation contract. State what done means before implementation: expected behavior, checks to run, user flows to exercise, and evidence required in the worker handoff. For UI, CLI, integration, or workflow changes, include at least one validator angle that uses the product the way a user would rather than only reading code.
665
+ 3. Plan when useful. For complex work, call `planner` or write a plan doc yourself and get approval before implementation. For simple work, confirm shared understanding and explicitly note why planning is skipped.
666
+ 4. Implement with one writer. After approval, launch `worker` asynchronously with a proper meta prompt that includes clarified requirements, relevant context, plan path or summary, the validation contract, and output expectations. Packaged `worker` defaults to forked context; pass `context: "fresh"` only when you intentionally want a fresh child. While it runs, prepare validation or inspect adjacent code instead of editing the same worktree.
667
+ 5. Require a useful worker handoff. Ask the worker to report changed files, what was implemented, what was left undone, commands run with exit codes, validation evidence, surprises or new risks, decisions made inside approved scope, and decisions needing parent approval.
668
+ 6. Review after implementation. After the worker completes, launch parallel async fresh-context `reviewer` agents for correctness/regressions, tests/validation, and simplicity/maintainability. Add security, performance, docs/API, domain-specific, or user-flow validators for complex work, risky changes, broad refactors, or many changed lines. Use `output: false` unless review artifacts are explicitly needed.
669
+ 7. Synthesize, then run the fix worker. Separate blockers, fixes worth doing now, optional improvements, and feedback to ignore/defer, then launch an async forked `worker` to apply fixes worth doing now when the workflow is implementation-authorized. If reviewers found scope/product/architecture choices that were not approved, ask the user first instead of applying them.
670
+ 8. Review again when warranted. If the fix worker made substantial changes or addressed non-trivial findings, run another focused parallel review round before final validation.
671
+ 9. Validate and complete. After the fix worker and any follow-up review return, inspect the final diff yourself, run or confirm focused validation, update docs/changelog when relevant, and summarize what changed and why.
649
672
 
650
673
  Example implementation handoff after clarification and optional planning:
651
674
 
652
675
  ```typescript
653
676
  subagent({
654
677
  agent: "worker",
655
- task: "Implement the approved feature.\n\nClarified requirements:\n- ...\n\nPlan: see ~/Documents/docs/...-plan.md\n\nValidation expected:\n- ..."
678
+ task: "Implement the approved feature.\n\nClarified requirements:\n- ...\n\nPlan: see ~/Documents/docs/...-plan.md\n\nValidation contract:\n- ...\n\nReturn a handoff with changed files, what was implemented, what was left undone, commands run with exit codes, validation evidence, surprises/new risks, and decisions needing parent approval.",
679
+ async: true
656
680
  })
657
681
  ```
658
682
 
@@ -661,12 +685,13 @@ Example review pass after implementation:
661
685
  ```typescript
662
686
  subagent({
663
687
  tasks: [
664
- { agent: "reviewer", task: "Review the current diff for correctness and regressions. Inspect changed files directly.", output: false },
665
- { agent: "reviewer", task: "Review the current diff for tests and validation quality. Inspect changed files directly.", output: false },
688
+ { agent: "reviewer", task: "Review the current diff for correctness and regressions. Inspect changed files directly; do not rely on the worker's reasoning.", output: false },
689
+ { agent: "reviewer", task: "Review the current diff for tests and validation quality against the validation contract. Inspect changed files directly.", output: false },
666
690
  { agent: "reviewer", task: "Review the current diff for simplicity and maintainability. Inspect changed files directly.", output: false }
667
691
  ],
668
692
  concurrency: 3,
669
- context: "fresh"
693
+ context: "fresh",
694
+ async: true
670
695
  })
671
696
  ```
672
697
 
@@ -675,13 +700,18 @@ Example fix worker after parallel reviews:
675
700
  ```typescript
676
701
  subagent({
677
702
  agent: "worker",
678
- task: "Apply the synthesized reviewer feedback below. Only apply fixes worth doing now; preserve user-approved scope; ask before unapproved product or architecture changes. Run focused validation and summarize what changed.\n\nReviewer synthesis:\n..."
703
+ task: "Apply the synthesized reviewer feedback below. Only apply fixes worth doing now; preserve user-approved scope; ask before unapproved product or architecture changes. Run focused validation and summarize what changed.\n\nReviewer synthesis:\n...",
704
+ async: true
679
705
  })
680
706
  ```
681
707
 
682
708
  ### Review loop
683
709
 
684
- Do not treat review as the final step for implementation work. Use the implementation, fresh-reviewer, and fix-worker examples above: run reviewers, synthesize their findings, then launch a final `worker` for accepted fixes.
710
+ Do not treat review as the final step for implementation work. Run reviewers and validators, synthesize their findings against user scope and the validation contract, then launch one `worker` for accepted fixes when implementation is authorized.
711
+
712
+ When an async implementation worker completes, treat the worker handoff as an intermediate state. The next parent action is review fanout, then synthesis, then a fix worker if reviewers found fixes worth doing now. This can be planned as an initial async chain when the whole workflow is known, or continued as follow-up subagent runs when the parent only launched the first worker initially. Initial chains should pass `async: true` so the main chat is unblocked; `clarify: true` is the explicit foreground opt-in.
713
+
714
+ For explicit review-loop requests, repeat worker → fresh-reviewer → synthesized-fix-worker cycles until reviewers find no blockers or fixes worth doing now, remaining feedback is optional or intentionally deferred, an unapproved product/scope/architecture decision needs the user, or the max review-round cap is reached. Default to 3 review rounds unless the user sets a different cap. For complex work, many changed lines, or any fix pass that materially changes the diff, run another focused review round before the parent’s final look; otherwise stop instead of chasing optional polish.
685
715
 
686
716
  ### Parallel non-conflicting analysis
687
717
 
@@ -445,7 +445,7 @@ DIAGNOSTICS:
445
445
  }
446
446
  const isParallel = (args.tasks?.length ?? 0) > 0;
447
447
  const parallelCount = effectiveParallelTaskCount(args.tasks as Array<{ count?: unknown }> | undefined);
448
- const asyncLabel = args.async === true && !isParallel ? theme.fg("warning", " [async]") : "";
448
+ const asyncLabel = args.async === true && args.clarify !== true && !isParallel ? theme.fg("warning", " [async]") : "";
449
449
  if (args.chain?.length)
450
450
  return new Text(
451
451
  `${theme.fg("toolTitle", theme.bold("subagent "))}chain (${args.chain.length})${asyncLabel}`,
@@ -152,7 +152,7 @@ export const SubagentParams = Type.Object({
152
152
  Type.String({ description: "Directory to store session logs (default: temp; enables sessions even if share=false)" }),
153
153
  ),
154
154
  // Clarification TUI
155
- clarify: Type.Optional(Type.Boolean({ description: "Show TUI to preview/edit before execution (default: true for chains, false for single/parallel). Implies sync mode." })),
155
+ clarify: Type.Optional(Type.Boolean({ description: "Show TUI to preview/edit before execution. Explicit clarify: true keeps the run foreground for the clarify UI; omitted clarify can still run in the background when async: true is set." })),
156
156
  control: Type.Optional(ControlOverrides),
157
157
  // Solo agent overrides
158
158
  output: Type.Optional(Type.Unsafe({
@@ -18,6 +18,7 @@ import { resolvePiPackageRoot } from "../shared/pi-spawn.ts";
18
18
  import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "../../agents/skills.ts";
19
19
  import { resolveChildCwd } from "../../shared/utils.ts";
20
20
  import { buildModelCandidates, resolveModelCandidate, type AvailableModelInfo } from "../shared/model-fallback.ts";
21
+ import { resolveEffectiveThinking } from "../../shared/model-info.ts";
21
22
  import { resolveExpectedWorktreeAgentCwd } from "../shared/worktree.ts";
22
23
  import {
23
24
  type ArtifactConfig,
@@ -309,11 +310,13 @@ export function executeAsyncChain(
309
310
  const task = injectSingleOutputInstruction(`${readInstructions.prefix}${s.task ?? "{previous}"}${progressInstructions.suffix}`, outputPath);
310
311
 
311
312
  const primaryModel = resolveModelCandidate(behavior.model ?? a.model, availableModels, ctx.currentModelProvider);
313
+ const model = applyThinkingSuffix(primaryModel, a.thinking);
312
314
  return {
313
315
  agent: s.agent,
314
316
  task,
315
317
  cwd: stepCwd,
316
- model: applyThinkingSuffix(primaryModel, a.thinking),
318
+ model,
319
+ thinking: resolveEffectiveThinking(model, a.thinking),
317
320
  modelCandidates: buildModelCandidates(behavior.model ?? a.model, a.fallbackModels, availableModels, ctx.currentModelProvider).map((candidate) =>
318
321
  applyThinkingSuffix(candidate, a.thinking),
319
322
  ),
@@ -525,6 +528,10 @@ export function executeAsyncSingle(
525
528
  const validationError = validateFileOnlyOutputMode(outputMode, outputPath, `Async single run (${agent})`);
526
529
  if (validationError) return formatAsyncStartError("single", validationError);
527
530
  const taskWithOutputInstruction = injectSingleOutputInstruction(task, outputPath);
531
+ const model = applyThinkingSuffix(
532
+ resolveModelCandidate(params.modelOverride ?? agentConfig.model, availableModels, ctx.currentModelProvider),
533
+ agentConfig.thinking,
534
+ );
528
535
  let spawnResult: { pid?: number; error?: string } = {};
529
536
  try {
530
537
  spawnResult = spawnRunner(
@@ -535,7 +542,8 @@ export function executeAsyncSingle(
535
542
  agent,
536
543
  task: taskWithOutputInstruction,
537
544
  cwd: runnerCwd,
538
- model: applyThinkingSuffix(resolveModelCandidate(params.modelOverride ?? agentConfig.model, availableModels, ctx.currentModelProvider), agentConfig.thinking),
545
+ model,
546
+ thinking: resolveEffectiveThinking(model, agentConfig.thinking),
539
547
  modelCandidates: buildModelCandidates(params.modelOverride ?? agentConfig.model, agentConfig.fallbackModels, availableModels, ctx.currentModelProvider).map((candidate) =>
540
548
  applyThinkingSuffix(candidate, agentConfig.thinking),
541
549
  ),
@@ -1,6 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import { formatDuration, formatTokens, shortenPath } from "../../shared/formatters.ts";
3
+ import { formatDuration, formatModelThinking, formatTokens, shortenPath } from "../../shared/formatters.ts";
4
4
  import { formatActivityLabel, formatParallelOutcome } from "../../shared/status-format.ts";
5
5
  import { type ActivityState, type AsyncJobStep, type AsyncParallelGroupStatus, type AsyncStatus, type SubagentRunMode, type TokenUsage } from "../../shared/types.ts";
6
6
  import { readStatus } from "../../shared/utils.ts";
@@ -25,6 +25,7 @@ interface AsyncRunStepSummary {
25
25
  tokens?: TokenUsage;
26
26
  skills?: string[];
27
27
  model?: string;
28
+ thinking?: string;
28
29
  attemptedModels?: string[];
29
30
  error?: string;
30
31
  }
@@ -160,6 +161,7 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
160
161
  ...(step.tokens ? { tokens: step.tokens } : {}),
161
162
  ...(step.skills ? { skills: step.skills } : {}),
162
163
  ...(step.model ? { model: step.model } : {}),
164
+ ...(step.thinking ? { thinking: step.thinking } : {}),
163
165
  ...(step.attemptedModels ? { attemptedModels: step.attemptedModels } : {}),
164
166
  ...(step.error ? { error: step.error } : {}),
165
167
  };
@@ -235,7 +237,8 @@ function formatStepLine(step: AsyncRunStepSummary): string {
235
237
  const parts = [`${step.index + 1}. ${step.agent}`, step.status];
236
238
  const activity = formatActivityFacts(step);
237
239
  if (activity) parts.push(activity);
238
- if (step.model) parts.push(step.model);
240
+ const modelThinking = formatModelThinking(step.model, step.thinking);
241
+ if (modelThinking) parts.push(modelThinking);
239
242
  if (step.durationMs !== undefined) parts.push(formatDuration(step.durationMs));
240
243
  if (step.tokens) parts.push(`${formatTokens(step.tokens.total)} tok`);
241
244
  return parts.join(" | ");
@@ -2,6 +2,7 @@ import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import type { AgentToolResult } from "@earendil-works/pi-agent-core";
4
4
  import { formatAsyncRunList, formatAsyncRunOutputPath, formatAsyncRunProgressLabel, listAsyncRuns } from "./async-status.ts";
5
+ import { formatModelThinking } from "../../shared/formatters.ts";
5
6
  import { formatActivityLabel } from "../../shared/status-format.ts";
6
7
  import { ASYNC_DIR, RESULTS_DIR, type AsyncStatus, type Details } from "../../shared/types.ts";
7
8
  import { resolveSubagentIntercomTarget } from "../../intercom/intercom-bridge.ts";
@@ -142,8 +143,10 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
142
143
  ].filter((line): line is string => Boolean(line));
143
144
  for (const [index, step] of (status.steps ?? []).entries()) {
144
145
  const stepActivityText = step.status === "running" ? formatActivityLabel(step.lastActivityAt, step.activityState) : undefined;
146
+ const modelThinking = formatModelThinking(step.model, step.thinking);
147
+ const modelText = modelThinking ? ` (${modelThinking})` : "";
145
148
  const errorText = step.error ? `, error: ${step.error}` : "";
146
- lines.push(`${stepLineLabel(status, index)}: ${step.agent} ${step.status}${stepActivityText ? `, ${stepActivityText}` : ""}${errorText}`);
149
+ lines.push(`${stepLineLabel(status, index)}: ${step.agent} ${step.status}${modelText}${stepActivityText ? `, ${stepActivityText}` : ""}${errorText}`);
147
150
  const stepOutputPath = path.join(asyncDir, `output-${index}.log`);
148
151
  if (stepOutputPath !== outputPath && fs.existsSync(stepOutputPath)) lines.push(` Output: ${stepOutputPath}`);
149
152
  if (step.status === "running") {
@@ -66,6 +66,7 @@ import {
66
66
  formatWorktreeTaskCwdConflict,
67
67
  type WorktreeSetup,
68
68
  } from "../shared/worktree.ts";
69
+ import { resolveEffectiveThinking } from "../../shared/model-info.ts";
69
70
  import { writeInitialProgressFile } from "../../shared/settings.ts";
70
71
 
71
72
  interface SubagentRunConfig {
@@ -545,6 +546,7 @@ interface SingleStepContext {
545
546
  registerInterrupt?: (interrupt: (() => void) | undefined) => void;
546
547
  childIntercomTarget?: string;
547
548
  orchestratorIntercomTarget?: string;
549
+ onAttemptStart?: (attempt: { model?: string; thinking?: string }) => void;
548
550
  onChildEvent?: (event: ChildEvent) => void;
549
551
  }
550
552
 
@@ -596,6 +598,7 @@ async function runSingleStep(
596
598
 
597
599
  for (let index = 0; index < candidates.length; index++) {
598
600
  const candidate = candidates[index];
601
+ ctx.onAttemptStart?.({ model: candidate, thinking: resolveEffectiveThinking(candidate, step.thinking) });
599
602
  const outputSnapshot = captureSingleOutputSnapshot(step.outputPath);
600
603
  const { args, env, tempDir } = buildPiArgs({
601
604
  baseArgs: ["--mode", "json", "-p"],
@@ -912,6 +915,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
912
915
  ...(step.sessionFile ? { sessionFile: step.sessionFile } : {}),
913
916
  skills: step.skills,
914
917
  model: step.model,
918
+ thinking: step.thinking,
915
919
  attemptedModels: step.modelCandidates && step.modelCandidates.length > 0 ? step.modelCandidates : step.model ? [step.model] : undefined,
916
920
  recentTools: [],
917
921
  recentOutput: [],
@@ -1007,6 +1011,14 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
1007
1011
  appendControlEvent(event);
1008
1012
  return true;
1009
1013
  };
1014
+ const updateStepModel = (flatIndex: number, model: string | undefined, thinking: string | undefined, now = Date.now()): void => {
1015
+ const step = statusPayload.steps[flatIndex];
1016
+ if (!step) return;
1017
+ step.model = model;
1018
+ step.thinking = thinking;
1019
+ statusPayload.lastUpdate = now;
1020
+ writeAtomicJson(statusPath, statusPayload);
1021
+ };
1010
1022
  const updateStepFromChildEvent = (flatIndex: number, event: ChildEvent): void => {
1011
1023
  const step = statusPayload.steps[flatIndex];
1012
1024
  if (!step) return;
@@ -1332,6 +1344,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
1332
1344
  registerInterrupt: (interrupt) => {
1333
1345
  activeChildInterrupt = interrupt;
1334
1346
  },
1347
+ onAttemptStart: (attempt) => updateStepModel(fi, attempt.model, attempt.thinking),
1335
1348
  onChildEvent: (event) => updateStepFromChildEvent(fi, event),
1336
1349
  });
1337
1350
  if (task.sessionFile) {
@@ -1346,6 +1359,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
1346
1359
  statusPayload.steps[fi].durationMs = taskDuration;
1347
1360
  statusPayload.steps[fi].exitCode = singleResult.exitCode;
1348
1361
  statusPayload.steps[fi].model = singleResult.model;
1362
+ statusPayload.steps[fi].thinking = resolveEffectiveThinking(singleResult.model, statusPayload.steps[fi].thinking);
1349
1363
  statusPayload.steps[fi].attemptedModels = singleResult.attemptedModels;
1350
1364
  statusPayload.steps[fi].modelAttempts = singleResult.modelAttempts;
1351
1365
  statusPayload.steps[fi].error = singleResult.error;
@@ -1475,6 +1489,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
1475
1489
  registerInterrupt: (interrupt) => {
1476
1490
  activeChildInterrupt = interrupt;
1477
1491
  },
1492
+ onAttemptStart: (attempt) => updateStepModel(flatIndex, attempt.model, attempt.thinking),
1478
1493
  onChildEvent: (event) => updateStepFromChildEvent(flatIndex, event),
1479
1494
  });
1480
1495
  if (seqStep.sessionFile) {
@@ -1522,6 +1537,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
1522
1537
  statusPayload.steps[flatIndex].durationMs = stepEndTime - stepStartTime;
1523
1538
  statusPayload.steps[flatIndex].exitCode = singleResult.exitCode;
1524
1539
  statusPayload.steps[flatIndex].model = singleResult.model;
1540
+ statusPayload.steps[flatIndex].thinking = resolveEffectiveThinking(singleResult.model, statusPayload.steps[flatIndex].thinking);
1525
1541
  statusPayload.steps[flatIndex].attemptedModels = singleResult.attemptedModels;
1526
1542
  statusPayload.steps[flatIndex].modelAttempts = singleResult.modelAttempts;
1527
1543
  statusPayload.steps[flatIndex].error = singleResult.error;
@@ -2127,9 +2127,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
2127
2127
  return toExecutionErrorResult(effectiveParams, error);
2128
2128
  }
2129
2129
  const requestedAsync = effectiveParams.async ?? deps.asyncByDefault;
2130
- const backgroundRequestedWhileClarifying = hasTasks && requestedAsync && effectiveParams.clarify === true;
2131
- const effectiveAsync = requestedAsync
2132
- && (hasChain ? effectiveParams.clarify === false : effectiveParams.clarify !== true);
2130
+ const backgroundRequestedWhileClarifying = (hasChain || hasTasks) && requestedAsync && effectiveParams.clarify === true;
2131
+ const effectiveAsync = requestedAsync && effectiveParams.clarify !== true;
2133
2132
  const controlConfig = resolveControlConfig(deps.config.control, effectiveParams.control);
2134
2133
 
2135
2134
  const artifactConfig: ArtifactConfig = {
@@ -3,6 +3,7 @@ export interface RunnerSubagentStep {
3
3
  task: string;
4
4
  cwd?: string;
5
5
  model?: string;
6
+ thinking?: string;
6
7
  modelCandidates?: string[];
7
8
  tools?: string[];
8
9
  extensions?: string[];
@@ -7,6 +7,7 @@ import * as path from "node:path";
7
7
  import type { Usage, SingleResult } from "./types.ts";
8
8
  import type { ChainStep } from "./settings.ts";
9
9
  import { isParallelStep } from "./settings.ts";
10
+ import { splitKnownThinkingSuffix, THINKING_LEVELS } from "./model-info.ts";
10
11
 
11
12
  /**
12
13
  * Format token count with k suffix for large numbers
@@ -15,6 +16,18 @@ export function formatTokens(n: number): string {
15
16
  return n < 1000 ? String(n) : n < 10000 ? `${(n / 1000).toFixed(1)}k` : `${Math.round(n / 1000)}k`;
16
17
  }
17
18
 
19
+ export function formatModelThinking(model?: string, thinking?: string): string {
20
+ const parsed = model ? splitKnownThinkingSuffix(model) : undefined;
21
+ let displayModel = parsed?.baseModel ?? model;
22
+ const explicitThinking = THINKING_LEVELS.find((level) => level === thinking?.trim());
23
+ const displayThinking = parsed?.thinkingSuffix ? parsed.thinkingSuffix.slice(1) : explicitThinking;
24
+ if (displayModel) {
25
+ const slashIdx = displayModel.lastIndexOf("/");
26
+ if (slashIdx !== -1) displayModel = displayModel.slice(slashIdx + 1);
27
+ }
28
+ return [displayModel, displayThinking ? `thinking ${displayThinking}` : undefined].filter(Boolean).join(" · ");
29
+ }
30
+
18
31
  /**
19
32
  * Format usage statistics into a compact string
20
33
  */
@@ -27,6 +27,16 @@ export function toModelInfo(model: RegistryModelLike): ModelInfo {
27
27
  };
28
28
  }
29
29
 
30
+ /** Resolve the effective thinking level from a model string (which may contain a known suffix like `:high`)
31
+ * and an explicit thinking config value. Returns `undefined` when no thinking is applicable
32
+ * (e.g. no model was specified, or the model has no suffix and no config was provided). */
33
+ export function resolveEffectiveThinking(model: string | undefined, configThinking: string | undefined): string | undefined {
34
+ if (!model) return undefined;
35
+ const { thinkingSuffix } = splitKnownThinkingSuffix(model);
36
+ if (thinkingSuffix) return thinkingSuffix.slice(1);
37
+ return THINKING_LEVELS.find((level) => level === configThinking);
38
+ }
39
+
30
40
  export function splitKnownThinkingSuffix(model: string): { baseModel: string; thinkingSuffix: string } {
31
41
  const colonIdx = model.lastIndexOf(":");
32
42
  if (colonIdx === -1) return { baseModel: model, thinkingSuffix: "" };
@@ -315,6 +315,7 @@ export interface AsyncStatus {
315
315
  tokens?: TokenUsage;
316
316
  skills?: string[];
317
317
  model?: string;
318
+ thinking?: string;
318
319
  attemptedModels?: string[];
319
320
  modelAttempts?: ModelAttempt[];
320
321
  error?: string;
package/src/tui/render.ts CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  MAX_WIDGET_JOBS,
16
16
  WIDGET_KEY,
17
17
  } from "../shared/types.ts";
18
- import { formatTokens, formatUsage, formatDuration, formatToolCall, shortenPath } from "../shared/formatters.ts";
18
+ import { formatTokens, formatUsage, formatDuration, formatModelThinking, formatToolCall, shortenPath } from "../shared/formatters.ts";
19
19
  import { getDisplayItems, getLastActivity, getSingleResultOutput } from "../shared/utils.ts";
20
20
  import { flatToLogicalStepIndex } from "../runs/background/parallel-groups.ts";
21
21
  import { aggregateStepStatus, formatActivityLabel, formatAgentRunningLabel, formatParallelOutcome } from "../shared/status-format.ts";
@@ -357,7 +357,8 @@ function widgetParallelAgentDetails(job: AsyncJobState, theme: Theme): string[]
357
357
  const marker = index === job.steps!.length - 1 ? "└" : "├";
358
358
  const activity = widgetStepActivity(step);
359
359
  const itemTitle = job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step";
360
- return ` ${theme.fg("dim", `${marker} ${widgetStepGlyph(step.status, theme)} ${itemTitle} ${index + 1}/${total}: ${step.agent} · ${widgetStepStatus(step.status, theme)}${activity ? ` · ${activity}` : ""}`)}`;
360
+ const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
361
+ return ` ${theme.fg("dim", `${marker} ${widgetStepGlyph(step.status, theme)} ${itemTitle} ${index + 1}/${total}: ${step.agent} · ${widgetStepStatus(step.status, theme)}${modelDisplay}${activity ? ` · ${activity}` : ""}`)}`;
361
362
  });
362
363
  }
363
364
 
@@ -576,6 +577,11 @@ function widgetStepStats(theme: Theme, step: NonNullable<AsyncJobState["steps"]>
576
577
  ]);
577
578
  }
578
579
 
580
+ function modelThinkingBadge(theme: Theme, model?: string, thinking?: string): string {
581
+ const label = formatModelThinking(model, thinking);
582
+ return label ? theme.fg("dim", ` (${label})`) : "";
583
+ }
584
+
579
585
  function widgetStepActivityLine(step: NonNullable<AsyncJobState["steps"]>[number], width: number, expanded: boolean): string {
580
586
  const toolLine = formatCurrentToolLine(step, width, expanded);
581
587
  if (toolLine) return toolLine;
@@ -602,7 +608,8 @@ function foregroundStyleWidgetStepLines(
602
608
  ): string[] {
603
609
  const status = widgetStepStatus(step.status, theme);
604
610
  const stats = widgetStepStats(theme, step);
605
- const lines = [` ${widgetStepGlyph(step.status, theme)} ${itemTitle} ${index}/${total}: ${themeBold(theme, step.agent)} ${theme.fg("dim", "·")} ${status}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`];
611
+ const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
612
+ const lines = [` ${widgetStepGlyph(step.status, theme)} ${itemTitle} ${index}/${total}: ${themeBold(theme, step.agent)} ${theme.fg("dim", "·")} ${status}${modelDisplay}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`];
606
613
  const activity = widgetStepActivityLine(step, width, expanded);
607
614
  if (activity) lines.push(` ${theme.fg("dim", `⎿ ${activity}`)}`);
608
615
  if (step.status === "running") {
@@ -661,7 +668,8 @@ function compactSingleWidgetLines(job: AsyncJobState, theme: Theme, width: numbe
661
668
  const activity = widgetStepActivityLine(step, width, false);
662
669
  const stepStats = widgetStepStats(theme, step);
663
670
  const activitySuffix = activity ? ` ${theme.fg("dim", "·")} ${theme.fg("dim", activity)}` : "";
664
- lines.push(` ${widgetStepGlyph(step.status, theme)} ${itemTitle} ${index + 1}/${total}: ${themeBold(theme, step.agent)} ${theme.fg("dim", "·")} ${status}${activitySuffix}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}`);
671
+ const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
672
+ lines.push(` ${widgetStepGlyph(step.status, theme)} ${itemTitle} ${index + 1}/${total}: ${themeBold(theme, step.agent)} ${theme.fg("dim", "·")} ${status}${modelDisplay}${activitySuffix}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}`);
665
673
  }
666
674
  if (job.steps.some((step) => step.status === "running")) lines.push(theme.fg("accent", " Press Ctrl+O for live detail"));
667
675
  return lines.map((line) => truncLine(line, width));
@@ -837,7 +845,8 @@ function renderSingleCompact(d: Details, r: Details["results"][number], theme: T
837
845
  ]);
838
846
  const c = new Container();
839
847
  const width = getTermWidth() - 4;
840
- c.addChild(new Text(truncLine(`${resultGlyph(r, output, theme, isRunning)} ${theme.fg("toolTitle", theme.bold(r.agent))}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`, width), 0, 0));
848
+ const modelDisplay = modelThinkingBadge(theme, r.model);
849
+ c.addChild(new Text(truncLine(`${resultGlyph(r, output, theme, isRunning)} ${theme.fg("toolTitle", theme.bold(r.agent))}${modelDisplay}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`, width), 0, 0));
841
850
 
842
851
  if (isRunning && r.progress) {
843
852
  const activity = compactCurrentActivity(r.progress);
@@ -1166,7 +1175,7 @@ export function renderSubagentResult(
1166
1175
  ? theme.fg("warning", "warning")
1167
1176
  : theme.fg("success", "done");
1168
1177
  const stats = rProg ? ` | ${rProg.toolCount} tools, ${formatDuration(rProg.durationMs)}` : "";
1169
- const modelDisplay = r.model ? theme.fg("dim", ` (${r.model})`) : "";
1178
+ const modelDisplay = modelThinkingBadge(theme, r.model);
1170
1179
  const stepLabel = resultRowLabel(d, multiLabel, i, stepNumber);
1171
1180
  const stepHeader = rRunning
1172
1181
  ? `${statusIcon} ${stepLabel}: ${theme.bold(theme.fg("warning", r.agent))}${modelDisplay}${stats}`