Bump the canonical CopilotKit pin across all showcase integrations + shell
to 1.61.2 (canonical-pins.json, every package.json + package-lock.json),
which carries CopilotKit#5611: passing a catalog to the provider
(`<CopilotKit a2ui={{ catalog }}>`) now auto-enables A2UI and defaults tool
injection on, so the runtime no longer needs an explicit `a2ui` config.
Demonstrate the feature on the A2UI dynamic (declarative-gen-ui) demos by
removing the now-redundant runtime `a2ui` block (`injectA2UITool: true` +
`defaultCatalogId`) from:
- langgraph-python, langgraph-fastapi, langgraph-typescript
- strands, strands-typescript
- google-adk
The forwarded catalog supplies its own catalogId (sdk-js A2UI middleware
auto-derives `defaultCatalogId` from it), so the previous "Catalog not found"
fallback no longer applies.
Verified: validate-pins drift ratchet unchanged (38 / same hash);
langgraph-python D6 `gen-ui-declarative` green end-to-end (no Catalog-not-found).
The secondary-LLM prompt was far thinner than the canonical generation guidelines,
so it emitted trees that (correctly) failed the renderer's paint gate → surface-missing.
Port the canonical generation rules into the prompt, add output validation, add catalog
parity (DataTable + info-row), ground the planner with sales-context, and record
multi-turn aimock fixtures. Includes CR fixes: two-arg z.record for the DataTable rows
schema (zod@4 API), index-based DataTable row key, and Metric trendValue rendering for
neutral trend.
The showcase authors A2UI catalog defs with root zod@4, but @a2ui/web_core's
GenericBinder schema scraper inspects Zod-3 internals (_def.typeName==='ZodUnion').
A zod@4 union reports _def.typeName===undefined → misclassified STATIC → the raw
{path} binding object reaches render → React error #31. Author this demo's catalog
with a zod-v3 (npm:zod@3.25.76) alias so the binder resolves bindings. Includes CR
hardening of the shared a2ui factory validation (plain-object data guard, unique-id
check, fail-loud on non-string secondary-LLM return).
## Summary
Doc-only single-file correction to
`showcase/integrations/built-in-agent/PARITY_NOTES.md`. The
`gen-ui-agent` D6 cell already passes end-to-end locally, but its
PARITY_NOTES entry still documented it as RED/blocked on a `STATE_DELTA
→ useAgent` gap in `@copilotkit/react-core`. That premise is stale and
is now refuted by local D6 runs. This rewrites the entry to
GREEN/reclaimed and rescopes the surrounding section header to the
remaining A2UI render-layer demos.
## RED → GREEN proof (from the work log)
The "RED" here is documentary, not behavioral: the cell **passes**
despite the stale RED doc.
Local RED baseline (`bin/showcase test built-in-agent:gen-ui-agent --d6
--direct --isolate`):
```
[conversation-runner] turn 3/3 — assertions passed
[conversation-runner] conversation completed successfully { turnsCompleted: 3 }
✓ d6:built-in-agent green (44.1s)
1 passed (44.1s)
```
Local GREEN value-test (same command, `--repeat 3`):
```
3 passed (130.1s)
✓ Tests passed for built-in-agent:gen-ui-agent
```
3/3 stable — not a flake. The doc correction changes no runtime
behavior.
Why it works: the backend `set_steps` server-tool result is converted to
a `STATE_DELTA` `[{op:"add", path:"/steps", value:steps}]` in
`src/lib/factory/tanstack-factory.ts` (`add`, not `replace`, so
`@ag-ui/client@0.0.57` doesn't drop it as
`OPERATION_PATH_UNRESOLVABLE`). `@ag-ui/client` applies the patch and
fires `onStateChanged`; the core state-manager fans it to subscribers;
`useAgent` re-renders off `agent.state.steps`. Fully wired in published
1.61.1 — no react-core change needed.
## Key finding: there was NO config quarantine
`gen-ui-agent` was never in the manifest `not_supported_features`, never
excluded in `shared/constraints.yaml`, and
`shared/feature-registry.json` has no per-feature status field. The
harness already routes it into `runnable` and grades it green. The
**stale doc was the only artifact** — there was no executable quarantine
to lift, so this is a pure doc correction.
## The two genuinely-RED demos are out of scope
`a2ui-fixed-schema` (React #31 crash from an unresolved `{path}` A2UI
binding) and `declarative-gen-ui` (surface never paints; secondary-LLM
op-shape) are real bugs, but both belong to `@copilotkit/a2ui-renderer`
/ showcase — **not** `@copilotkit/react-core`. They are being addressed
in a separate spec and are intentionally left untouched here.
## Test plan
- [x] `oxfmt --check` on the changed file — passes (correctly formatted)
- [x] `oxlint` on the changed file — 0 warnings, 0 errors
- [x] `commitlint` on the commit message — passes (`docs(showcase):`)
- [x] D6 cell passes locally, 3/3 stable
- [ ] CI green
The built-in-agent gen-ui-agent D6 cell already passes end-to-end locally;
the PARITY_NOTES entry that documented it as RED/blocked on a STATE_DELTA
to useAgent gap in @copilotkit/react-core was stale. The set_steps to
STATE_DELTA {op:"add", path:"/steps"} workaround merged in
tanstack-factory.ts closed that gap: @ag-ui/client applies the patch and
fires onStateChanged, the core state-manager fans it to subscribers, and
useAgent re-renders off agent.state.steps. No react-core change is needed.
Rewrites the gen-ui-agent entry to GREEN/reclaimed and rescopes the
section header to the remaining A2UI render-layer demos (a2ui-fixed-schema,
declarative-gen-ui), whose fixes belong to @copilotkit/a2ui-renderer, not
react-core. Doc-only; no config quarantine existed (gen-ui-agent was never
in manifest not_supported_features), so the cell stays a counted green.
Local RED baseline: cell passes (1 passed) despite the stale RED doc.
Local GREEN value-test: --repeat 3 => 3 passed (130.1s), stable.
The dojo's preview iframe built its src from `integration.backend_url`,
which generate-registry.ts bakes into registry.json at Docker BUILD time
(default `showcase-{slug}-production.up.railway.app`). So the staging
dojo iframed PROD integration backends — the exact staging->prod leakage
the shell's SU-13 runtime-derivation fix already prevents, but which was
never ported to shell-dojo.
Port the `backendHostPattern` slice of SU-13:
- copy shell's backend-url.ts verbatim (resolveBackendUrl + the
NEXT_PUBLIC_LOCAL_BACKENDS local-dev override); a scripts drift-guard
test keeps it byte-identical to the shell's and pins the default
pattern across backend-url.ts and generate-registry.ts.
- add `backendHostPattern` to shell-dojo's RuntimeConfig (server reads
SHOWCASE_BACKEND_HOST_PATTERN at request time; client carries the SSR
sentinel) — the existing layout injection picks it up automatically.
- page.tsx derives previewUrl via resolveBackendUrl at request time,
gated on a `mounted` flag so the SSR-phase sentinel host never reaches
an iframe src (shell-dojo loads the registry synchronously, so unlike
the shell it has no data-loading guard to defer the read past
hydration).
Staging dojo's SHOWCASE_BACKEND_HOST_PATTERN is set to
`showcase-{slug}-staging.up.railway.app`; prod stays unset (= default
prod pattern), so prod behavior is byte-identical.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Summary
`showcase/aimock/RAILWAY.md` described the aimock fixture infrastructure
as it worked during an earlier migration phase, not as it works today.
The stale claims actively misled debugging. This PR corrects them to
match the real, verified infra.
## Before → After
| Claim | Before (stale) | After (correct) |
| --- | --- | --- |
| Fixture delivery (§4/§5) | Three fixtures fetched at boot from
GitHub-raw URLs: `d5-all.json`, `smoke.json`, `feature-parity.json` |
Fixtures are **baked into the image** at build time under
`/fixtures/{shared,d4,d6}`; no remote fetch |
| `d5-all.json` | Authoritative D5 bundle, re-bundled from
`harness/fixtures/d5/*` | **Gone** — a one-time migration source split
into the per-slug `showcase/aimock/d6/<slug>/` tree (the source of
truth) |
| startCommand `--fixtures` (§5) | 3 `raw.githubusercontent.com` URLs |
`--fixtures /fixtures/shared --fixtures /fixtures/d4 --fixtures
/fixtures/d6` (the baked-in dirs). Added a warning that a single
`--fixtures /fixtures` parent loads nothing |
| Runtime image (§3) | Bare `ghcr.io/copilotkit/aimock:<version>` pulled
directly | `showcase-aimock` image, `FROM
ghcr.io/copilotkit/aimock:latest` + baked fixtures, built by
`showcase_build.yml` |
| Dockerfile (§8) | "Dead code / legacy wrapper, safe to remove" |
**LIVE** — `showcase_build.yml` builds it (`dockerfile:
showcase/aimock/Dockerfile`) and it bakes `shared/`, `d4/`, `d6/` into
`/fixtures/`. Do not remove |
| Env vars (§6), references (§9) | Referenced public GitHub-raw fixture
URLs and ~5 min raw-edge cache propagation | Local baked dirs;
propagation is via image rebuild + Railway deploy |
Also documents the working Railway mutation auth path by mechanism only
(§2): account-scoped `RAILWAY_TOKEN` from the DevOps `showcase`
1Password item via GraphQL `Authorization: Bearer` — the CLI session
token is not authorized for showcase-project mutations. No token value
is in the doc.
## Verification
- `showcase/aimock/d5-all.json` does not exist; the tree is `shared/`,
`d4/`, `d6/`.
- `showcase/aimock/Dockerfile` is `FROM
ghcr.io/copilotkit/aimock:latest` + `COPY shared|d4|d6 ->
/fixtures/...`.
- `.github/workflows/showcase_build.yml` matrix entry `showcase-aimock`
builds `dockerfile: showcase/aimock/Dockerfile`, context
`showcase/aimock`.
Docs-only change. Do not merge / do not enable auto-merge.
## Summary
Corrects the `pydantic-ai` `gen-ui-declarative` aimock fixture. PR #5661
merged it mis-templated from `ms-agent-dotnet`: the inner secondary-LLM
blocks gated on `_design_a2ui_surface` (ms-agent's tool) instead of
pydantic-ai's actual **`render_a2ui`**, and only **1 of the 4**
declarative pills was covered (the rest were dead KPI/pie/bar/status
blocks that match no real pill). So the cell still 503'd.
Re-authored 1:1 from the canonical `langgraph-python` fixture: inner
tool **`render_a2ui`**, all **4 pills** (sales-dashboard, rep-vs-quota,
at-risk, biggest-account) as outer `generate_a2ui` + inner `render_a2ui`
+ tool-result triplets, `context: "pydantic-ai"`. Dead blocks removed.
## Effect (real-surface proven via `bin/showcase test
pydantic-ai:declarative-gen-ui --d6 --direct`)
- `no_fixture_match` 503 count **6 → 0** — the 503 is fully gone.
- **Necessary, not sufficient:** the cell still can't paint (advances
503 → `surface-missing`) due to **separate pydantic-ai integration
gaps** — missing `DataTable` renderer, `InfoRow` lacking the
`declarative-info-row` testid, and the `injectA2UITool: false` A2UI
delivery path. Filed as a follow-up (Slack `#` showcase alerts); out of
scope here.
- No-regression: `multimodal` cell still green; fixture-only change
(+187/-336, one file).
## Test plan
- [x] Real-surface red-green: `no_fixture_match` 6 → 0
- [x] Schema valid (`validateFixtures` 0 issues), no fixture shadowing
(zero delta)
- [x] Inner `render_a2ui` arguments byte-identical to canonical
langgraph-python
- [ ] CI green
PR #5661 mis-templated this fixture from ms-agent-dotnet: the inner
secondary-LLM blocks used toolName "_design_a2ui_surface" (never matches
pydantic-ai, whose inner tool is render_a2ui) and only the sales-dashboard
pill had any block, so the other 3 declarative pills matched nothing and
the cell 503'd with no_fixture_match on turn 1.
Re-author 1:1 from the canonical langgraph-python fixture (identical inner
tool render_a2ui + per-pill surfaces), adapted for pydantic-ai: context
"pydantic-ai" on the outer generate_a2ui + narration entries, inner
render_a2ui entries matched by toolName alone (the agent's inner OpenAI()
client does not forward x-aimock-context). All 4 pills (sales-dashboard,
team-performance, at-risk, top-account) now get both an outer
(generate_a2ui) and inner (render_a2ui) block whose component payloads meet
each pill's probe assertion. Dead KPI/pie/bar/status blocks removed.
Real-probe proof: with this fixture the inner render_a2ui call matches and
the outer narration renders ("Here's your Q2 sales dashboard.") — the cell
advances from "Strict mode: no fixture matched / 503" to all LLM calls
matched. Remaining surface-missing failure is a non-fixture frontend/agent
A2UI delivery gap (see PR description), out of scope for this fixture fix.
Add the shared-state-read demo entry to the strands and strands-typescript
manifests, mirroring the gold-standard langgraph-python entry. The fleet
enumerates D6 cells only from manifest demos that have both an id and a
route; shared-state-read was declared as a feature (and is not in
not_supported_features) but had no demo entry, so it resolved to status
unshipped and never ran on staging.
This makes the aimock fixture fix from #5673 actually take effect on the
fleet: both integrations now enumerate and run the shared-state-read cell
green.
The showcase-aimock RAILWAY.md described fixtures as fetched from GitHub-raw
URLs at boot and called the Dockerfile dead code. Both are false and actively
misled debugging: fixtures are baked into the image at build time under
/fixtures/{shared,d4,d6}, the Dockerfile is the live image builder driven by
showcase_build.yml, and the d5-all.json bundle no longer exists (split into
the per-slug d6/ tree). Corrects sections 3, 4, 5, 6, 7, 8, 9 to match the
real infra, fixes the startCommand to load the three baked-in subdirectories,
and documents the account-scoped RAILWAY_TOKEN mutation path by mechanism.
## Summary
- Moves the canonical `/threads` guide into the **Build Chat UIs** nav
group, immediately after prebuilt components
- Keeps `/premium/threads-explained` under **Intelligence Platform** as
the architecture/persistence explanation
- Adds contextual cross-links between the Threads guide, Threads
architecture page, and relevant prebuilt chat UI docs
- Shows `Threads` in the authored framework sidebars next to their chat
UI basics
## Why
Threads are primarily discovered by developers adding saved
conversations, history, and thread switching to a chat UI. The
implementation guide belongs with chat UI docs, while the platform page
remains the deeper explanation of persistence, realtime sync, and
Enterprise Intelligence Platform backing.
## Screenshots
**Root docs navigation: `/threads` now appears with the chat UI basics,
immediately after Prebuilt Components.**

**Authored framework navigation: framework-specific docs now show
Threads next to Prebuilt Components too.**

**Intelligence Platform navigation: the architecture page stays in the
platform section.**

## Validation
- `git diff --check origin/main...HEAD`
- `git diff --check`
- `npm run typecheck` from `showcase/shell-docs`
- Local route smoke checks for `/threads`, `/premium/threads-explained`,
`/prebuilt-components`, and `/prebuilt-components/chat` returned 200
- Authored framework route smoke checks returned 200
## Summary
- Adds a `thread_persistence_pattern` manifest flag so shared docs can
render selected-framework Threads guidance.
- Marks LangGraph Python, LangGraph TypeScript, LangGraph FastAPI, and
Google ADK with the appropriate thread persistence pattern.
- Extends `WhenFrameworkHas` support so the shared Threads guide can
show LangGraph-only and ADK-only callouts.
- Clarifies that `useThreads` manages Enterprise Intelligence Platform
thread records, not native framework stores.
- Adds framework-selected callouts to the root/shared Threads guide
without adding a third setup path.
## Notes
The new callouts intentionally avoid claiming external store listing,
lifecycle sync, migration/import tooling, or durable ADK sessions by
default. Those remain product/runtime follow-ups tracked separately.
## Validation
- `git diff --check`
- `npm run pretypecheck` in `showcase/shell-docs`
- `npm run lint` in `showcase/shell-docs` (passes with existing
warnings)
- `npm run typecheck` in `showcase/shell-docs`
- `npm run build` in `showcase/shell-docs` (passes with existing
Turbopack/NFT warning)
- Local route smoke checks:
- `/threads` hides framework callouts
- `/langgraph-python/threads` shows LangGraph callout only
- `/langgraph-typescript/threads` shows LangGraph callout only
- `/langgraph-fastapi/threads` shows LangGraph callout only
- `/google-adk/threads` shows ADK callout only
The strands-typescript SSOT entry is ciBuilt:true but staging-only (prod
instance not yet provisioned). redeploy-env.ts's default scope was the full
CI_BUILT_SERVICES set for BOTH envs, so a staging-only ciBuilt service would
wrongly enter the prod default scope and fail a manual `redeploy-env.ts prod`
(no prod instance). Filter the default scope by env declaration (explicit
--services stays unfiltered, preserving the contract-pin that an operator can
force a named service in an env it does not declare). imageOf expansion was
already env-aware; this extends the same invariant to the base scope.
Update the inventory-lock test counts for the new service (total 40->41,
CI_BUILT 38->39, staging default scope 39->40; prod default scope stays 38 as
the staging-only service is now correctly excluded).
Wire the strands-typescript showcase integration for staging deployment,
mirroring how the Python strands integration is deployed.
- manifest: flip deployed: true so the shell lists it in the integration menu
- railway-envs.ts: add showcase-strands-typescript SSOT entry (staging-only
for now: prod instance not yet provisioned, so it omits the prod env and is
gateIgnore'd until promoted dual-env); regenerate railway-envs.generated.json
- showcase_build.yml + showcase_build_check.yml: add the strands-typescript
build matrix entry, change-detection filter, and dispatch option (railway_id
is the new Railway service id)
- golden fixture + image-ref-gate inventory tests updated for the new service
Railway staging service showcase-strands-typescript provisioned
(showcase-strands-typescript-staging.up.railway.app, health /api/health,
OpenAI-via-aimock env). Prod is added later via the promote pipeline.
## Summary
The pydantic-ai showcase integration crashed with `assert_never` in
pydantic-ai's `_map_user_prompt` whenever AG-UI multimodal
`InputContent` (images / documents / binary, data: and url: sources)
reached the model — AG-UI content types were never normalized to the
native pydantic-ai types (`str` / `ImageUrl` / `BinaryContent` /
`DocumentUrl`) the mapper requires.
## What changed
- **`_MultimodalFlattenModel(WrapperModel)`** normalizes content at the
**model-call boundary** (overriding `request` / `request_stream` /
`count_tokens`) — deliberately NOT a `history_processor`, because that
hook persists its return into `message_history` and would leak flattened
content back to the UI.
- **Supported-type gating + degrade centralized at the single
native-type emission choke point** (instead of scattered
per-content-branch): unsupported image subtypes (HEIC/SVG/TIFF/BMP),
audio/video, and non-fetchable url attachments degrade to a text
placeholder rather than emitting a native type the OpenAI Responses
vision API rejects (which would fail the turn).
- **Mime normalized once** — strips RFC-2045 params/whitespace,
lowercases, and aliases the common non-canonical `image/jpg` →
`image/jpeg` before the allow-list (png/jpeg/gif/webp) test, so real
JPEGs are no longer silently dropped.
- Identity-based (`is`) no-op detection replaces fragile structural
`==`.
## Why it matters
Unblocks multimodal turns in the pydantic-ai showcase integration;
eliminates the `assert_never` crash and stops valid `image/jpg` JPEGs
and url-borne documents from being silently mishandled.
## Testing
- **43 unit tests** (clean pinned venv, pydantic-ai 1.0.18), red-green
proven per gap: `image/jpg` forwarded as a supported JPEG; url-media /
non-PDF-doc degrade instead of emitting an unconditional `DocumentUrl`;
parameterized mime forwarded; state-leak guard (`request_stream`
forwards flattened, not raw); `count_tokens` override; `assert_never`
provably unreachable (all flatten paths return a native type or raise).
- 9 rounds of code review (7 agents/round) to a clean confirmation round
(zero blocking findings).
## Test plan
- [ ] CI green
## Summary
- The pydantic-ai `generate_a2ui` declarative D6 turn was missing an
aimock fixture, producing HTTP 503 `no_fixture_match` on staging
(pydantic-ai 503 vs ms-agent-dotnet 200 for the same turn) — a source of
dashboard flapping.
- Adds the canonical mirror fixtures (outer `generate_a2ui` + matching
inner `_design_a2ui_surface`) to
`showcase/aimock/d6/pydantic-ai/gen-ui-declarative.json`. These are
deterministic canonical mirrors matching the langgraph-python convention
— **not** a non-deterministic real-LLM recording — preserving the
mandatory LGP 1:1 parity.
## Red-green proof
- **RED:** exact failing request (`POST /v1/responses`, gpt-4.1, "Show
me my sales dashboard for this quarter.", tools=[`generate_a2ui`],
header `x-aimock-context: pydantic-ai`, strict) against the pre-fix
fixture set → **HTTP 503 `no_fixture_match`** (reproduces staging
exactly; confirmed live on staging too).
- **GREEN:** same request against the new set → **HTTP 200** SSE
emitting the `generate_a2ui` tool call; the inner `_design_a2ui_surface`
turn also returns 200 with the dashboard surface.
- Independently re-verified. `validate-on-load` clean (no fixture
shadowing); existing pydantic-ai D6 turns (KPI/pie/bar/status) still
match identically — no regression.
## Notes
- No credentials in the committed fixture — the OpenAI key was never
even resolved (canonical mirror, not a recording). Credential scan of
the diff + full blob: zero matches.
## Test plan
- [ ] CI green
- [ ] After deploy, confirm the `generate_a2ui` declarative D6 cell
flips red→green on staging
## Summary
Makes the showcase harness probe's turn-done signal **reliable**,
killing the dominant class of dashboard false-red flaps without ever
hiding a real failure.
`waitForTurnComplete` previously relied on a fragile SSE fetch-counter
conjunct that false-reds healthy demos whenever the page-side fetch
wrapper missed the runtime URL/transport. This change makes the
**`data-copilot-running` DOM attribute** (driven directly by the agent
run lifecycle, `RUN_STARTED`→true / `RUN_FINISHED`→false,
transport-independent) the **PRIMARY** done-signal, with the SSE counter
demoted to a **headless-only fallback** (headless demos never render
`CopilotChatView`, so the attribute is absent).
Design (all three preserved — no false-green, no false-red, hangs still
red):
- **Primary signal** = the `data-copilot-running` true→false
**transition** with a **stayed-stopped quiescence window** (a stop must
persist on the same run-start count for `settleMs`; a new sub-run resets
it) — so it cannot complete on an intermediate stop in a multi-step
turn.
- **SSE counter** = headless fallback only; never an OR-trigger when the
DOM signal is present.
- **`done-signal-missing` backstop** (gated on `attrPresent===true` +
`runningNow!==true`) reds a genuine painted-but-never-finished DOM turn
before the hard timeout; headless turns use their full timeout for their
only signal.
## How it was reviewed
A full 4-round `cr-loop` (7 unbiased agents/round + confirmation rounds
+ a Procedure-3 promotion audit) caught and fixed **5 distinct
correctness defects** in the implementation before merge:
- **F1** — SSE OR-trigger could complete a multi-step turn early on an
intermediate stop (false-GREEN), in both the loop and the post-loop
classifier.
- **F2** — the run-start baseline was captured *after* the message send,
killing the primary signal on fast turns (false-RED).
- **F3** — non-atomic double `surfaceReady` read per poll (latent hazard
+ wasted round-trip).
- **F4** — the surface-mount (`completeOnMount`) path had no quiescence
window (false-GREEN on intermediate stop + false-RED on a still-running
gen-UI turn).
- **F5** — the early backstop false-redded slow-but-healthy **headless**
turns (now gated on the DOM signal).
Bidirectional red-green tests for F1–F5 plus a systematic `{DOM,
headless} × {completes, lagging-recovers, genuine-hang} × {text,
surface}` completion/backstop matrix. Full harness unit suite: **3173
passed / 18 skipped / 0 failed**; `tsc --noEmit` clean; lint 0 errors;
build clean.
## Known follow-ups (NOT in this PR — pre-existing / non-blocking)
- **Theoretical edge (not reachable on real or realistically-streamed
turns):** if a run completed within a single synchronous microtask
(zero-duration), the page-side MutationObserver could miss the true edge
while `attrPresent===true` → false-red. Real LLM turns and aimock
realistic-streaming hold the attribute true across many event-loop
ticks, so the observer reliably latches it. A naive "re-add SSE fallback
for DOM-present" fix would reintroduce F1's multi-step false-green, so
it's intentionally not done here.
- **Recommended quick follow-up (latency only, no wrong verdict):**
capture `baselineBannerText` pre-`sendTurnMessage` (mirroring the
run-start/count baselines) so a fast-erroring cold-start turn fast-fails
(#5142) instead of burning the full timeout.
- **Pre-existing sse-interceptor capture/counter internals** (none
load-bearing for the new done-signal; verified STAY_IN_C by the
Procedure-3 audit): page-side counter soft-nav/multi-capture reset,
`__hk_fetchWrapped` pattern reuse + hardcoded fallback, g/y-flag
stateful RegExp, TextDecoder end-of-stream flush, bare-catch
reader-error swallow, framenav payload discard/TOCTOU,
CDP-wallTime-vs-Date.now TTFT, addInitScript/close-listener
re-registration accumulation.
## Test plan
- [x] `pnpm test` (harness) — 3173 passed / 18 skipped / 0 failed
- [x] `tsc --noEmit` exit 0, lint 0 errors, build exit 0
- [ ] Verify on staging that auth / prebuilt-sidebar / claude-sdk-tools
(and other previously-flapping cells) stop false-redding while
genuinely-broken cells stay red
Please review the replay/primary-signal approach. Not auto-merging.
Brings google-adk to 39/39 D6 (reproduced across two independent full-matrix
runs, zero regressions). Four changes:
- entrypoint.sh: remove ADK_DISABLE_PROGRESSIVE_SSE_STREAMING=1. That flag's
non-progressive aggregation path ended ADK's agentic loop after the first
tool round (no post-tool LLM re-invoke), which broke every demo needing a
second turn: the subagents chain (research -> writing -> critique),
tool-rendering-reasoning-chain (AAPL -> MSFT), shared-state-read-write's
confirmation, and the custom-catchall narration. The partial-event abort it
guarded against is already handled in-callback by stop_on_terminal_text.
- manifest.yaml: un-skip-list tool-rendering-reasoning-chain (now passes with
the loop restored).
- headless_complete_agent.py: add AGUIToolset() so the frontend highlight_note
tool is injected and routed to the browser. Removing the flag unmasked this
pre-existing gap — turn 3 dispatched highlight_note server-side and the
backend registry rejected it. langgraph-python auto-injects frontend tools;
ADK needs AGUIToolset() in the agent's tools list.
- aimock/d6/google-adk/gen-ui-interrupt.json: order each pill's narration leg
(toolCallId) before its emit leg and drop the thread-global hasToolResult
gate, so the alice pill no longer 503s after the sales pill leaves a tool
result in the thread.
Flatten AG-UI attachment content into native pydantic-ai content types in
the OUTGOING request only, via a WrapperModel-scoped flatten rather than a
history_processor (so the flatten never persists into ctx.state.message_history
and leak into UI state). Normalize mime types, gate on supported content types,
and degrade unsupported types at the single emission choke point. Fixes the
_map_user_prompt assert_never crash on raw AG-UI multimodal content.
Fail loud on a dropped #oss-alerts page: the failure-alert cross-post no longer
swallows a 200/ok:false Slack response, so a dropped page-the-humans alert reds
the renderer job instead of vanishing on a green run. The thread reply stays
warn-only. Both posts capture the response via a shared slack_alert_posted_ok
predicate, mirrored byte-identically across the live workflow and the dry-run
helper.
Debt cleanup: drop a dead failed_count var, correct a misleading gha_url comment,
and validate the decoded blob run_id against ^[0-9a-f]{6}$ in the render step so
a malformed run_id can't reach Slack or the run name.
Tests: predicate edge cases (non-JSON, malformed, missing/null ok), an anti-drift
parity guard asserting the predicate is identical in both files, and call-site
tests locking the #oss-alerts fail-loud vs thread warn-only exit semantics.
- @ag-ui/aws-strands 0.2.2 -> 0.2.3 (strands-typescript)
- ag_ui_strands 0.2.1 -> 0.2.2 (strands)
These releases carry the A2UI-dynamic (declarative-gen-ui) run-completion fix:
the auto-injected generate_a2ui now completes after the A2UI surface paints,
so the run emits RUN_FINISHED instead of hanging 'Running'. Should green the
gen-ui-declarative D6 cell on both integrations (-> 35/35) and resolve the
real-LLM staging hang. Pending local D6 re-verify.
Takes d6:strands and d6:strands-typescript from 32/35 to 34/35.
- shared-state-read: the turn-2 fixture leg wrongly pinned turnIndex:0, so the
aimock matcher skipped it on turn 2 -> 404 -> turn-2 sse-missing. Drop
turnIndex to mirror the langgraph-python gold-standard fixture.
- multimodal: sample.png/pdf/wav shipped as git-LFS pointers, so deploy/test
environments without 'git lfs pull' served the ~130-byte pointer text as the
upload -> the run never started (runsFinished=0). Ship them as regular
binaries via a per-integration .gitattributes lfs-unset + real bytes,
mirroring langgraph-python's convention.
Remaining red (gen-ui-declarative) is a Strands A2UI-dynamic run-completion bug
(reproduces on real-LLM staging too): the surface paints but generate_a2ui
never completes, so the run hangs 'Running'. Tracked separately.
Switch declarative-gen-ui to runtime auto-injection (injectA2UITool: true, plain
agent), matching the langgraph-python / AWS Strands gold standard. The ag-ui-adk
0.7.0 adapter auto-injects generate_a2ui via plan_a2ui_injection. Verified under
the canonical D6 driver: all four declarative pills pass (sales-dashboard,
team-performance, at-risk, top-account), plus a2ui-fixed-schema and
beautiful-chat.
Remove the stale known-failing mark from the a2ui-recovery heal e2e and the
OSS-374 inner-render-disambiguation notes (fixture _meta, QA doc, agent
docstring, route comment). Verified against the aimock journal: ag-ui-adk 0.7.0
forwards the run conversation into the inner render_a2ui call, so each pill
matches its own fixture by userMessage and both heal and exhaust pass. The
recovery demo stays backend-owned (the only path with the recovery loop) and
ADK-only (OSS-375 tracks langgraph-python parity).
Bump every @copilotkit/* dependency across the showcase integrations and
the shell from 1.60.2 (and stray "latest" override pins) to an exact
1.61.1 pin, and move the canonical pin source of truth to match.
Regenerate each standalone npm package-lock.json with the same
--legacy-peer-deps flag the Dockerfiles use for "npm ci".
- showcase/integrations/*/package.json + package-lock.json
- showcase/integrations/langgraph-typescript/src/agent/*
- showcase/shell/package.json + package-lock.json
- showcase/scripts/showcase-canonical-pins.json: canonical 1.60.2 to 1.61.1
aimock stays on its own version line (1.26.1). The Python copilotkit SDK
was already 0.1.94 across every requirements.txt, so no change there.
validate-pins ratchet is unchanged (FAIL=38, identical hash);
validate-parity, validate-fixture-tool-surface, and the showcase/scripts
vitest suite (2102 tests) all pass.
Replace the "Tracked in Linear" placeholders with the filed issue IDs:
OSS-374 (showcase-aimock inner-subagent disambiguation) and OSS-375
(recovery-demo langgraph-python parity).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The `heal` e2e test fails against the live showcase aimock because the harness
cannot disambiguate the two pills' inner render_a2ui sub-agent calls (the
backend-owned middleware issues the inner call with a generic render prompt +
shared suggestion context, so the "last user turn" aimock keys on is not the
pill prompt). Both pills match the same inner fixture (the EXHAUST one, by
first-match order), so the heal pill exhausts instead of healing.
This is an aimock harness limitation, not a middleware/demo bug: the middleware
heals free-form args correctly in the OSS-158 toolkit gate and against real
Gemini. Keep the test running (not skipped) as a deliberate demonstration for
the Showcase team; it does not red CI (these specs aren't run for google-adk in
CI). Update the spec header, the heal-test note, the fixture _note, the qa doc,
and the recovery_agent docstring to reflect this accurately (and drop the stale
sequenceIndex description). Tracked in Linear: aimock inner-subagent
disambiguation + recovery-demo LP parity.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The heal fixture relied on per-attempt aimock response switching (sequenceIndex
0=invalid -> 1=valid), but sequenceIndex doesn't advance across the recovery
loop's intra-turn render_a2ui calls in the per-integration Playwright context
(no X-Test-Id), so aimock served invalid on every attempt and the demo never
healed (0 metrics).
Switch the heal pill to a single deterministic response: the inner render_a2ui
returns FREE-FORM args (components/data as JSON strings) that the middleware
heals via parse_and_fix into a valid surface in one pass — no per-attempt
switching. Verified in-sandbox: free-form args -> a2ui_operations with 2 Metric
components. The exhaust pill is unchanged (structurally invalid every attempt ->
a2ui_recovery_exhausted hard-fail, already verified e2e).
Updates the agent docstring, qa checklist, and spec test name/comment to reflect
heal = parse_and_fix healing (not invalid->valid retry). aimock-fixtures schema
test: 818 passed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The clickPill helper (copied from declarative-gen-ui.spec.ts) matched the user
message with the stale '[data-message-role="user"]' selector, which no longer
exists in @copilotkit/react-core/v2 >= 1.60 — the user bubble is now
'[data-testid="copilot-user-message"]' (CopilotChatUserMessage). The message
dispatched fine; the helper just never found the bubble, timing out before the
heal/exhaust assertions ran. The sibling google-adk specs carry the same stale
selector (they don't run in CI for this integration, so it drifted unnoticed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adding a2ui-recovery to the shared feature-registry cross-joins it across all
20 integrations (wired for google-adk, unshipped elsewhere), so the catalog
generator's hardcoded counts move: cross-join 920->940, metadata.total_cells
900->920, LGP 46->47 (unshipped 7->8). docs_only stays 20; the relative
sum-checks are unchanged. Fixes the Validate Showcase failure on #5662.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Playwright spec for the ADK-only a2ui-recovery demo, mirroring the AG-UI dojo
reference (apps/dojo/e2e/tests/adkMiddlewareTests/a2uiRecovery.spec.ts):
assert the STABLE end-states and skip the transient 'Retrying...' label
(threshold-gated/timing-dependent).
- heal pill: recovered surface paints (>=2 declarative-metric tiles), no
hard-failure UI, no render-error banners
- exhaust pill: 'Couldn't generate the UI' shows (A2UIRecoveryStates on the
a2ui_recovery_exhausted envelope), no faulty surface ever paints, chat stays usable
Selectors verified against @copilotkit/react-core/v2 A2UIRecoveryStates (failure
text) and the showcase declarative catalog testids. Clears the validate-parity
'no e2e spec' warning. Runs in the browser-e2e stack (frontend + aimock + agent_server).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements the Declarative Generative UI (A2UI dynamic schema) demo for both
the strands (Python) and strands-typescript integrations, bringing them to 1:1
parity with the canonical langgraph-python demo.
Backend:
- Add a dedicated dynamic-A2UI agent (a2ui_dynamic.py / buildA2uiDynamicAgent)
that wires no generate_a2ui tool. The runtime route sets
injectA2UITool: true + defaultCatalogId "declarative-gen-ui-catalog"; the
Strands adapter auto-injects generate_a2ui and drives a secondary render_a2ui
planner. StrandsAgentConfig.a2ui supplies the catalog id and a
composition_guide (sales dataset + composition rules) so the planner is
self-contained.
- Mount the agent on the /declarative-gen-ui sub-path and point the route's
HttpAgent at it, mirroring the a2ui-fixed-schema pattern.
- Bump ag_ui_strands 0.1.9 -> 0.2.1 (Python): the A2UI auto-injection config
(default_catalog_id, guidelines, render planner) landed in 0.2.0. Pulls
ag-ui-a2ui-toolkit transitively. strands-typescript already on 0.2.2.
Frontend:
- Port the langgraph-python declarative-gen-ui frontend (suggestions,
sales-context, definitions incl DataTable, renderers, chat) so the demo
matches the shared D5/D6 probe's sales-analyst pills and catalog components.
Fixtures:
- Author gen-ui-declarative D6 fixtures for both slugs from the langgraph-python
template (3 legs per pill: outer generate_a2ui, inner render_a2ui, narration).
Verified on real OpenAI (gpt-4o): both integrations paint a real A2UI surface
(4 Metric tiles + Revenue-by-Region PieChart + Monthly Revenue BarChart +
narration), matching the probe's expected testids and counts.
Note: the gen-ui-declarative D6 cell currently cannot go green because the
shared harness hits "ReferenceError: __name is not defined" in the probe's
page.evaluate (turn 1 preFill), before any agent call. That harness-tooling bug
is owned separately; the fixtures and wiring here are correct and the real-LLM
paint confirms the expected surface.
Strands mints UUID tool_call_ids for tool results (confirmed via the aimock
journal), so the langgraph-python toolCallId-keyed follow-up fixtures never
matched and the agent re-emitted the tool, looping (text-unstable) across
frontend-tools, gen-ui-agent, gen-ui-open, gen-ui-open-advanced,
gen-ui-headless-complete, reasoning-chain and the weather pills. Re-key the
affected multi-leg fixtures to the id- and thread-history-invariant
sequenceIndex pattern (as built-in-agent does), for both integrations.
Also:
- Add write_document poem/email/quantum fixtures for shared-state-streaming
(was a stale single fixture that 404d).
- Strip content from reasoning-chain tool legs (content+toolCalls in one
fixture is undefined behavior); reasoning rides toolCalls alone.
- Narrow the over-broad d4 summarize catch-all to "Summarize the sales
pipeline" so it stops shadowing gen-ui-agent competitor pill; matches
langgraph-python.
- Commit the real multimodal sample.png/sample.pdf (were git-LFS pointers
the harness could not resolve).
Add a write_document tool + document state hook to both Strands agents
so the shared-state-streaming demo can stream a document into
state.document (mirrors langgraph-python StateStreamingMiddleware). Strands
updates state from the full tool args, which the D6 probe tolerates.
Remove the get_weather stopStreamingAfterResult / stop_streaming_after_result
guard from both agents so weather concludes with a natural follow-up turn,
matching the langgraph-python gold standard; the accompanying multi-turn
id-invariant weather fixtures prevent the aimock replay loop without it.
The D6 fleet worker drives each integration over its insecure Docker
origin (http://<slug>:10000), where crypto.randomUUID is undefined so
hand-rolled headless chats threw and never mounted (sse-missing). tsx/
esbuild also wraps named inner functions in __name(...) calls that leak
into page.evaluate and throw __name is not defined.
Add installBrowserContextShims (init-scripts.ts): a __name no-op helper
and a crypto.randomUUID secure-context polyfill, registered via
addInitScript at document_start of every D6 page; wired into the
d6-all-pills newPage goto path.