Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ jobs:
node-version: 22
cache: npm
- run: npm ci
- run: npx tsx apps/cockpit/scripts/deploy-smoke.ts --url https://cockpit.stream-resource.dev --dry-run
- run: npx tsx apps/cockpit/scripts/deploy-smoke.ts --url https://cockpit.cacheplane.ai --dry-run

mcp:
name: MCP — build / smoke
Expand Down Expand Up @@ -239,7 +239,7 @@ jobs:
if: steps.affected.outputs.website == 'true'
run: npx nx e2e website --skip-nx-cache
env:
BASE_URL: https://stream-resource.dev
BASE_URL: https://cacheplane.ai
- name: Prepare cockpit Vercel project
if: steps.affected.outputs.cockpit == 'true'
run: |
Expand All @@ -259,7 +259,7 @@ jobs:
- name: Verify deployed cockpit
if: steps.affected.outputs.cockpit == 'true'
run: |
npx tsx apps/cockpit/scripts/deploy-smoke.ts --url https://cockpit.stream-resource.dev --retries 20 --retry-delay-ms 5000
npx tsx apps/cockpit/scripts/deploy-smoke.ts --url https://cockpit.cacheplane.ai --retries 20 --retry-delay-ms 5000

# ── Angular examples deploy ──────────────────────────────────────────
- name: Check if examples changed
Expand Down Expand Up @@ -314,6 +314,6 @@ jobs:
- name: Run production smoke tests
run: npx playwright test apps/cockpit/e2e/production-smoke.spec.ts --reporter=list
env:
BASE_URL: https://cockpit.stream-resource.dev
EXAMPLES_URL: https://examples.stream-resource.dev
BASE_URL: https://cockpit.cacheplane.ai
EXAMPLES_URL: https://examples.cacheplane.ai
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
2 changes: 1 addition & 1 deletion COMMERCIAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Any use in a for-profit product, service, or organization requires a paid commer

## Purchase or inquire

- Website: https://stream-resource.dev/pricing
- Website: https://cacheplane.ai/pricing
- Email: hello@cacheplane.ai

See [`LICENSE-COMMERCIAL`](./LICENSE-COMMERCIAL) for the full commercial license terms.
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<p align="center">
<img
src="https://stream-resource.dev/assets/hero.svg"
src="https://cacheplane.ai/assets/hero.svg"
alt="Angular Agent Framework — The Enterprise Streaming Resource for LangChain and Angular"
width="100%"
/>
Expand All @@ -17,7 +17,7 @@
<a href="./LICENSE">
<img alt="License: PolyForm Noncommercial + Commercial" src="https://img.shields.io/badge/license-PolyForm%20Noncommercial%20%2B%20Commercial-6C8EFF?labelColor=080B14&style=flat-square" />
</a>
<a href="https://stream-resource.dev">
<a href="https://cacheplane.ai">
<img alt="Angular 20+" src="https://img.shields.io/badge/Angular-20%2B-6C8EFF?labelColor=080B14&style=flat-square" />
</a>
<a href="https://langchain-ai.github.io/langgraph/">
Expand Down Expand Up @@ -110,7 +110,7 @@ That's it. `chat.messages()` is an Angular Signal. Bind it directly in your temp

<p align="center">
<img
src="https://stream-resource.dev/assets/arch-diagram.svg"
src="https://cacheplane.ai/assets/arch-diagram.svg"
alt="Angular Agent Framework architecture: Angular Component → agent() → StreamManager Bridge → LangGraph Platform, with signals returned reactively"
width="100%"
/>
Expand All @@ -129,17 +129,17 @@ That's it. `chat.messages()` is an Angular Signal. Bind it directly in your temp
| **Application Deployment** | $2,000 / app | One-time per application — covers dev, staging, and prod |
| **Enterprise** | Custom | Volume licensing, priority support, custom contract |

