fix(web): deduplicate subagent navigation
This commit is contained in:
@@ -29,7 +29,7 @@ The Figma [subagent list](https://www.figma.com/design/jRBBK7zBgcszdVWQ0Fh5J8/Ha
|
||||
| The session header shows a subagent count and opens a compact list. | The header action shows the direct durable catalog, including healthy entries and explicit diagnostic rows. |
|
||||
| Selecting a row opens a child with ordinary conversation chrome, title, transcript, and composer. | The child reuses the conversation UI, but history and input route through the addressed subagent RPCs. The composer sends human follow-ups only while the exact parent Agent is live and otherwise explains the read-only state. |
|
||||
| Nested agents can be explored progressively. | Expanding a row loads only that child's direct catalog and inserts it as the next tree level. The client never materializes an eager recursive catalog. |
|
||||
| Rows show labels, activity dots, and relative time, while the sidebar omits the old subagent tree. | Labels and coarse `running` or `inactive` activity come from the catalog. An optional log-backed title and relative last-activity time come from the ordinary session summary; they are not Activation outcome or duration. Complete sidebar de-duplication remains deferred. |
|
||||
| Rows show labels, activity dots, and relative time, while the sidebar omits duplicate subagent rows. | Labels and coarse `running` or `inactive` activity come from the catalog. An optional log-backed title and relative last-activity time come from the ordinary session summary; they are not Activation outcome or duration. A durable coarse `SessionHeader.origin` classification removes duplicate subagent rows without hiding ordinary forks. |
|
||||
|
||||
## Product contract
|
||||
|
||||
@@ -61,7 +61,7 @@ The adapter belongs in `dsh-host-apiproxy`, which owns the channel-independent c
|
||||
|
||||
## Client object layer and presentation
|
||||
|
||||
The React-free client runtime will own durable catalog snapshots, in-flight refreshes, subagent addresses, and prompt/history routing. Opening a catalog child records its `{ parentSessionId, childSessionId }` address before opening the resident `Session`; that Session uses `subagent.history` and `subagent.prompt`, while ordinary sessions keep their existing transport. A child address discovered from the catalog is the only browser fact that selects this route; `parentId` alone is insufficient because ordinary forks share the same lineage field.
|
||||
The React-free client runtime will own durable catalog snapshots, in-flight refreshes, subagent addresses, and prompt/history routing. Opening a catalog child records its `{ parentSessionId, childSessionId }` address before opening the resident `Session`; that Session uses `subagent.history` and `subagent.prompt`, while ordinary sessions keep their existing transport. Re-selecting the same child through an ordinary selection path retains the known address so navigation cannot silently switch transports. A child address discovered from the catalog is the only browser fact that selects this route; `parentId` or `origin` alone is insufficient because ordinary forks share the lineage field and origin is only a presentation classifier.
|
||||
|
||||
Catalog data is projected through the existing sessions snapshot consumed by `useSessions`, rather than placed in a component store or exposed through a feature-defined hook. The tree reads ordinary session summaries from the same snapshot for optional title and last-activity presentation. While the root or an expanded descendant catalog is open, its consumer attaches to the existing host-frame fan-out, as the workspaces manager already does: a `host/session-status` frame naming a listed child flips that child's `running`/`inactive` activity in place through the same optimistic mutation path as ordinary session `running`, with no `subagent.list` refetch. A `host/session-added` frame whose parent matches an open branch triggers one debounced, single-flight `subagent.list` refetch to admit the new member and its label and descriptor. Component-local state owns dropdown visibility, expanded branch ids, and keyboard focus.
|
||||
|
||||
@@ -91,7 +91,7 @@ The default Web composition will mount the model-facing `send_message` and `list
|
||||
|
||||
**Build an eager recursive tree.** Rejected because `listChildren()` is direct and can scan every candidate log. The presentation composes a recursive tree from lazy direct-child queries, preserving each catalog's ordering and diagnostic semantics without multiplying work across an unseen hierarchy.
|
||||
|
||||
**Remove every subagent from the sidebar session tree.** Deferred because `session.list` exposes lineage but not continuable identity, and the catalog is parent-addressed rather than a cheap global classifier. Hiding all child sessions would also hide ordinary forks. The header becomes the authoritative subagent entry point, while complete sidebar de-duplication awaits a scalable durable projection.
|
||||
**Infer sidebar filtering from lineage or a global catalog scan.** Rejected because ordinary forks share `parentSession`, while a global catalog scan is parent-addressed and too expensive for a navigation classifier. Every in-process subagent-backed session instead stamps `SessionHeader.origin: 'subagent'` before publication; `session.list` and `host/session-added` project it to the client, and the shared sidebar filter omits only those rows. The header catalog remains the navigation entry point and descriptor authority; `origin` never proves lifecycle mode, resumability, or authorization.
|
||||
|
||||
**Push catalog changes as a dedicated server stream.** Deferred in favor of reusing the existing `host/session-status` and `host/session-added` fan-out. A `subagent.catalog` delta frame would make membership and diagnostics fully live without any refetch, but it is a new host wire contract and a real-time projection over the durable catalog — exactly the derived index the [durable catalog](../../implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md) leaves to measured scale. The first version flips activity from the existing liveness frame and refetches only on membership change.
|
||||
|
||||
@@ -106,6 +106,7 @@ The default Web composition will mount the model-facing `send_message` and `list
|
||||
- A listed `running` child settling to `inactive` updates its activity in place from the live frame stream without a `subagent.list` refetch; a newly created direct child appears after one debounced refetch.
|
||||
- A child whose parent is absent remains readable and rejects input without auto-resuming the parent. No child history, prompt, or stop gesture calls the ordinary Agent APIs.
|
||||
- Refresh and reconnect rebuild an addressed child through the subagent history path without duplicating events or losing events emitted across cold-resume publication.
|
||||
- Both grouped and flat sidebars omit `origin: 'subagent'` rows, including the current child, while ordinary fork rows remain visible; the same-child ordinary selection path retains the catalog-derived address and therefore keeps subagent history/prompt routing.
|
||||
- Host protocol tests pin schemas, id echoing, direct-parent validation, non-activating history, live-parent enforcement, error mapping, and inbox-message acknowledgement. Client object tests pin catalog/address state and transport selection; jsdom tests pin the header tree, lazy nested disclosure, diagnostics, enabled/read-only composer states, keyboard behavior, and draft restoration.
|
||||
- A keyless assembled Web snapshot demonstrates a settled continuable child plus a descriptor-bearing persisted grandchild, progressive catalog expansion without Activation, opening from persistence, and accepting one human follow-up into a cold-resumed Activation's inbox.
|
||||
|
||||
@@ -116,5 +117,5 @@ The default Web composition will mount the model-facing `send_message` and `list
|
||||
- Parent availability and child activity are process-local snapshots. Publication, Activation disposal, another sender, or another process may win after listing; explicit prompt failure remains normal behavior rather than an invariant violation.
|
||||
- A child Activation may publish between history fetch and mux subscription. The existing sequence reconciliation must be proven against this cold-to-live, subagent-specific open path.
|
||||
- Switching the default Web delegation tools to continuable background mode changes the model-visible acknowledgement and durability requirements for `run_in_background`; snapshot coverage must land with the assembly change.
|
||||
- Keeping subagents in the sidebar temporarily creates two navigation paths. Removing the duplicate safely requires a later durable classification or global projection rather than presentation-only filtering.
|
||||
- Persisted subagent origin adds one coarse product-classification field to every local child header and its list/increment projections. It is intentionally weaker than the descriptor and addressed continuation contracts, so navigation de-duplication cannot become an authorization shortcut.
|
||||
- The feature has no correct cancellation button, durable outcome, Activation duration, deletion, pagination for the catalog, or independently interactive offline child. The UI must not imply those capabilities; its relative time is only the session summary's last-activity hint.
|
||||
|
||||
Reference in New Issue
Block a user