Repository Wiki
deepseek-ai/deepseek-harness

web-client.subagent-sidebar

web-client.subagent-sidebar covers the web end-to-end behavior that represents delegated subagent activity in the session sidebar. The verified scenario creates an idle owner session, starts a continuable child subagent, connects the owner workspace to the browser client, and asserts that the visible owner row and its expanded running-child control both expose the descendant's ongoing state.

Purpose and Scope

This page documents the sidebar behavior demonstrated by the web e2e: sidebar subagent activity scenario. Its scope is the observable contract between the agent runtime, subagent manager, workspace/session attachment, and the web client's Sessions tree:

  • an owner agent may be idle while a delegated descendant remains running;
  • the owner row displays a 1 subagent running indicator;
  • the ongoing-state marker is present on both the owner row and the running-child trigger;
  • selecting the owner row exposes the running-subagent control; and
  • the complete browser flow remains free of page errors and console warnings.

The supplied source grounding is an end-to-end test rather than the sidebar component implementation itself. Therefore, exact React component names, CSS ownership, rendering-state reducers, route definitions, and production-side event transport are not established here. Implementation details not found in the supplied source are intentionally not inferred.

Overview

The test models a two-level session hierarchy:

  1. A parent session named sidebar-activity-owner is created with a workspace working directory.
  2. The parent receives the user request Delegate a background job. and completes its owner turn.
  3. A continuable child is started through scaffold.ctx.subagents.startContinuable, using the parent agent as its parent.
  4. A staged language-model adapter deliberately holds the child call open. This makes the child observable as running while the parent is already idle.
  5. The browser connects to the same workspace and attaches the parent session.
  6. The Sessions tree is queried by accessible role and name. The owner row must report one running subagent, and the expanded control must report the same state.

The important behavioral distinction is that owner activity and descendant activity are separate dimensions. The assertion parentHandle.agent.status is idle, while the owner row still announces 1 subagent running. This proves that the sidebar aggregates descendant activity rather than simply mirroring the selected owner's own status.

Architecture

The following verified architecture is limited to objects and boundaries named by the supplied test. The browser-side visual component and its internal state model are not present in the source grounding.

text
1Browser Page 2 | 3 | authenticated navigation and accessible Sessions-tree queries 4 v 5Web Scaffold / Workspace Registry 6 | 7 | workspace resolution and session attachment 8 v 9Parent Agent (sidebar-activity-owner) 10 | 11 | parent relationship supplied to startContinuable 12 v 13Subagent Manager 14 | 15 v 16Child Agent 17 | 18 | provider: web-test-hold, model: hold 19 v 20StagedAdapter (held model call)

The parent-child relationship is created by passing parent: parentHandle.agent to startContinuable. The staged adapter is registered under the provider web-test-hold, and the child uses model hold; the adapter's second stream call waits until its abort signal fires. This creates a deterministic running-child fixture without depending on an external model service.

Verified End-to-End Setup

Test fixture construction

launchWebScaffold() creates the web test environment. The test then registers a StagedAdapter through scaffold.ctx.effect, creates a workspace directory beneath scaffold.workspaceCwd, and creates the owner agent with provider/model options pointing at the staged adapter.

The owner receives a user follow-up through parentHandle.agent.followup(createUserMessage(...)). The test awaits parentHandle.agent.whenIdle() before starting the child. This ordering is essential: it establishes the condition under test in which the owner is idle but its descendant is active.

Deterministic child activity

StagedAdapter.stream() completes the first model invocation immediately by yielding a finish chunk with a stop reason. Subsequent invocations require a defined abort signal and increment activeCalls while waiting indefinitely for cancellation. The test therefore has an explicit observable condition for the child model call: adapter.activeCalls === 1.

waitForRunningChild() polls both the adapter call count and the child agent status. It times out after ten seconds and fails with held child did not enter its running model call if the child never reaches the expected running state. The polling interval is ten milliseconds.

Browser and workspace attachment

After the child is confirmed running, the test launches Chromium, creates an English page, installs a console tripwire, navigates to scaffold.authenticatedUrl, waits for a frame selector, and connects a fresh workspace. It resolves the workspace by the child fixture's working-directory path and fails explicitly if registration did not occur. Finally, it attaches the parent session ID to that workspace before querying the sidebar.

Core Flow

text
1Create owner -> Complete owner turn -> Start child with parent link 2 -> Hold child model call -> Launch authenticated browser 3 -> Connect workspace -> Attach owner session 4 -> Locate owner row -> Verify descendant-running indicator 5 -> Click owner row -> Verify running-child trigger

The test's sequencing prevents false positives. If the browser were connected before the child became active, the initial sidebar snapshot could legitimately represent a pre-running state. If the owner were not awaited to idle, the indicator could reflect owner activity rather than descendant activity. If the child model call were not held, the running indicator could disappear before the assertions execute.

Owner row

The Sessions tree is located with the accessible role tree and name Sessions. The owner row is located as a treeitem whose accessible name matches 1 subagent running Delegate a background job. This verifies both the activity count and the original owner prompt are included in the row's accessible representation.

The test additionally asserts that the owner row contains exactly one descendant matching [data-state="ongoing"]. The count is strict: zero would mean the ongoing state is missing, while more than one would indicate duplicate or unexpectedly nested ongoing markers.

Running-child trigger

Clicking the owner row exposes a button named 1 subagent running. The test waits for that button and again requires exactly one [data-state="ongoing"] marker within it. Thus the indicator is not only present in the collapsed/row representation; it remains represented by the control used to inspect running descendant activity.

Accessibility-oriented assertions

The scenario deliberately uses roles and accessible names rather than implementation-specific element selectors for the primary behavior. The only implementation-level selector used for the state assertion is the data-state="ongoing" marker. This combination verifies both user-facing accessibility semantics and the concrete ongoing-state decoration.

