1import {
2 ChangeDetectionStrategy,
3 Component,
4 computed,
5 inject,
6 input,
7} from "@angular/core";
8import { ActivatedRoute } from "@angular/router";
9import { injectAgentStore, registerRenderToolCall } from "@copilotkit/angular";
10
11import { agentIdForRoute } from "../../feature-agent";
12import { FeatureHeaderComponent } from "../feature-header.component";
13import { ShowcaseChatHostComponent } from "../showcase-chat-host.component";
14import {
15 AgentStateCardComponent,
16 DelegationLogComponent,
17} from "./agent-state-cards";
18import type { SubAgentName } from "./agent-state-model";
19import { readDelegations, readSteps } from "./agent-state-model";
20import { subAgentRendererConfig } from "./subagent-renderer-config";
21
22@Component({
23 selector: "showcase-agent-state-transcript-children",
24 imports: [AgentStateCardComponent],
25 changeDetection: ChangeDetectionStrategy.OnPush,
26 template: `
27 @if (steps().length > 0) {
28 <showcase-agent-state-card [steps]="steps()" [isRunning]="isRunning()" />
29 }
30 `,
31})
32export class AgentStateTranscriptChildrenComponent {
33 readonly state = input<unknown>({});
34 readonly isRunning = input(false);
35 protected readonly steps = computed(() => readSteps(this.state()));
36}
37
38@Component({
39 selector: "showcase-agent-state-feature",
40 imports: [
41 DelegationLogComponent,
42 FeatureHeaderComponent,
43 ShowcaseChatHostComponent,
44 ],
45 changeDetection: ChangeDetectionStrategy.OnPush,
46 host: { class: "feature-page" },
47 template: `
48 <showcase-feature-header />
49 <main class="agent-state-page" [class.subagents]="feature === 'subagents'">
50 @if (feature === "subagents") {
51 <aside aria-label="Live supervisor delegation state">
52 <showcase-delegation-log [delegations]="delegations()" />
53 </aside>
54 }
55 <section class="chat-surface" aria-label="CopilotKit assistant">
56 <showcase-chat-host
57 [messageViewChildrenComponent]="
58 feature === 'gen-ui-agent' ? plannerTranscriptChildren : undefined
59 "
60 />
61 </section>
62 </main>
63 `,
64 styles: `
65 .agent-state-page {
66 min-height: 0;
67 background: #eef3f7;
68 }
69 .chat-surface {
70 min-width: 0;
71 height: 100%;
72 background: #fff;
73 }
74 .subagents {
75 display: grid;
76 grid-template-columns: minmax(18rem, 0.85fr) minmax(0, 1.35fr);
77 gap: 1rem;
78 padding: 1rem;
79 }
80 .subagents aside {
81 min-width: 0;
82 overflow: auto;
83 }
84 .subagents .chat-surface {
85 overflow: hidden;
86 border: 1px solid #d8e0ea;
87 border-radius: 1rem;
88 }
89 @media (max-width: 52rem) {
90 .subagents {
91 grid-template-columns: 1fr;
92 grid-template-rows: auto minmax(30rem, 55vh);
93 overflow: auto;
94 }
95 }
96 `,
97})
98export class AgentStateFeatureComponent {
99 private readonly route = inject(ActivatedRoute);
100 protected readonly feature =
101 (this.route.snapshot.data["feature"] as string | undefined) ??
102 "gen-ui-agent";
103 private readonly agentId = agentIdForRoute(this.feature, this.route);
104 protected readonly plannerTranscriptChildren =
105 AgentStateTranscriptChildrenComponent;
106 private readonly agentStore = injectAgentStore(this.agentId);
107 protected readonly delegations = computed(() =>
108 readDelegations(this.agentStore().state()),
109 );
110
111 constructor() {
112 if (this.feature === "subagents") {
113 this.registerSubAgent("research_agent");
114 this.registerSubAgent("writing_agent");
115 this.registerSubAgent("critique_agent");
116 }
117 }
118
119 private registerSubAgent(name: SubAgentName): void {
120 registerRenderToolCall(subAgentRendererConfig(name));
121 }
122}
123