Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export const appConfig: ApplicationConfig = {
};
```

`FakeAgent` extends `AbstractAgent` and emits a canned `RUN_STARTED -> TEXT_MESSAGE_START -> TEXT_MESSAGE_CONTENT x N -> TEXT_MESSAGE_END -> RUN_FINISHED` sequence. Drop-in replacement for `provideAgent({ url })` while you're prototyping.
`FakeAgent` extends `AbstractAgent` and emits a canned `RUN_STARTED -> TEXT_MESSAGE_START -> TEXT_MESSAGE_CONTENT x N -> TEXT_MESSAGE_END -> RUN_FINISHED` sequence. It's a drop-in replacement for `provideAgent({ url })` while you're prototyping.

## Custom transport

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> **Picking an adapter?** This guide covers `@threadplane/ag-ui` — the AG-UI protocol adapter. If you're talking to LangGraph Platform directly via the LangGraph SDK, use [`@threadplane/langgraph`](/langgraph) instead. See [Choosing an adapter](/docs/choosing-an-adapter) for a side-by-side comparison.

`@threadplane/ag-ui` is the runtime adapter that wraps an [AG-UI](https://github.com/ag-ui-protocol/ag-ui) `AbstractAgent` into the runtime-neutral `Agent` contract from `@threadplane/chat`. The chat UI primitives consume the Agent contract; the AG-UI adapter translates between the contract and the AG-UI event protocol.
`@threadplane/ag-ui` is the runtime adapter that wraps an [AG-UI](https://github.com/ag-ui-protocol/ag-ui) `AbstractAgent` into the runtime-neutral `Agent` contract from `@threadplane/chat`. The chat UI primitives consume the Agent contract, and the AG-UI adapter translates between the contract and the AG-UI event protocol.

<Callout type="info" title="What is AG-UI?">
AG-UI is the open agent-to-UI protocol from the CopilotKit ecosystem. It standardizes how agent runtimes stream events (messages, tool calls, state updates) to a frontend. Used by **CrewAI**, **Mastra**, **Microsoft Agent Framework**, **AG2**, **Pydantic AI**, **AWS Strands**, and the **CopilotKit runtime**. One adapter unlocks all of them.
Expand Down Expand Up @@ -32,7 +32,7 @@ AG-UI is the open agent-to-UI protocol from the CopilotKit ecosystem. It standar

## What's covered

Scope of the first release:
Here's what the first release handles:
- `messages` (streaming token deltas via `TEXT_MESSAGE_*` events)
- `status` / `isLoading` / `error` (lifecycle via `RUN_STARTED/FINISHED/ERROR`)
- `toolCalls` (streaming tool calls via `TOOL_CALL_*` events)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Quick Start

Bind `<chat>` from `@threadplane/chat` to an AG-UI backend in 5 minutes.
Let's bind `<chat>` from `@threadplane/chat` to an AG-UI backend in 5 minutes.

<Callout type="info" title="Prerequisites">
Angular 20+ project with Node.js 22+. If you need setup help, see the [Installation](/docs/ag-ui/getting-started/installation) guide.
Expand Down Expand Up @@ -30,7 +30,7 @@ export const appConfig: ApplicationConfig = {
};
```

For offline development without a backend, swap to `provideFakeAgent({})` - it serves canned streaming responses for UI work.
No backend yet? Swap to `provideFakeAgent({})` - it serves canned streaming responses so you can build UI offline.

</Step>
<Step title="Use in a component">
Expand Down Expand Up @@ -67,7 +67,9 @@ That's it. The `<chat>` composition handles streaming messages, tool calls, erro

## Switching backends without changing UI

The point of the runtime-neutral `Agent` contract is that swapping backends is a one-line change in `app.config.ts`. The component code stays the same:
For me, the runtime-neutral `Agent` contract is the whole payoff: your component never learns which protocol it's talking to. The cost is a thin translation layer per adapter, but you pay it once and your UI code stops caring.

So swapping backends is a one-line change in `app.config.ts`, and the component code stays the same:

