-
Notifications
You must be signed in to change notification settings - Fork 3k
docs: record where a background agent shows up, and who owns it #10951
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+72
−0
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| # Where a background agent shows up | ||
|
|
||
| > Status: decision recorded, one surface unbuilt | ||
| > Baseline: `678ac2e1ec` (`origin/main`, 2026-09-03) | ||
| > Implements the CLI half in #10942, #10943, #10949. The Web Shell half is the open part. | ||
|
|
||
| ## 0. The decision, first | ||
|
|
||
| **The Agent View roster is the authority on background agents. Every other surface reads it.** | ||
|
|
||
| The alternative — letting the daemon keep its own idea of what is running in the background — is what produced the situation in §1, and adding a Web Shell panel on top of the daemon's model would make it permanent. | ||
|
|
||
| One consequence to state plainly, because it is the part that costs work: the daemon does not read the roster today and will need to. That is a reader, not a new model. | ||
|
|
||
| ## 1. Three things already answer "what is running" | ||
|
|
||
| Measured at `678ac2e1ec`. | ||
|
|
||
| | Model | Written by | Stored at | Read by | | ||
| | ------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------- | | ||
| | **Live-process registry** | every interactive session, unconditionally (`startInteractiveUI.tsx:421`) | `~/.qwen/sessions/<pid>.json` | `qwen sessions ps`, peer messaging discovery (`ipc/peer-directory.ts`) | | ||
| | **Agent View roster** | the supervisor (`supervisor-store.ts`) | `~/.qwen/daemon/roster.json` + `~/.qwen/jobs/` | `qwen sessions ps` (#10942), `peek`/`answer`/`stop` (#10949) | | ||
| | **Daemon sessions** | `qwen serve` | daemon process memory (`serve/conversations/standalone-session-service.ts`) | Web Shell | | ||
|
|
||
| The third does not know about the first two: `rg 'listLiveSessions|session-registry' packages/cli/src/serve` returns nothing across 94,136 lines. | ||
|
|
||
| Only the roster carries a background agent's _semantic_ state — `needs_input`, `working`, `failed` — because only the supervisor is told by the worker. The registry knows a process is alive. The daemon's model knows a conversation exists. Neither can answer "is it stuck waiting for me", which is the only question a background agent surface exists to answer. | ||
|
|
||
| That is the whole argument for the decision in §0. | ||
|
|
||
| ## 2. What the CLI half already does | ||
|
|
||
| - `qwen --bg "<prompt>"` records a session and a supervisor spawns it (#10943). | ||
| - `qwen sessions ps` lists managed sessions beside interactive ones, deduplicated by session id, each with its real state (#10942). | ||
| - `qwen sessions peek | answer | stop <id>` reads the question, answers it, ends the session (#10949). | ||
|
|
||
| **A background session is peer-addressable for free.** The worker is launched as `qwen --session-id <id> --prompt-interactive=<prompt>` (`supervisor-dispatch.ts:196`), which is a full interactive session, so it registers in the live-process registry and — when `agents.crossSessionMessaging` is on — binds a peer inbox. It therefore appears in another session's `list_agents` and can be addressed with `send_message`, with no code that makes it so. | ||
|
|
||
| This is worth naming because it decides what the Web Shell should _not_ build: a background agent is already reachable by the messaging tier. The Web Shell needs to show it, not to invent a second way to talk to it. | ||
|
|
||
| ## 3. The unbuilt surface | ||
|
|
||
| A Web Shell panel that lists background agents and lets one be answered. Concretely: | ||
|
|
||
| 1. A daemon route that reads the roster — `listAgentViewSessionSnapshots()` plus `deriveAgentViewPresentation`, which is exactly what `managed-rows.ts` does for the CLI. Same source, same labels, no third vocabulary. | ||
| 2. A panel that renders the rows and posts an answer back through the supervisor's existing `answer` operation. | ||
|
|
||
| Two rules for whoever builds it: | ||
|
|
||
| - **Do not add a fourth model.** If the daemon needs to remember something about a background agent, it belongs in the roster. | ||
| - **Label by task state, not by display group.** The roster's group folds `ready`, `stopped` and `failed` into `completed`; the roster UI can afford that because it also paints an icon tone. Any surface without a second channel must not print "completed" beside a session that failed — see `managed-rows.ts`. | ||
|
|
||
| ## 3.1 Two terminal channels, and which one is which | ||
|
|
||
| Attaching to a background agent is a surface too, and there are currently two ways to put an agent on a terminal: | ||
|
|
||
| | | Owner | Status | | ||
| | ---------------------------------- | ------------------------- | --------------------------------------------------------------------------------------------- | | ||
| | `TmuxBackend` (`agents/backends/`) | Agent Arena | opt-in via `agents.displayMode: "tmux"`, falls back to in-process when tmux is missing or old | | ||
| | `pty-host` (`agent-view/`) | the Agent View supervisor | merged, and the thing `attach` will use | | ||
|
|
||
| **Keep both, and keep them apart.** Arena's tmux path is small, opt-in, and degrades safely; it puts _arena competitors_ side by side in one terminal the user is already looking at. The supervisor's PTY host owns terminals for sessions that outlive the shell that started them — a different lifetime and a different owner. | ||
|
|
||
| What must not happen is a third one. If Agent View attach ever wants tmux panes, it should drive them through the supervisor's PTY host rather than reaching into the Arena backend, whose `Backend` interface is a display abstraction with a semantic handle bolted on (`backends/types.ts`) and whose only wired implementation no-ops most of it. | ||
|
|
||
| `ITermBackend` is the counter-example already in the tree: it exists, has never had a consumer, and `detectBackend` now reports it as unsupported rather than handing anyone an untested path. | ||
|
|
||
| ## 4. Left to a human | ||
|
|
||
| **Should daemon-owned sessions adopt into the roster?** `qwen serve` starts sessions of its own (scheduled tasks, channel workers). They are background agents by any user's definition, and under §0 they would be visible in `qwen sessions ps` too. The supervisor already has an `adopt` operation for exactly this shape. Doing it means the daemon writes to the roster as well as reads it, which is a larger change than the panel and should be decided on its own. | ||
|
|
||
| Not proposed here: moving the live-process registry or the daemon's session model under the roster. They answer different questions and both have consumers. | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
R1-1 · Minor:
managed-rows.tsdoes not exist at this head (full-tree search returns nothing). The group-folding behaviour this note points to —ready/stopped/failed→completed— lives inpackages/cli/src/agent-view/presentation.ts:deriveGroup(lines 245–259). The same dead reference appears again at line 51 (see managed-rows.ts). Consider citingpresentation.ts:deriveGrouphere, or noting this file is planned in #10942.