返回 DeepSeek-Reasonix
SUBAGENT_PROGRESS.md
根目录 / docs / SUBAGENT_PROGRESS.md
1 # Local Sub-Agent Progress
2
3 Status: **implemented** — per-child progress previews for local sub-agent runs
4 (`task`, `read_only_task`, `parallel_tasks`, `fleet`) in the desktop app and the
5 CLI, on top of the persisted transcripts and `read_subagent_result` (see
6 [`CHECKPOINTS.md`](CHECKPOINTS.md) for the persistence model).
7
8 ## Goal
9
10 While a sub-agent works, the user should see **what it is doing** without the
11 sub-agent's reasoning/text bodies entering the parent conversation: a progress
12 card shows the child's phase, running elapsed time, and recent activity; the
13 desktop card can be expanded for a bounded reasoning / response / notice
14 preview, and the CLI shows the same previews in `/verbose` mode. Everything is
15 zero-configuration — there are no new settings.
16
17 ## Wire contract
18
19 Progress previews reuse the existing `ToolProgress` event with four reserved
20 `Tool.Name` values. These names are an internal contract between the agent
21 progress tracker and local frontends; they must never be presented as
22 provider-visible tool names:
23
24 | Name | Payload |
25 |---|---|
26 | `reasonix.subagent.status` | exactly one of `queued`, `running`, `reasoning`, `responding`, `tool`, `retrying`, `completed`, `failed`, `cancelled` |
27 | `reasonix.subagent.reasoning` | bounded UTF-8 text delta (the child's thinking) |
28 | `reasonix.subagent.text` | bounded UTF-8 text delta (the child's response preview) |
29 | `reasonix.subagent.notice` | bounded UTF-8 text delta (the child's notices) |
30
31 Field conventions:
32
33 - `Tool.ID` — the child task card ID (progress lookup is by ID, never by body).
34 - `Tool.Output` — the phase value (status) or a text delta (previews).
35 - `Tool.Truncated` — set when this round's preview was truncated or merged.
36 - `Tool.DurationMs` — the final duration, carried on terminal status events.
37 - `Tool.ParentID` — follows the existing nesting relationship (empty for a
38 top-level `task`; the group call ID for `parallel_tasks`/`fleet` children).
39
40 ## Behavior
41
42 State machine (emitted by the unified run chain in `RunProfileSpec`, shared by
43 `task`, `read_only_task`, `parallel_tasks`, and `fleet` — no per-entry copies):
44
45 - Foreground runs start with `running`.
46 - Background runs emit `queued` at registration and `running` once the job
47 acquires its execution slot.
48 - `parallel_tasks`/`fleet` group cards get an explicit lifecycle of their own:
49 `running` when children start and exactly one terminal after every child
50 settles (`completed`, `cancelled` for cancellation/deadline, `failed` when
51 any child failed or the call errored — including validation failures).
52 Frontends never infer group completion from the children observed so far,
53 since background children dispatch asynchronously and a fast first child
54 can finish before later ones appear.
55 - The child's `Reasoning` / `Text` / `Notice` / `Retrying` events become the
56 corresponding preview channels; the child's real tool activity flips the
57 phase to `tool` while the nested tool cards render as before.
58 - Every run emits exactly **one** terminal status: `completed` on success,
59 `cancelled` for context cancellation or deadline, `failed` for provider,
60 tool, storage, or panic errors. Pending previews are flushed synchronously
61 before the terminal; events arriving after the terminal are ignored.
62
63 Pacing and memory bounds (per parent task group):
64
65 - One pending slot per (child, channel); previews merge for up to 250 ms before
66 one event is emitted, so deltas never accumulate unboundedly.
67 - At most 32 non-terminal events/sec per group — phase transitions and content
68 previews share the same budget, round-robined across children so one hot
69 child cannot starve the others. Only the initial `queued`/`running` states
70 and the terminal event bypass the limit.
71 - When the budget trims buffered content, the loss is flagged `Truncated` on
72 the next actually-emitted channel (or surfaced as a truncated notice at the
73 terminal flush), so frontends always learn that some preview was dropped.
74 - Each child's unsent pending buffer is capped at 8 KiB total (notice is
75 dropped first, then reasoning, then text); overflow keeps a UTF-8-safe tail
76 and sets `Truncated`. The desktop retains per-channel preview caps (8 KiB
77 reasoning/text, 2 KiB notice); the CLI keeps 4 KiB reasoning/text tails for
78 `/verbose`.
79
80 What is **not** done:
81
82 - The child's `Message`, reasoning, and text bodies never enter the parent
83 transcript or provider context.
84 - No new event kinds, no new wire fields, no provider tool list/schema/system
85 prompt changes, no configuration.
86 - Previews are never persisted: after a restart the complete sub-agent
87 transcript (and `read_subagent_result`) remains the source of truth.
88 - ACP and bot consumers keep ignoring `ToolProgress` bodies entirely.
89
90 ## Desktop
91
92 - A sub-agent tool card shows a phase chip (phase + running elapsed + "N s
93 ago" recent activity) in its header; the chip ticks once a second while the
94 child is live and settles to a phase + duration summary.
95 - Expanding the card shows isolated reasoning / response preview / notices —
96 never mixed with ordinary tool output.
97 - A background call that already returned its job id stays in the running
98 state while child progress is non-terminal; `parallel_tasks`/`fleet` group
99 cards settle only from their own lifecycle terminal event, so neither a
100 job-id result arriving before any child nor a fast first child finishing
101 before later children dispatch can settle the group prematurely.
102 - `completed` / `failed` / `cancelled` reuse the existing done / error /
103 stopped visuals; after a terminal the card folds by default unless the user
104 explicitly expanded it.
105
106 ## CLI
107
108 - Each child keeps its own progress state and a fixed transcript slot keyed by
109 its call ID — independent of the single live tool stream, so concurrent
110 children never cross-stream.
111 - By default only the phase, elapsed, and recent activity are shown; the
112 reasoning/text bodies appear in `/verbose` (Ctrl+O) mode, bounded to the
113 recent 4 KiB tails.
114 - Terminal children fold to a one-line summary; verbose keeps the bounded
115 preview.
116 - Terminals without in-place redraw (Termux native scrollback) print a status
117 line on phase changes and terminal only; verbose previews print at most once
118 every 2 seconds per child.
119
120 ## Serve
121
122 - A call carrying `parentId` renders inside its parent's card, never as a
123 top-level entry: a delegated command must not read as the session's own
124 work. The parent opens while it runs and folds when it settles, unless the
125 user took over the toggle.
126 - Narrow layouts hide the status badge, except on a call the host refused
127 (`not_run` shell state) — there a red icon alone would read as "failed"
128 rather than "never ran".
129
130 ## Contract stability
131
132 Frontends match the reserved names by the `reasonix.subagent.` prefix, so a
133 future channel added by a newer agent is ignored (never appended to ordinary
134 tool output) by older frontends.
135
135 lines MARKDOWN