```diff
- providers: [provideAgent({ apiUrl: '...' })], // LangGraph
Expand Down
10 changes: 6 additions & 4 deletions apps/website/content/docs/chat/getting-started/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

A complete walkthrough for installing `@threadplane/chat` in an Angular 20+ application, activating a commercial license, and rendering your first chat in under 30 minutes.

This guide goes deeper than the Quick Start: license activation, peer dependencies, and the warnings you'll see in the console.

<Callout type="info" title="Just bought a license?">
This guide is written for customers who purchased a Developer Seat, Team, or Enterprise plan and received a `THREADPLANE_LICENSE` token by email. If you're evaluating `@threadplane/chat` for noncommercial use, you can skip the license steps — the library runs without a token (with a one-time advisory warning).
</Callout>
Expand All @@ -16,7 +18,7 @@ This guide is written for customers who purchased a Developer Seat, Team, or Ent
Required for the Angular build toolchain. `node --version` should report `v18` or newer.
</Step>
<Step title="A running agent backend (or a mock)">
You need something for the chat UI to talk to. Two officially supported adapters cover virtually every backend:
The chat UI needs something to talk to. Two officially supported adapters cover virtually every backend:

- **`@threadplane/langgraph`** — pick this if your backend is LangGraph or LangGraph Platform.
- **`@threadplane/ag-ui`** — pick this for any AG-UI compatible backend (CrewAI, Mastra, Microsoft Agent Framework, AG2, Pydantic AI, AWS Strands, CopilotKit runtime).
Expand Down Expand Up @@ -53,7 +55,7 @@ eyJzdWIiOiJjdXN0QGV4YW1wbGUuY29tIiwidGllciI6ImRldmVsb3Blci1zZWF0IiwiaWF0IjoxNzM

The token is signed with Ed25519. Verification is **offline** and **advisory** — a missing or expired token logs one `console.warn` line on first boot and the chat keeps working. There is no kill switch, no network call, no telemetry.

That makes how you store the token a matter of secret-management preference rather than runtime correctness. Pick whichever flow fits your team.
So how you store the token is a matter of secret-management preference, not runtime correctness. Pick whichever flow fits your team.

### Option A: `.env` file (recommended for local dev)

Expand Down Expand Up @@ -95,7 +97,7 @@ provideChat({
});
```

This is the lowest-friction path for small teams. Do **not** commit the token to a public repository — anyone with the token can use the seats it grants.
For me, this is the lowest-friction path for a small team — the tradeoff is that everyone with repo access also gets the token. Do **not** commit it to a public repository — anyone with the token can use the seats it grants.

## 3. Wire `provideChat()` and `provideAgent()`

Expand Down Expand Up @@ -132,7 +134,7 @@ provideChat({
});
```

`provideChat()` itself is optional — chat components fall back to sensible defaults — but you'll want it once you start passing a license, an `assistantName`, or other global config.
`provideChat()` itself is optional — chat components fall back to sensible defaults — but you'll want it once you start passing a license, an `assistantName`, or any other global config.

## 4. Render your first chat

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Introduction

`@threadplane/chat` is the Angular UI component library for building chat interfaces on top of a runtime-neutral `Agent` contract. It provides a complete set of composable, Signal-driven components that render messages, handle user input, display tool calls, manage interrupts, and support generative UI -- all styled with CSS custom properties and built for Angular 20+.
`@threadplane/chat` is the Angular UI component library for building chat interfaces on top of a runtime-neutral `Agent` contract. It's a set of composable, Signal-driven components that render messages, handle user input, display tool calls, manage interrupts, and support generative UI -- all styled with CSS custom properties and built for Angular 20+.

<Callout type="info" title="What you'll learn">
This guide explains the library's two-tier architecture, how it relates to the rest of the Threadplane stack, and when to reach for the all-in-one composition versus assembling primitives yourself.
Expand All @@ -12,7 +12,7 @@ The library is organized into two layers: **primitives** and **compositions**.

### Primitives

Primitives are low-level, headless components that read from an `Agent` and expose their state through content projection (`ng-template`). They carry no opinion about layout or styling. Use them when you need full control over how chat elements render.
Primitives are low-level, headless components that read from an `Agent` and expose their state through content projection (`ng-template`). They carry no opinion about layout or styling. Reach for them when you need full control over how chat elements render.

| Primitive | Selector | Purpose |
|-----------|----------|---------|
Expand Down Expand Up @@ -64,7 +64,7 @@ LangGraph Platform

## When to Use `ChatComponent` vs. Custom Assembly

**Use `ChatComponent`** when you want a complete, styled chat interface with minimal setup. It includes message rendering (with markdown), a text input, typing indicator, error display, interrupt banner, and an optional thread sidebar. Drop it in, pass an `Agent`, and you have a working chat.
**Use `ChatComponent`** when you want a complete, styled chat interface with minimal setup. It includes message rendering (with markdown), a text input, typing indicator, error display, interrupt banner, and an optional thread sidebar. Drop it in, pass an `Agent`, and you've got a working chat.

```html
<chat [agent]="chatRef" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Quick Start