[Full pricing details and license terms →](https://stream-resource.dev/pricing)
[Full pricing details and license terms →](https://cacheplane.ai/pricing)

---

## Documentation

- [Getting Started](https://stream-resource.dev/docs/getting-started)
- [API Reference](https://stream-resource.dev/api-reference)
- [Testing with MockAgentTransport](https://stream-resource.dev/docs/testing)
- [Human-in-the-Loop / Interrupts](https://stream-resource.dev/docs/interrupts)
- [Subagent Streaming](https://stream-resource.dev/docs/subagents)
- [Getting Started](https://cacheplane.ai/docs/getting-started)
- [API Reference](https://cacheplane.ai/api-reference)
- [Testing with MockAgentTransport](https://cacheplane.ai/docs/testing)
- [Human-in-the-Loop / Interrupts](https://cacheplane.ai/docs/interrupts)
- [Subagent Streaming](https://cacheplane.ai/docs/subagents)

---

Expand All @@ -150,4 +150,4 @@ That's it. `chat.messages()` is an Angular Signal. Bind it directly in your temp
- **PolyForm Noncommercial 1.0.0** — free for noncommercial use (personal projects, academic, research, non-profit internal tooling). See [`LICENSE`](./LICENSE).
- **Angular Agent Framework Commercial License** — required for any for-profit or revenue-generating use. See [`LICENSE-COMMERCIAL`](./LICENSE-COMMERCIAL) and [`COMMERCIAL.md`](./COMMERCIAL.md).

This is **not** an open-source license. Commercial use — including use in a for-profit product, service, or organization — requires a paid commercial license. See [pricing](https://stream-resource.dev/pricing).
This is **not** an open-source license. Commercial use — including use in a for-profit product, service, or organization — requires a paid commercial license. See [pricing](https://cacheplane.ai/pricing).
8 changes: 4 additions & 4 deletions apps/cockpit/e2e/production-smoke.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import { expect, test } from '@playwright/test';
* Production smoke test — verifies the deployed stack works end-to-end.
*
* Requires:
* EXAMPLES_URL - e.g., https://examples.stream-resource.dev
* EXAMPLES_URL - e.g., https://examples.cacheplane.ai
* OPENAI_API_KEY - for send/receive tests (optional)
*
* Run:
* BASE_URL=https://cockpit.stream-resource.dev \
* EXAMPLES_URL=https://examples.stream-resource.dev \
* BASE_URL=https://cockpit.cacheplane.ai \
* EXAMPLES_URL=https://examples.cacheplane.ai \
* npx playwright test apps/cockpit/e2e/production-smoke.spec.ts
*/

const EXAMPLES_URL = process.env['EXAMPLES_URL'] ?? 'https://examples.stream-resource.dev';
const EXAMPLES_URL = process.env['EXAMPLES_URL'] ?? 'https://examples.cacheplane.ai';

const CAPABILITIES = [
'langgraph/streaming',
Expand Down
12 changes: 6 additions & 6 deletions apps/cockpit/scripts/deploy-smoke.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ describe('deploy smoke helper', () => {
expect(
parseDeploySmokeArgs([
'--url',
'https://cockpit.stream-resource.dev',
'https://cockpit.cacheplane.ai',
'--dry-run',
'--retries',
'5',
'--retry-delay-ms',
'1000',
])
).toEqual({
url: 'https://cockpit.stream-resource.dev',
url: 'https://cockpit.cacheplane.ai',
expectedTitle: 'Cockpit',
dryRun: true,
retries: 5,
Expand All @@ -25,11 +25,11 @@ describe('deploy smoke helper', () => {
it('formats dry-run output without performing a network request', async () => {
await expect(
runDeploySmoke({
url: 'https://cockpit.stream-resource.dev',
url: 'https://cockpit.cacheplane.ai',
expectedTitle: 'Cockpit',
dryRun: true,
})
).resolves.toBe('dry-run:https://cockpit.stream-resource.dev:Cockpit');
).resolves.toBe('dry-run:https://cockpit.cacheplane.ai:Cockpit');
});

it('retries until the deployment responds with the expected title', async () => {
Expand All @@ -46,13 +46,13 @@ describe('deploy smoke helper', () => {

await expect(
runDeploySmoke({
url: 'https://cockpit.stream-resource.dev',
url: 'https://cockpit.cacheplane.ai',
retries: 1,
retryDelayMs: 1,
fetchImpl,
sleep,
})
).resolves.toBe('pass:https://cockpit.stream-resource.dev:Cockpit');
).resolves.toBe('pass:https://cockpit.cacheplane.ai:Cockpit');

expect(fetchImpl).toHaveBeenCalledTimes(2);
expect(sleep).toHaveBeenCalledTimes(1);
Expand Down
6 changes: 3 additions & 3 deletions apps/cockpit/src/lib/content-bundle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ describe('resolveRuntimeUrl', () => {
});

it('uses NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL when set', () => {
vi.stubEnv('NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL', 'https://examples.stream-resource.dev');
vi.stubEnv('NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL', 'https://examples.cacheplane.ai');
expect(
resolveRuntimeUrl({ runtimeUrl: 'langgraph/streaming', devPort: 4300 })
).toBe('https://examples.stream-resource.dev/langgraph/streaming');
).toBe('https://examples.cacheplane.ai/langgraph/streaming');
});

it('falls back to localhost with devPort when no env var is set', () => {
Expand All @@ -49,7 +49,7 @@ describe('resolveRuntimeUrl', () => {
});

it('returns null when runtimeUrl is undefined even with env var set', () => {
vi.stubEnv('NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL', 'https://examples.stream-resource.dev');
vi.stubEnv('NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL', 'https://examples.cacheplane.ai');
expect(
resolveRuntimeUrl({ runtimeUrl: undefined, devPort: undefined })
).toBeNull();
Expand Down
135 changes: 135 additions & 0 deletions apps/website/content/docs/render/api/views.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
---
title: views()
description: Create and compose view registries for generative UI rendering
---

# views()

Creates an immutable view registry mapping names to Angular components. Views are rendered inline in the chat when the agent produces a JSON spec.

## Usage

```typescript
import { views } from '@cacheplane/render';

const ui = views({
'plan-checklist': PlanChecklistComponent,
'file-preview': FilePreviewComponent,
'code-output': CodeOutputComponent,
});
```

Pass to the chat component:

```html
<chat [ref]="stream" [views]="ui" />
```

Or provide globally via DI:

```typescript
import { provideViews } from '@cacheplane/render';

// app.config.ts
providers: [provideViews(ui)]
```

## Composition

Compose registries via object spread. Last key wins for overrides:

```typescript
const all = views({
...thirdPartyViews,
...myViews,
'chart': MyCustomChart, // overrides thirdPartyViews['chart']
});
```

## API

### `views(map)`

Creates a frozen `ViewRegistry` from a `Record<string, Type<unknown>>`.

| Parameter | Type | Description |
|-----------|------|-------------|
| `map` | `Record<string, Type<unknown>>` | Name → Angular component mapping |
| **Returns** | `ViewRegistry` | Frozen immutable registry |

### `withViews(base, additions)`

Adds new views without overwriting existing entries. Existing keys in `base` are preserved.

```typescript
const extended = withViews(base, {
'new-widget': NewWidget, // added
'existing': Other, // IGNORED — base already has 'existing'
});
```

### `withoutViews(base, ...names)`

Removes views by name:

```typescript
const restricted = withoutViews(base, 'dangerous-widget', 'internal-tool');
```

### `provideViews(registry)`

Angular DI provider. Works at app level or route level:

```typescript
// Global
providers: [provideViews(ui)]

// Route-scoped
{ path: 'planning', providers: [provideViews(planningViews)] }
```

### `toRenderRegistry(registry)`

Converts a `ViewRegistry` to the low-level `AngularRegistry` type used by `<render-spec>`. Called internally by the chat component — most developers won't need this.

## View Components

View components are standard Angular standalone components. They receive props as `input()` signals:

```typescript
@Component({
selector: 'plan-checklist',
standalone: true,
template: `
<div class="border rounded-xl p-4">
<h4>{{ title() }}</h4>
<ng-content />
</div>
`,
})
export class PlanChecklistComponent {
readonly title = input<string>('Plan');
}
```

## How Specs Are Detected

The chat component checks each message for a `ui` field containing a valid spec:

```json
{
"type": "tool",
"content": "...",
"ui": {
"root": "plan",
"elements": {
"plan": {
"type": "plan-checklist",
"props": { "title": "My Plan" }
}
}
}
}
```

The `type` in the spec is matched against the view registry to resolve the Angular component.
6 changes: 3 additions & 3 deletions apps/website/emails/drip-whitepaper-followup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function dripWhitepaperFollowupHtml(day: number): { subject: string; html
<p style="font-size:11px;font-family:monospace;text-transform:uppercase;letter-spacing:0.08em;color:#004090;font-weight:700;margin:0 0 8px">Whitepaper Follow-up</p>
<p style="font-size:20px;font-weight:700;color:#1a1a2e;margin:0 0 14px;line-height:1.3">Did you get a chance to read Chapter 3?</p>
<p style="font-size:14px;color:#555770;line-height:1.7;margin:0 0 24px">Chapter 3 covers <strong>tool-call rendering</strong> — how to surface agent actions as real UI instead of raw JSON. It's the chapter most teams bookmark first.</p>
<a href="https://stream-resource.dev/whitepaper.pdf" style="display:inline-block;background-color:#004090;color:#fff;padding:12px 28px;border-radius:8px;font-size:14px;font-weight:700;text-decoration:none">Read the Guide →</a>
<a href="https://cacheplane.ai/whitepaper.pdf" style="display:inline-block;background-color:#004090;color:#fff;padding:12px 28px;border-radius:8px;font-size:14px;font-weight:700;text-decoration:none">Read the Guide →</a>
`,
showUnsubscribe: true,
}),
Expand All @@ -24,7 +24,7 @@ export function dripWhitepaperFollowupHtml(day: number): { subject: string; html
<p style="font-size:11px;font-family:monospace;text-transform:uppercase;letter-spacing:0.08em;color:#004090;font-weight:700;margin:0 0 8px">Production Readiness</p>
<p style="font-size:20px;font-weight:700;color:#1a1a2e;margin:0 0 14px;line-height:1.3">The gap between demo and production</p>
<p style="font-size:14px;color:#555770;line-height:1.7;margin:0 0 24px">Half of GenAI projects die after proof of concept. The gap isn't the model — it's the frontend production path: streaming state, thread persistence, human approval flows, and deterministic testing.</p>
<a href="https://stream-resource.dev/docs" style="display:inline-block;background-color:#004090;color:#fff;padding:12px 28px;border-radius:8px;font-size:14px;font-weight:700;text-decoration:none">See How It Works →</a>
<a href="https://cacheplane.ai/docs" style="display:inline-block;background-color:#004090;color:#fff;padding:12px 28px;border-radius:8px;font-size:14px;font-weight:700;text-decoration:none">See How It Works →</a>
`,
showUnsubscribe: true,
}),
Expand All @@ -44,7 +44,7 @@ export function dripWhitepaperFollowupHtml(day: number): { subject: string; html
<p style="font-size:13px;color:#555770;margin:0 0 4px;line-height:1.6"><strong style="color:#004090">Month 1</strong> · First agent in staging</p>
<p style="font-size:13px;color:#555770;margin:0;line-height:1.6"><strong style="color:#004090">Month 3</strong> · Production deployment</p>
</div>
<a href="https://stream-resource.dev/pilot-to-prod" style="display:inline-block;background-color:#004090;color:#fff;padding:12px 28px;border-radius:8px;font-size:14px;font-weight:700;text-decoration:none">Learn About the Pilot →</a>
<a href="https://cacheplane.ai/pilot-to-prod" style="display:inline-block;background-color:#004090;color:#fff;padding:12px 28px;border-radius:8px;font-size:14px;font-weight:700;text-decoration:none">Learn About the Pilot →</a>
`,
showUnsubscribe: true,
}),
Expand Down
2 changes: 1 addition & 1 deletion apps/website/emails/email-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function wrapEmail(opts: {
${opts.body}
<div style="border-top:1px solid #e4e4e7;margin-top:28px;padding-top:16px">
<p style="font-size:11px;color:#a1a1aa;line-height:1.5;margin:0">Angular Agent Framework — Signal-native streaming for LangGraph.</p>
${opts.showUnsubscribe ? '<p style="font-size:10px;color:#d4d4d8;margin:6px 0 0"><a href="https://stream-resource.dev/api/unsubscribe?email=RECIPIENT" style="color:#d4d4d8;text-decoration:underline">Unsubscribe</a></p>' : ''}
${opts.showUnsubscribe ? '<p style="font-size:10px;color:#d4d4d8;margin:6px 0 0"><a href="https://cacheplane.ai/api/unsubscribe?email=RECIPIENT" style="color:#d4d4d8;text-decoration:underline">Unsubscribe</a></p>' : ''}
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion apps/website/emails/newsletter-welcome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export function newsletterWelcomeHtml(): string {
body: `
<p style="font-size:20px;font-weight:700;color:#1a1a2e;margin:0 0 8px;line-height:1.3">Welcome to Angular Agent Framework updates</p>
<p style="font-size:14px;color:#555770;line-height:1.7;margin:0 0 24px">You'll receive updates on new capabilities, production patterns, and Angular agent best practices. We keep it focused and infrequent — no spam.</p>
<a href="https://stream-resource.dev/docs" style="display:inline-block;background-color:#004090;color:#fff;padding:12px 28px;border-radius:8px;font-size:14px;font-weight:700;text-decoration:none">Explore the Docs</a>
<a href="https://cacheplane.ai/docs" style="display:inline-block;background-color:#004090;color:#fff;padding:12px 28px;border-radius:8px;font-size:14px;font-weight:700;text-decoration:none">Explore the Docs</a>
`,
});
}
2 changes: 1 addition & 1 deletion apps/website/emails/whitepaper-download.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { wrapEmail, esc } from './email-wrapper';

const DOWNLOAD_URL = 'https://stream-resource.dev/whitepaper.pdf';
const DOWNLOAD_URL = 'https://cacheplane.ai/whitepaper.pdf';

export function whitepaperDownloadHtml(name?: string): string {
return wrapEmail({
Expand Down
2 changes: 1 addition & 1 deletion apps/website/public/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ Add to ~/.claude/settings.json:
{"mcpServers":{"angular":{"command":"npx","args":["@angular/mcp"]}}}

## Version check
If this file is stale, fetch the latest: https://stream-resource.dev/llms-full.txt
If this file is stale, fetch the latest: https://cacheplane.ai/llms-full.txt
2 changes: 1 addition & 1 deletion apps/website/public/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ Add to ~/.claude/settings.json:
{"mcpServers":{"angular":{"command":"npx","args":["@angular/mcp"]}}}

## Version check
If this file is stale, fetch the latest: https://stream-resource.dev/llms-full.txt
If this file is stale, fetch the latest: https://cacheplane.ai/llms-full.txt
Loading