mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
mme/subagents
12716 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
40205f3900 |
fix(deps): resolve @ag-ui peers to the renamed build, not the old canary
The lockfile installed TWO incompatible copies of the protocol: the PR-2350 preview carrying `subagentRunId`, and the old `0.0.59-canary.1785518626.0` carrying `subagentId`. Any subagent-attributed event passing through a package bound to the canary copy lost its attribution silently -- the registry would read a field the stream no longer emits, so every lookup returned undefined with no error. Reported in review of the rename commit. The cause is peer auto-install, which took a while to find. `pnpm.overrides` governs DECLARED dependencies. `@ag-ui/langgraph`, `@ag-ui/a2a` and `@ag-ui/mcp-apps-middleware` declare `@ag-ui/core` / `@ag-ui/client` as PEERS, and four workspace packages depended on them without declaring those peers themselves -- so pnpm auto-installed the peers straight from the registry, resolving to the canary and bypassing the overrides. That is why the overrides looked ignored: they were never consulted for those edges. Fixed the conventional way, by declaring the peers so they resolve from the graph instead of being fetched: - `packages/sdk-js` -- @ag-ui/core, @ag-ui/client - `examples/v2/angular/demo-server` -- @ag-ui/core, @ag-ui/client - `examples/showcases/generative-ui-playground` -- @ag-ui/core - `examples/v2/vue/demo` -- @ag-ui/client (it declared mcp-apps-middleware but no client, which was the last remaining edge) Result: zero references to the canary in the lockfile, and exactly ONE @ag-ui/core installed -- the renamed preview. Verified by deleting the stale store directories and reinstalling from the committed lockfile alone: they are not recreated, so nothing can reach the old copy. Things that did NOT work, recorded so they are not retried: plain install, `--force`, `pnpm dedupe`, `>`-scoped overrides (`@ag-ui/a2a>@ag-ui/core`), version-specific overrides, and deleting the lockfile to regenerate from scratch -- which reproduced it byte-identically, since the resolution was correct given the package.json files rather than stale. These four declarations are TEMPORARY, like the overrides they support, and come out with them when @ag-ui publishes the rename. Verified: @copilotkit/core, react-core and sdk-js typecheck; react-core 1424 tests and vue 1071 tests pass. |
||
|
|
c2fca03ff7 |
refactor(core)!: rename subagentId to subagentRunId to match AG-UI
AG-UI renamed the subagent attribution field because the old name implied a reusable subagent DEFINITION when the value identifies one INVOCATION -- two runs of the same subagent get two different values. See ag-ui-protocol/ag-ui#2350. subagentId -> subagentRunId parentSubagentId -> parentSubagentRunId Applied to both sides of this package's surface: - Reading the wire. SubagentRegistry consumes `event.subagentRunId` from SUBAGENT_STARTED / FINISHED / ERROR. This is the part that MUST move: the events are typed as SubagentStartedEvent etc. from @ag-ui/core, so against the renamed protocol the old field simply is not there and the registry would silently stay empty -- every subagent lookup returning undefined, with no error. - CopilotKit's own API. `useSubagent({ subagentRunId })` and `SubagentState.subagentRunId`. Renaming these is what makes the API self-consistent: the hook already distinguishes `subagentRunId` (exact, one invocation) from `subagentName` (the declared `subagent_type`, not unique), which is precisely the definition-vs-invocation split the old name blurred. This is a breaking change to `useSubagent`, taken now because the hook has never shipped -- it exists only on this unmerged branch, so there is no released consumer to migrate and no alias to maintain. The @ag-ui override repin is in the same commit rather than a separate `chore(deps)` one because the two are atomically coupled: the rename cannot typecheck against the old canaries, and the repin alone would leave the registry reading a field the stream no longer emits. Splitting them would mean one commit that is definitely broken either way. Verified: @copilotkit/core and @copilotkit/react-core typecheck against the renamed protocol, and react-core's 1424 tests pass. The typecheck is load-bearing here, not incidental -- injecting a deliberately wrong field name produces 5 TS errors, so a green check really does prove the field matches @ag-ui/core's types. Note: the registry and hook still have no test coverage of their own; the only subagent assertions live downstream in the AG-UI dojo demo. |
||
|
|
b21358efa0 | chore(deps): re-pin @ag-ui/* to 1785518626 subagents preview canaries | ||
|
|
94c952b239 | fix(core): guard subagent bucket access in handleFinished | ||
|
|
f1a474a293 | chore(deps): pin @ag-ui/* to subagents preview canaries | ||
|
|
2a6b0ee01d |
feat(core): subagent registry + useSubagent hook
Track subagent lifecycle (SUBAGENT_STARTED/FINISHED/ERROR) per owning agent
in a new SubagentRegistry delegate on CopilotKitCore, exposed via
getSubagents(agentId) and an onSubagentsChanged subscription. Add a
useSubagent({ subagentId | subagentName, agentId? }) hook to resolve a
message's subagentId to a name, description, and running status, warning
once (dev) when a subagentName is ambiguous.
Requires @ag-ui/client + @ag-ui/core >= 0.0.58; bumps the pins accordingly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
b24c3e4135 |
feat(web-components,react,vue,angular): CopilotThreadsDrawer UX redesign (ENT-1051) (#5823)
Redesigns the shared `<copilotkit-threads-drawer>` element (`@copilotkit/web-components`) to the new Figma UX, keeping the React, Vue, and Angular wrappers in lockstep. Pure-VIEW change — no `@copilotkit/core`, runtime, or `useThreads` changes. **Ticket:** [ENT-1051](https://linear.app/copilotkit/issue/ENT-1051) · **Figma:** [Thread Drawer](https://www.figma.com/design/feSsBJw1qCfLp0JNnOurrJ/CopilotKit-Intelligence?node-id=723-78) · **Spec:** [Notion](https://app.notion.com/p/3953aa381852819ab464dae3894e7f18) ## What changed - **Header** → right-aligned icon row. On desktop it holds the **collapse** toggle (sidebar glyph); on mobile the **close** toggle. No title text, no "+ New" pill. Optional `slot="header"` preserved (empty by default; the toggle right-aligns after it). - **New Conversation** row (plus-square + label) below the header — keeps `part="new-thread-button"` + the `new-thread` event. - **Recent Conversations** heading + **funnel** filter icon → Active/All popover. Preserves `_filter` + `filter-change` and `part="filter-active"`/`filter-all"`. - **Per-row kebab menu** (⋮) holding Archive/Unarchive + Delete — preserves those events + parts. An open kebab now shields the rest of the list from hover so it reads as a single surface (see Review fixes). - **Delete confirm** is a native `<dialog>` opened with `showModal()` (browser top layer), centered over the drawer's visible box — it can't paint under other UI or drop below the fold. jsdom falls back to the `open` attribute. - **Archived rows** render italic/muted inline in the "All" view. - **Desktop collapse** → `collapsed` / `collapsible` props (default **expanded**) + a `collapse-change` event / `CollapseChangeDetail`. Collapsing sets `--cpk-drawer-reserved-width: 0` on the document root so the host grid reclaims the column with no hydration flicker. - **Unified floating cluster** (Figma "closed" mockup) = `[sidebar-toggle] [+ New Conversation]`, shown in both the mobile-closed and desktop-collapsed states. Mobile stays an off-canvas modal (backdrop / Escape / focus-trap). ## Compatibility - All existing `::part()` names and events are preserved; only **additive** parts are introduced: `collapse-toggle`, `close-toggle`, `section-heading`, `filter-toggle`, `row-menu`, `row-menu-popover`, `launcher-cluster`, `launcher`, `launcher-new-thread`, plus `confirm-dialog`/`confirm-cancel`/`confirm-delete`/`backdrop`. One additive event: `collapse-change`. - Additive wrapper props: `recentLabel` (all frameworks); `collapsible`/`collapsed` + `onCollapseChange` (React) / equivalents in Vue & Angular. - **Usage note (now in the docs):** the drawer and `<CopilotChat>` must share a chat-configuration provider so the drawer drives the chat — `CopilotChatConfigurationProvider` (React/Vue) / `provideCopilotChatConfiguration()` (Angular). The v2 `CopilotKitProvider` does not provide that context on its own. - Verified: **no example `::part()` theme changes required** — every example themes the drawer via inherited `--cpk-drawer-*` custom properties. ## Descoped / changed during development (re: earlier review) - **Client-side search was removed at the designer's request.** There is **no** search UI, `search` event, `search-toggle`/`search-input` part, or `onSearch` wrapper prop in the shipped element. Any remaining "search" mention in older comments is stale. - **Desktop collapse was briefly backed out, then re-restored** per the designer (commit `8bfd245305`). The shipped element **has** collapse (`collapsed` is a live public property — it was not removed). ## Review fixes (commit `0b4f6ca392`) Addressing @MikeRyanDev and @marthakelly: - **`core/threads.ts`** — a full-list refetch (filter-change / retry) now clears `fetchMoreError` on both `listRequested` and `listSucceeded`, so the inline "couldn't load more — retry" banner no longer survives onto a fresh list. - **Escape while confirming delete** — the host keydown handler now consumes Escape while a confirmation is open; previously the bubbled keydown fell through and closed the whole mobile drawer along with the confirmation. - **Open kebab menu shields the list** — `.list.menu-open .row:not(.menu-open)` gets `pointer-events: none`, so other rows no longer reveal their kebab / paint a host `::part(row):hover` background around or behind the open popover. Click-away dismissal is preserved via the existing document pointerdown handler. (Verified live in the langgraph-js example.) - **Docs token** — dropped the removed `--cpk-drawer-rail-width` from the web-components README. ## Testing All suites run via `nx`, green through each package's lefthook pre-commit gate: - `@copilotkit/web-components` — **92** drawer element tests + `:build` green. Covers header collapse/close toggles, New Conversation, funnel filter switch, `recentLabel`, kebab open + archive/delete routing, confirm-dialog gating + native cancel + **backdrop-click dismiss**, **Escape-while-confirming (no drawer close)**, **open-menu row shield**, collapse/cluster/column-reclaim, archived-italic, preserved parts/events, `header` slot + `label` aria-labels. - `@copilotkit/core` — **553** tests incl. the new `clears a lingering fetchMoreError when a full list refetch succeeds`. - `@copilotkit/react-core` — CopilotThreadsDrawer suite + full package **1419** green. - `@copilotkit/vue` — **32** incl. SSR + the `collapsible` boolean-prop-default regression test. - `@copilotkit/angular` — CopilotThreadsDrawer spec **35** (incl. **scoped-chat-input focus**: prefers the ancestor `copilot-chat-view` over the document-global fallback); full package **178**. ## Follow-on - Docs (screenshot + reference/guide) on the release-gated docs PR **#5780**. - Release (`web-components` + `react-core` + `vue` + `angular`, lockstep) + CLI scaffolding bump. - Example grid/theme updates ride the release in **#5828**. - **[ENT-1080](https://linear.app/copilotkit/issue/ENT-1080)** — dedup the per-wrapper `findChatInput` / open-state fallback (marthakelly #6, deliberately deferred as a cross-package refactor). 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
e297bf9e9e |
ci(showcase): drop unused runner apt repos before installing bats (#5855)
## Problem The **Shell script tests (bats + shellcheck)** job in `showcase_validate.yml` installs `bats` with: ```yaml sudo apt-get update sudo apt-get install -y bats ``` GitHub's `ubuntu-latest` runner image preconfigures third-party apt repos (Microsoft / `azure-cli`, pointing at `packages.microsoft.com`) for preinstalled tooling. `apt-get update` refreshes **every** configured repo, not just the ones a job needs. When one of those Microsoft repos serves invalid release metadata: ``` E: Failed to fetch https://packages.microsoft.com/.../InRelease Clearsigned file isn't valid, got 'NOSPLIT' ##[error]Process completed with exit code 100. ``` `apt-get update` exits non-zero and — because the step runs under `bash -e` — the whole step aborts before `bats` installs. This fails the job even though `bats` comes from Ubuntu's own `universe` repo, which is unaffected. It's a runner-image / external-repo outage, not anything in the shell tests. ## Fix This job only needs Ubuntu packages, so remove the unused third-party repos before updating: ```yaml sudo rm -f /etc/apt/sources.list.d/*microsoft* /etc/apt/sources.list.d/*azure-cli* sudo apt-get update sudo apt-get install -y bats ``` Ubuntu's archive (where `bats` lives) is in the base `sources.list` and is untouched, so `bats` still installs; `apt-get update` no longer refreshes the broken, unused Microsoft repos, so it stops failing the job. ## Testing - Change is confined to the `Install bats` step of the `shell-script-tests` job. The rest of the job (shellcheck + the bats suite) is unchanged. - CI on this PR exercises the modified step directly — a green `shell-script-tests` run here confirms the install path. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
c149b618a9 |
ci(showcase): drop unused runner apt repos before installing bats
The shell-script-tests job installs bats via apt. GitHub's ubuntu-latest runner image preconfigures third-party apt repos (Microsoft / azure-cli) for preinstalled tooling this job never uses. When one of those repos serves invalid release metadata, `apt-get update` exits non-zero and `bash -e` aborts the step before bats installs — even though bats comes from Ubuntu's own `universe` repo, which is unaffected. This job only needs Ubuntu packages, so remove those unused third-party repos before `apt-get update`. |
||
|
|
0b4f6ca392 |
fix(threads-drawer): clear stale fetch-more error, trap Escape while confirming, shield rows behind open menu (ENT-1051)
Addresses PR #5823 review (MikeRyanDev + marthakelly): - core/threads.ts: a full-list refetch (filter-change / retry) now clears fetchMoreError on both listRequested and listSucceeded, so the inline 'couldn't load more - retry' banner no longer survives onto a fresh list. - web-components: Escape while the confirm-delete <dialog> is open is now consumed by a confirm guard in the host keydown handler; previously the bubbled keydown fell through to the mobile branch and closed the whole drawer along with the confirmation. - web-components: an open kebab popover now shields the rest of the list - .list.menu-open .row:not(.menu-open) gets pointer-events:none so other rows no longer reveal their kebab or paint a host ::part(row):hover background around/behind the menu. Click-away dismissal is preserved via the existing document pointerdown handler. - README: drop the removed --cpk-drawer-rail-width from the documented tokens. Tests: core clears-fetchMoreError-on-refetch; web-components Escape-while- confirming, confirm-dialog backdrop-click, menu-open row shield; Angular scoped-chat-input focus (ancestor copilot-chat-view over the global fallback). Menu-shield verified live in the langgraph-js example (:3002). |
||
|
|
2236f8e4c6 |
fix: resolve useAgent agentId from chat configuration (#5533) (#5592)
## Summary Fixes #5533 When a runtime registers an agent under a **non-default** name (e.g. `agents: { TravelBookingAgent }`) and the frontend renders `<CopilotChat agentId="TravelBookingAgent" />` without an `agent` prop on `<CopilotKit>`, the app throws after runtime sync: > useAgent: Agent 'default' not found after runtime sync (runtimeUrl=…). Known agents: [TravelBookingAgent] ## Root cause `useAgent()` (`packages/react-core/src/v2/hooks/use-agent.tsx`) resolved its `agentId` only from its own prop, falling back straight to `DEFAULT_AGENT_ID`. It never consulted the surrounding `CopilotChatConfigurationProvider` — even though `CopilotChat` installs that provider around its subtree with the resolved (non-default) agentId. `CopilotChat` resolves its *own* `useAgent` call correctly, so the chat works. But any **descendant** that calls `useAgent()` without re-passing `agentId` (a custom message/tool-render component, a sibling hook) silently resolves to `'default'`. Once `/info` sync lands and the registry holds only the non-default agent, that consumer throws — which is why the thrown id is `'default'`, not `'TravelBookingAgent'`. ## Fix Resolve `agentId` in `useAgent` with the same precedence `CopilotChat` already uses: ```ts const resolvedAgentId = agentId ?? chatConfig?.agentId ?? DEFAULT_AGENT_ID; ``` The hook already imported and called `useCopilotChatConfiguration` (for `threadId`); the call is hoisted and reused — no duplicate hook call. An explicit `agentId` prop still wins; with no chat config it still falls back to `DEFAULT_AGENT_ID`. No changes to core or the providers. ## Tests added `packages/react-core/src/v2/hooks/__tests__/use-agent-nondefault-agentid.test.tsx` — a `useAgent()` consumer inside a chat configured for `TravelBookingAgent` (runtime synced to `agents:{TravelBookingAgent}`) must not throw `Agent 'default' not found`, and must inherit `TravelBookingAgent`. Fails before the fix, passes after. ## Checklist - [x] Failing test written and confirmed failing before the fix - [x] Fix applied, test passes - [x] Full `@copilotkit/react-core` suite passes (1291 passed) - [x] Build succeeds (`nx build @copilotkit/react-core`) - [x] Formatter passes (`pnpm format`) |
||
|
|
f452699510 | Merge branch 'main' into fix/issue-5533-agentid-runtime-sync | ||
|
|
a1c97501c9 |
style(web-components): drop the redundant New Conversation top margin now the header shows [ENT-1051]
The collapse toggle keeps the header bar visible on desktop, so the row's 12px top margin doubled up with the header's padding (extra gap vs mobile). Make the top margin conditional on the header being HIDDEN (collapsible=false + no header slot) via .header[hidden] + .new-conversation; otherwise the header supplies the top spacing on both breakpoints. |
||
|
|
068f097627 |
style(web-components): designer nits — 24px cluster gutter, no selected-row border, cap radius at 4px [ENT-1051]
- Floating cluster/launcher default gutter → 24px on both top and left (was 12px). - Selected row: drop the border-color; the background change alone marks it (base row keeps its 1px transparent border for layout stability). - Cap --_radius at 4px via min(theme, 4px) and lower the hardcoded 6px button radii to 4px, so no bordered element exceeds a 4px corner radius. |
||
|
|
38a94ad1eb |
fix(web-components): center delete modal over the drawer's VISIBLE box [ENT-1051]
Intersect the .root rect with the viewport before centering the confirm dialog. A host grid that doesn't bound the drawer's row lets .root grow to content height, so centering over the raw rect dropped the modal far down the page (seen in the langgraph-js example, whose grid has no row bound). Clamping to the on-screen band keeps it centered in the visible drawer regardless of host sizing. |
||
|
|
bddaa80f35 |
fix(banking-demo): cap native TEI warmup batch to survive Metal fault (#5846)
## What `examples/showcases/banking/run-demo.sh` launches a native Metal `text-embeddings-router` (TEI) on `:7067` for the self-hosted durable-memory path. This passes `--max-batch-tokens 512` so TEI's warmup uses a small forward pass. ## Why On some Apple Silicon machines, TEI's default `--max-batch-tokens` (16384) **faults the Metal backend during its warmup forward pass**. Observed two failure modes at the exact same step (`Warming up model`): - **Deadlock** — every thread, including the main thread, parked in `__psynch_cvwait` at 0% CPU. Never binds `:7067`. - **Silent death** — process exits mid-warmup with no panic / no OOM line (the signature of a GPU-level abort). Either way `:7067` never comes up, the script's `wait_http … 300` times out, and the demo appears to "crash" with only: ``` ERROR: native Metal TEI did not come up at http://localhost:7067/health within 300s ``` The 300s timeout looks like a slow model download (the weights are ~1.1 GB), but that's a red herring — with weights cached the process still hangs at warmup. Two different `--dtype` values (fp16, float32) both failed identically, ruling out dtype; the variable is the warmup batch size. ## Fix `--max-batch-tokens 512` shrinks the warmup forward pass, which clears reliably (`Ready` in ~3s, health `200`, verified 1024-dim `/embed`). It bounds only per-request tokens — memory texts are short — **not** the embedding vectors, so recall stays byte-identical to the docker/CI embedder (the runbook's byte-identical guarantee holds). ## Scope One-line flag change + explanatory comment. Only affects the Apple Silicon native-TEI branch of the self-hosted demo path; amd64/CI (docker `tei`) is untouched. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
258820e4ce |
fix(banking-demo): cap native TEI warmup batch to survive Metal fault
The self-hosted `run-demo.sh` path launches a native Metal `text-embeddings-router` on :7067 for the durable-memory demo. TEI's default `--max-batch-tokens` (16384) can fault the Metal backend during its warmup forward pass on some Apple Silicon machines. The process then either deadlocks (every thread parked in a pthread cond wait at 0% CPU) or dies silently with no panic — a GPU-level abort — so it never binds :7067 and the 300s health wait times out. The demo appears to "crash" with no actionable error. Pass `--max-batch-tokens 512` so warmup uses a small forward pass, which clears reliably. This only bounds per-request tokens (memory texts are short), not the embedding vectors, so recall stays byte-identical to the docker/CI embedder. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
8ab9b9613a |
fix(web-components): right-align the desktop collapse toggle in the header [ENT-1051]
Move the collapse toggle after the header slot so it right-aligns (the slot has flex:1 and pushes it over), matching the mobile close button — it was reading as left-aligned above New Conversation, which the designer flagged. |
||
|
|
8bfd245305 |
feat(web-components,react,vue,angular): restore collapse + drawer-centered modal [ENT-1051]
Design iteration (Ben's designer): - RESTORE desktop collapse. Re-add collapsed/collapsible + collapse-change (element + all three wrappers, lockstep), the desktop header collapse toggle, and the CollapseChangeDetail type/re-export. Default is EXPANDED. - UNIFY the closed affordance into one floating cluster (Figma 'closed' mockup): a sidebar-glyph toggle + a New Conversation (+) icon button, shown in BOTH the mobile-closed state (adds New Conversation to the old single launcher) and the desktop-collapsed state. Parts: launcher-cluster, launcher, launcher-new-thread. - COLUMN RECLAIM (no empty reserved gap): on desktop-collapse the element sets --cpk-drawer-reserved-width: 0px on the document root (reaches the grid past the wrapper host via :root inheritance); hosts read it in grid-template-columns. Default expanded never sets it, so no hydration flicker. - DELETE MODAL centered over the DRAWER PANEL, not the viewport: keeps the top-layer showModal() robustness (never clipped) but drives --confirm-cx/cy from the visible .root rect and caps width to the drawer band. - Vue fix: default collapsible to true in the wrapper. Vue coerces an omitted Boolean prop to false, which was silently forcing collapsible=false (collapse toggle vanished) — React/Angular pass undefined and keep the element default. Validated live in the Nuxt (Vue) and Angular demos against managed Intelligence: collapse/expand, cluster + New Conversation, column reclaim, drawer-centered top-layer modal, mobile cluster. Tests: web-components 89, react-core 1419, vue 32, angular 34 — all green. |
||
|
|
1eadd3a4b2 |
fix(showcase/langroid): emit a2ui container via TOOL_CALL_RESULT (v0.9 nested) (#5843)
## Summary
- Fixes langroid `a2ui-fixed-schema` D6 cell
(`d6:langroid/gen-ui-a2ui-fixed`): the a2ui container was emitting via
`TextMessageContentEvent` so the A2UI middleware never detected it and
the flight card never mounted — raw JSON rendered as plain text in chat
- Fixed by emitting `ToolCallResultEvent` (matching the
claude-sdk-python peer)
- Also fixed the operation shape: upgraded from legacy flat form
(`{"type": "create_surface", ...}`) to v0.9 nested form (`{"version":
"v0.9", "createSurface": {...}}`) — the renderer silently ignores the
flat form
- Adds the missing langroid aimock D6 fixture for `gen-ui-a2ui-fixed`
(without it, aimock returned 'no fixture match', causing a fetch error)
## Changes
**`showcase/integrations/langroid/src/agents/a2ui_fixed_agent.py`**
- Added `ToolCallResultEvent` import
- Replaced `TextMessageStart` + `TextMessageContent` + `TextMessageEnd`
block with a single `ToolCallResultEvent(tool_call_id=call_id,
content=json.dumps(operations))`
- Upgraded `_build_a2ui_operations()` to emit v0.9 nested op shape
**`showcase/aimock/d6/langroid/gen-ui-a2ui-fixed.json`** (new file)
- Adds aimock fixtures: turn 1 (no-tool-result → call `display_flight`)
and turn 2 (has-tool-result → confirmation text)
**Peer matched:** claude-sdk-python (event path), strands / google-adk
(v0.9 op shape)
## RED → GREEN Proof
### RED (before fix)
```
✗ d6:langroid/gen-ui-a2ui-fixed red (0.0s)
state=red
0 passed, 1 failed
```
Flap-diagnostics: `expected [data-testid="a2ui-fixed-card"] to mount
within 60000ms` — bodyTextSnippet showed raw `{"a2ui_operations":
[{"type": "create_surface"...` text rendered inline in chat.
### GREEN (after fix — rebuilt container + v0.9 ops +
ToolCallResultEvent + aimock fixture)
```
langroid [d6]
[conversation-runner] turn 1/1 — assertions passed
[conversation-runner] conversation completed successfully { turnsCompleted: 1, totalDurationMs: 2121 }
✓ d6:langroid green (2.6s)
1 passed (2.6s)
```
## Aimock Ceiling Check
`__tests__/aimock-fixtures.test.ts` passes as-is (825/825) — the new
langroid `gen-ui-a2ui-fixed.json` fixture uses `context: "langroid"`
scoping with unique `hasToolResult: false/true` discriminators, so no
new duplicate-ceiling collisions introduced.
## Note
This is a re-home of #5839. The original PR's head was on a
`worktree-agent-*` branch which never triggers CI workflows (0 runs,
even after close+reopen). Branch renamed to
`fix/langroid-a2ui-tool-call-result` to trigger normal CI.
|
||
|
|
0be474d562 |
fix(showcase/langroid): emit a2ui container via TOOL_CALL_RESULT so the card mounts
Two bugs fixed:
1. Tool result event path: the `a2ui_operations` container was emitted
inside a `TextMessageContentEvent` block. The A2UI middleware only scans
`TOOL_CALL_RESULT` events for the container, so the card never mounted
and the raw JSON appeared as plain text in the chat. Fixed by emitting a
`ToolCallResultEvent` (matching the claude-sdk-python peer).
2. Operation shape: the ops used the legacy flat form
(`{"type": "create_surface", ...}`) which the renderer silently ignores.
Updated to the v0.9 nested form (`{"version": "v0.9", "createSurface":
{...}}`) used by every other working peer (claude-sdk-python, strands,
google-adk).
Also adds the missing langroid aimock D6 fixture for `gen-ui-a2ui-fixed`
(`display_flight` → tool result → confirmation text) so the D6 probe has
a mock response to drive the full surface-render assertion.
D6 cell: d6:langroid/gen-ui-a2ui-fixed red → green
|
||
|
|
30873ab52b |
fix(showcase/spring-ai): emit v0.9 nested A2UI ops in DisplayFlightTool (#5838)
## Summary
Spring-ai's `DisplayFlightTool` was emitting legacy flat A2UI operations
(`{"type":"create_surface",...}`) but the A2UI middleware expects v0.9
nested operations (`{"version":"v0.9","createSurface":{...}}`). This is
the same flat→nested migration done for Python/TS in #5832 and langroid
in #5839. The flat shape was silently ignored by the middleware, so the
flight card never mounted and the `a2ui-fixed-schema` D6 cell was
permanently red.
**Root cause** (confirmed by prior local red-green on the disproven TS
fix):
`DisplayFlightTool.apply()` emitted `a2ui_operations` in the legacy flat
format. The middleware's `tryParseA2UIOperations` parses the container
correctly, but the op dispatchers inside require the v0.9 shape. No
surface
was ever created → card never mounted.
## Fix
Updated the three operations in `DisplayFlightTool.java` to v0.9 nested
format:
| Before (flat, ignored) | After (v0.9 nested, works) |
|---|---|
| `{"type":"create_surface","surfaceId":...,"catalogId":...}` |
`{"version":"v0.9","createSurface":{"surfaceId":...,"catalogId":...}}` |
| `{"type":"update_components","surfaceId":...,"components":...}` |
`{"version":"v0.9","updateComponents":{"surfaceId":...,"components":...}}`
|
| `{"type":"update_data_model","surfaceId":...,"data":{...}}` |
`{"version":"v0.9","updateDataModel":{"surfaceId":...,"path":"/","value":{...}}}`
|
Shape matches `sdk-python/copilotkit/a2ui.py` and the shared Python
tools.
## Local Red-Green Proof (real control-plane probe, `--rebuild` both
runs)
**RED — original flat ops (`{"type":"create_surface",...}`):**
```
$ showcase test spring-ai:a2ui-fixed-schema --d6 --rebuild --keep
✗ d6:spring-ai/gen-ui-a2ui-fixed red (0.0s)
state=red
⚠ Tests failed for spring-ai:a2ui-fixed-schema (exit 1)
```
**GREEN — v0.9 nested ops
(`{"version":"v0.9","createSurface":{...}}`):**
```
$ showcase test spring-ai:a2ui-fixed-schema --d6 --rebuild --keep
✓ d6:spring-ai/gen-ui-a2ui-fixed green (0.0s)
1 passed
✓ Tests passed for spring-ai:a2ui-fixed-schema
```
## Java Build & Tests
All 64 existing spring-ai JUnit tests pass after the change (`mvn test`:
64 run, 0 failures, 0 errors). Code compiles cleanly (`mvn compile -q`).
## Files Changed
-
`showcase/integrations/spring-ai/src/main/java/com/copilotkit/showcase/springai/tools/DisplayFlightTool.java`
— v0.9 nested op format
The `route.ts` file is unchanged from main (the prior no-op `a2ui: {
injectA2UITool: true }` addition has been reverted — it was disproven as
a fix by a real local red-green).
|
||
|
|
4727b89083 |
fix(showcase): resolve aimock exact-duplicate ceiling regression from #5426 (#5841)
## Summary - PR #5426 added `showcase/aimock/d6/ag2/multimodal.json` with two fixtures keyed on `userMessage + turnIndex:0 + context:ag2` (the image and PDF multimodal probes). - Those exact same match keys were already present in `showcase/aimock/d6/ag2/agentic-chat.json` (placed there at the same time #5426 updated the agentic-chat file). - Result: 2 exact duplicates within the `ag2` context scope → collision count 297→299, tripping the ceiling (297). ## Fix Dedupe: remove the two multimodal-probe entries from `agentic-chat.json`. The dedicated `multimodal.json` (added by #5426) is the authoritative home. The `agentic-chat` probe never sends image/PDF turns; aimock routes those to `multimodal.json` via `context: ag2`. ## RED → GREEN **RED** (origin/main at `81c577f067`): CI run #28840506865 (`Showcase: Validate main`): ``` AssertionError: Exact duplicate count (299) exceeds ceiling (297). ``` **GREEN** (this branch, `fe96b3f254`): local run against worktree fixtures: ``` ✓ fixture collision detection > no exact duplicate match keys within the same context scope 2ms Tests 824 passed (824) ``` ## Test plan - [x] `__tests__/aimock-fixtures.test.ts > fixture collision detection > no exact duplicate match keys` passes (count ≤ 297) - [x] No new fixtures added or ceiling bumped — pure dedupe - [ ] CI `Showcase: Validate main` flips green on this branch 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
fe96b3f254 |
fix(showcase): resolve aimock exact-duplicate ceiling regression from #5426
PR #5426 (ag2 multimodal unquarantine) added showcase/aimock/d6/ag2/multimodal.json with two fixtures keyed on: - userMessage: "can you tell me what is in this demo image I just attached", turnIndex: 0, context: ag2 - userMessage: "can you tell me what is in this demo pdf I just attached", turnIndex: 0, context: ag2 Those same keys already existed in showcase/aimock/d6/ag2/agentic-chat.json, creating 2 exact duplicates within the ag2 context scope and pushing the collision count from 297 → 299 (ceiling = 297), breaking the validate CI job. Fix: remove the two multimodal-probe entries from agentic-chat.json since the dedicated multimodal.json is now the authoritative home. The agentic-chat probe does not send image/PDF turns; the multimodal probe matches via context "ag2" against multimodal.json directly. RED: AssertionError: Exact duplicate count (299) exceeds ceiling (297) → confirmed in CI run #28840506865 (Showcase: Validate main) GREEN: all 824 tests pass after removing the duplicate entries |
||
|
|
81c577f067 |
fix(showcase/ag2): unquarantine multimodal — normalize AG-UI image content for autogen (#5426)
## Summary
- **Restores ag2's `multimodal` D6 pill** from `skipped-incapable` (NSF)
to a working feature by adding a showcase-local ASGI middleware that
normalises AG-UI image/document/binary content parts to OpenAI Chat
Completions `image_url` parts before they hit AG2's `ConversableAgent`.
- **Surgical scope**: middleware mounted only on the multimodal sub-app
— other ag2 routes never see image parts and pay no body-buffer cost.
- **No upstream wait**: option (A) showcase shim, not an autogen PR.
autogen still lacks AG-UI image-part support; the moment they add it the
normalizer is a no-op and the RED-half regression pin flips to alert us.
- **Reverses commit d8a0a25db** for the multimodal half: removes
`multimodal` from `not_supported_features`, adds it back to `features`,
and restores the D6 aimock fixture pair.
`tool-rendering-reasoning-chain` stays quarantined (a different upstream
gap — no `REASONING_MESSAGE_*` events emitted by AGUIStream).
## What was failing
AG2's `autogen.code_utils.content_str` only accepts content-part types
`{"text", "input_text", "image_url", "input_image", "function",
"tool_call", "tool_calls"}`. The harness sends user messages whose
`content` carries:
- modern AG-UI: `{"type": "image" \| "document", "source": {"type":
"data" \| "url", "value": ..., "mime_type": ...}}`
- legacy mirror (appended by `legacy-converter-shim.tsx` for LangChain
integrations): `{"type": "binary", mimeType, data \| url}`
Both trip the gate with `ValueError("Wrong content format: unknown type
image within the content")` BEFORE the request reaches the vision model
— observed live on staging in the D6 multimodal probe. That's why the
feature was quarantined NSF in d8a0a25db.
## How the fix works
`agents/_multimodal_normalize.py` adds a raw-ASGI middleware (mirrors
the existing `RequestUserMessageMiddleware` pattern) that:
1. Buffers each inbound POST body.
2. Walks `messages[*].content` on user-role messages only.
3. Rewrites each AG-UI image/document/binary part to `{"type":
"image_url", "image_url": {"url": ...}}` — data sources become
`data:<mime>;base64,<value>` URLs; URL sources pass through unchanged.
4. Updates the request's `content-length` header.
5. Replays the rewritten body to the downstream AGUIStream endpoint.
Non-user messages, plain-text content, already-normalised parts, and
unknown shapes pass through untouched (identity-preserved on no-op
turns). Any body-parse failure logs at WARNING and replays the ORIGINAL
body so autogen's verbatim error surface stays intact — visibility, not
silent rewrite.
## RED → GREEN evidence
`tests/python/test_multimodal_normalize.py` — 14 unit tests, all pass:
| # | Test | What it pins |
|---|------|------|
| 1 | `test_autogen_rejects_raw_agui_image_part` | RED: `content_str`
raises the verbatim `ValueError` text the D6 probe surfaced |
| 2 | `test_normalized_content_is_accepted_by_autogen` | GREEN: after
normalize, `content_str` returns the rendered string with `<image>`
placeholder |
| 3-7 | shape coverage | image data/url, document data, binary data/url,
mimeType camelCase alias |
| 8-10 | passthrough | text-only, plain-string content, assistant/tool
messages |
| 11 | idempotency | re-running on already-normalised content is a no-op
|
| 12 | error path | unrecognised source → text placeholder (not a hard
fail) |
| 13 | tripwire | middleware class exposes `__init__(app)` + `__call__`
|
RED was independently verified by monkey-patching
`_normalize_content_part` to passthrough — that reproduces the exact
`ValueError("Wrong content format: unknown type image within the
content")` from the staging probe. Restoring the normalizer flips it
back to GREEN.
End-to-end ASGI smoke (run inline during development): a synthetic AGUI
POST body with a modern image part is sent through
`MultimodalContentNormalizerMiddleware` → inner ASGI app sees rewritten
body with correct `content-length`. PASS.
## Out of scope / follow-ups
- **PDF rendering**: PDFs ride through as
`data:application/pdf;base64,...` inside an `image_url` part — they
survive autogen's gate but the vision model can't read them natively.
Flattening PDFs to inline text (the pattern langgraph-python uses via
pypdf) is a separate enhancement; this PR's scope is unblocking the
image path that the D6 `multimodal` pill assertion checks.
- **Upstream**: autogen could fix this in `content_str` by accepting
AG-UI's `image`/`document`/`binary` content types directly. When/if that
lands, the normalizer becomes a no-op and the RED-half test will start
failing (which is the signal to delete the shim).
## Test plan
- [x] `cd showcase/integrations/ag2 && python -m pytest tests/python/` —
16 passed (2 pre-existing gen_ui guard tests + 14 new
multimodal_normalize tests)
- [x] `ruff format --check` on touched python files — clean
- [x] `ruff check` on touched python files — clean
- [x] `cd showcase/scripts && pnpm validate-manifests` — ag2 manifest
validates
- [x] `oxfmt --check showcase/aimock/d6/ag2/multimodal.json` — clean
- [x] Verified `multimodal_app.user_middleware` includes
`MultimodalContentNormalizerMiddleware` after import
- [x] End-to-end ASGI smoke: middleware rewrites body + updates
content-length, downstream app sees normalised payload
- [ ] Staging deploy: D6 `multimodal` pill flips from
`skipped-incapable` to GREEN with image fixture (1×1 PNG → "image
attachment shows a small abstract test pattern..."). Validated
post-merge via the staging deploy.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||
|
|
538443597c | style: auto-fix formatting | ||
|
|
46b751b611 |
fix(showcase/ag2): resolve Pyright findings in multimodal normalize
- Remove unused imports: Iterable, ConversableAgent (from autogen),
AGStreamInput (from autogen.ag_ui.adapter) — none appear in executable
code, only in docstring prose.
- Fix raw_msgs possibly-unbound at dispatch guard: initialize to None
before the try block so the identity check at line 302 is always
safe even if model_dump raises before raw_msgs is assigned. Also
tighten the guard to `raw_msgs is not None` to make the no-normalization
fallback explicit.
autogen.ag_ui import unresolved and LLMConfig(dict) "Expected 0 positional
arguments" are ENVIRONMENT findings — autogen.ag_ui ships only in the
ag2[ag-ui] extra (present in the container, not in local Pyright's venv),
and LLMConfig({...}) is the codebase-wide pattern that works at runtime
with ag2>=0.9 as installed in the container.
|
||
|
|
3b1f628266 |
fix(showcase/ag2): unquarantine multimodal — normalize AG-UI image/document/binary content parts to autogen image_url
AG2's ConversableAgent runs every user message through
``autogen.code_utils.content_str``, which only accepts content-part
types in {"text", "input_text", "image_url", "input_image", "function",
"tool_call", "tool_calls"}. CopilotChat / the AG-UI runtime emits image
and document attachments as the modern shape
{"type": "image" | "document", "source": {...}}
and the demo page's legacy-converter-shim.tsx ALSO appends a legacy
{"type": "binary", mimeType, data | url}
mirror alongside it (to keep the @ag-ui/langgraph converter happy on
LangChain-based integrations — it rides through on the ag2 path too).
Both shapes trip autogen's allowed-types gate with
ValueError("Wrong content format: unknown type image within the
content")
…BEFORE the request reaches the vision model — observed live in the
D6 multimodal probe (commit d8a0a25db, which originally quarantined
the feature as NSF).
Fix
---
Add ``agents/_multimodal_normalize.py``: a ``NormalizingAGUIStream``
subclass of ``AGUIStream`` that overrides ``dispatch()`` to normalize
AG-UI image/document/binary content parts to OpenAI Chat Completions
``image_url`` parts AFTER ``RunAgentInput`` Pydantic parsing and BEFORE
``AgentService`` serialises the messages for autogen.
This is the only correct interception point:
- Too early (ASGI body rewrite before Pydantic): ``RunAgentInput``
rejects ``image_url`` because it is not an AG-UI standard type —
the discriminated union only accepts image/document/binary/text.
- Too late (inside ConversableAgent): requires patching autogen
internals.
The override works by calling ``normalize_messages_for_autogen()`` on
the dict-serialised messages (same form as ``run_stream`` produces via
``model_dump()``) and re-injecting them via a ``_PatchedRunAgentInput``
wrapper that overrides only ``.messages``, delegating all other
attribute access to the original ``RunAgentInput``.
Conversions:
- {"type": "image", "source": {"type": "data", value, mime_type}} →
{"type": "image_url", "image_url": {"url": "data:<mime>;base64,<value>"}}
- {"type": "image", "source": {"type": "url", value}} →
{"type": "image_url", "image_url": {"url": value}}
- {"type": "document", "source": ...} → image_url with the document's
mime preserved (data:application/pdf;base64,...). The vision model
still can't natively read PDFs, but the request reaches the model
instead of being rejected upstream, which is the failure mode this
fix targets.
- {"type": "binary", mimeType, data | url} → image_url (the
legacy-shim parts ride through cleanly).
- {"type": "text", ...} and already-normalised image_url parts pass
through unchanged (identity-preserved on no-op turns).
Failure path: any normalization error is logged at WARNING and the
original messages are forwarded unchanged — autogen's own ValueError
fires verbatim with its error surface intact.
Manifest + fixture
------------------
- showcase/integrations/ag2/manifest.yaml: remove multimodal from
not_supported_features (with its now-stale comment) and add it back
to the features list next to voice.
- showcase/aimock/d6/ag2/multimodal.json: add the D6 fixture pair
using the actual autoPrompt strings from sample-attachment-buttons.tsx
("can you tell me what is in this demo image I just attached" /
"can you tell me what is in this demo pdf I just attached").
TDD evidence (red-green)
------------------------
showcase/integrations/ag2/tests/python/test_multimodal_normalize.py
contains 14 unit tests, pinned at three layers:
1. RED/GREEN against autogen's actual content gate:
* test_autogen_rejects_raw_agui_image_part — confirms
content_str([{type: image, source: ...}]) raises the verbatim
ValueError the D6 probe surfaced. This is the regression pin: if
autogen ever relaxes the gate, this test fails and we know to
revisit the normalizer.
* test_normalized_content_is_accepted_by_autogen — after
normalize_messages_for_autogen(...), content_str accepts every
part and renders "<image>" for the image_url part.
2. Shape coverage: modern image data/url, modern document, legacy
binary data/url, mimeType camelCase alias, plain-text passthrough,
plain-string content, assistant/tool messages untouched,
unrecognised source → text placeholder, idempotency.
3. NormalizingAGUIStream class surface tripwire.
Control-plane D6 RED→GREEN:
RED (no normalizer, pre-fix container): d6:ag2/multimodal → red
(HTTP 500 agent_run_error_event from content_str ValueError)
GREEN (NormalizingAGUIStream applied): d6:ag2/multimodal → green
|
||
|
|
13ef0982d5 |
docs(showcase): session-stack discipline + cleanup guidance for isolated runs (#5724)
## What Adds a **Session-stack discipline / Cleanup after isolated runs** subsection to `showcase/TESTING.md`, governing how `--isolate`/`--keep` is used across a debugging/testing session. ## Why `--keep` correctly lets an `--isolate <name>` stack survive a run so it can be reused for a session-long test set. The leak was **agent discipline**, not the flag: 1. Agents minted a **new** named kept stack per individual cell instead of reusing ONE stack for the whole session — which is how Docker accumulated `cvtest2`, `greenproof`, `conformred`, `conformgreen`, `gp1`..`gp10`, `showcase-iso2/4`, etc. 2. When the session's work was done, the stacks it created were never torn down — each one holds a slot and offset ports until the host fills up. ## The discipline encoded 1. **One stack per session, reused** — choose ONE stable `--isolate <session-name> --keep` and reuse it for ALL tests in the session (derive the name from the primary slug, e.g. `--isolate <slug>-session`). Never mint a new named stack per cell/feature/pill. 2. **`--keep` is for intra-session reuse only, never a license to leak** — if you pass `--keep`, you OWN teardown at session end. 3. **Tear down at session end** — use the survival-notice command `docker compose -p <name> down --remove-orphans --volumes && rm -rf <run-dir> <slot-dir>`. `bin/showcase down` does NOT tear down isolated stacks (it only stops the default `showcase-*` project). Bare `--isolate` (no `--keep`) auto-cleans and is preferred for one-off tests. Teardown mechanics live once in `DEBUGGING.md → Cleanup` (cross-linked); this section owns the discipline. ## Verification This is a docs-only behavior change — no probe/Playwright/code red-green surface applies. Doc quality gates run instead: - `oxfmt --check showcase/TESTING.md` → passes (the file was correctly formatted on `main`; the only formatter touch was `*new*` → `_new_`). - Diff is purely additive (+49 lines, one file). - Cross-link anchor `DEBUGGING.md#cleanup` verified against the `### Cleanup` heading. The three harness facts the guidance relies on were verified by reading `scripts/cli/_common.sh`, `cmd-test.sh`, and `bin/showcase`: each `--keep` run claims a fresh slot + idempotent pre-down + brings the stack up (no attach); a same-name re-run against a still-live kept stack fails loudly on the duplicate-name guard; the exact teardown command matches the survival notice at `_common.sh:~1074`. ## Follow-up (not in this PR) The harness could make this self-enforcing — e.g. warn when a session uses >1 distinct kept `--isolate` name, or add a `bin/showcase slots --reap-mine` convenience to tear down all stacks this user created. Noted for later; no harness changes here. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
65d2100d24 |
fix(showcase/spring-ai): emit v0.9 nested A2UI ops in DisplayFlightTool
The DisplayFlightTool was emitting legacy flat A2UI operations
({"type":"create_surface",...}) but the A2UI middleware expects v0.9
nested operations ({"version":"v0.9","createSurface":{...}}).
This is the same flat→nested migration done for Python/TS in #5832 and
langroid in #5839. The flat shape was silently ignored by the middleware,
so the flight card never mounted and the a2ui-fixed-schema D6 cell was
permanently red.
Fix: update the three ops to the v0.9 nested format:
- createSurface (was type:create_surface)
- updateComponents (was type:update_components)
- updateDataModel with value: (was type:update_data_model with data:)
Local red-green proof (real control-plane probe, --rebuild both times):
RED: d6:spring-ai/gen-ui-a2ui-fixed red (flat ops, card never mounts)
GREEN: d6:spring-ai/gen-ui-a2ui-fixed green (v0.9 nested ops, card mounts)
|
||
|
|
a0ea120e67 |
fix(showcase-harness): pnpm-packages discovery must skip out-of-prefix deep-glob patterns instead of throwing (version_drift enumerate failure) (#5290)
## Summary
The `version_drift` probe fails on the fleet control-plane with
`probe.discovery-enumerate-failed` / `discoveryFailed:true` and writes 0
PB rows.
**Root cause:** the fleet `pnpm-workspace.yaml` carries a multi-segment
glob `examples/v2/*/apps/*`. The `pnpm-packages` discovery source's
`matchPattern` only supports literals and trailing `/*`, and throws
`DiscoverySourceSchemaError("unsupported pnpm-workspace glob pattern")`
for any pattern whose segment after the first `*` is not `""`/`"/"`.
That throw happens inside `expandPatterns` during enumeration —
**before** the probe's `pathPrefix: "packages/"` filter is applied — so
it aborts the entire enumeration. `version_drift` never gets to filter
to `packages/`; it just fails.
**Fix (option a):** thread `pathPrefix` into `expandPatterns` and skip
any pattern whose static (wildcard-free) leading prefix cannot intersect
the requested `pathPrefix`, **before** validating its glob shape.
`examples/v2/*/apps/*` (static prefix `examples/v2/`) is skipped when
the probe only wants `packages/`, so enumeration completes and returns
the `packages/` set.
Preserved behavior:
- An unsupported deep-glob that DOES overlap the requested prefix (e.g.
`packages/*/apps/*` with `pathPrefix: packages/`) still throws the
strict-shape SchemaError.
- With no `pathPrefix`, every pattern is in scope and the existing
strict-throw behavior is unchanged.
## Changes
- `showcase/harness/src/probes/discovery/pnpm-packages.ts` —
`staticPrefix` + `patternIntersectsPrefix` helpers; `expandPatterns` now
takes `pathPrefix` and skips out-of-prefix include/exclude patterns
before shape validation.
- `showcase/harness/src/probes/discovery/pnpm-packages.test.ts` —
red→green regression tests.
## Test plan
- [x] Red: new "skips out-of-prefix deep-glob" test fails against
unfixed source (throws SchemaError in `expandPatterns`)
- [x] Green: same test passes after the fix; out-of-prefix deep-glob
skipped, `packages/` packages still enumerated
- [x] Preserved: unsupported deep-glob overlapping the prefix still
throws; existing "non-trailing `*` throws" test (no pathPrefix)
unchanged
- [x] Full harness suite: 2137 passed (120 files)
- [x] `tsc -p tsconfig.build.json` clean
|
||
|
|
dc1d1ce2c4 |
feat(showcase): two-miss tolerance for soft probe errorClass on dashboard (pool-fleet step C) (#5278)
## Summary
Wires the starter-smoke probe's keyed `errorClass` into the dashboard
cell-state flip logic (`buildStarterBadge` in `live-status.ts`) so
transient SOFT failures get **two-miss tolerance**, reducing dashboard
flapping on transport hiccups.
- **SOFT** (`transport-error`, `aborted`): a *single* miss is tolerated
— the cell renders **amber `~`** instead of flipping red. Flips red only
on a **second consecutive** miss.
- **HARD** (`smoke-failed`): flips red **immediately**, no tolerance.
- A soft miss followed by a green tick renders a clean green ✓
(recovery).
## How the consecutive-miss count works (no new dashboard state)
`errorClass` was previously unused on the dashboard. The flip gate
reuses the **producer-maintained `fail_count`** — the harness
`status-writer`'s persisted consecutive-red counter (`1` on green→red,
incremented on sustained red, `0` on red→green). So
`resolveCell`/`buildStarterBadge` stay a **pure function of the current
row**: there is no dashboard-side counter to thread or reset. Tolerance
is applied as a `state` → `degraded` downgrade inside
`buildStarterBadge` (the same additive pattern as the existing
stale-green→degraded fold), so the connection/tooltip/drilldown-signal
are all preserved and a `.row.state` reader sees `degraded` (agreeing
with the amber tone), never a latent false-red.
Threshold: `fail_count >= 2` flips; `fail_count <= 1` tolerates.
## errorClass values used (soft/hard split)
Mirror of the harness `StarterFailureClass` union in
`showcase/harness/src/probes/drivers/starter-smoke.ts`:
| class | split | meaning |
|---|---|---|
| `transport-error` | **SOFT** | timeout / cold-start wake / connection
failure |
| `aborted` | **SOFT** | external-abort / outer-timeout |
| `smoke-failed` | **HARD** | real HTTP-level content regression |
Added as a dashboard-side mirror `STARTER_FAILURE_CLASSES` (the
dashboard imports only `@/*` and cannot reach across the package
boundary), guarded by a new **`starter-error-class-drift.test.ts`**
set-equality lint against the harness source — mirroring the existing
`commError-contract-drift.test.ts` pattern.
## Semantics chosen / ambiguity flagged (conservative defaults)
These were genuinely ambiguous; the most conservative sensible behavior
was chosen and is flagged here for review:
1. **A tolerated soft miss renders AMBER `~`, NOT green.** The probe
literally just failed, so claiming a green ✓ would be a false-green lie
(the codebase guards against false-green everywhere). Amber says
"transient, not yet actionable" — distinct from both the flap-to-red and
a dishonest green.
2. **Tolerance applies ONLY to an *explicit* soft `errorClass`.** A red
row with **no** `errorClass` (or an unrecognized value) flips
immediately as before — we only soften when the producer explicitly tags
the failure transient. This preserves all pre-existing red-row tests.
3. **`fail_count <= 1` (not strictly `== 1`) is tolerated** to guard the
legacy/edge boundary where a first failure reports `0`.
4. **Unsupported columns are unaffected** — the 🚫 mapping-derived state
still wins over any row data.
## Test plan (red → green)
- [x] RED first: the 3 single-soft-miss tolerance assertions failed
against current `main` (soft single miss flipped red); the 6
behavior-preserving assertions passed.
- [x] GREEN after implementation: all 9 new tests pass.
- [x] New drift guard `starter-error-class-drift.test.ts` passes
(set-equal vs harness `StarterFailureClass`).
- [x] Full dashboard vitest suite: **922 passed, 1 skipped (59 files)**
— incl. `STATUS_LIST_FIELDS` guard, comm-error contract tests, and all
pre-existing starter-badge tests.
- [x] `tsc --noEmit` clean.
## Reconciliation with peer "speedup" work
No speedup-owned symbols were modified: `summarizeSignal`,
`STATUS_LIST_FIELDS`, the `rowsAreNoop` signal-presence clause, and
`extractSignalFields` (which lives in `cell-drilldown.tsx`, not
`live-status.ts`) are all untouched. The tolerance logic is fully
self-contained (`toleratedSoftMissRow` + the taxonomy mirror) and layers
onto the existing badge path. The diff is `live-status.ts` (+119
additive), its test file, and one new drift test.
|
||
|
|
075807f24a |
feat(showcase): two-miss tolerance for soft probe errorClass on dashboard (pool-fleet step C)
Wire the starter-smoke probe's keyed errorClass into the dashboard cell-state
flip logic so transient SOFT failures (transport-error / aborted) get two-miss
tolerance: a single soft miss renders amber ~ ("transient, not yet actionable")
instead of flapping the cell red, and only flips red on a second consecutive
miss. HARD failures (smoke-failed) and untagged reds flip immediately.
The flip gate reuses the producer-maintained fail_count (the persisted
consecutive-red counter: 1 on green->red, incremented on sustained red, 0 on
red->green) so the dashboard stays a pure function of the current row — no
dashboard-side counter to thread or reset. Tolerance is applied as a
state->degraded downgrade in buildStarterBadge (same pattern as the existing
stale-green fold), keeping the change additive and self-contained.
Adds STARTER_FAILURE_CLASSES as a dashboard-side mirror of the harness
StarterFailureClass union (the dashboard imports only @/*), guarded by a new
starter-error-class-drift.test.ts set-equality lint against the harness source.
|
||
|
|
737dff7643 | style: auto-fix formatting | ||
|
|
ee61a99bab |
fix(showcase-harness): pnpm-packages discovery must skip out-of-prefix deep-glob patterns instead of throwing (version_drift enumerate failure)
The fleet pnpm-workspace.yaml carries a multi-segment glob (`examples/v2/*/apps/*`) that the strict matcher rejects with a SchemaError. Because that throw happens during enumeration — before the probe's `pathPrefix` filter applies — it aborted the entire version_drift discovery, surfacing as probe.discovery-enumerate-failed / discoveryFailed with 0 PB rows. Skip patterns whose static (wildcard-free) prefix cannot intersect the requested `pathPrefix` BEFORE validating their glob shape, so a deep-glob for an unrelated subtree no longer aborts a probe that only wants `packages/`. An unsupported pattern that DOES overlap the requested prefix still surfaces the strict-shape SchemaError, and behavior with no pathPrefix is unchanged. |
||
|
|
f85c5333bc | docs(showcase): add session-stack discipline + cleanup guidance to TESTING.md | ||
|
|
35063285b6 |
test(showcase): update generate_a2ui tests to A2UI v0.9 nested op format (#5834)
## Summary Follow-up to #5832 which updated `generate_a2ui.py` to emit A2UI v0.9 nested op format. The 6 tests in `showcase/integrations/langroid/tests/python/test_generate_a2ui.py` still asserted the old flat format and were failing in CI. - Updates assertions from flat format (`ops[0]["type"] == "create_surface"`, `ops[0]["surfaceId"]`, `ops[2]["data"]`) to v0.9 nested format (`ops[0]["version"] == "v0.9"`, `ops[0]["createSurface"]["surfaceId"]`, `ops[2]["updateDataModel"]["value"]`) - No assertions were weakened — all structural checks were preserved and extended to verify the full nested shape ## Red-Green Proof **RED** (before fix): 6 failed, 0 passed ``` FAILED tests/python/test_generate_a2ui.py::test_generate_a2ui_happy_path_returns_operations FAILED tests/python/test_generate_a2ui.py::test_generate_a2ui_happy_path_json_string_arguments_also_work FAILED tests/python/test_generate_a2ui.py::test_generate_a2ui_legacy_function_call_path FAILED tests/python/test_generate_a2ui.py::test_multi_tool_call_picks_first_and_warns FAILED tests/python/test_generate_a2ui.py::test_tool_call_missing_function_attr_falls_through_to_legacy_path FAILED tests/python/test_generate_a2ui.py::test_tool_call_with_function_arguments_none_falls_through_to_legacy_path ``` **GREEN** (after fix): 6 passed; full suite: **118 passed, 1 skipped** |
||
|
|
000b65ba2b |
chore: migrate github-actions updates to renovate (#5019)
Remove the Dependabot `github-actions` ecosystem config plus its companion `dependabot-auto-merge` and `dependabot-major-analysis` workflows. Renovate (via `renovate.json` → `local>CopilotKit/renovate`, Dependency Dashboard #592) now owns github-actions updates. Also cleans stale references to the deleted files: - `.github/zizmor.yml`: drop the `dangerous-triggers` ignores for the two dependabot workflows, remove the now-empty `dependabot-cooldown` rule, and update the `unpinned-uses` comment to reference Renovate. - `.github/workflows/security_zizmor.yml`: drop the `.github/dependabot.yml` path triggers. `.github/dependabot.yml` contained ONLY the github-actions ecosystem, so it is deleted in full. No npm/pip/docker or other ecosystem was touched — npm is untouched. Rebased onto current main; all CI green (zizmor pass, commitlint pass, build/types/unit/package-quality all pass). |
||
|
|
e752a1101c | style: auto-fix formatting | ||
|
|
439a35b7d4 | test(showcase): update generate_a2ui tests to A2UI v0.9 nested op format (follow-up to #5832) | ||
|
|
db667891a4 |
showcase(claude): add SDK demo parity (#5508)
## Summary - Productizes the Claude SDK Python and TypeScript showcase demos with LangGraph-parity frontends. - Wires the Claude demo backends through the official Claude Agent SDK/AG-UI adapter paths using `claude-sonnet-4.6`. - Keeps Claude integration docs hidden for this PR and excludes generated/authored docs artifacts from scope. ## Why The goal is to bring the productized LangGraph demo surface to Claude Agents SDKs without publishing integration docs in this pass. This keeps the PR focused on local showcase demos, runtime behavior, fixtures, and validation support. ## How - Ported the demo frontend surfaces and local shell-dojo support for Claude SDK Python/TypeScript. - Added official Claude SDK adapter/backend wiring plus real-Claude local compose support. - Updated Claude aimock fixtures and validation ratchets for the expanded demo set. - Set both Claude manifests to `docs_mode: hidden` and removed docs setup/snippet artifacts from the PR scope. |
||
|
|
8f693ca376 |
fix(showcase): emit A2UI v0.9 nested op format for a2ui-middleware v0.0.10 (#5832)
## Problem
\`@ag-ui/a2ui-middleware\` v0.0.10's \`getOperationSurfaceId()\` reads
only the A2UI v0.9 NESTED op format:
\`\`\`json
{"version": "v0.9", "createSurface": {"surfaceId": "...", "catalogId":
"..."}}
\`\`\`
The integrations were emitting the legacy FLAT format:
\`\`\`json
{"type": "create_surface", "surfaceId": "...", "catalogId": "..."}
\`\`\`
Result: all ops fell back to the \"default\" surface key → frontend
never mounted the named surface → \`surface-missing\` failure on
\`declarative-gen-ui\` across ~11 integrations.
## Fix
Convert all a2ui op builders and inline ops to the nested v0.9 format
in:
- \`tools/generate_a2ui.py\` — 9 integrations (agno, claude-sdk-python,
crewai-crews, langgraph-fastapi, langgraph-python, langroid, llamaindex,
pydantic-ai, strands)
- \`tools/search_flights.py\` — 11 integrations (ag2, agno,
claude-sdk-python, crewai-crews, langgraph-fastapi, langgraph-python,
langroid, llamaindex, ms-agent-python, pydantic-ai, strands)
- \`src/agents/a2ui_fixed_agent.py\` / \`a2ui_fixed.py\` /
\`beautiful_chat.py\` — agno, crewai-crews, langroid, pydantic-ai
Already-correct integrations skipped: google-adk,
ms-agent-python/generate\_a2ui.py, ag2/generate\_a2ui.py.
**Total: 25 files changed.**
## Verification
Zero flat-format ops remain in non-comment/non-test code. 96 occurrences
of \`"version": "v0.9"\` present in changed integrations (excluding
google-adk which was already correct).
## Red→Green
\`bin/showcase test\` runs against Docker containers — requires
infrastructure startup. The structural change is a mechanical
search-and-replace: \`getOperationSurfaceId()\` in \`a2ui-middleware\`
v0.0.10 reads \`op.createSurface?.surfaceId\` (nested), which is exactly
what these changes now emit. The old flat \`op.surfaceId\` path is not
read at all by the middleware, explaining the surface-missing fallback.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||
|
|
3288f1cdd2 |
fix(showcase): update multimodal aimock fixture keys to match current autoPrompt strings (#5831)
## Summary - Commit `7c3edca2b7` (May 11) changed the `autoPrompt` strings in all `sample-attachment-buttons.tsx` from `"describe the sample image"` / `"summarize the sample document"` to `"can you tell me what is in this demo image I just attached"` / `"can you tell me what is in this demo pdf I just attached"` — but never updated the 41 aimock fixture files - Every integration that uses the auto-send pattern was sending a message matching no fixture → strict MISS → 404 → agent error banner → `dom-missing` / `done-signal-missing` D6 timeouts - Fixed 19 `multimodal.json` D6 fixtures, 20 `agentic-chat.json` D6 fallback fixtures, 1 shared D5 `multimodal.json`, and the `split-fixtures.ts` router ## Red-Green Proof **RED (before fix) — `langgraph-typescript:multimodal --d6 --direct`:** ``` turn 1: TIMEOUT dom-missing (60s) — aimock 404, no assistant text rendered turn 2: TIMEOUT dom-missing (60s) — same ``` **GREEN (after fix) — `langgraph-typescript:multimodal --d6 --direct`:** ``` turn 1: PASS — assistant text "The attached image is the CopilotKit logo..." settled turn 2: PASS — assistant text with document summary settled ``` ## Remaining failures (out of scope, separate issues) - `ms-agent-python`, `crewai-crews`: Python backend `ChatClientException` when receiving binary (image/PDF) AG-UI content parts — same class as active `wt-pydantic-multimodal` worktree - `built-in-agent`, `claude-sdk-python`: DOM-inject only (no `agent.addMessage` / `copilotkit.runAgent` auto-send) — probe design mismatch, not a fixture issue ## Test plan - [x] `langgraph-typescript:multimodal --d6 --direct` RED before / GREEN after - [ ] D6 repro sweep after merge to confirm cluster clears for auto-send integrations 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
3b5474cb80 | style: auto-fix formatting | ||
|
|
bf3c02ef85 | fix(showcase): also convert pydantic-ai tools/ a2ui ops to v0.9 nested format | ||
|
|
c9907b07a2 | fix(showcase): emit A2UI v0.9 nested op format for a2ui-middleware v0.0.10 (gen-ui-declarative surface-missing) | ||
|
|
b2811f4feb |
fix(showcase): update multimodal fixture match keys to match actual autoPrompts
Commit
|
||
|
|
7c6c54007a |
chore: migrate github-actions updates to renovate
Remove the Dependabot github-actions ecosystem config and its companion auto-merge / major-analysis workflows. Renovate (via renovate.json -> local>CopilotKit/renovate, Dependency Dashboard #592) now owns github-actions updates. Also clean stale references to the deleted files: - zizmor.yml: drop dangerous-triggers ignores for the two dependabot workflows, remove the now-empty dependabot-cooldown rule, and update the unpinned-uses comment to reference Renovate. - security_zizmor.yml: drop the .github/dependabot.yml path trigger. npm and other ecosystems are untouched (dependabot.yml had only the github-actions ecosystem). |
||
|
|
1646fdf3d8 |
fix(react-core): externalize @copilotkit/web-components in the library build [ENT-1051]
Parity with the vue fix: the react-core drawer wrapper references the Lit
`<copilotkit-threads-drawer>` element from @copilotkit/web-components, which was
not externalized in the main ESM/CJS build entry — so tsdown inlined the whole
element + a second copy of lit-html into the react-core dist.
This bloats the library and breaks Vite-based React consumers with a duplicate
lit-html binding ("Identifier 'h' has already been declared"); webpack/Next
consumers dedupe it so it went unnoticed. Externalizing it (as @copilotkit/core,
@copilotkit/shared, @copilotkit/web-inspector, @copilotkit/a2ui-renderer already
are) resolves the import to the single real package at runtime. The self-contained
UMD builds intentionally keep it inlined.
react-core test suite: 1416/1416 pass.
|
||
|
|
0d551822dd |
fix(vue): externalize @copilotkit/web-components in the library build [ENT-1051]
The drawer wrapper loads the Lit `<copilotkit-threads-drawer>` element via a client-only dynamic import of @copilotkit/web-components/threads-drawer. That package was not in rollupOptions.external, so vite bundled the whole element + a second copy of lit-html inline into the vue dist (a ~1MB chunk). Under Vite/Nuxt consumers this inlined duplicate lit-html produced a fatal "Identifier 'h' has already been declared" parse error (h = lit-html's minified `html`), and it also risks double custom-element registration. Externalizing it (as @copilotkit/core, @copilotkit/shared, @copilotkit/web-inspector already are) makes the dynamic import resolve to the single real package at runtime. Verified live: the redesigned drawer now renders + operates in the Nuxt vue demo (licensed, real threads, kebab Archive/Delete, top-layer delete dialog, mobile off-canvas + sidebar-glyph close). Vue wrapper unit tests: 29/29 pass. |