Build a streaming chat UI with `@threadplane/chat` in 5 minutes.
Let's build a streaming chat UI with `@threadplane/chat` in 5 minutes.

<Callout type="info" title="Prerequisites">
Angular 20+ project with an agent provider configured. See [Agent Installation](/docs/langgraph/getting-started/installation) if you need help.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

`@threadplane/licensing` is the shared license-check helper used by `@threadplane/chat` and by custom integrations that opt into the same warning behavior. It verifies compact Ed25519-signed tokens offline, evaluates the result into a small status set, and emits non-blocking warnings when appropriate.

The package itself is MIT licensed. `COMMERCIAL.md` states that the libraries in this repository are free to use, modify, and distribute in commercial and noncommercial projects. The proprietary part called out there is the internal minting service, not this package.
The package itself is MIT licensed. `COMMERCIAL.md` says the libraries in this repository are free to use, modify, and distribute in commercial and noncommercial projects. The proprietary part it calls out is the internal minting service, not this package.

## Public API shape

Expand Down Expand Up @@ -61,10 +61,10 @@ The default grace window is 14 days.

## Runtime posture

The higher-level check is designed not to block app startup:
The higher-level check is built not to block app startup:

- signature verification is local;
- warning output goes through `console.warn` unless a custom `warn` function is supplied;
- no network request is made by the licensing check.

The code returns statuses instead of throwing for normal license states. Consumers can choose what to do with the status, and `@threadplane/chat` uses it as a warning and visibility mechanism, not as an app kill switch.
The code returns statuses instead of throwing for normal license states. For me, that's the right default here: you get to decide what a missing or expired license means for your app instead of having the check make that call for you. The tradeoff is that nothing stops a consumer from ignoring the status entirely. `@threadplane/chat` treats it as a warning and visibility mechanism, not an app kill switch.
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ The library requires the following peer dependencies:
| `@json-render/core` | `^0.16.0` |

<Callout type="info" title="Angular packages">
`@angular/core` and `@angular/common` are already part of any Angular 20+ project. You only need to install `@json-render/core` as an additional dependency if your package manager does not install peer dependencies automatically.
`@angular/core` and `@angular/common` are already part of any Angular 20+ project. You only need to install `@json-render/core` yourself if your package manager doesn't install peer dependencies automatically.
</Callout>

## Configure the Provider

Add `provideRender()` to your application configuration. This sets global defaults for all `<render-spec>` instances in your application.
Add `provideRender()` to your application config. This sets global defaults for every `<render-spec>` instance in your app.

```typescript
// app.config.ts
Expand All @@ -57,12 +57,12 @@ export const appConfig: ApplicationConfig = {
```

<Callout type="tip" title="provideRender() is optional">
`provideRender()` is a convenience for setting global defaults. You can skip it entirely and pass `registry`, `store`, `functions`, and `handlers` as inputs directly to `<render-spec>`. Inputs always take precedence over provider config.
`provideRender()` is just a convenience for setting global defaults. You can skip it entirely and pass `registry`, `store`, `functions`, and `handlers` as inputs directly to `<render-spec>`. Inputs always win over provider config.
</Callout>

## Minimal App Setup

Here is a complete minimal application setup:
Here's a complete minimal setup:

