Resolve event-renderer.ts onRunFinishedEvent: keep the native turn stream open
(finalized in finish()) AND retain the legacy per-message stream drain from main
(#5573) as a no-op-in-native safety net. app/index.ts (telegram adapter from
#5520 + showToolStatus:false) and create-bot.test.ts auto-merged.
## What
Gives the example's runtime agent **web search** (OpenAI's hosted
`web_search` tool).
`BuiltInAgent`'s classic `tools` only accepts CopilotKit's handler-based
`ToolDefinition[]` (each needs an `execute`), so it can't carry a
provider/hosted tool like web search. The fix is to run the agent in
**factory mode** (`type: "tanstack"`) and drive the LLM call with
TanStack AI's `chat()` — which exposes provider tools and managed MCP
directly.
## How (`examples/slack/runtime.ts`)
- **Adapter:** `openaiText(model)` from `@tanstack/ai-openai` (OpenAI
Responses API; `gpt-5.5` default).
- **Web search:** `webSearchTool({ type: "web_search" })` from
`@tanstack/ai-openai/tools`, passed in `chat({ tools: [...] })`.
- **MCP:** Linear + Notion via `@tanstack/ai-mcp` `createMCPClient`
(HTTP transport + `Authorization: Bearer`), created per-run inside the
factory; `chat()` discovers their tools and closes the connections when
the run ends.
- **Bridge:** `convertInputToTanStackAI(ctx.input)` (already in
`@copilotkit/runtime`) maps AG-UI input → `chat()` messages +
systemPrompts; `BuiltInAgent` converts `chat()`'s stream back to AG-UI
events via its built-in `convertTanStackStream`.
- The big triage `SYSTEM_PROMPT` is prepended to `systemPrompts`.
Adds `@tanstack/ai`, `@tanstack/ai-openai`, `@tanstack/ai-mcp` to the
example.
> **OpenAI-only now.** Web search is an OpenAI provider tool, so this
drops the previous multi-provider path (anthropic/google). `AGENT_MODEL`
accepts a bare OpenAI id or `openai/<id>` (prefix stripped).
## Verification
- `tsc --noEmit -p examples/slack/tsconfig.json` clean (validates the
full `chat()` wiring — adapter, tools, MCP, `convertInputToTanStackAI`,
factory return type).
- Runtime **boots** on the factory: `agent "triage" ready · MCP: Linear,
Notion`, server listening — confirms the `@tanstack/*` ESM imports
resolve and the agent constructs. (A live web-search query is exercised
through the Slack bot.)
- oxfmt + oxlint clean.
> Pre-commit `test-and-check-packages` bypassed: the lockfile change
makes nx mark everything affected, and the only failure is the
pre-existing, environmental `@copilotkit/sqlite-runner:test` (native
sqlite on Node 24), unrelated to this change.
Bump @tanstack/ai-openai 0.14.4 → 0.15.2 (pins @tanstack/openai-base 0.8.7,
TanStack/ai#790) and @tanstack/ai → 0.32.0. 0.8.7 emits strict:false for tool
schemas outside OpenAI's strict subset in the provider-path function-tool
converter, so MCP tools (e.g. Notion's API-post-search) no longer 400 — no
local patch needed. Verified end-to-end against the live Slack bot.
- runtime.ts: pass the bridge's forwarded client tools (convertInputToTanStackAI's
tools) into chat() alongside web_search + MCP, so generative-UI cards and the
confirm_write HITL gate work; drop the prompt line that made the model narrate
charts with a trailing "Charting …" sentence (it landed after the image).
- render-chart / render-diagram: post the caption as a header BEFORE the image
(a file upload's message lands a beat after postFile resolves, so caption-first
keeps a stable caption → image order) and drop the false "rendered above" wording.
Depends on the @copilotkit/runtime factory tool-lifecycle fix (#5572) and the
bot-slack HITL/render-order fix (#5573).
The example's runtime agent needed OpenAI's hosted `web_search` tool, but
BuiltInAgent's classic `tools` only accepts handler-based `ToolDefinition[]`
(needs `execute`) — it can't carry a provider/hosted tool. So switch the
agent to BuiltInAgent **factory mode** (`type: "tanstack"`) and drive it with
TanStack AI's `chat()`:
- `openaiText(model)` adapter (OpenAI Responses API; gpt-5.5 default)
- `webSearchTool({ type: "web_search" })` provider tool (`@tanstack/ai-openai/tools`)
- Linear/Notion MCP via `@tanstack/ai-mcp` `createMCPClient` (HTTP + bearer),
created per-run; `chat()` discovers their tools and closes the connections
- `convertInputToTanStackAI(ctx.input)` bridges AG-UI input → `chat()`;
BuiltInAgent converts `chat()`'s stream back to AG-UI events
OpenAI-only now (web search is OpenAI-specific); AGENT_MODEL accepts a bare
OpenAI id or an "openai/<id>" form. Adds @tanstack/ai, @tanstack/ai-openai,
@tanstack/ai-mcp to the example.
Two fixes surfaced by exercising the bot's generative-UI / HITL tools
end-to-end in Slack.
## 1. HITL never resumed in an assistant-pane DM
An assistant-pane DM is **threaded**, so the ingress path
(`assistant.ts`) keys the turn's conversation by **`thread_ts`**. But
`decodeInteraction` forced **`DM_SCOPE`** for any `D…` channel. So the
HITL `awaitChoice` waiter was registered under `D…::<thread_ts>` while a
button click looked it up under `D…::dm` — the waiter was never
resolved. Clicking **Create/Cancel** swapped the card UI (the button's
`onClick` ran) but the agent run never resumed: no write, no reply.
**Fix:** honor an explicit `thread_ts` as the conversation scope even in
DMs (matching ingress); fall back to `DM_SCOPE` only for a genuinely
unthreaded DM. + a `decodeInteraction` regression test.
## 2. Render-tool output landed out of order
Defensively finalize any text stream still open at the end of a run
(`onRunFinishedEvent`), so a run's streamed text is fully posted before
the run-loop executes tool handlers that post out-of-band content
(images, cards).
All 200 `@copilotkit/bot-slack` tests pass. Verified live:
`confirm_write` now gates a write and resumes on approval; chart/diagram
output renders in order.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## What
Adds a **Telegram platform adapter** (`@copilotkit/bot-telegram`) for
`@copilotkit/bot`, at feature parity with `@copilotkit/bot-slack`, plus
a runnable example that drives **both** a Slack bot and a Telegram bot
from one app.
## `@copilotkit/bot-telegram` (new package)
A grammY-based adapter implementing the `PlatformAdapter` interface:
- **Ingress:** long-polling by default; webhook / `auto` (serverless-env
detection) opt-in.
- **Threading:** topic-aware hybrid — `tg:<chatId>:<scope>` where scope
is `dm`, `topic:<id>` (forum supergroups, gated on `chat.is_forum`), or
`user:<userId>` (non-forum groups).
- **Rendering:** the platform-agnostic `@copilotkit/bot-ui` JSX IR →
Telegram HTML, with a plain-text format fallback when HTML entity
parsing fails; length-bounded.
- **Streaming:** chunked `editMessageText` (placeholder → repeated
edits) with HTML-expansion headroom.
- **Interactions:** `callback_query` HITL (ack-first; 64-byte
`callback_data` via hashed action ids resolved through the ActionStore).
- **Inbound files:** photo/document ingestion wired into the agent turn
(size-capped, token-redacted).
- Ships `defaultTelegramTools` (user lookup) + `defaultTelegramContext`
(tagging / HTML / thread-model guidance).
- **Capabilities:** `supportsModals: false`, `supportsTyping: true`,
`supportsStreaming: true`, `supportsThreadTitle: true`,
`supportsReactions: false`, `supportsSuggestedPrompts: false`.
## `examples/slack` → Slack **and** Telegram from one app
Rather than maintain a diverging copy, the example now starts a Slack
bot and/or a Telegram bot (env-conditional on which credentials are
present) from one **platform-neutral** app layer — shared components,
tools, context, commands, render helpers. Each platform gets its own
`createBot` with its platform-specific default tools/context; the shared
components emit the cross-platform JSX IR that each adapter renders
natively.
To make the shared layer truly neutral: unicode glyphs instead of Slack
mrkdwn `:shortcode:` strings (Telegram doesn't expand them; Slack
renders unicode fine), no Slack-Block-Kit `raw` fallbacks, and neutral
context/tool wording (per-platform tagging guidance comes from each
adapter's default context). The separate `examples/telegram` app was
removed; its e2e smoke harness + BotFather setup docs were migrated into
`examples/slack`.
## Testing
- `@copilotkit/bot-telegram`: 138 unit tests; `nx build` (typecheck)
clean.
- `slack-example`: 42 tests (incl. `renderTelegram` parity assertions);
typecheck clean.
- `nx run-many -t build` green; root `pnpm install --frozen-lockfile` in
sync.
- Reviewed via a multi-round adversarial CR loop to convergence (zero
load-bearing findings) + a bucket-(c) promotion audit.
## Note: `bot-telegram` is unpublished
The example references it as `workspace:*` and **runs from the
monorepo** (`pnpm --filter slack-example start`). Standalone deploys
(the example's own lockfile) work for Slack today; once `bot-telegram`
publishes alongside its siblings, switch the dep to `~0.0.2` and
regenerate the standalone lockfile to enable standalone Telegram
deploys. Documented in the example README.
## Known limitations / follow-ups (out of scope, pre-existing or
inherent)
- **render_table** hardening (pre-existing in the Slack example):
`clamp()` overflow `notes` are computed but not surfaced (silent
truncation); "Max 100 rows" doc vs `MAX_DATA_ROWS = 99` off-by-one;
monospace fallback ignores column alignment.
- **Telegram e2e harness** is a documented best-effort **manual-trigger
smoke**: `getUpdates` contends with the bot's own long-poller
(single-consumer Bot API limit); multi-chunk reply assembly and
follow-up message selection are approximate. Automated upgrade path
(second sender bot) documented in `e2e/TELEGRAM-README.md`.
- **`_status` glyph mapping** uses substring matching, so non-default
Linear workflow state names (e.g. "Unstarted") can map to the wrong
glyph — identically on both platforms.
- **Bold-inside-link** (`[**text**](url)`) renders literal asterisks on
both Slack and Telegram (link labels aren't formatted on either) —
pre-existing, cosmetic.
- Telegram long-poll failures (revoked token, 409 conflict) are logged
inside the adapter and not surfaced to `start()`, so startup reports
success even if polling later fails.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Problem
`BuiltInAgent`'s TanStack factory mode (`type: "tanstack"`) was unusable
with server-executed tools. The stream converter
(`convertTanStackStream`) stopped converting at the **first per-turn
`RUN_FINISHED`**, assuming tools are always executed client-side and
re-prompted. But `chat()` executes MCP-server and provider tools
**itself** across multiple turns — so the `TOOL_CALL_RESULT` and the
model's final answer were dropped, and MCP-backed turns returned
**nothing**. It also couldn't surface frontend (generative-UI / HITL)
tools at all in factory mode.
## Fix
- **`convertTanStackStream`**: drop TanStack's per-turn
`RUN_STARTED`/`RUN_FINISHED` (the `Agent` wrapper owns the single outer
pair) and convert **every** turn's events; dedupe tool `START`/`END` by
id (chat() re-announces a call when it re-prompts); surface `RUN_ERROR`
by throwing instead of silently dropping it.
- **`convertInputToTanStackAI`**: return `input.tools` as TanStack
**client-side** tools so the frontend's generative-UI / HITL tools work
in factory mode, and **sanitize** their JSON Schema (recursively close
open objects → `additionalProperties: false`) so OpenAI accepts them —
mirroring what the classic (Vercel AI SDK) path did implicitly via its
Zod round-trip.
## Tests
Adds coverage to the converter + input-converter suites: multi-turn (no
truncation at per-turn `RUN_FINISHED`), tool-call dedup, `RUN_ERROR`
surfacing, client-tool conversion, and schema sanitizing. Full
`@copilotkit/runtime` suite green.
Verified end-to-end against a live Slack bot (`examples/slack`): web
search + Linear/Notion MCP + generative-UI cards + HITL all work through
factory mode.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Two fixes surfaced by exercising generative-UI / HITL tools through the bot:
- interaction.ts: an assistant-pane DM is threaded, so the ingress path keys the
turn by thread ts — but decodeInteraction forced DM_SCOPE for any "D…" channel.
The awaitChoice waiter was registered under D…::<thread_ts> while the button
click looked it up under D…::dm, so it was never resolved: clicking Create/
Cancel swapped the card UI but the run never resumed (no write, no reply).
Honor an explicit thread_ts as the scope even in DMs; fall back to DM_SCOPE
only for a genuinely unthreaded DM.
- event-renderer.ts: defensively finalize any text stream still open at run end,
so a run's streamed text is fully posted before the run-loop executes tool
handlers that post out-of-band content.
Adds a decodeInteraction regression test for the threaded-DM scope.
The TanStack stream converter stopped at the first per-turn RUN_FINISHED,
assuming tools are executed client-side. That truncated runs whose tools are
executed by chat() itself (MCP servers, provider tools): the TOOL_CALL_RESULT
and the model's final answer were dropped, so MCP-backed turns returned nothing.
- convertTanStackStream: drop TanStack's per-turn RUN_STARTED/RUN_FINISHED (the
Agent wrapper owns the outer pair) and convert every turn's events; dedupe
tool START/END by id; surface RUN_ERROR instead of silently dropping it.
- convertInputToTanStackAI: return input.tools as TanStack client-side tools so
the frontend's generative-UI / HITL tools work in factory mode, and sanitize
their JSON Schema (close open objects) so OpenAI accepts them.
Adds converter + input tests (multi-turn, dedup, error surfacing, client-tool
conversion, schema sanitizing).
Under the pinned-prod contract, prod is intentionally digest-pinned behind
:latest. image-drift now renders such prod services green (pinnedExpected)
instead of red; a genuinely missing digest stays red. Staging unchanged.
Prod services are now born pinned to a resolved @sha256 digest (mirroring
the Ruby promote CLI's GHCR resolver) instead of the mutable :latest tag,
fail-loud on resolve failure, and goLive asserts the prod source.image is
digest-pinned. Adds DI-testable assertProdDigestPinned + coverage.
## Summary
Fix-forward for the RED `Showcase: Validate` on `main` introduced by
#5569 (MS Agent Harness (.NET) full column).
`showcase/scripts/__tests__/calculator-fixture-routing.test.ts`
hard-pins the count of discovered `_from-feature-parity.json`
integration fixtures. #5569 legitimately added the 19th integration
(`ms-agent-harness-dotnet`) and its calculator fixtures are already
mirrored in
`showcase/aimock/d6/ms-agent-harness-dotnet/_from-feature-parity.json`.
The test's own failure message prescribes the fix: bump the pin.
## Changes
- Bump the discovered-integration pin from 18 → 19 (test name +
`toHaveLength`).
- Update the explanatory comment counts (16/18 → 17/19) to match the new
integration set. The two pill-less integrations (built-in-agent,
claude-sdk-python) are unchanged; `ms-agent-harness-dotnet` carries the
full calculator pill, so it joins the 17 with a real pill.
No source/behavior change — pin reflects reality (the harness fixture
file exists and passes all per-integration routing/ordering assertions).
## Red → Green proof
RED (original pin=18, glob discovers 19):
```
FAIL __tests__/calculator-fixture-routing.test.ts > beautiful-chat calculator fixture routing > discovers all 18 integration _from-feature-parity.json files
AssertionError: integration count changed — new integration added? mirror its calculator fixtures from langgraph-python and update this pin: expected [ 'ag2', 'agno', …(17) ] to have a length of 18 but got 19
- 18
+ 19
Test Files 1 failed (1)
Tests 1 failed | 57 skipped (58)
```
GREEN (after pin=19, full file):
```
Test Files 1 passed (1)
Tests 58 passed (58)
```
(58 = 19 integrations × 3 per-integration tests + the discovery/pin
test.)
## Summary
Adds the full **MS Agent Harness (.NET)** showcase column
(`ms-agent-harness-dotnet`) — a 1:1 port of the green framework sibling
`ms-agent-dotnet`, rebuilt on the `AsHarnessAgent` construction surface
(Microsoft.Agents.AI 1.6.1).
- **326 files** (+30k): 243 frontend demo files + shared components/API
routes, 24 backend agent `.cs` on the `AsHarnessAgent` pattern, 38 D6 +
1 D4 aimock fixtures (cribbed from the framework, `match.context`
re-keyed), manifest/csproj registration.
- The column was already registered (slug-map, compose, dashboard,
smoke) on main; this lands the implementation.
## .NET adaptations (the novel surface)
- **Microsoft.Agents.AI 1.6.1 API migration** —
`AgentThread`→`AgentSession`,
`AgentRunResponse(Update)`→`AgentResponse(Update)`,
`RunAsync`→`RunCoreAsync` across the ported agents.
- **aimock-header** aligned to the `IHttpContextAccessor` design so
`x-aimock-context` propagates across the AG-UI SSE-pump boundary
(matches the framework).
- **JsonElement-capable `TypeInfoResolver`** threaded to the
shared-state agents (fixes a startup crash).
- **`crypto.randomUUID` secure-context fallback** in the headless demos
(the container serves over plain HTTP at a non-localhost host; symmetric
with the existing `multimodal` helper).
## LOCAL==STAGING parity fix
- `control-plane-run.ts` now threads each scope's
`not_supported_features` into the synthesized local roster, so the D6
driver reclassifies architecturally/upstream-blocked features as
`skipped-incapable` locally exactly as staging does. **Benefits every
column** (the local control-plane previously dropped NSF). +3 unit tests
(RED→GREEN).
## LGP / framework parity
- Frontend demos, fixtures (`response.content` untouched — only
`match.context` re-keyed), and backend behavior are 1:1 with the
framework sibling. The `hitl` demo was reverted to byte-identical
framework parity.
## D6 status
- Per-demo D6 GREEN verified locally (control-plane): `headless-simple`,
`headless-complete`, `gen-ui-agent`, `shared-state-read`, `multimodal`.
- 3 features inherited as `not_supported`/skipped-incapable
(architecturally/upstream-blocked, same as the framework):
`shared-state-streaming`, `gen-ui-interrupt`, `interrupt-headless`.
- `declarative-gen-ui` verified structurally parity-clean
(byte-identical to the green framework). **CI runs the authoritative
full-column D6.**
## Review
- 7-agent cr-loop converged (0 mandatory findings; Procedure 3 promotion
audit 0-promote). All review findings were either framework-inherited
(fixing breaks 1:1 parity) or pre-existing `control-plane-run.ts`
poll-hardening (tracked as a follow-up — not introduced here).
## Test plan
- [ ] CI: Showcase: Validate green
- [ ] CI: full-column D6 (`ms-agent-harness-dotnet`) green
(authoritative)
- [ ] CI: .NET image build green
Approach (A) reorder: decode -> html-strip -> headTailCap -> scrub(head)+scrub(tail).
The prior order scrubbed the FULL decoded body before the cap, so a body >32KB hit
scrubSecrets' bounded-prefix path: it truncated to a ...[unscanned:N] prefix BEFORE
headTailCap, which (1) lost the real tail, (2) never scanned the real tail for secrets,
and (3) zeroed elided_count. Reordering scrubs each retained <=16KB segment AFTER the
cap: input is bounded per-segment (linear regex x bounded length = ReDoS-impossible, no
scan-budget truncation), the captured head AND tail are the REAL ends of the body, and
elided_count comes from headTailCap on the FULL body. The unrelated 2KB metadata-hot-path
scan guard (SCRUB_MAX_SCAN_LEN) is untouched; the now-unneeded RAW_BYTE_SCAN_MAX export
is removed.
The emit-hardening redesign added a 2KB SCRUB_MAX_SCAN_LEN guard inside
scrubSecrets, correct for the DEFAULT-tier metadata hot path (legit values
<=512B). But raw-byte-capture.ts also calls scrubSecrets on the DECODED wire
body, which is legitimately large (<=16KB head + <=16KB tail = up to 32KB).
The 2KB guard truncated the body to ~2KB before headTailCap ran, so for an
>32KB body elided_count became 0 and the head+tail elision never triggered.
Approach: parameterize the scan cap (chosen over the reorder-and-scrub-segments
alternative because it is a minimal, local change that preserves the documented
decode->scrub->html-strip->headtail pipeline order and keeps the metadata
default behavior byte-identical for all existing callers).
- scrub.ts: scrubSecrets gains an optional maxScanLen param defaulting to the
exported SCRUB_MAX_SCAN_LEN (2KB). Metadata/validateMetadata callers are
unchanged; the ReDoS guard on the hot path is untouched.
- raw-byte-capture.ts: passes RAW_BYTE_SCAN_MAX (head+tail cap = 32KB) so the
full retained sample is scanned (no secret in the kept head/tail escapes) and
headTailCap sees the un-truncated body. The three scrub regexes are linear at
any bounded length, so the larger bound stays ReDoS-safe.
- scrub.test.ts: asserts the default param still applies the 2KB guard AND an
explicit larger cap does not truncate, proving both contexts.
The redesigned scrub regexes are linear, but the previous hard <50ms bound
flaked under JIT warm-up and shared-runner load (observed 77ms on a loaded
machine). Add a timeScrub helper that discards a warm-up call before timing
and assert against a 500ms ReDoS ceiling — wide enough to be jitter-immune,
still ~3x under the legacy ~1.4s catastrophic-backtracking blowup it guards
against.
## What
1. Add `suppressHydrationWarning` to `<body>` across **all 14
integration demo templates**
(`examples/integrations/*/src/app/layout.tsx`).
2. Fix a pre-existing **double-escaped Windows path** bug in the parity
manifest's `packageJsonOverrides`.
## Why (hydration)
**Mike Ryan hit a hydration error on first load of a fresh
`langgraph-python` init — caused by his Grammarly browser extension.**
Grammarly (and similar extensions) inject attributes onto `<body>`
*before* React hydrates:
```
data-new-gr-c-s-check-loaded="9.98.0"
data-gr-ext-installed=""
```
Those attributes are in the client DOM but absent from the server HTML,
so Next.js reports:
> A tree hydrated but some attributes of the server rendered HTML didn't
match the client properties.
It's a **false positive** — the app works, and end users (without dev
extensions) never see it — but it's a red console error on the first
load of our flagship eval/showcase templates, which is a poor first
impression.
## Fix (hydration)
`suppressHydrationWarning` on `<body>` is the React/Next.js-recommended
escape hatch for this. It is **scoped and one level deep**: it only
relaxes the check for `<body>`'s *own* attributes/text — **everything
rendered inside `<body>` (the whole app) is still fully
hydration-checked** — and `<body>`'s only attribute here is a static
`className`, so none of our own markup is masked. An inline comment
documents this so a future maintainer who adds dynamic `<body>`
attributes knows the check is relaxed.
`agent-spec` already had `suppressHydrationWarning` on `<html>`; the
Grammarly attributes land on `<body>`, so it needed the body-level
relaxation too (the `<html>` one is a level up and doesn't cover
`<body>`'s attributes).
## Commits
1. `b1fa482a7` — north-star (`langgraph-python`) + parity instances
(`langgraph-js`, `langgraph-fastapi`, `strands-python`) via `pnpm
parity:sync`.
2. `9f9c415d9` — the non-parity templates (not tracked by
`_parity/manifest.json`): `adk`, `agno`, `crewai-crews`, `crewai-flows`,
`llamaindex`, `mastra`, `ms-agent-framework-dotnet`,
`ms-agent-framework-python`, `pydantic-ai`, `agent-spec`. *(The repo's
`oxfmt` pre-commit hook also collapsed some multiline `<CopilotKit …>`
JSX in these files — standard auto-format on touched files; the only
semantic change is the suppression.)*
3. `7d60e49de` — parity manifest path-escaping fix (see below).
## The manifest bug (commit 3)
While syncing I found the `langgraph-js` and `strands-python`
`packageJsonOverrides` double-escaped the Windows `.bat` fallback,
producing `scripts\\run-agent.bat` (two backslashes) instead of
`scripts\run-agent.bat`:
- `langgraph-js/package.json` had already been synced with the broken
value.
- `strands-python/package.json` was still correct — and `parity:sync`
would have **corrupted** it on the next run (which is what surfaced
this).
Fixed the three overrides and re-ran `parity:sync`, which corrects
`langgraph-js/package.json` and leaves `strands-python`'s correct value
intact.
## Test plan
- [x] `pnpm parity:verify` → 0 errors
- [x] lefthook pre-commit green on all 3 commits (lint + `packages/**`
tests + commitlint)
- [x] All 14 templates confirmed to have body-level
`suppressHydrationWarning`
- [ ] Reviewer with Grammarly installed: run/`init` a template and
confirm no hydration error on first load
build-and-push.sh defaulted IMAGE to a personal GHCR namespace; use an
OWNER placeholder (still IMAGE-overridable) so the showcase ships no
personal/private registry path. Resolves the pre-merge follow-up.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Synced from the cookbook dev repo (verified live on Railway + Playwright):
- db/init/01-create-user.sql: create the `cookbook` user on a dedicated
ASSM tablespace (cookbook_ts) as its DEFAULT, instead of inheriting
SYSTEM/MSSM (FREEPDB1's default on the Free :latest-lite image). JSON and
VECTOR columns are SecureFile-backed and cannot live in an MSSM
tablespace, so the old user failed schema creation with ORA-43853 and
memory never persisted. Self-heals a user stranded on SYSTEM
(drop+recreate); leaves a working ASSM user (local :latest USERS) alone.
- frontend: a new thread is named from its OWN first message, not the
previous thread's. ThreadTitler now titles off the agent's message/run
events gated on agent.threadId === activeThreadId, replacing a useEffect
keyed on activeThreadId that read the shared, agentId-scoped transcript
while it still held the prior thread's messages during a switch. Adds a
Playwright regression test (e2e/thread-title.spec.ts).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The langgraph-js and strands-python packageJsonOverrides in
_parity/manifest.json double-escaped the Windows .bat fallback, so the
synced value became `scripts\\run-agent.bat` (two backslashes) instead of
the intended `scripts\run-agent.bat`. langgraph-js's package.json had
already been synced with the broken value; strands-python's was still
correct (and parity:sync would have corrupted it on the next run).
Fix the three overrides and re-run parity:sync, which corrects
langgraph-js/package.json and leaves strands-python's correct value intact.
Extend the same `<body suppressHydrationWarning>` fix to the integration
templates that are not tracked by examples/integrations/_parity/manifest.json,
so they don't surface a Grammarly-style hydration mismatch on first load:
adk, agno, crewai-crews, crewai-flows, llamaindex, mastra,
ms-agent-framework-dotnet, ms-agent-framework-python, pydantic-ai, agent-spec.
agent-spec already had suppressHydrationWarning on <html>; the Grammarly
attributes land on <body>, so it needs the body-level relaxation too (the
<html> one is one level up and does not cover <body>'s attributes).
## Summary
Two focused, pre-existing fixes in the `generative-ui-playground`
example. These were surfaced as out-of-subject bucket-(d) findings
during the `@copilotkitnext` → `/v2` migration CR (#5562) and split out
into this follow-up.
### 1. Invalid model id in the opengenui route
`src/app/api/copilotkit-opengenui/[[...slug]]/route.ts:16` set `const
MODEL = "openai/gpt-5.2"` — a nonexistent model id, so every chat turn
errored. Changed to `"openai/gpt-4o"`, the verified-real id used by the
sibling `showcase/shell/src/app/api/copilotkit/[[...slug]]/route.ts`.
### 2. Missing `serverExternalPackages` in next.config.ts
`next.config.ts` lacked `serverExternalPackages`, but the example's API
routes import `@copilotkit/runtime/v2` (a server-only package). Added
`serverExternalPackages: ["@copilotkit/runtime"]` (base package name
covers the `/v2` subpath), mirroring `showcase/shell/next.config.ts`.
## Red → Green evidence
**Model id**
- RED: `route.ts:16` was `const MODEL = "openai/gpt-5.2";`; sibling
`showcase/shell` route uses `model: "openai/gpt-4o"`.
- GREEN: `route.ts:16` is now `const MODEL = "openai/gpt-4o";`.
**serverExternalPackages**
- RED: `grep -n serverExternalPackages next.config.ts` -> no match (exit
1).
- GREEN: `grep -n serverExternalPackages next.config.ts` -> `9:
serverExternalPackages: ["@copilotkit/runtime"],`.
## Build outcome
`next build` is **blocked by SEPARATE pre-existing module-resolution
failures unrelated to these fixes** and NOT addressed here:
- `copilotkit` and `copilotkit-a2ui` routes import
`createCopilotEndpoint` from the bare v1 `@copilotkit/runtime`, which
does not export it.
- The `copilotkit-opengenui` route (and its client-component chain)
imports from `@copilotkitnext/runtime`, which is not a declared
dependency of this package and fails with `Module not found: Can't
resolve '@copilotkitnext/runtime'`.
Because the build fails at module resolution for these pre-existing
imports before compiling the changed lines, a clean end-to-end build
cannot be obtained. Both edits are statically correct: the model id
matches the verified-real sibling, and `serverExternalPackages` mirrors
the working `showcase/shell` config. Fixing the pre-existing
`createCopilotEndpoint` / `@copilotkitnext/runtime` import errors is
explicitly out of scope for this follow-up.
The opengenui route used the nonexistent model id "openai/gpt-5.2", causing
every chat turn to error. Switch to "openai/gpt-4o", the verified-real id used
by the sibling showcase/shell copilotkit route.
The example's API routes import @copilotkit/runtime/v2 (a server-only package),
but next.config.ts lacked serverExternalPackages, so Next.js attempted to bundle
it. Add serverExternalPackages: ["@copilotkit/runtime"] (base package name covers
the /v2 subpath), mirroring showcase/shell/next.config.ts.
## Summary
Migrates all `@copilotkitnext/*` usages onto the v2 entrypoints of the
existing `@copilotkit/*` packages and removes the `@copilotkitnext`
dependency surface from `showcase/shell` and the
`generative-ui-playground` example.
- `@copilotkitnext/react` → `@copilotkit/react-core/v2`
- `@copilotkitnext/agent` & `@copilotkitnext/runtime` →
`@copilotkit/runtime/v2`
- `globals.css` styles import → `@copilotkit/react-core/v2/styles.css`
- `showcase/shell` deps `@copilotkit/{react-core,runtime}` pinned
`latest` → `^1.60.2` (lockfile regenerated → coherent 1.61.0 set)
- dropped `@copilotkitnext/runtime` from `serverExternalPackages`
Result: zero `@copilotkitnext` references in touched source; `next
build` of `showcase/shell` passes (27/27 routes), all `/v2` imports +
styles resolve.
## CR
Reviewed via 7-agent cr-loop, 3 rounds to convergence + Procedure 3
bucket-(c) promotion audit (PROMOTE_TO_A: 0). The R1 mandatory fix was a
missed `.css` migration target (globals.css); the dep-pin hardening
fixed a stale-prerelease lockfile mismatch.
## Follow-up (separate PR)
Pre-existing, out-of-subject issues in the generative-ui-playground
example (tracked separately): invalid model id `openai/gpt-5.2`, and its
own missing `serverExternalPackages`.