The Apr 21 "refactor(runtime): Support durable compaction of threads" commit
re-added a2ui_dynamic_schema.py and a2ui_fixed_schema.py to the
langgraph-python-threads example in a stripped form. Most of the simplification
was cleanup, but two docstrings carried load-bearing instructions for the
sub-LLM and those got lost:
- render_a2ui's Args block said the root component must have id "root".
Without it, the LLM emits a valid flat component list with no entry point;
the A2UI renderer (A2uiSurface.tsx:152) hard-codes id="root" and falls
through to a shimmer placeholder — the "Sales Dashboard (A2UI Dynamic)"
demo renders as an empty white square.
- search_flights' docstring spelled out airline logo URLs, date format,
and status-icon colors, producing consistently-styled flight cards.
This restores both files verbatim from the non-threads example, which is
the known-good template. All differences in the agent/src dir are now
removed. Debug prints and headers come back with the restore; happy to
trim them in a follow-up if the intent was to keep the -threads version
terser.
Replace `RUN curl -LsSf https://astral.sh/uv/install.sh | sh` across all
starter Dockerfiles with `COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx
/usr/local/bin/`. The curl|sh form has a pipe-swallow latent bug: when
astral.sh returns a 5xx, curl fails but `sh` gets no stdin and exits 0,
so the layer "succeeds" with no uv binary. A later `RUN uv sync` then
crashes with `uv: not found` (exit 127). This already bit the agno
starter today (run 24809910399) when astral.sh had a transient outage;
upstream recovered on its own so this is latent-bug cleanup, not a
hotfix.
Using the official uv image is uv's own recommended pattern: it's
cache-friendly, network-free at build time, and sidesteps the pipe
failure mode entirely.
Scope: all 20 Dockerfiles under examples/integrations/*/Dockerfile,
examples/integrations/*/docker/Dockerfile.agent, and
examples/showcases/scene-creator/agent/Dockerfile.
Verified locally: `docker build -f docker/Dockerfile.agent ./agent`
for agno succeeds against the new pattern.
Wrap <ExampleLayout> in <CopilotChatConfigurationProvider agentId="default"
threadId={threadId}> so the canvas's useAgent() inherits the active threadId
via the existing fallback in use-agent.tsx. Without this wrapper, the canvas
calls useAgent() with no args and resolves to the registry agent instead of
the per-thread clone that the chat's /connect replay populates, so
STATE_SNAPSHOT events never reach it — todos rendered blank on thread resume
even though the final persisted snapshot contained them.
CopilotChat no longer needs explicit agentId/threadId props; it inherits
from the same provider, keeping one source of truth.
- Skip copilotkit.connectAgent when CopilotChat lacks a caller-supplied
threadId — a locally-minted UUID has no backend record, so /connect
would always 404 on the intelligence platform.
- Suppress the welcome screen while a connect is in flight and
unconditionally when the caller has supplied a threadId
(hasExplicitThreadId). Prevents the "How can I help you today?"
flash on thread switch.
- Gate suggestions on !isConnecting && !isRunning to avoid painting
them against a mid-replay message tree.
- Defer the isConnecting release by one animation frame so trailing
bootstrap renders commit before the flag flips.
- Reserve room for the "Powered by CopilotKit" license badge via a
new --copilotkit-license-banner-offset CSS var published by the
banner on mount; chat input consumes it only when bottom-anchored.
- Sort and display threads by lastRunAt (fallback to updatedAt →
createdAt) so metadata-only actions like archive/rename don't
reshuffle the list.
- useThreads waits for runtimeConnectionStatus === Connected before
dispatching the store context, eliminating the speculative /threads
fetch that fired before /info returned wsUrl.
Threads example polish: restore button + tooltips on
archive/restore/delete, segmented Active/All filter, graceful error
state, skeleton rows on initial load, stable scrollbar gutter,
pre-paint dark-mode class, logo position stable across app/chat
modes, drop dynamic-import drawer wrapper that caused null first
paint, archived-row dimming via child colors instead of opacity.
Tests:
- CopilotChat.absentThreadConnect: connect is skipped without a
threadId, fires when supplied via prop or config.
- CopilotChatView.connectingGate: isConnecting suppresses welcome;
hasExplicitThreadId suppresses welcome on empty chat.
- threads (core): lastRunAt sort fallback ordering.
- use-threads: Connecting-state gate defers /threads until Connected.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The scaffolded BFF was hitting two issues out of the box:
1. tsx doesn't auto-load .env, and the template's .env lives at the
monorepo root. process.env.COPILOTKIT_LICENSE_TOKEN was undefined
at runtime so the runtime logged 'No license token configured'
even when the user had populated .env via `copilotkit license`.
2. @copilotkit/runtime: next resolved to 1.55.0-next.9, which
predates the thread-naming UUID fix (v1.55.3). The BFF blew up
creating LangGraph threads with 'Invalid thread ID: must be a
UUID' because the runtime passed a compound thread-name:<uuid>:
<uuid> string.
Fix both:
- Switch dev script to `tsx watch --env-file=../../.env src/server.ts`
so tsx loads the monorepo root .env.
- Pin @copilotkit/runtime to `latest` so scaffolds get the
thread-naming fix. (The next dist-tag is still stale on npm.)
Switch docker-compose to pull app-api, db-migrations, and realtime-gateway
from public.ecr.aws/cpk/intelligence/* at tag 0.1.0-rc.5 instead of locally
built cpki/*:local images, and drop the build-local-images prerequisite
from the README and compose header.
- Add --validate-on-load to all aimock invocations (4 workflows/scripts
+ 13 integration docker-compose files)
- Replace hardcoded 2-file fixture list with dynamic discovery across
showcase/, examples/integrations/*/, scripts/doc-tests/ (16 fixtures)
- Add sanity check to prevent silent zero-test pass when discovery fails
- Extend showcase_validate.yml path filter to trigger on
examples/integrations/**/fixtures/** and scripts/doc-tests/fixtures/**
- Import and use ValidationResult type for callback parameters
- Fix scripts/doc-tests/fixtures/default.json to use { fixtures: [...] }
envelope shape
Playwright install stderr is diagnostic signal — version/network errors,
missing system libs, or browser download failures all surface here. Only
suppress stderr on the noisy npm install step. Also pin the image-tag /
client-version relationship with a code comment on one compose file so
future bumps know to keep them aligned.
The Playwright Docker image (mcr.microsoft.com/playwright:v1.52.0-noble)
ships with all Chromium system libraries and browsers pre-installed, so
`--with-deps` adds no runtime value — it only forces a redundant
`apt-get update && apt-get install` inside the tests container.
That apt-get call is the sole source of the intermittent
"Installation process exited with code: 100" / "Failed to install
browsers" failures that rotate across the 12 smoke matrix entries. The
underlying cause is transient Ubuntu archive mirror hash/size mismatches
("File has unexpected size ... Mirror sync in progress?", "Hash Sum
mismatch"), which cause apt to abort with exit 100. Because each matrix
job races apt against archive.ubuntu.com independently, the failing
subset rotates per run (run 24526747926 hit 5 starters; run 24509673514
hit a different 5; run 24495760568 hit 3) — classic flake, not a per-
starter regression.
Dropping --with-deps eliminates the apt-get call entirely. The browsers
themselves are already present at /ms-playwright/chromium-* in the base
image, and `npx playwright install chromium` remains as a cheap no-op
that self-heals if the pinned Playwright version ever drifts from the
image's bundled browser build.