<Tabs>
<Tab label="app.config.ts">
Expand Down Expand Up @@ -132,13 +132,13 @@ export class AppComponent {

## Verify Installation

After setting up, run your application and verify the rendered output:
Once you're set up, run your app and check the rendered output:

```bash
ng serve
```

If you see your component rendered with the expected props, the installation is complete.
If you see your component rendered with the expected props, you're done.

## Troubleshooting

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

## Why @threadplane/render?

Building dynamic UIs from server-driven specifications is a common pattern in AI applications, form builders, and CMS-powered frontends. `@threadplane/render` bridges the gap between `@json-render/core` (a framework-agnostic spec evaluation engine) and Angular's component model.
Building dynamic UIs from server-driven specs shows up everywhere -- AI apps, form builders, CMS-powered frontends. `@threadplane/render` bridges `@json-render/core` (a framework-agnostic spec evaluation engine) and Angular's component model.

Instead of writing imperative rendering logic, you describe your UI as a JSON spec and let the library handle the rest:
You don't write imperative rendering logic. You describe your UI as a JSON spec and let the library handle the rest:

```typescript
const spec: Spec = {
Expand All @@ -25,7 +25,7 @@ const spec: Spec = {
<render-spec [spec]="spec" [registry]="registry" />
```

The library resolves `Text` from your component registry, evaluates the `$state` expression against the state store, and renders your `TextComponent` with `label` set to `"Hello, world!"`. When the state changes, the component updates automatically via Angular Signals.
The library resolves `Text` from your component registry, evaluates the `$state` expression against the state store, and renders your `TextComponent` with `label` set to `"Hello, world!"`. When the state changes, the component updates on its own via Angular Signals.

## How It Relates to @json-render/core

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Angular 20+ project with `@threadplane/render` installed. See the [Installation]
<Steps>
<Step title="Create a component to render">

Define a simple Angular component that will be rendered by the spec. Every rendered component receives inputs matching the `AngularComponentInputs` interface -- your custom props are spread alongside the standard inputs.
Let's start with a simple Angular component for the spec to render. Every rendered component receives inputs matching the `AngularComponentInputs` interface -- your custom props are spread alongside the standard inputs.

```typescript
// text.component.ts
Expand Down Expand Up @@ -46,7 +46,7 @@ export const uiRegistry = defineAngularRegistry({
</Step>
<Step title="Build a spec">

A spec describes the UI tree as a flat map of elements. The `root` key points to the entry element.
Now let's describe the UI tree itself. A spec is a flat map of elements, and the `root` key points to the entry element.

```typescript
// app.component.ts
Expand Down Expand Up @@ -93,7 +93,7 @@ Open `http://localhost:4200`. You should see "Hello from @threadplane/render!" r

## Adding Reactive State

Specs become powerful when you connect them to a state store. Props with `$state` expressions read from the store reactively.
Specs get a lot more interesting once you connect them to a state store. Props with `$state` expressions read from the store reactively.

```typescript
import { Component, ChangeDetectionStrategy } from '@angular/core';
Expand Down Expand Up @@ -135,7 +135,7 @@ Clicking the button updates the store, which reactively updates the rendered `Te

## Using Spec-Embedded State

You can also embed initial state directly in the spec. When no external store is provided, `RenderSpecComponent` automatically creates an internal `signalStateStore` from `spec.state`:
You can also embed initial state right in the spec. When you don't pass an external store, `RenderSpecComponent` creates an internal `signalStateStore` from `spec.state` for you:

```typescript
spec: Spec = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The important boundary is simple:
- Node telemetry is opt-out and used by package lifecycle or server adapter code;
- shared utilities expose event and environment helpers.

Do not treat the browser and Node entry points as interchangeable. They have different runtime assumptions and different privacy defaults.
Don't treat the browser and Node entry points as interchangeable. They have different runtime assumptions and different privacy defaults.

## Entry points

Expand All @@ -35,7 +35,7 @@ When enabled, delivery is chosen in this order:
2. `endpoint`;
3. `posthogKey`.

`sink` and `endpoint` are the preferred app-owned paths. `posthogKey` and `posthogHost` still exist for older integrations and are marked deprecated in source comments.
For me, `sink` and `endpoint` are the paths to reach for. They keep delivery app-owned, so you decide where events go. `posthogKey` and `posthogHost` still exist for older integrations, but they're marked deprecated in source comments, so don't build anything new on them.

## Node posture

Expand Down
Loading
Loading