Sitelet https://github.com/cacheplane/threadplane/pull/1215/files
Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

### 🚀 Features

- **render, chat:** in development, A2UI surfaces and json-render specs report which elements mounted, showed a fallback, did not resolve against the registry, or did not render, as a `threadplane:devtools` `CustomEvent` with `kind: 'render'` — names and states only, never props; stripped from production builds; same opt-out
- **langgraph, ag-ui:** in development, the AG-UI DevTools run simulator can script an agent's next runs with `threadplane:devtools:arm` / `:disarm` / `:ack` `CustomEvent`s on `window`; the scripted frames or events go through the adapter's real event handling with no network call; stripped from production builds; same opt-out
- **langgraph, ag-ui:** in development, each agent reports which signals every event wrote as a `threadplane:devtools` `CustomEvent` on `window` — names and timing only, never values; stripped from production builds; opt out with `window.__THREADPLANE_DEVTOOLS_DISABLED__ = true`
- **ag-ui:** upgrade to AG-UI 1.0.1; the adapter now declares `^1.0.1` peers on `@ag-ui/client` and `@ag-ui/core`, imports validators from `@ag-ui/core/schemas`, declares `protocolVersion` on every run, and runs the private request path through the 1.0 compatibility boundary
Expand Down
1 change: 1 addition & 0 deletions libs/chat/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

### Added

- **A development-only render report for the AG-UI DevTools UI tab.** `<a2ui-surface>` and `<chat-generative-ui>` now report, per surface, how the spec they render resolved: a `threadplane:devtools` `CustomEvent` with `{ v: 1, kind: 'render', surface, seq, registry, elements, tMs }`, where each element is `{ key, type, state }` and `state` is `mounted`, `fallback`, `unresolved` (no registry entry) or `hidden` (not rendered, including everything under an unresolved element). `surface` is the A2UI `surfaceId`, or `'spec:' + root key` for json-render. Names, keys, types and states only — prop values are never read. At most 500 registry names and 2,000 elements; sent only when the resolution changes. Ships as the private `ɵcreateRenderDevtoolsReporter` / `ɵprovideRenderDevtools`, behind the signals report's development-only gate and opt-out; production builds strip it.
- **Development-only scripted runs for the AG-UI DevTools run simulator.** A `threadplane:devtools:arm` `CustomEvent` (`{ v: 1, armId, adapter, runs }`) makes the next runs of that adapter's agents stream the scripted LangGraph frames or AG-UI events through the adapter's real event handling instead of the network; `threadplane:devtools:disarm` cancels, and every step is answered with a `threadplane:devtools:ack`. At most 8 runs of 5,000 frames or events and 2 MB per arm, one pending arm per adapter, expiring after 10 minutes. The store ships here as the private `ɵdevtoolsScriptedRuns`, behind the same development-only gate and opt-out as the signals report; production builds strip it.
- **A development-only devtools hook for the runtime adapters.** `@threadplane/langgraph` and `@threadplane/ag-ui` now report, per event, the names of the signals it wrote as a `threadplane:devtools` `CustomEvent` on `window` (names and timing only — never values), for the AG-UI DevTools extension's Signals tab. The emitter ships here as the private `ɵcreateDevtoolsEmitter`; production builds strip it, and `window.__THREADPLANE_DEVTOOLS_DISABLED__ = true` turns it off in development.
- **`--tplane-chat-launcher-offset-x` / `--tplane-chat-launcher-offset-y`** (both `1rem`) position the `<chat-popup>` launcher, replacing hard-coded corner offsets. The popup window reads the horizontal one too, so it stays aligned when you move the launcher clear of a bottom bar or a consent banner.
Expand Down
24 changes: 19 additions & 5 deletions libs/chat/scripts/verify-devtools-bundle.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,45 @@
// the adapters' providers plus the chat emitter they call, with Angular's
// production constant `ngDevMode = false` — and asserts that nothing of the
// hook survives: not its report event, not the scripted-run arm/disarm/ack
// events, not its opt-out flag. The same bundle with
// events, not the render report (chat's reporter and render's element
// tracker), not its opt-out flag. The same bundle with
// `ngDevMode` left to the runtime must contain them all, so the check cannot pass by
// bundling the wrong thing. Run after building chat, langgraph and ag-ui.
// bundling the wrong thing. Run after building render, chat, langgraph and ag-ui.
import assert from 'node:assert/strict';
import { existsSync } from 'node:fs';
import { resolve } from 'node:path';
import { build } from 'esbuild';

