Subagents and subgraphs are discovered eagerly but streamed lazily. The root hook keeps cheap identity snapshots on stream.subagents, stream.subgraphs, and stream.subgraphsByNode. To render a subagent's content, pass its snapshot into a companion selector hook — the subscription is scoped to the subagent's namespace and reference-counted.
Learn more: For deep-agent UI patterns, see the Deep Agents frontend documentation.
Runnable examples: The streaming cookbook
streamingpackage hassubagents:*,subagent-status:*, andsubgraphs:*scripts, and thea2uiapp renders a Deep Agent's output live.
Subagent / subgraph identity is always available from the root:
interface SubagentDiscoverySnapshot {
readonly id: string; // tool-call id that spawned it
readonly name: string; // "researcher", "writer", …
readonly namespace: readonly string[];
readonly parentId: string | null;
readonly depth: number;
readonly status: "pending" | "running" | "complete" | "error";
}
Subgraph snapshots carry the same metadata plus the producing node id:
interface SubgraphDiscoverySnapshot {
readonly id: string;
readonly namespace: readonly string[];
readonly nodeId: string;
readonly status: "pending" | "running" | "complete" | "error";
}
The root hook exposes three discovery maps. In <script setup> they
are Vue refs, so read them with .value; templates auto-unwrap them:
stream.subagents.value — a ReadonlyMap of SubagentDiscoverySnapshot keyed by id.stream.subgraphs.value — a ReadonlyMap of SubgraphDiscoverySnapshot keyed by id.stream.subgraphsByNode.value — the same subgraph snapshots keyed by the producing graph node.They update live as the server emits discovery events, without opening per-subagent subscriptions.
Pass the snapshot as the target argument to any selector hook. Messages, tool calls, and values stream only for the subagents that are actively rendered:
<!-- Researchers.vue -->
<script setup lang="ts">
import { computed } from "vue";
import { useStreamContext } from "@langchain/vue";
import SubagentCard from "./SubagentCard.vue";
const stream = useStreamContext();
const researchers = computed(() =>
[...stream.subagents.value.values()].filter((s) => s.name === "researcher"),
);
</script>
<template>
<SubagentCard v-for="sub in researchers" :key="sub.id" :subagent="sub" />
</template>
<!-- SubagentCard.vue -->
<script setup lang="ts">
import { type PropType } from "vue";
import {
useMessages,
useStreamContext,
useToolCalls,
useValues,
type SubagentDiscoverySnapshot,
} from "@langchain/vue";
const props = defineProps({
subagent: {
type: Object as PropType<SubagentDiscoverySnapshot>,
required: true,
},
});
const stream = useStreamContext();
const messages = useMessages(stream, () => props.subagent);
const toolCalls = useToolCalls(stream, () => props.subagent);
const values = useValues<ResearcherState>(stream, () => props.subagent);
</script>
<template>
<section>
<header>{{ subagent.name }} — {{ subagent.status }}</header>
<Bubble v-for="msg in messages" :key="msg.id" :msg="msg" />
<pre v-for="toolCall in toolCalls" :key="toolCall.id">
{{ toolCall.name }}: {{ toolCall.status }}
</pre>
</section>
</template>
Subscriptions open on mount and close when the last consumer for a given (channel, namespace) tuple unmounts. Components that don't render a subagent's content never pay for its wire traffic — this is the single biggest wire-cost win of the design.
To find subagents by status or name, filter the discovery map inline:
const active = computed(() =>
[...stream.subagents.value.values()].filter((s) => s.status === "running"),
);
const researcher = computed(() =>
[...stream.subagents.value.values()].find((s) => s.name === "researcher"),
);
Subgraph snapshots work the same way. The subgraphsByNode map is handy for laying out nested-graph visualisations:
<script setup lang="ts">
import { computed } from "vue";
import { useStreamContext } from "@langchain/vue";
const stream = useStreamContext();
const subgraphsByNode = computed(() => [...stream.subgraphsByNode.value]);
</script>
<template>
<div v-for="[nodeId, subgraphs] in subgraphsByNode" :key="nodeId">
<h3>{{ nodeId }}</h3>
<SubgraphCard v-for="subgraph in subgraphs" :key="subgraph.id" :subgraph="subgraph" />
</div>
</template>