State and Lifecycle

text
1Owner creation 2 | 3 v 4Owner turn running --whenIdle()--> Owner idle 5 | 6 | startContinuable(parent = owner) 7 v 8 Child running 9 | 10 | child.cancel({ kind: 'user' }) 11 v 12 Child idle / teardown

The source grounding explicitly verifies the owner reaches idle before child creation. During teardown, the child is cancelled with { kind: 'user' }, and the test awaits the child's idle state. The parent is then disposed, followed by browser and scaffold cleanup.

Usage Examples

The supplied source grounding contains test implementation rather than a reusable production API example. The scenario does provide verified usage patterns:

  • register a provider-specific adapter with scaffold.ctx.llm.registerAdapter;
  • create an owner through scaffold.ctx.agents.create with a SessionId, meta.cwd, and agentOptions;
  • start a continuable child with scaffold.ctx.subagents.startContinuable and request.parent; and
  • inspect the web contract through accessible tree, treeitem, and button queries.

No standalone application code example is available in the supplied source grounding. Exact production component APIs and event-subscription APIs are not documented because their implementations were not provided.

Configuration and Test Controls

Option or fixtureVerified valueRole
HOLD_PROVIDERweb-test-holdProvider key used to register and select the staged adapter.
HOLD_MODELholdModel value passed to the owner agent and used by the child fixture.
MODEwebSnapshotMode()Controls snapshot comparison/refresh behavior.
SNAPSHOT_DIRexpected/sidebar-subagent-activity relative to the test moduleLocation of the sidebar fixture snapshot.
RUNNING_OWNER_EXPECTEDowner-running.expected.md within SNAPSHOT_DIRGolden file compared with the stable ARIA capture.
Child wait deadline10,000 msMaximum time for the child to enter its running model call.
Poll interval10 msInterval used by waitForRunningChild.
Browser setup timeout60,000 msTimeout applied to the suite's beforeAll.
Sidebar row wait timeout10,000 msTimeout used while waiting for the owner row and running trigger.

These are test fixture controls, not confirmed production configuration keys. No environment variable mapping or deployed-client configuration was present in the supplied source.

API and Runtime Interactions

StagedAdapter.stream(options)

The test override accepts GenerateOptions and returns an AsyncIterable<StreamChunk>. On the first call it yields a terminal stop chunk. On later calls it requires options.signal, increments activeCalls, and waits for abort. When the signal is already aborted or later aborts, the promise rejects and the finally block decrements activeCalls.

Because the implementation is supplied only as test grounding, exception behavior beyond the explicit missing-signal error and abort rejection is not established.

waitForRunningChild(scaffold, adapter, childId)

This helper waits until both conditions hold:

  • adapter.activeCalls === 1; and
  • scaffold.ctx.agents.get(childId)?.status === 'running'.

It throws after the ten-second deadline. The helper is a synchronization barrier between runtime setup and browser assertions.

startContinuable

The test invokes scaffold.ctx.subagents.startContinuable with provider: 'spawn', an abort signal, a prompt, and a parent agent. The returned started.childId is retained for status polling and teardown. The source does not expose the complete method signature or return type beyond this observed call site, so those details should not be generalized.

Failure Modes and Cleanup

Setup failures

The suite fails if the staged adapter does not receive a turn signal, if the child does not reach running state within ten seconds, if the browser cannot locate the expected frame, or if the connected workspace cannot be resolved by path. These failures stop the test before sidebar assertions and identify infrastructure or synchronization problems rather than UI mismatches.

UI failures

The owner row must appear within ten seconds, the running trigger must appear after selecting the owner, the golden ARIA snapshot must match or be refreshed according to MODE, and the ongoing marker count must equal one in both locations. Any page error or console warning also fails the test through the final tripwire assertions.

Teardown behavior

afterAll collects teardown failures rather than abandoning cleanup at the first error. It cancels the child, waits for child idleness, closes the browser, disposes the parent handle, and closes the scaffold. One failure is rethrown directly; multiple failures are wrapped in an AggregateError with message sidebar subagent activity teardown failed.

Cancellation semantics

The held adapter is released by aborting its signal during child cancellation. Its abort handler rejects with the signal reason when that reason is an Error; otherwise it creates an Error with message holding Web adapter aborted. The finally block guarantees that activeCalls is decremented even when cancellation is the path out of the held call.

Snapshot and Regression Coverage

The test captures stable ARIA output for the Sessions tree using captureStableAria(page, '[role="tree"][aria-label="Sessions"]', scaffold.workspaceCwd) and compares it with owner-running.expected.md. This protects the accessible structure in addition to the targeted role/name assertions.

assertFixtureInventory verifies that the snapshot directory contains the expected fixture. The test also installs onTestFailed handling to save a failure screenshot named web-e2e-sidebar-subagent-activity, providing a visual artifact when the behavior regresses.

Boundaries and Missing Source Evidence

The supplied grounding proves the external browser contract and the runtime setup needed to reproduce it. It does not prove:

  • the production sidebar component/module name;
  • how subagent events are transported to the browser;
  • whether activity is computed locally, server-side, or through a shared session store;
  • the complete child-tree aggregation algorithm;
  • production persistence or reconnection behavior; or
  • styling and visual design beyond the data-state="ongoing" marker.

Those aspects require the corresponding web-client and runtime implementation files. Documenting them here would exceed the available evidence.

  • For the agent lifecycle and parent/child runtime implementation, see the agent and subagent documentation pages.
  • For workspace registration and session attachment, see the workspace integration documentation.
  • For visual snapshot conventions and browser scaffold utilities, see the web end-to-end testing documentation.