const dist = (path) => resolve('dist/libs', path);
const local = {
'@threadplane/render': dist('render/fesm2022/threadplane-render.mjs'),
'@threadplane/chat': dist('chat/fesm2022/threadplane-chat.mjs'),
'@threadplane/langgraph': dist('langgraph/fesm2022/threadplane-langgraph.mjs'),
'@threadplane/ag-ui': dist('ag-ui/fesm2022/threadplane-ag-ui.mjs'),
};
for (const file of Object.values(local)) {
assert(existsSync(file), `${file} is missing; build chat, langgraph and ag-ui first`);
assert(existsSync(file), `${file} is missing; build render, chat, langgraph and ag-ui first`);
}

const entry = `
export { provideAgent as provideLangGraphAgent } from '@threadplane/langgraph';
export { provideAgent as provideAgUiAgent, toAgent } from '@threadplane/ag-ui';
export { ɵcreateDevtoolsEmitter, ɵdevtoolsScriptedRuns } from '@threadplane/chat';
export {
ɵcreateDevtoolsEmitter,
ɵdevtoolsScriptedRuns,
ɵcreateRenderDevtoolsReporter,
ɵprovideRenderDevtools,
A2uiSurfaceComponent,
ChatGenerativeUiComponent,
} from '@threadplane/chat';
export { RenderSpecComponent, RenderElementComponent } from '@threadplane/render';
`;

/** The Threadplane packages come from dist; every other import stays external, as a CDN would leave it. */
const resolveThreadplane = {
name: 'threadplane-dist',
setup(builder) {
builder.onResolve({ filter: /^@threadplane\/(chat|langgraph|ag-ui)$/ }, (args) => ({ path: local[args.path] }));
builder.onResolve({ filter: /^@threadplane\/(render|chat|langgraph|ag-ui)$/ }, (args) => ({ path: local[args.path] }));
builder.onResolve({ filter: /^[@#a-z]/ }, (args) =>
local[args.path] ? undefined : { path: args.path, external: true });
},
Expand Down Expand Up @@ -60,6 +70,10 @@ const markers = [
'threadplane:devtools:arm',
'threadplane:devtools:disarm',
'threadplane:devtools:ack',
// The render report: chat's reporter builds `{ v: 1, kind: 'render', … }`;
// render's element tracker ranks states in `{ …, unresolved: 1, … }`.
'kind:"render"',
'unresolved:1',
'__THREADPLANE_DEVTOOLS_DISABLED__',
];

Expand Down
10 changes: 9 additions & 1 deletion libs/chat/src/lib/a2ui/surface.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
Component, computed, effect, input, output, untracked, ChangeDetectionStrategy, Type,
Component, computed, effect, inject, input, output, untracked, ChangeDetectionStrategy, Type,
} from '@angular/core';
import { NgComponentOutlet } from '@angular/common';
import type { A2uiSurface, A2uiActionMessage, A2uiErrorMessage, A2uiCheck } from '@threadplane/a2ui';
Expand All @@ -13,6 +13,7 @@ import { buildA2uiActionMessage } from './build-action-message';
import { A2uiDefaultFallbackComponent } from './a2ui-default-fallback.component';
import type { A2uiSurfaceState } from './surface-store';
import type { A2uiViews } from './views';
import { ɵprovideRenderDevtools } from '../devtools/devtools-render-report';

@Component({
selector: 'a2ui-surface',
Expand All @@ -23,6 +24,13 @@ import type { A2uiViews } from './views';
NgComponentOutlet,
],
changeDetection: ChangeDetectionStrategy.OnPush,
// Development only: the AG-UI DevTools render report, keyed by surfaceId.
viewProviders: [
ɵprovideRenderDevtools(() => {
const host = inject(A2uiSurfaceComponent);
return () => (host.state()?.surface ?? host.surface())?.surfaceId;
}),
],
// The host applies the agent-set surface theme (`createSurface.theme`)
// as inline CSS custom properties. Catalog components consume
// `--a2ui-primary` for accents (buttons, sliders, focus, etc.).
Expand Down
Loading
Loading