Repository navigation
feat: run subagents as tasks with background mode, events and profiles - #396
Merged
Merged
Conversation
…tools - `Agent.start`/`run`/`session` accept an open `Tape` as well as a tape name, so a run can start from another run's tape. - `ToolContext.step_messages` holds the user messages the calling model step sent, which the tape records only after the step's tool calls finish. - `ToolContext.call_id` is the model's ID of the call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d profiles Fixes: - A child run copied the parent's `_runtime_thread_id`, so it drained steering messages meant for the parent. Children now get their own thread ID and drop the parent's tool scope and running tape. - `session="inherit"` merged the child's turn into the parent session out of order. It is now an alias of `fork`: the child starts from a snapshot of the current conversation, including the calling step's messages, and is discarded. - `output` is the child's final answer instead of every text delta. New: - `background=true` returns a task ID at once; the result reaches the parent as a `<subagent-notification>` steering message unless read with `subagent.wait`. - `subagent.wait`, `subagent.send` and `subagent.cancel` tools. - `SubagentStarted`, `SubagentOutput` and `SubagentFinished` events on the agent's bus, so channels can observe subagents without replacing the tool. - Subagent profiles in `.agents/agents/*.md`. - Child tools and skills stay within the parent's; concurrency limit (`BUB_SUBAGENT_MAX_CONCURRENCY`) and timeout (`BUB_SUBAGENT_TIMEOUT_SECONDS`). - Cancelling the parent call, `quit` or runtime exit cancels subagents; ending a temporary subagent stops its background shells. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
bub | 2e0692b | Commit Preview URL Branch Preview URL |
Nov 04 2026, 02:40 AM |
Drop the `background` parameter of `subagent` and `subagent.send`: every subagent returns a task ID at once and reports with a <subagent-notification>. So that a parent never ends its turn before its subagents report, a run that started subagent tasks waits for the next one to finish when a step would end the run, then continues with its notification. `MessagesPending.step_ended` tells that emission apart from the one made while a request is built. A notification sent through the steering inbox now marks the task delivered, so a result is never handed to the parent twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
frostming
added a commit
to bubbuild/bub-contrib
that referenced
this pull request
Nov 4, 2026
Bub's `subagent` tool now always returns a task ID at once (bubbuild/bub#396), so the tests wait for each task's result with `subagent.wait`, and a failing child is reported as a failed task rather than a raised tool error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
frostming
added a commit
to bubbuild/bub-contrib
that referenced
this pull request
Nov 4, 2026
…agents (#98) * feat(bub-acp-server): observe Bub subagent tasks and support Lody subagents Bub now runs subagents as tasks and publishes SubagentStarted, SubagentOutput and SubagentFinished on the agent's event bus (bubbuild/bub#396). The ACP server subscribes to those events instead of replacing the `subagent` tool. - `subagentEvents` runs advertise `cancel: true` and `outputRead: "live_tail"`. - New Lody `subagents` capability: the task lifecycle as `_meta.lody.task` on the `subagent` tool call that started it, and `_lody/subagents/list|cancel|output` (the task ID is the run ID). - Closing an ACP session cancels its running subagent tasks. - Terminal snapshots no longer send an explicit null `reason.message`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(bub-acp-server): follow Bub subagents running in the background Bub's `subagent` tool now always returns a task ID at once (bubbuild/bub#396), so the tests wait for each task's result with `subagent.wait`, and a failing child is reported as a failed task rather than a raised tool error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: lock bub with subagent tasks Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
What changed and why
Bub's
subagenttool was a synchronous call: run the child, concatenate its text, return. Compared with the subagent mechanisms of T3 Code, Claude Code and Codex, it lacked task identity and lifecycle, and it had three correctness bugs. This PR fixes the bugs and turns subagents into tasks.Fixes
_runtime_thread_id, which keys the steering inbox, so a user message sent while a subagent ran was drained into the child. Children now get their own thread ID (subagent:<task_id>) and drop the parent's tool scope and running tape. A regression test fails on the old behavior.session="inherit"produced out-of-order history. The child forked the committed tape, not the running turn, and merged back before the parent's turn. It is now an alias offork, which starts from a snapshot of the current conversation, including the calling step's messages that are not yet on the tape, and is discarded afterwards.outputmixed every step's narration. It is now the child's final answer: the text of its last step that made no tool calls.New
subagentreturns atask_idat once, and the result reaches the parent as a<subagent-notification>message, unless the parent already read it withsubagent.wait. There is nobackgroundparameter.max_stepsturns) report through the session's steering inbox.subagent.wait(withtimeout_seconds=0it only checks status),subagent.send(steer a running task, or continue a named session) andsubagent.cancel.SubagentStarted,SubagentOutputandSubagentFinishedevents onagent.events, so channels such as ACP can observe child runs without replacing the tool..agents/agents/*.md(workspace, then home), with model, tools, skills, reasoning effort and timeout; the body is added to the child's system prompt. The parent's prompt lists the profiles.BUB_SUBAGENT_MAX_CONCURRENCY(default 4) andBUB_SUBAGENT_TIMEOUT_SECONDS.quitand runtime exit cancel running subagents; ending atemporforksubagent stops the background shells it started.Core additions
Agent.start/run/sessionaccept an openTapeto fork, as well as a tape name.ToolContext.step_messagesandToolContext.call_id.MessagesPending.step_ended, true for the emission at the end of a step without tool calls.Impacted modules
bub.core.agent,bub.core.toolsbub.builtin.subagents(new;bub.builtin.toolsre-exportsrun_subagent,SubAgentInputandSubAgentResult),bub.builtin.agent,bub.builtin.settings,bub.builtin.hook_implSubAgentResultgainstask_idandstatus; the tool now returns before the child finishes. A follow-up in bub-contrib moves bub-acp-server from replacing thesubagenttool to observing these events, and adds the Lodysubagentscapability.Known limits
temp/forktask cannot be continued withsubagent.sendafter it ends; use a named session for that.Verification
uv run ruff check src tests: passeduv run mypy src: passeduv run pytest -q: 724 passed, 20 skippedtests/test_tracing.pyandtests/test_otlp.pywith the OpenTelemetry SDK installed: 24 passedpnpm build): passed🤖 Generated with Claude Code