diff --git a/implementation-plans/_index.md b/implementation-plans/_index.md
index b1ddd70..83af8fa 100644
--- a/implementation-plans/_index.md
+++ b/implementation-plans/_index.md
@@ -16,3 +16,4 @@
| #12 | Session history sidebar | COMPLETED | [issue-012.md](issue-012.md) |
| #13 | Session config sidebar component | COMPLETED | [issue-013.md](issue-013.md) |
| #14 | Preset configurations | COMPLETED | [issue-014.md](issue-014.md) |
+| #15 | Agent lineage visualization | COMPLETED | [issue-015.md](issue-015.md) |
diff --git a/implementation-plans/issue-015.md b/implementation-plans/issue-015.md
new file mode 100644
index 0000000..58eac10
--- /dev/null
+++ b/implementation-plans/issue-015.md
@@ -0,0 +1,39 @@
+---
+---
+
+# Issue #15: Agent lineage visualization
+
+**Status:** COMPLETED
+**Issue:** https://git.shahondin1624.de/llm-multiverse/llm-multiverse-ui/issues/15
+**Branch:** `feature/issue-15-agent-lineage`
+
+## Acceptance Criteria
+
+- [x] Dedicated `/lineage` route
+- [x] Tree/graph visualization of agent spawn chain
+- [x] Each node displays: agent_id, agent_type, spawn_depth
+- [x] Visual hierarchy reflecting parent-child agent relationships
+- [x] Custom SVG-based rendering (no external graph library needed)
+- [x] Updates as new agents appear in the stream (reactive via `$derived`)
+- [x] Clickable nodes to show agent details
+
+## Implementation
+
+### New Files
+- `src/lib/types/lineage.ts` — lineage types (`LineageNode`, `SimpleAgentIdentifier`), tree builder, color/label helpers, sample data
+- `src/lib/components/LineageTree.svelte` — custom SVG tree visualization with horizontal layout, bezier edges, colored nodes by agent type, click selection
+- `src/routes/lineage/+page.svelte` — dedicated lineage route with tree, legend, and detail panel
+- `src/routes/lineage/+page.ts` — SSR disabled for this route
+
+### Modified Files
+- `src/routes/+page.svelte` — added navigation links to Chat and Agent Lineage
+- `src/routes/chat/+page.svelte` — added Lineage link in header alongside Config button
+
+### Key Decisions
+- Uses sample/demo data since the API does not yet expose lineage information (AuditService is write-only, ProcessRequestResponse does not include lineage)
+- `SimpleAgentIdentifier` type decouples the UI from protobuf Message dependency, making it easy to adapt when real data arrives
+- Horizontal tree layout: root on left, children branching right, computed via recursive leaf-counting algorithm
+- SVG bezier curves for edges, rounded rectangles for nodes
+- Agent type colors: Orchestrator=blue, Researcher=green, Coder=purple, SysAdmin=orange, Assistant=teal, Unspecified=gray
+- Detail panel slides in from right when a node is clicked, showing full agent info and child list
+- Tree layout is fully reactive via `$derived` runes — updating the `agents` array recomputes the tree automatically
diff --git a/src/lib/components/LineageTree.svelte b/src/lib/components/LineageTree.svelte
new file mode 100644
index 0000000..9a30b3b
--- /dev/null
+++ b/src/lib/components/LineageTree.svelte
@@ -0,0 +1,281 @@
+
+
+
+ {#if nodes.length === 0}
+
+ No lineage data available
+
+ {:else}
+
+ {/if}
+
diff --git a/src/lib/types/lineage.ts b/src/lib/types/lineage.ts
new file mode 100644
index 0000000..5a6638f
--- /dev/null
+++ b/src/lib/types/lineage.ts
@@ -0,0 +1,200 @@
+import { AgentType } from '$lib/proto/llm_multiverse/v1/common_pb';
+
+/**
+ * A node in the agent lineage tree, enriched with children references
+ * for tree rendering.
+ */
+export interface LineageNode {
+ id: string;
+ agentType: AgentType;
+ spawnDepth: number;
+ children: LineageNode[];
+}
+
+/**
+ * Flat agent identifier matching the proto shape but without protobuf Message dependency.
+ * Used for sample data and as input to tree building.
+ */
+export interface SimpleAgentIdentifier {
+ agentId: string;
+ agentType: AgentType;
+ spawnDepth: number;
+ parentId?: string;
+}
+
+/**
+ * Maps AgentType enum values to human-readable labels.
+ */
+export function agentTypeLabel(type: AgentType): string {
+ switch (type) {
+ case AgentType.ORCHESTRATOR:
+ return 'Orchestrator';
+ case AgentType.RESEARCHER:
+ return 'Researcher';
+ case AgentType.CODER:
+ return 'Coder';
+ case AgentType.SYSADMIN:
+ return 'SysAdmin';
+ case AgentType.ASSISTANT:
+ return 'Assistant';
+ default:
+ return 'Unspecified';
+ }
+}
+
+/**
+ * Maps AgentType enum values to Tailwind-friendly color tokens.
+ */
+export function agentTypeColor(type: AgentType): {
+ fill: string;
+ stroke: string;
+ text: string;
+ badge: string;
+} {
+ switch (type) {
+ case AgentType.ORCHESTRATOR:
+ return {
+ fill: '#dbeafe',
+ stroke: '#3b82f6',
+ text: '#1e40af',
+ badge: 'bg-blue-100 text-blue-700'
+ };
+ case AgentType.RESEARCHER:
+ return {
+ fill: '#dcfce7',
+ stroke: '#22c55e',
+ text: '#166534',
+ badge: 'bg-green-100 text-green-700'
+ };
+ case AgentType.CODER:
+ return {
+ fill: '#f3e8ff',
+ stroke: '#a855f7',
+ text: '#6b21a8',
+ badge: 'bg-purple-100 text-purple-700'
+ };
+ case AgentType.SYSADMIN:
+ return {
+ fill: '#ffedd5',
+ stroke: '#f97316',
+ text: '#9a3412',
+ badge: 'bg-orange-100 text-orange-700'
+ };
+ case AgentType.ASSISTANT:
+ return {
+ fill: '#ccfbf1',
+ stroke: '#14b8a6',
+ text: '#115e59',
+ badge: 'bg-teal-100 text-teal-700'
+ };
+ default:
+ return {
+ fill: '#f3f4f6',
+ stroke: '#9ca3af',
+ text: '#374151',
+ badge: 'bg-gray-100 text-gray-700'
+ };
+ }
+}
+
+/**
+ * Builds a tree of LineageNode from a flat list of SimpleAgentIdentifier.
+ *
+ * The algorithm groups agents by spawnDepth. Depth-0 agents become root nodes.
+ * Each agent at depth N is attached as a child of the last agent at depth N-1
+ * (based on insertion order), which models a sequential spawn chain.
+ *
+ * If parentId is provided, it is used for explicit parent-child linking.
+ */
+export function buildLineageTree(agents: SimpleAgentIdentifier[]): LineageNode[] {
+ if (agents.length === 0) return [];
+
+ const nodeMap = new Map();
+
+ // Create all nodes first
+ for (const agent of agents) {
+ nodeMap.set(agent.agentId, {
+ id: agent.agentId,
+ agentType: agent.agentType,
+ spawnDepth: agent.spawnDepth,
+ children: []
+ });
+ }
+
+ const roots: LineageNode[] = [];
+ const depthLastNode = new Map();
+
+ for (const agent of agents) {
+ const node = nodeMap.get(agent.agentId)!;
+
+ if (agent.parentId && nodeMap.has(agent.parentId)) {
+ // Explicit parent link
+ nodeMap.get(agent.parentId)!.children.push(node);
+ } else if (agent.spawnDepth === 0) {
+ roots.push(node);
+ } else {
+ // Attach to the last node at depth - 1
+ const parent = depthLastNode.get(agent.spawnDepth - 1);
+ if (parent) {
+ parent.children.push(node);
+ } else {
+ // Fallback: treat as root if no parent found
+ roots.push(node);
+ }
+ }
+
+ depthLastNode.set(agent.spawnDepth, node);
+ }
+
+ return roots;
+}
+
+/**
+ * Returns sample/demo lineage data for visualization development.
+ * This will be replaced with real API data when available.
+ */
+export function getSampleLineageData(): SimpleAgentIdentifier[] {
+ return [
+ {
+ agentId: 'orch-001',
+ agentType: AgentType.ORCHESTRATOR,
+ spawnDepth: 0
+ },
+ {
+ agentId: 'research-001',
+ agentType: AgentType.RESEARCHER,
+ spawnDepth: 1,
+ parentId: 'orch-001'
+ },
+ {
+ agentId: 'coder-001',
+ agentType: AgentType.CODER,
+ spawnDepth: 1,
+ parentId: 'orch-001'
+ },
+ {
+ agentId: 'sysadmin-001',
+ agentType: AgentType.SYSADMIN,
+ spawnDepth: 1,
+ parentId: 'orch-001'
+ },
+ {
+ agentId: 'assist-001',
+ agentType: AgentType.ASSISTANT,
+ spawnDepth: 2,
+ parentId: 'research-001'
+ },
+ {
+ agentId: 'coder-002',
+ agentType: AgentType.CODER,
+ spawnDepth: 2,
+ parentId: 'coder-001'
+ },
+ {
+ agentId: 'research-002',
+ agentType: AgentType.RESEARCHER,
+ spawnDepth: 3,
+ parentId: 'coder-002'
+ }
+ ];
+}
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index edef9a5..fae0170 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -1,2 +1,15 @@
+
+
LLM Multiverse UI
Orchestration interface
+
diff --git a/src/routes/chat/+page.svelte b/src/routes/chat/+page.svelte
index 07c01d7..821e218 100644
--- a/src/routes/chat/+page.svelte
+++ b/src/routes/chat/+page.svelte
@@ -29,6 +29,7 @@
create(SessionConfigSchema, { overrideLevel: OverrideLevel.NONE })
);
let showConfig = $state(false);
+ const lineageHref = resolveRoute('/lineage');
const isNonDefaultConfig = $derived(
sessionConfig.overrideLevel !== OverrideLevel.NONE ||
@@ -136,6 +137,15 @@
Chat
+
+
+
+ Lineage
+
+
+
diff --git a/src/routes/lineage/+page.svelte b/src/routes/lineage/+page.svelte
new file mode 100644
index 0000000..de909be
--- /dev/null
+++ b/src/routes/lineage/+page.svelte
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Agent Types:
+ {#each agentTypeLegend as type (type)}
+ {@const colors = agentTypeColor(type)}
+
+
+ {agentTypeLabel(type)}
+
+ {/each}
+
+
+
+
+ {#if selectedNode}
+ {@const colors = agentTypeColor(selectedNode.agentType)}
+
+ {/if}
+
+
diff --git a/src/routes/lineage/+page.ts b/src/routes/lineage/+page.ts
new file mode 100644
index 0000000..ae88a27
--- /dev/null
+++ b/src/routes/lineage/+page.ts
@@ -0,0 +1,2 @@
+export const prerender = false;
+export const ssr = false;