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
4a65196ee
CopilotKit Showcase