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 +13 -0
- package/README.md +10 -4
- package/package.json +1 -1
- package/prompts/review-loop.md +41 -0
- package/skills/pi-subagents/SKILL.md +51 -21
- package/src/extension/index.ts +1 -1
- package/src/extension/schemas.ts +1 -1
- package/src/runs/background/async-execution.ts +10 -2
- package/src/runs/background/async-status.ts +5 -2
- package/src/runs/background/run-status.ts +4 -1
- package/src/runs/background/subagent-runner.ts +16 -0
- package/src/runs/foreground/subagent-executor.ts +2 -3
- package/src/runs/shared/parallel-utils.ts +1 -0
- package/src/shared/formatters.ts +13 -0
- package/src/shared/model-info.ts +10 -0
- package/src/shared/types.ts +1 -0
- package/src/tui/render.ts +15 -6
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
|
|
624
|
-
{ chain: [...],
|
|
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.
|
|
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
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
`/
|
|
543
|
-
`/gather-context-and-clarify`, and
|
|
544
|
-
|
|
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/validators → async fix worker → follow-up review when warranted → parent review
|
|
637
651
|
```
|
|
638
652
|
|
|
639
|
-
The
|
|
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.
|
|
645
|
-
3.
|
|
646
|
-
4.
|
|
647
|
-
5.
|
|
648
|
-
6.
|
|
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
|
|
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.
|
|
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
|
|
package/src/extension/index.ts
CHANGED
|
@@ -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}`,
|
package/src/extension/schemas.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
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 = {
|
package/src/shared/formatters.ts
CHANGED
|
@@ -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
|
*/
|
package/src/shared/model-info.ts
CHANGED
|
@@ -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: "" };
|
package/src/shared/types.ts
CHANGED
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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}`
|