mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
v1.62.2
12420 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1f36894bc5 |
feat(showcase): emit CVDIAG backend boundaries agent-side for strands-typescript
Emit CVDIAG backend boundary markers from the agent process for strands-typescript (byteLength fix on sseChunkByteLength), enable the emitter in docker-compose.local.yml, vendor src/cvdiag, and exclude tests from tsconfig. |
||
|
|
2ab8514671 |
feat(showcase): forward inbound X-AIMock-Strict end-to-end through strands-typescript two-process hop
Forward inbound X-AIMock-Strict header through the two-process strands-typescript hop (Next route -> agent -> sub-agent fetch), with null-guard on the forwarding proxy fetch and supporting unit tests. |
||
|
|
965c91d594 |
docs(showcase): A2UI catalog auto-inject + manual opt-out for generated frameworks
Bring the agnostic root A2UI docs up to the catalog-on-provider model and make every generated framework serve them consistently. - Root /generative-ui/a2ui (index, fixed-schema, dynamic-schema): lead with passing a catalog on the provider (auto-enables A2UI and auto-injects the generate_a2ui tool), add a manual opt-out section explaining the two pieces you wire yourself (the generate_a2ui agent tool and the A2UIMiddleware), and set fixed-schema to injectA2UITool: false since the agent owns the tool. - Flip langgraph-fastapi, strands, strands-typescript to docs_mode: generated so they serve the shared root A2UI docs 1:1 with langgraph-python. Generated frameworks covered: langgraph-python/fastapi/typescript, google-adk, strands, strands-typescript. deepagents (authored) is handled separately. |
||
|
|
f0a427852c |
fix(ci): scope format auto-commit trigger to PR files to avoid empty-commit failures (#5723)
## Root cause
The shared `static / quality` workflow's **`format`** job auto-formats
PR files and pushes a fixup commit. Two pieces were misaligned:
- **"Run formatter (fix on PR)"** set `format_fixed=true` from a
**whole-tree** `git diff --name-only` (old line ~126).
- **"Commit formatting fixes"** (gated on `format_fixed == 'true'`)
staged **only the scoped** PR files (`xargs -a
.pr-format-files.existing.txt git add --`, old line ~163) and ran a bare
`git commit` (old line ~164).
When a PR's own files are already formatter-clean **but the runner's
working tree is dirty for an unrelated reason** — e.g. an LFS smudge on
`examples/teams/appPackage/*.png` (declared `*.png filter=lfs`) — the
whole-tree diff falsely set `format_fixed=true`, the **scoped `git add`
staged nothing**, and `git commit` exited **1** ("nothing to commit") →
the job **failed**.
This intermittently red-flagged any PR depending on per-runner
LFS-smudge state (cf. #5715, where the format check failed and then
passed on re-run with no code change).
## The fix (minimal, `format` job only)
1. **Scope the trigger.** Set `format_fixed` from a **scoped** diff
(`git diff --quiet -- <scoped files>`) guarded by `[ -s
.pr-format-files.existing.txt ]` so unrelated working-tree drift no
longer triggers the commit path, and the empty case never degrades to a
whole-tree diff.
2. **Guard the commit.** After the scoped `git add`, treat an empty
index as a no-op (`git diff --cached --quiet && exit 0`) instead of
letting `git commit` exit 1.
The real behavior is preserved: when a scoped PR file genuinely needs
formatting, the job still stages, commits, and pushes the fix. No other
job is touched.
```diff
@@ Run formatter (fix on PR) @@
- if [ -n "$(git diff --name-only)" ]; then
+ # shellcheck disable=SC2046 # intentional split: each path is a
+ # separate `git diff` pathspec arg; the `-s` guard rules out the
+ # empty-arg (whole-tree) case, and PR paths never contain spaces.
+ if [ -s .pr-format-files.existing.txt ] && \
+ ! git diff --quiet -- $(cat .pr-format-files.existing.txt); then
echo "format_fixed=true" >> "$GITHUB_ENV"
fi
@@ Commit formatting fixes @@
xargs -a .pr-format-files.existing.txt git add --
+ if git diff --cached --quiet; then
+ echo "No scoped formatting changes to commit"
+ exit 0
+ fi
git commit -m "style: auto-fix formatting"
git push
```
## Local RED → GREEN proof
GitHub Actions can't run locally, so the job's **exact shell** was
reproduced in a throwaway `/tmp` git repo, using real `oxfmt@0.36` and
GNU `gxargs` for Linux-runner fidelity (`xargs -a` is GNU-only). Scoped
PR file = `app.js`; unrelated tracked file = `unrelated.bin`, left
**dirty** to simulate the LFS smudge.
**RED — current logic (whole-tree trigger + scoped add + bare commit):**
```
[RED] trigger env='format_fixed=true' # whole-tree diff saw unrelated.bin
[staged after scoped add]: '' # app.js already clean → nothing staged
nothing to commit
>>> RED git commit exit code: 1 (JOB FAILS)
```
**GREEN — fixed logic, same repo state:**
```
[GREEN] trigger env='' # scoped app.js clean; unrelated.bin ignored
[gate] format_fixed NOT set -> Commit step SKIPPED
>>> GREEN exit code: 0 (job passes, no false trigger)
```
**POSITIVE — scoped file genuinely needs formatting
(committed-unformatted `app.js`, `unrelated.bin` still dirty):**
```
after oxfmt — app.js: const z = 3;
[POS] trigger env='format_fixed=true'
[staged]: 'app.js' # only the scoped file
[POS] commit exit=0
[POS] git log: 46d4849 style: auto-fix formatting
[POS] worktree: ' M unrelated.bin' # unrelated drift left uncommitted
```
So: false-trigger failure is eliminated (RED→GREEN), and the real
auto-format path still commits & pushes exactly the scoped fix
(positive).
## Validation
- `python3 yaml.safe_load(...)` → YAML OK
- `actionlint .github/workflows/static_quality.yml` → exit 0 (baseline
on `main` is also clean; the SC2046 word-split warning introduced by the
scoped diff is suppressed with a narrowly-scoped, commented `shellcheck
disable` for the intentional split).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||
|
|
aeb8561d11 |
fix(ci): scope format auto-commit trigger to PR files to avoid empty-commit failures
The format job set format_fixed=true from a whole-tree git diff and then git-committed only the scoped PR files. When a PR's own files are already formatter-clean but the runner's working tree is dirty for an unrelated reason (e.g. an LFS smudge on examples/teams/appPackage/*.png, which are *.png filter=lfs), the whole-tree diff falsely triggered the commit path while the scoped git add staged nothing, so git commit exited 1 and failed the job. This intermittently red-flagged any PR depending on per-runner LFS-smudge state (cf. #5715). - Trigger format_fixed only when a SCOPED file actually changed. - Guard the commit so an empty staged set is a no-op (exit 0) instead of a hard failure. |
||
|
|
a17635a6c5 |
fix(showcase): backfill prod harness-workers into SSOT so image rebuilds bounce it (#5715)
## Root cause
Prod's `harness-workers` fleet worker runs a **stale `showcase-harness`
image** because it had **no `prod` env entry** in the railway-envs SSOT.
- The worker (`serviceId c2aa8a0b-350e-4b76-8541-3012dfac41d0`, prod
instance `7c48ee43-6df4-457b-b977-10f1f1ac1680`, `HARNESS_ROLE=worker`)
consumes the shared `showcase-harness` image via `imageOf: "harness"`.
- `expandImageConsumers(names, env)` is **env-aware**: a consumer only
enters an env's redeploy scope if it declares that env
(`redeploy-env.ts:278` — `if (!Object.hasOwn(entry.environments, env))
continue;`).
- Because the worker modeled **staging only**, a rebuilt
`showcase-harness:latest` bounced the prod control-plane but **silently
skipped the prod worker**, which kept its stale **2026-06-19** image.
- That stale image bakes a **1-demo `registry.json`** for
`ms-agent-harness-dotnet` (only `beautiful-chat`). The hourly
`e2e_demos` driver runs on the worker → resolves 1 demo → writes only
`e2e:ms-agent-harness-dotnet/beautiful-chat`. The other 38 feature rows
never exist in prod PocketBase → `resolveD3.exists === false` → `UI`
badge omitted → broken D3 rung collapses the ladder → **D0**.
(d5/d6 populate fully in prod because the D5/D6 drivers enumerate from a
**compiled-in** script registry, not the `registry.json` data file —
only `e2e_demos` is data-driven, which is why only `UI` was affected.)
## Fix
Backfill the live prod worker as a real `prod` env entry in
`scripts/railway-envs.ts` (real serviceInstance ID `7c48ee43…`), flip
`gateIgnore` off, and set `gateValidated: true`. The env-aware `imageOf`
expansion now pulls the prod worker into the **prod** redeploy scope on
every `showcase-harness` rebuild, so it can no longer drift onto a stale
image.
Also regenerates `railway-envs.generated.json` (Ruby/jq boundary
artifact) and the golden behavior-preservation fixture, and updates the
two gate-count assertions (`gateValidated` services 40→41;
`harness-workers` removed from the gateIgnore set).
## Local RED → GREEN proof
Failure surface: the real `expandImageConsumers("harness", "prod")`
against the real SSOT must include `harness-workers`.
**RED** (prod env entry absent from SSOT — the bug):
```
× includes harness-workers in the PROD redeploy scope when showcase-harness rebuilds
AssertionError: expected [ 'harness' ] to include 'harness-workers'
at __tests__/redeploy-env.harness-worker-prod-scope.test.ts:31:19
Tests 1 failed | 1 passed (2)
```
**GREEN** (after adding the prod `harness-workers` env entry):
```
✓ includes harness-workers in the PROD redeploy scope when showcase-harness rebuilds
✓ still includes harness-workers in the STAGING redeploy scope (no regression)
Tests 2 passed (2)
```
Full SSOT-dependent suite (golden snapshot, emit-json, image-ref gate,
promote closure, verify-matrix, redeploy-env): **140 passed**.
## Note (out of scope for this PR)
This SSOT change ensures the prod worker is bounced on **future**
rebuilds. The currently-live prod worker still needs a one-time
redeploy/restart onto the current `showcase-harness:latest` (39-demo
registry) to immediately backfill the 38 missing rows; that is an
operational step, not a code change.
|
||
|
|
f399afbbdc |
fix(showcase): conform claude-sdk-python, built-in-agent & ms-agent-harness-dotnet auth demos to langgraph-python gold standard (#5716)
## What Brings the **Authentication demo** of three integrations into 1:1 conformance with the `langgraph-python` (LGP) gold standard, completing the work started in #5713. The showcase Iron Law: LGP is the reference; every integration must have (1) identical tests, (2) near-identical frontends, (3) minimal backends, (4) per-integration fixtures. A conformance audit against LGP found 3 violators (the other 17 integrations already conform): | Integration | Violation | Fix | |---|---|---| | **claude-sdk-python** | Legacy auth-*first* shape: class `ChatErrorBoundary` + `lastError`, no `handleAuthError`, missing `sign-in-card.tsx`, divergent banner/hook | Ported `page.tsx`/`use-demo-auth.ts`/`auth-banner.tsx` **byte-identical** to LGP + new `sign-in-card.tsx`; added the shared shadcn primitives it lacked (`lib/utils.ts`, `components/ui/{button,card}.tsx`) + `radix-ui@^1.4.3` (matching the claude-sdk-typescript peer) | | **built-in-agent** | Distinct legacy variant: `ChatErrorBoundary`→`auth-demo-chat-boundary`, local 401-regex `onError`, auth-first hook | Normalized error-handling shape + hook to LGP; **preserved** the forced `<CopilotKitProvider>` (default-agent) + raw-Tailwind divergences (documented in a new `README.md`) | | **ms-agent-harness-dotnet** | Missing `tests/e2e/auth.spec.ts` (rule 1) | Added LGP's spec **byte-identical** (sha256 `603a68e5…`) | After this PR, all auth `page.tsx`/hook files are byte-identical to LGP except documented, forced per-integration wiring; all `auth.spec.ts` share LGP's sha256. ## Red–green proof (per integration, on the real probe surface) The shared `d5-auth.ts` probe accepts *either* `auth-demo-error` *or* `auth-demo-chat-boundary`, so it passes leniently on the legacy shape — the **discriminating gate is the byte-identical `auth.spec.ts`** (asserts unauth-first `SignInCard` + `auth-authenticate-button` + post-sign-out `auth-demo-error`): - **claude-sdk-python:** legacy frontend → `auth.spec.ts` **6/6 FAIL** (timeout on `auth-sign-in-button`); conformed → **6/6 PASS** (`next build` clean). - **built-in-agent:** legacy → 6/6 FAIL; conformed → 4 conformance assertions flip FAIL→PASS incl. unauthenticated-send surfaces `auth-demo-error` (`next build` clean). - **ms-agent-harness-dotnet:** spec absent (coverage gap) → added → `--d5 --isolate` green, full real-browser auth flow passes. ## Review 7-agent CR round + mandatory 7-agent confirmation round → **converged to zero findings** (correctness, conformance, types/build, deps/lockfile, tests, silent-failures, cross-integration regressions). 2 P2 conformance nits found and fixed (import-style alignment; restored `DEMO_TOKEN` so built-in-agent's hook is byte-identical to LGP). ## Known limitation (non-blocking, pre-existing infra) The GHA workflow `test_e2e-showcase-on-demand.yml` runs Playwright only for slugs with a Python agent, so the **built-in-agent / ms-agent-harness-dotnet auth specs are not executed in PR CI**. This is a pre-existing infra gap (those integrations have no Python agent), not introduced here. Coverage **does** exist post-merge: the Railway staging **d6 harness** enumerates services language-agnostically and runs the auth probe against live `/demos/auth` for both — verified, and it's what drives their dashboard cells green at D6. A follow-up to add a non-Python e2e execution path is warranted. ## Notes (pre-existing, not introduced) - `npm ci`/`npm install` in `showcase/integrations/claude-sdk-python` shows a micromark/unified desync and a zod/openai ERESOLVE peer conflict — both reproduce identically at the base commit `ab85b939ac` (independent of the `radix-ui` add); handled by the existing `--legacy-peer-deps` path. Ref: #5713 (original post-sign-out auth rejection fix). |
||
|
|
b706b9e84c |
test(showcase): wire a2ui-recovery into the d5/d6 harness fleet
Add a d5-a2ui-recovery probe so the A2UI error-recovery demo runs on every PR via the d5/d6 fleet harness, not only the manual on-demand workflow. - New probe d5-a2ui-recovery.ts drives both pills in one session: HEAL asserts >=2 newly-mounted declarative-metric tiles and no hard-failure card; EXHAUST asserts the "Couldn't generate the UI" card appears and no surface paints. Deltas (vs a pre-send baseline) keep the two mutually-exclusive negatives correct across the shared session. The transient "Retrying..." label is not asserted (timing-flaky). - Prompts are sent as typed input, keyed per integration slug, mirroring each slug's suggestions.ts message verbatim. The recovery prompts are unique per slug because the inner render_a2ui calls carry no x-aimock-context; a typed message is byte-identical to the pill dispatch, so it matches the same fixture. Sending via input (not a preFill pill click) lets the runner snapshot its run-lifecycle baseline first, avoiding a false done-signal-missing failure. - Register a2ui-recovery in d5-registry, map it in d5-feature-mapping, add its representative fixture, and mirror the mapping in the dashboard CATALOG_TO_D5_KEY (kept in lock-step via the drift test). Verified green locally on both recovery paths: langgraph-python (backend-owned get_a2ui_tools) and strands (auto-inject middleware). |
||
|
|
3349413332 |
feat(showcase): A2UI Error Recovery demo for langgraph + strands (#5720)
## What Ports the google-adk **A2UI Error Recovery** demo to five more frameworks: - LangGraph (Python) - LangGraph (FastAPI) - LangGraph (TypeScript) - AWS Strands (Python) - AWS Strands (TypeScript) Each integration gets a dedicated recovery agent, a scoped runtime route, the demo page/chat/suggestions, a `manifest.yaml` feature + demo entry, aimock D6 fixtures, an e2e spec, and a QA doc. The demo reuses each integration's existing `declarative-gen-ui` catalog (`declarative-gen-ui-catalog`), so no new components are introduced. ## How recovery is wired (two paths) - **LangGraph (py/fastapi/ts):** backend-owned. The graph owns `generate_a2ui` via `ag_ui_langgraph.get_a2ui_tools` / `@ag-ui/langgraph` `getA2UITools` with `recovery`, and the route sets `injectA2UITool: false` so the runtime does not double-inject. (langgraph-python adds `ag-ui-langgraph==0.0.41` to requirements.) - **Strands (py/ts):** the adapter runs the toolkit validate-retry loop on its auto-inject path, so the recovery agent is a dedicated clone of the dynamic agent with no explicit tool. Two pills per demo: - **Recover a bad render** (heal): first render is structurally invalid, the loop retries, the second render is valid and paints. - **Show an unrecoverable failure** (exhaust): every attempt is invalid, the loop hits the cap and returns `a2ui_recovery_exhausted`, rendered as a graceful failure. ## Fixture design notes - The toolkit's `validate_a2ui_components` rejects the whole surface on any invalid entry (no single-pass sanitize), so the heal is a genuine invalid-then-valid sequence staged via aimock `sequenceIndex` (0 invalid, 1 valid). ADK's stringified `parse_and_fix` single-pass heal is ADK-middleware-specific and does not apply on the langgraph/strands toolkit loop. - The inner `render_a2ui` sub-agent call carries no `x-aimock-context` header (only the harness sets it), and aimock loads every framework's D6 dir into one process. To avoid cross-framework fixture collisions AND to make the demos fire for real browser (dojo) traffic, each framework uses unique recovery prompts and the fixtures carry no `context` match field (userMessage alone disambiguates). - Also hardens the strands `declarative-gen-ui` composition guide to name the exact catalog component (`Metric`, not `MetricTile`), which a real LLM was mis-naming. ## Verification - langgraph-python recovery e2e: 3/3 (page load, heal paints, exhaust shows the failure UI). - Both backend paths confirmed in a real browser via the local dojo (langgraph-python + strands heal and paint). - Strands declarative + recovery confirmed grounded under a real LLM (correct `declarative-gen-ui-catalog` + real components). - `showcase/scripts` suite green (836 tests), incl. updated `generate-catalog` (langgraph-python wired 36 to 37) and `aimock-fixtures` collision checks. Manifest validation 20/20. ## Known limitation The heal stages invalid-then-valid via aimock `sequenceIndex`, whose match counter only resets with a fresh `x-test-id` (which the browser does not send). On a long-lived/shared aimock a repeat heal click advances past the staged pair. Exhaust is fully repeatable. A follow-up can send a per-session `x-test-id` so the heal is repeatable and multi-user safe. ADK is intentionally left as-is (its recovery fixtures remain context-scoped). |
||
|
|
a7bc444814 |
fix(showcase): bump @ag-ui/langgraph 0.0.39 -> 0.0.42 for lg-ts recovery render
getA2UITools changed signature: 0.0.39 is getA2UITools(model, options) (positional),
0.0.42 is getA2UITools(params) (single object). The agent code (recovery-agent.ts
and graph.ts) calls the single-object form, but the override pinned 0.0.39, so the
whole params object was treated as the model -> e.bindTools undefined -> the tool
returned {"error":"Provided model does not support bindTools"} and the render
sub-agent never ran. Bumping the override to 0.0.42 aligns the dep with the API the
code uses; verified the recovery graph now emits a healed a2ui_operations surface
(invalid seq0 -> valid seq1) and fires the render_a2ui sub-agent.
|
||
|
|
ab8b39a9bc |
fix(showcase): register a2ui_recovery graph in langgraph-typescript agent server
The lg-ts agent serves graphs from a hardcoded graphSpec in src/agent/server.mjs (mirrors langgraph.json). The a2ui_recovery graph was added to langgraph.json but not graphSpec, so the langgraph server returned 404 on its runs and the demo never dispatched. Add a2ui_recovery to graphSpec. NOTE: this fixes graph REGISTRATION. The lg-ts recovery render does not yet fire (getA2UITools 0.0.39 returns from generate_a2ui without invoking the render sub-agent); tracked separately, likely needs @ag-ui/langgraph >= 0.0.42. |
||
|
|
b985449e50 |
feat(showcase): add A2UI Error Recovery demo for langgraph + strands
Port the google-adk a2ui-recovery demo to langgraph (python, fastapi, typescript) and aws-strands (python, typescript). Each ships a dedicated recovery agent, route, demo page/chat/suggestions, manifest entry, aimock d6 fixtures, e2e spec, and QA doc. Backend-owned recovery on langgraph via get_a2ui_tools / getA2UITools (injectA2UITool=false); auto-inject recovery on the strands adapter path. Heal stages an invalid-then-valid render via aimock sequenceIndex (the toolkit validate->retry loop rejects the whole surface, so a single-pass parse_and_fix heal is ADK-specific and does not apply here). Recovery prompts are unique per framework and the fixtures carry no context match field, so they fire for real browser (dojo) traffic, not just the harness. Also harden the strands declarative-gen-ui composition guide to name the exact catalog component (Metric, not MetricTile) and update the generate-catalog + aimock-fixtures test expectations. |
||
|
|
47e255f670 |
feat(react-core): add client-side onAction interceptor to v2 A2UI renderer
Add an optional onAction interceptor to createA2UIMessageRenderer so apps can handle A2UI actions client-side (e.g. navigate) instead of forwarding every action to the agent. - onAction(action, forward) runs before forwarding. Return null to handle client-side and stop forwarding, return a modified action to forward it, or return undefined to forward unchanged. - Threaded from createA2UIMessageRenderer options through ReactSurfaceHost. - Extracted runA2UIAction helper for deterministic unit testing. - Default behavior is unchanged when onAction is not supplied. |
||
|
|
eed342cf1c |
feat(bot): typed Renderable, durable <Message onReaction>, and richer JSX props (#5718)
## What
Types the bot UI surface and broadens the cross-platform component
vocabulary, adding **only** capabilities more than one adapter can
express.
### 1. `ui` typed as `Renderable` (was `unknown`)
The `Thread` interface's `post` / `update` / `awaitChoice` /
`postEphemeral` now accept `Renderable` instead of `unknown`. JSX is
type-checked at the call site, and the `{ raw }` escape hatch is the
explicit way out when the JSX vocabulary doesn't fit. (The concrete
`Thread` class already used `Renderable` — only the interface leaked
`unknown`.)
### 2. `<Message onReaction>` — per-message reaction callback
```tsx
<Message onReaction={(emoji, r) => (r.added && emoji === "bug" ? triage() : ack())}>
Deploy finished — react 🐛 to file a bug
</Message>
```
- First arg is the emoji (matches the common `r => r === "bug"` shape);
second carries `{ added, user, rawEmoji, messageId }`. Fires on add
**and** remove.
- The handler is stripped from the IR before it reaches the adapter,
then associated with the posted message's id; inbound reactions route to
it.
- **Durable on the same terms as a component `onClick`:** a `{
component, props }` snapshot is persisted and the component is
re-rendered to re-derive the handler after a restart (when the
`<Message>` comes from a registered component + a durable `store`).
Inline handlers route in-process only — identical degradation to an
inline `onClick`.
### 3. `Button.url` link buttons
`<Button url="…">` → native link buttons on **Slack, Discord, Teams,
Telegram** (Telegram already supported it; now typed + wired
everywhere).
### 4. `Field.label`
`<Field label="Status">Online</Field>` — **Discord and Telegram already
read this prop untyped** (latent type gap); now typed and additionally
rendered on Slack.
### 5. `Select.multi` multi-select
`<Select multi onSelect={(vals) => …}>` — **Slack**
(`multi_static_select` in an input block, since Slack forbids it in
`actions` blocks; decodes `selected_options` → `string[]`), **Discord**
(min/max values; decodes via `interaction.component` bounds), **Teams**
(`isMultiSelect`). Telegram/WhatsApp degrade to single-select.
`onSelect` widened to `ClickHandler<string | string[]>`.
### Intentionally dropped (Slack-only)
`Button.confirm`, `Image.title`,
`Input.label`/`initialValue`/`required`, `Select.initialValue` —
single-platform, excluded by design.
## Testing
- All 7 affected packages (`bot-ui`, `bot`, `bot-slack`, `bot-discord`,
`bot-teams`, `bot-telegram`, `bot-whatsapp`) build clean and pass their
full suites.
- New coverage: reaction routing + durability cold-resolve (`bot`), link
button / field label / multi-select render + source-ordering
(`bot-slack`), link button + multi-select bounds + multi decode
(`bot-discord`/`bot-slack`), `Action.OpenUrl` + `isMultiSelect`
(`bot-teams`).
- Two code-review passes (incl. an adversarial one on the durability
change); all findings addressed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||
|
|
f97d0c5213 |
feat(bot): give <Message onReaction> a thread + messageRef (onClick parity)
The reaction handler now receives the conversation `thread` and an
update-capable `messageRef` for the reacted message — the same surface an
`onClick` gets via `ctx.thread`/`ctx.message.ref`. A reaction can now post new
UI (`thread.post`), swap the message in place (`thread.update(messageRef, …)`),
run the agent, or block on a human choice (`thread.awaitChoice`, HITL).
- bot-ui: `MessageReaction` gains `thread` and `messageRef`.
- platform-adapter: `IncomingReaction` gains an optional adapter-provided
`messageRef` (engine falls back to `{ id: messageId }`).
- create-bot: threads `thread` + `messageRef` into both the global
`ReactionEvent` and the per-message handler.
- Slack/Discord/Telegram reaction decoders emit a platform-specific,
update-capable `messageRef` (channel+ts / channelId+id / chatId+messageId).
|
||
|
|
d6b01d379a |
feat(bot): typed Renderable, durable <Message onReaction>, and richer JSX props
Type the bot UI surface and broaden the cross-platform component vocabulary,
adding only capabilities that more than one adapter can express.
- bot-ui: type the `Thread` interface's `ui` params (`post`/`update`/
`awaitChoice`/`postEphemeral`) as `Renderable` instead of `unknown`, so JSX
is checked at the call site and the `{ raw }` escape hatch is explicit.
- `<Message onReaction>`: per-message reaction callback `(emoji, { added, user,
rawEmoji, messageId })`. Stripped from the IR before it reaches the adapter,
routed from reaction ingress by message id. Durable on the same terms as a
component `onClick` — a `{ component, props }` snapshot is persisted and the
component is re-rendered to re-derive the handler after a restart; inline
handlers route in-process only.
- `Button.url` link buttons — Slack, Discord, Teams, Telegram.
- `Field.label` — typed (Discord and Telegram already consumed it untyped);
newly rendered on Slack.
- `Select.multi` multi-select — Slack (`multi_static_select` in an input block
+ `selected_options` decode), Discord (min/max values + `component` bounds
decode), Teams (`isMultiSelect`); Telegram/WhatsApp degrade to single-select.
Slack-only capabilities (Button.confirm, Image.title, Input label/initialValue/
required, Select.initialValue) were intentionally left out.
|
||
|
|
ce0ea8bd20 |
test(showcase): update SSOT tests for dual-env prod harness-workers
The prod harness-workers backfill (
|
||
|
|
c04318b193 | fix(showcase): align auth conformance import style + restore DEMO_TOKEN to match gold | ||
|
|
2983bbc69d |
fix(showcase): conform claude-sdk-python auth demo to langgraph-python gold standard
claude-sdk-python was the last integration still on the legacy auth-first shape: an authenticated-on-load page guarded by a class-based `ChatErrorBoundary`, a `useDemoAuth` exposing `authenticate`/`authenticated`, an `auth-banner` with an `onAuthenticate` prop and bespoke buttons, and NO `sign-in-card`. The byte-identical `auth.spec.ts` (which asserts an unauthenticated-first `SignInCard` with `auth-sign-in-button` / `auth-demo-token`) therefore failed all six cases against it. Port the four auth files verbatim from the langgraph-python gold standard (adapting nothing — the per-integration wiring, `agent="auth-demo"` and `runtimeUrl="/api/copilotkit-auth"`, was already identical): - use-demo-auth.ts: unauth-first, localStorage-backed, exposes `isAuthenticated`/`hasEverSignedIn`/`signIn`/`signOut`. - page.tsx: render `SignInCard` until first sign-in, then keep `<CopilotKit>` mounted across the sign-out cycle; shared `handleAuthError` on BOTH the provider and agent-scoped `<CopilotChat onError>`; clear-on-auth effect; amber `auth-demo-error` surface. - auth-banner.tsx: shared `<Button>`, `onSignIn`/`onSignOut` props. - sign-in-card.tsx: new, ported from the gold standard. Add the shared shadcn primitives the gold-standard frontend depends on and which claude-sdk-python was missing (`src/lib/utils.ts`, `src/components/ui/button.tsx`, `src/components/ui/card.tsx`) plus the `radix-ui` dependency they require, matching the claude-sdk-typescript peer. Red/green on the real surfaces: against the legacy frontend `auth.spec.ts` fails 6/6 (every test times out waiting for `auth-sign-in-button`); against the rebuilt frontend it passes 6/6 and the `--d5 --isolate` auth probe is green. |
||
|
|
2d451e3d66 |
fix(showcase): conform built-in-agent auth demo to langgraph-python gold
built-in-agent was the lone integration left on the legacy auth variant
when
|
||
|
|
9cb62acf94 |
fix(showcase): add byte-identical auth e2e spec to ms-agent-harness-dotnet
The Authentication demo frontend conforms to the langgraph-python gold standard but was missing its tests/e2e/auth.spec.ts (conformance rule 1: e2e tests must be byte-identical to LGP). Add the LGP auth.spec.ts verbatim (sha256 match) so the auth flow is e2e-covered. Verified green via showcase test ms-agent-harness-dotnet:auth --d5. |
||
|
|
e88d01a99f |
fix(showcase): backfill prod harness-workers into SSOT so image rebuilds bounce it
The prod `harness-workers` fleet worker (serviceId c2aa8a0b-350e-4b76-8541-3012dfac41d0, instance 7c48ee43-6df4-457b-b977-10f1f1ac1680) runs the shared `showcase-harness` image (`imageOf: "harness"`) but had NO `prod` env entry in the railway-envs SSOT. `expandImageConsumers` is env-aware — a consumer only joins an env's redeploy scope if it declares that env — so a rebuilt `showcase-harness:latest` bounced the prod control-plane but SILENTLY SKIPPED the prod worker, leaving it pinned to a stale 2026-06-19 image. That stale worker image carries a 1-demo `registry.json` for `ms-agent-harness-dotnet` (only `beautiful-chat`), so the hourly `e2e_demos` driver running on it produced only 1 of 39 `e2e:` rows in prod PocketBase. The other 38 feature rows were absent → `resolveD3` exists=false → `UI` badge omitted → broken D3 rung → D0. Backfill the live prod worker as a `prod` env entry (real serviceInstance ID), flip `gateIgnore` off, and set `gateValidated: true` so the env-aware `imageOf` expansion now pulls the prod worker into the prod redeploy scope on every `showcase-harness` rebuild. Regenerate the emitted JSON + golden fixture and update the two gate-count assertions accordingly. |
||
|
|
ab85b939ac |
fix(showcase): render post-sign-out auth rejection across showcase integrations (#5713)
## What
Fixes the showcase **Authentication** demo across **19 of 20
integrations** — the feature row that was red (D4) across the board on
the depth dashboard.
## Root cause
The post-sign-out rejection banner never rendered. After sign-out, the
agent run is correctly rejected with a 401 (`agent_run_failed`), but
that event is delivered **only on the agent-scoped `<CopilotChat
onError>` channel** — never the provider-level `<CopilotKit onError>`
the demos were listening on. So the auth D5/D6 probe's rejection-surface
assertion failed, capping the cell at **D4** across integrations.
## Fix (per integration, one file each: `auth/page.tsx`)
- Wire a stable `handleAuthError` onto the agent-scoped `<CopilotChat
onError>` (keeping the provider handler).
- Key the rejection-banner render off auth-error **state alone** with a
clear-on-auth effect (removes the `&& !isAuthenticated` post-sign-out
cross-slice race).
- Harden the banner's message fallback against nullish error events (no
more literal "null"/"undefined").
## Scope: 19 of 20
- **built-in-agent excluded** — its auth probe already passes (renders
via its `ChatErrorBoundary`, an accepted probe surface). Verified GREEN
unmodified.
- **strands** initially looked green but that was an **infra-aborted
probe (false-green)**; a re-probe with deps provisioned showed RED, and
it was fixed + verified GREEN — hence 19, not 18.
- **claude-sdk-python** adapted to its legacy error-boundary shape (same
fix, `lastError`/local `onError` naming).
- The originally-proposed harness change was **dropped** — investigation
confirmed it a no-op (the "representative-gating" cause was a
misdiagnosis; `auth` was already emitted for all integrations).
## Evidence
- Per-integration real-probe **red-green** (`bin/showcase test
<slug>:auth --d6`, RED→GREEN).
- **Value-test**: 4/4 non-representative cells flip green **end-to-end
(D5 and D6)** on the real probe.
- **CR**: converged (3 rounds, 11–15 agents/round) to zero bucket-(a);
**Procedure 3** promotion audit clean.
## Follow-ups (not in this PR)
- claude-sdk-python idiomatic-shape migration (parity).
- Banner `<code>` chip conditional render + tighten the hand-rolled
`AuthErrorEvent` type to the SDK `onError` type.
- `enableInspector={false}` divergence (only the 2 dotnet demos carry
it).
- built-in-agent error-boundary parity.
|
||
|
|
5057efce1a |
fix(showcase): render post-sign-out auth rejection across showcase integrations
The auth demo capped at D4 across integrations because the post-sign-out rejection banner never rendered. The post-sign-out `agent_run_failed` is delivered only on the agent-scoped `<CopilotChat onError>` channel — never the provider-level `<CopilotKit onError>` the demos listened on — so the D5/D6 auth probe's rejection-surface assertion failed and the cell was capped at D4. Fix (applied to all 19 integrations whose auth demo reproduced the bug): wire a stable `handleAuthError` onto the agent-scoped `<CopilotChat onError>` (keeping the provider handler), key the error surface off auth-error STATE alone with a clear-on-auth effect (removing the `&& !isAuthenticated` cross-slice race), and harden the rejection-banner message fallback against nullish error events. Scope: 19 of 20 integrations. built-in-agent already passes (renders via its ChatErrorBoundary); claude-sdk-python adapted to its legacy/error-boundary shape. |
||
|
|
54920c65be |
fix(showcase/oracle-agent-memory): background-persist lag + inline client-side booking
Sync the three travel-concierge UX fixes from the source repo (jerelvelarde/oracle-cookbook#3) into the showcase: - agent/concierge/server.py: persist memory in a serialized background task so the SSE stream closes at RUN_FINISHED instead of blocking ~2-13s on memory extraction + reconciliation (the post-generation loading lag). Adds a FastAPI lifespan drain + background-task failure surfacing. - frontend/.../FlightOptions.tsx: "Select this flight" now drives confirm -> book entirely client-side, so the booking confirm card renders inline in view instead of off-screen via a fresh agent turn. The conversational book_flight HITL path is unchanged. - frontend/e2e: add a covering test for the inline booking flow; rename sendAndPersist -> sendAndAwaitRun (stream-close is no longer a "memory written" signal now that persistence is backgrounded). oxfmt --check + tsc --noEmit clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
74ff2db70d |
chore: release bot-teams v0.1.0 (#5710)
## Release bot-teams v0.1.0 **Scope:** `bot-teams` | **Bump:** `minor` --- ### How this release process works 1. **This PR was created automatically** by the "release / create-pr" workflow. It bumped the `bot-teams` packages to `0.1.0` and generated AI-enhanced release notes. 2. **CI runs on this PR** — the full test suite (unit tests, lint, type checks, build) must pass before merging. This is the review gate. 3. **Review the release notes** in `release-notes.md` in this PR. If a Notion draft was created, you can edit the release notes there before merging. 4. **When this PR is merged**, the `release / publish` workflow automatically: - Builds all packages - Publishes the `bot-teams` packages to npm at version `0.1.0` - Creates git tag `bot-teams/v0.1.0` - Creates a GitHub Release with the final release notes ### Before merging - [ ] CI is green (tests, lint, types, build) - [ ] Version bumps look correct - [ ] Release notes are accurate (edit in Notion if a draft was created) --- > **Do not merge until CI is fully green.** The full test suite runs automatically on this PR.bot-teams/v0.1.0 |
||
|
|
34aecf1c97 | chore: release bot-teams v0.1.0 | ||
|
|
807666fe98 |
Add bot-teams release scope (#5708)
## Summary - Add `bot-teams` to the canary, stable, and publish release workflows - Register `@copilotkit/bot-teams` in release configuration - Extend the release scope type to include `bot-teams` and `bot-whatsapp` ## Testing - Not run (not requested) |
||
|
|
5f9c2404d1 | chore: format banking showcase | ||
|
|
a1b1792ef0 | Add bot-teams to release scopes | ||
|
|
02bf7f934b |
build(core): raise build heap ceiling to 8GB via cross-env (#5328)
## What Bake a heap ceiling into the `@copilotkit/core` build script: ```diff -"build": "tsdown", +"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 tsdown", ``` (`cross-env` is already used elsewhere in the repo; added here as a core devDependency.) ## Why The core build (tsdown + dts generation) has a ~3.45GB working set and OOMs under `nx run-many`, where it competes with sibling builds and GC falls behind. This mostly bites **local development**. Agents (and humans) routinely have to prefix commits with `NODE_OPTIONS=--max-old-space-size=8192` just to get the build through pre-commit hooks. CI already sets this flag at the workflow level, so baking it into the build script means every invocation path gets the same headroom without anyone remembering to add it: local lefthook hooks, `nx run-many`, a direct `pnpm build`, CI, and Windows (hence `cross-env`). Scoped to core only, since it's the single package that OOMs. |
||
|
|
92ba4d4c82 |
feat(bot-teams): Microsoft Teams PlatformAdapter with Adaptive Cards, streamed replies, and HITL (#5497)
## `@copilotkit/bot-teams` Microsoft Teams adapter for the platform-agnostic [`@copilotkit/bot`](../tree/main/packages/bot) engine, plus a runnable `examples/teams` demo. Same `PlatformAdapter` contract as [`@copilotkit/bot-slack`](../tree/main/packages/bot-slack): write the bot once with `createBot`, run it on Teams by adding `teams()`. Built on the Microsoft 365 Agents SDK, and reviewable in the M365 Agents Playground with no Microsoft credentials. It renders the `bot-ui` JSX vocabulary as Adaptive Cards (including native Teams charts via a `<Chart>` component, no headless browser), streams replies by edit, gates writes with HITL approval buttons, and reads/writes files. The `examples/teams` demo wires a `BuiltInAgent` that auto-renders cards, charts uploaded CSVs, and gates announcements. ## Files: how they reach the bot Teams hands a bot uploaded files differently per scope: - **1:1 (personal) chat**: delivered inline (`file.download.info`); needs `supportsFiles: true` (set). No extra setup. - **Channel / group chat**: Teams does **not** send the file to bots, so the adapter fetches it via Microsoft Graph (read the channel message for the SharePoint reference, download via `/shares`). Requires `Files.Read.All` (application, admin consent) + `ChannelMessage.Read.Group` (RSC, consented by a team owner at install). - **Anywhere**: pasting the data as text always works. Without Graph consent the bot degrades gracefully to this. ## Docs Microsoft Teams guide rewrite: #5615. |
||
|
|
b1ca211bb7 |
docs(teams): rewrite Microsoft Teams guide for @copilotkit/bot-teams (#5615)
Rewrites the Microsoft Teams guide for the new `@copilotkit/bot-teams` adapter added in #5497. The existing guide documented an older API (`createTeamsAgentBot`, a local "Teams DevTools" bridge) that shipped through copilotkitnext and no longer matches the package. This rewrites it to mirror the Slack guide: - Quickstart with `createBot` + the `teams()` adapter, verified in the M365 Agents Playground (no Microsoft account) - Interactive Adaptive Cards with inline `onClick` handlers - A human-approval gate via `thread.awaitChoice` - Splitting the bot from its agent over AG-UI - Azure sideloading into real Teams (tunnel, Entra app, Azure Bot, manifest) Also refreshes the frontend picker summary (Playground, not DevTools). ### Merge ordering This depends on #5497. The Teams guide is an `earlyAccess` page, so it should not go live until `@copilotkit/bot-teams` actually publishes. **Merge this after #5497 ships the package.** |
||
|
|
6835fe5908 |
docs(showcase): document promoting a staging-only integration to production (#5706)
## What Adds a **"Promoting a Staging-Only Integration to Production"** section to `showcase/RAILWAY.md` — the staging-first → promote-later procedure that was undocumented and caused the `strands-typescript` D6 false-red fixed in #5705. The procedure previously survived only as a comment inside the SSOT (`railway-envs.ts`); there was no human-facing SOP. Cross-links `INTEGRATION-CHECKLIST.md` §B (single-shot bring-up) both ways. ## Where placed `showcase/RAILWAY.md`, immediately after "Adding a New Railway Service" and before "Environment IDs" — the natural flow (provision a new service → promote a staging-only one to prod). Matches the file's existing `##`/`###` heading depth and voice. ## Accuracy gate — each prescribed step → its grounding evidence Verified against the post-#5705 repo state (branched off `origin/main`, which includes the #5705 merge `d78fc07afd`) and the #5705 diff. | Prescribed step / claim | Evidence | |---|---| | "When this applies": `gateValidated:false`, `gateIgnore:true`, staging-only env map, `legacyJsonCompat.domains.prod` placeholder | #5705 diff of `railway-envs.ts` (the removed lines) — borrowed staging host `showcase-strands-typescript-staging.up.railway.app` | | Gotcha: promote only moves digests to an EXISTING prod service, does not provision | `showcase_promote.yml` header "Promotes the staging-tested digest … to prod"; `bin/README.md` defers "new-service provisioning" to RAILWAY.md (line 6); no provisioning subcommand in `bin/railway` | | Gotcha: D6 false-reds whole column (404 → empty `backendUrl` → `goto-error`) | diag doc: prod `/api/health` 404, `health:strands-typescript` totalItems:0, `backendUrl:""`, `errorClass=goto-error` on every cell | | Provision via `environmentStageChanges` + `environmentPatchCommitStaged`, mirror peer prod TS `showcase-claude-sdk-typescript`; materialized instance `8a50728e…` | remediation doc Step 2; #5705 commit body | | SSOT edit: add `prod` env block w/ real `instanceId`, `gateValidated:true`, drop `gateIgnore`, remove `legacyJsonCompat` | #5705 diff of `railway-envs.ts` (exact before/after); `gateValidated` JSDoc at `railway-envs.ts:205-216` ("new SSOT services MUST land `gateValidated:true`"; `gateIgnore` only for untracked/domainless/single-env) | | `npx tsx showcase/scripts/emit-railway-envs-json.ts` (CI `--check`) | `emit-railway-envs-json.ts` header; `showcase_reconcile.yml:60`, `showcase_promote.yml:132` | | Regenerate golden fixture `__tests__/fixtures/railway-envs.golden.json`; intentional change | path exists; `railway-envs.golden.test.ts` is a behavior-preservation guard (header) | | `npx tsx showcase/scripts/sync-promote-service-options.ts` (CI `--check`) | `showcase_promote.yml:32` generated-block marker; `showcase_validate.yml:399`; `strands-typescript` now at `showcase_promote.yml:75` | | `npx tsx showcase/scripts/verify-railway-image-refs.ts` gate | `showcase_build.yml:341` | | `pnpm exec vitest run` from `showcase/`; `verify-railway-image-refs.test.ts` / `redeploy-env.test.ts` counts change | `showcase_validate.yml:362`; both test files exist | | Secrets via prod env var set / aimock, no inline secrets; `OPENAI_BASE_URL` asserted prod→prod | remediation doc Step 2 (no prod secret sourced, `sk-aim` placeholder); `railway-envs.ts:1167-1170` serviceRef comment | | Verify GREEN: `/api/health` 200, prod PocketBase `health:<slug>` record, D6 flips next hourly `:40` tick | remediation doc Steps 5-6 (health 404→200, record absent→present, cells flipped at 20:45 tick); diag doc dimension "d6", `d6-all-pills-e2e` hourly :40 | ## `showcase_deploy.yml` staleness — CONFIRMED, already fixed on main The doc-gap analysis flagged that docs referenced `showcase_deploy.yml` as the prod/build path. **Verified against the workflows:** `showcase_deploy.yml` is now "Showcase: Verify Deploy" (staging health-verification gate — "Push-to-main redeploys staging only"); the `ALL_SERVICES` build/push matrix lives in `showcase_build.yml` ("Build & Push"); the prod path is `showcase_promote.yml` ("Promote (staging → prod)"). **`RAILWAY.md` on main was already corrected** (it cites `showcase_build.yml` for the matrix and describes `showcase_deploy.yml` as the verify gate), so there is no stale reference left in RAILWAY.md to fix. The one remaining stale reference is **`INTEGRATION-CHECKLIST.md` §B.3**, which still names `showcase_deploy.yml` for the build job — left as a precise TODO in the new section (out of scope for this doc edit; flagged for a follow-up). ## Checks - Referenced file paths all verified to exist (bin/README.md, INTEGRATION-CHECKLIST.md, the 4 scripts, golden fixture + test, both `.test.ts`). - Commit passed commitlint (`docs(showcase):` prefix). lefthook's pre-commit oxlint/oxfmt excludes markdown, so no code-lint applies; no markdownlint config in repo. Does NOT merge. Branch only. |
||
|
|
e9fce5b8bb | style: auto-fix formatting | ||
|
|
4396f71ab0 |
docs(showcase): document promoting a staging-only integration to production
Adds a "Promoting a Staging-Only Integration to Production" section to showcase/RAILWAY.md — the staging-first -> promote-later procedure that was undocumented and caused the strands-typescript D6 false-red (PR #5705). The procedure previously survived only as a comment inside the SSOT (railway-envs.ts); there was no human-facing SOP. The new section is a start-to-finish checklist grounded in the PR #5705 worked example: - When it applies (gateValidated:false, gateIgnore:true, staging-only env map, legacyJsonCompat prod placeholder). - The critical gotcha up front: the promote pipeline (showcase_promote.yml / bin/railway promote) only moves digests to a prod service that ALREADY exists; it does NOT provision a new prod serviceInstance. Until that instance exists, D6 false-reds the whole column (404 -> empty backendUrl -> goto-error on every cell). - Ordered steps: provision the prod serviceInstance out-of-band (environmentStageChanges + environmentPatchCommitStaged, mirroring a peer prod TS service); edit the SSOT (add prod env block, gateValidated:true, drop gateIgnore, remove legacyJsonCompat); regenerate derived artifacts (emit-railway-envs-json.ts, golden fixture, sync-promote-service-options.ts) and run the gate (verify-railway-image-refs.ts + vitest); prod secrets via the prod env var set / aimock (no inline secrets); verify GREEN (/api/health 200, prod PocketBase health record, D6 flips on the next hourly :40 tick). Cross-links INTEGRATION-CHECKLIST.md §B (single-shot bring-up) both ways. Leaves a precise TODO that §B.3 still names the stale showcase_deploy.yml for the build matrix (the RAILWAY.md references were already corrected on main). |
||
|
|
ca920066ac |
build(core): raise build heap ceiling to 8GB via cross-env
The @copilotkit/core build (tsdown with dts generation) has a working set of about 3.45GB. Node's default old-space ceiling is roughly 4GB and CI pins NODE_OPTIONS to 4096, leaving almost no headroom. Under nx run-many the build competes for CPU with sibling builds, GC falls behind, and the process tips over the heap limit. nx flags it as a flaky task and pre-commit/CI builds fail intermittently with a V8 heap OOM. Bake the ceiling into the core build script with cross-env so every invocation path gets consistent headroom: local lefthook hooks, nx run-many, CI, and direct pnpm build, on every platform including Windows. 8192 matches the value already used by the e2e workflow and gives roughly 2x headroom over the working set. Scoped to core only; it is the single package that OOMs. |
||
|
|
d78fc07afd |
fix(showcase): promote strands-typescript to production (dual-env SSOT) (#5705)
## What
The `showcase-strands-typescript` integration was **staging-only** — it
had no production Railway serviceInstance. As a result the prod D6
dashboard column was a uniform **false-red**: every cell reported
`errorClass=goto-error` with `backendUrl=""`, because the harness had no
prod health record to hand the probe, so Playwright navigated a bare
relative path (`page.goto("/demos/…")`) and Chromium rejected it as an
invalid URL.
This brings the service into production so the prod D6 column mirrors
**staging's actual pattern** (the false goto-error reds disappear;
whatever staging genuinely passes, prod passes). Fixing staging's ~7
genuinely-broken feature cells is **out of scope**.
## Infra provisioned (production)
- **prod serviceInstance** `8a50728e-6119-43c4-b59c-d9535b6717a4`
created (via `environmentStageChanges` + `environmentPatchCommitStaged`,
mirroring the peer prod TS service `showcase-claude-sdk-typescript`).
- **domain** `showcase-strands-typescript-production.up.railway.app`,
**healthcheck** `/api/health`, image **pinned** to the GHCR `@sha256`
digest, `restartPolicyType` `ON_FAILURE`, auto-updates `minor`.
- **app vars** mirror staging's set with `OPENAI_BASE_URL` repointed at
prod aimock (`https://showcase-aimock-production.up.railway.app/v1`). No
real prod secret needed: the agent routes 100% to aimock and staging's
`OPENAI_API_KEY` is the `sk-aim…` aimock placeholder — so nothing secret
was sourced.
- deployment **SUCCESS**.
## SSOT / code changes
- `railway-envs.ts` — convert the `showcase-strands-typescript` block to
the dual-env `showcase-strands` shape: add the `prod` env entry with the
real instanceId, `gateValidated:true`, drop `gateIgnore`, remove the
`legacyJsonCompat` prod-domain placeholder, update the leading comment.
- `railway-envs.generated.json` — regenerated (real prod
instanceId/domain, `probe.prod:true`, prod healthcheck; service moves
into the promote closure, tier 2).
- `railway-envs.golden.json` — regenerated to include the new prod
`(service, env)` pair (intentional behavior change, not a refactor
regression).
- `showcase_promote.yml` — promote dropdown regenerated to list
`strands-typescript`.
- `verify-railway-image-refs.test.ts` / `redeploy-env.test.ts` — update
the gateValidated/scope counts (39→40 gate targets; prod default scope
38→39) and the now-stale "staging-only" comments.
## RED → GREEN proof (live prod)
| Check | RED (before) | GREEN (after) |
|---|---|---|
| prod `/api/health` | **404** (staging 200) | **200**
`{"status":"ok","integration":"strands-typescript"}` |
| prod PocketBase `health:strands-typescript` | **totalItems:0** |
**present** — `status:200`, `url=…-production…/api/health` |
| `d6:strands-typescript/agentic-chat` | `errorClass=goto-error`,
`backendUrl=""` | _flips on next D6 tick_ (staging: fail_count:0, 3
turns) |
| `d6:strands-typescript/prebuilt-popup` | `errorClass=goto-error`,
`backendUrl=""` | _flips on next D6 tick_ (staging: fail_count:0) |
| `d6:strands-typescript/chat-css` | `errorClass=goto-error`,
`backendUrl=""` | _flips on next D6 tick_ (staging: fail_count:0) |
`verify-railway-image-refs.ts` now reports **✓ 80 env-scoped instances
verified**. The D6 cell flip lands on the prod harness's next hourly
`d6-all-pills-e2e` tick (the harness already discovered the new prod
health record); this comment will be updated with the post-flip
payloads.
## Quality gates
`scripts` vitest: **2110 passing**. `emit --check`,
`sync-promote-service-options --check`, `verify-railway-image-refs.ts`:
all clean. oxlint: 0. tsc: changed files clean (one pre-existing
unrelated error in `generate-search-index.ts` on `main`).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||
|
|
64f3863872 |
fix(skills): correct copilotkit-upgrade v2 package paths (#5541)
`copilotkit-upgrade` was the one skill the v2 migration skipped. It routed users to packages that don't exist, so following it produces a non-installable app. Root cause: the skill was generated against a non-existent `packages/v1/*` / `packages/v2/*` layout. The repo is flat and v2 ships from the `/v2` subpath of the same packages. ## Fix (pure docs) Corrected every package/import reference across `SKILL.md` plus the three reference files against the live exports maps: | Phantom in skill | Real target | | --- | --- | | `@copilotkit/react` | `@copilotkit/react-core/v2` | | `@copilotkit/agent` | `@copilotkit/runtime/v2` (`BuiltInAgent`) | | bare `@copilotkit/runtime` for v2 APIs | `@copilotkit/runtime/v2` | | `createCopilotEndpointExpress` (doesn't exist) | `createCopilotExpressHandler` (`@copilotkit/runtime/v2/express`) | Also fixed the install commands and regenerated `sources.md` against the real flat layout. ## Verification Grep sweep shows zero remaining phantom references, and every v2 symbol the skill now cites was confirmed to exist in the live source barrels. |
||
|
|
27dcf4a404 |
fix(showcase): promote strands-typescript to production (dual-env SSOT)
The showcase-strands-typescript integration was staging-only: it had no production Railway serviceInstance, so the prod D6 dashboard column showed a uniform false-red (every cell errorClass=goto-error, backendUrl="") — the probe navigated a bare relative path because the harness had no prod health record / backendUrl to discover. Provisions the prod serviceInstance (8a50728e-6119-43c4-b59c-d9535b6717a4, domain showcase-strands-typescript-production.up.railway.app, healthcheck /api/health, image pinned to the GHCR @sha256 digest, OPENAI_BASE_URL at prod aimock) and brings the SSOT to the dual-env showcase-strands shape: - railway-envs.ts: add the prod env entry with the real instanceId, gateValidated:true, drop gateIgnore, remove the legacyJsonCompat prod-domain placeholder. - railway-envs.generated.json: regenerated (prod instanceId/domain, probe.prod true, prod healthcheck; moved into the promote closure, tier 2). - railway-envs.golden.json: regenerated to include the new prod (service,env) pair (intentional behavior change, not a refactor regression). - showcase_promote.yml: dropdown regenerated to list strands-typescript. - verify-railway-image-refs.test.ts / redeploy-env.test.ts: update the gateValidated/scope counts (39->40 gate targets, prod default 38->39) and the now-stale staging-only comments. RED->GREEN (live prod): BEFORE /api/health 404, prod PocketBase health:strands-typescript totalItems:0, the 3 named D6 cells all errorClass=goto-error backendUrl="". AFTER /api/health 200, prod PocketBase health:strands-typescript present (status:200, valid url), verify-railway-image-refs OK 80 instances. |
||
|
|
1aa40c7994 |
feat(examples): Microsoft Teams bot example with CSV→chart, file uploads, and HITL
Adds examples/teams: a runnable Teams bot built on @copilotkit/bot-teams. Demonstrates rendering charts from uploaded CSV files, inbound file handling over the Graph API, and human-in-the-loop confirmation flows, plus the Teams app manifest and packaging scripts. |
||
|
|
7a309fc210 |
feat(bot-teams): Microsoft Teams PlatformAdapter with Adaptive Cards, streamed replies, and HITL
Adds the @copilotkit/bot-teams package: a PlatformAdapter that bridges the CopilotKit runtime to Microsoft Teams. Renders agent output as Adaptive Cards (including native <Chart> components via bot-ui), streams replies with a typing-indicator heartbeat, supports human-in-the-loop confirmations, and handles inbound files via the Graph API with channel file support. |
||
|
|
b7412ab3d5 |
docs(showcase): correct factual drift in TESTING.md + RAILWAY.md docs (claim audit) (#5704)
## Summary
Claim-by-claim accuracy audit of the three showcase ops docs surfaced 32
WRONG + 2 STALE factual claims. This PR fixes every actionable one,
surgically and consistent with each doc's tone. Each fix was re-verified
against the repo / live Railway before writing (RED = current text
wrong; GREEN = new text matches reality). UNVERIFIABLE claims were left
unchanged.
**Supersedes #5702** — that PR only corrected the aimock recursion
warning; this PR applies BOTH that correction AND the §5 startCommand
code-block fix.
### showcase/TESTING.md (15 fixes)
- QA file count: ~498 across **20** packages (was "153 files, 17
packages"). `ls showcase/integrations/*/qa/*.md | wc -l` => 498.
- "PASS 17 packages" → "PASS 20 packages" (9 cells). `ls -d
showcase/integrations/*/ | grep -v _shared | wc -l` => 20.
- Per-package E2E: ~33–41 specs/package (was "9"); removed fabricated
`renderer-selector.spec.ts` claim + Known-gap #5.
- Aimock fixtures: removed non-existent `feature-parity.json`; fixed
`smoke.json` → `shared/smoke.json`.
- python-sdk: Python 3.10–3.14 matrix (was 3.12).
- Removed `.changeset` from `test_e2e-dojo` and `test_e2e-legacy-v1`
path filters (not in triggers).
- Corrected `showcase_validate.yml` path filter.
- Removed non-existent `test_smoke-starter-deployed.yml` matrix row +
footnote.
- **test/unit paths-ignore reversal:** `test/unit` does NOT fire on
`showcase/**` or `sdk-python/**` PRs — both ARE in `test_unit.yml`
paths-ignore (the doc claimed the opposite). Added `static/quality` to
the sdk-python PR list; fixed danger path to
`sdk-python/copilotkit/langgraph_agent.py`.
- Classic branch protection is fully **disabled** (API returns 404) —
there is no live `required_status_checks.contexts` array.
- Depot footnote: runner is `depot-ubuntu-24.04-4` and most workflows
(not just dojo) run on Depot.
### showcase/RAILWAY.md (8 fixes)
- **Pending Service Provisioning section was fully stale:**
`built-in-agent` is provisioned with real `railway_id f4f8371a-...` in
the `ALL_SERVICES` matrix in `showcase_build.yml`; `grep
PLACEHOLDER-CREATE-RAILWAY-SERVICE` => 0 matches repo-wide. Rewrote
section, removed the fail-loud paragraph.
- Fabricated `generate-starters.ts` → real `extract-starter.ts` /
`provision-starter-fleet.ts`.
- **autoUpdates 41→24/40:** live prod env (b14919f4) has 40 services,
only **24** with `autoUpdates.type=minor`, 16 with none; minor services
carry an all-day/every-day `schedule` array (not "no schedule").
- **showcase_build-vs-deploy attribution:** `serviceInstanceRedeploy`
(via `redeploy-env.ts`) runs from `showcase_build.yml` ("Build & Push");
`showcase_deploy.yml` ("Verify Deploy") only health-checks it. Fixed in
Auto-Updates section, Known Quirks, and the "Add to ALL_SERVICES matrix"
step.
- `smoke.yml` is auto-discovery driven — no edit needed to add a
`showcase-*` service.
### showcase/aimock/RAILWAY.md (6 fixes)
- §1: runs the `showcase-aimock` wrapper image (not the bare
`@copilotkit/aimock` container).
- §3: published `linux/amd64` only (arm64 not published).
- §4 browser-pool: `BROWSER_POOL_MAX_CONTEXTS=24` (was 40); D5 peak
4×4=16 contexts ~4.8 GB; knob is `FEATURE_CONCURRENCY_D6` in
`d6-all-pills.ts` (no `e2e-deep.ts` exists).
- **§5 startCommand:** both live prod (b14919f4) and staging (8edfef02)
aimock instances run a single `--fixtures /fixtures` (before the
provider flags), not three subdir flags. Replaced the code block + flag
table row and **corrected the recursion warning** (a single `--fixtures
/fixtures` loads the whole baked tree — verified live, healthy).
- §7: verify curl payload `"ping"` → `"Respond with exactly: OK"` (the
actual `smoke.json` match string).
## RED→GREEN evidence (highest-impact)
- **qa count:** `ls showcase/integrations/*/qa/*.md | wc -l` => 498; 20
packages.
- **test/unit paths-ignore:** `test_unit.yml` paths-ignore includes both
`showcase/**` and `sdk-python/**` — directly reverses the doc's
parenthetical.
- **showcase_build vs deploy:** `showcase_build.yml:810 npx tsx
showcase/scripts/redeploy-env.ts staging ...`; `showcase_deploy.yml`
name = "Showcase: Verify Deploy", no redeploy call.
- **autoUpdates 24/40:** live GraphQL on prod env b14919f4 — 40
services, 24 minor, 16 none; minor schedule = 7 days, startHour 0 /
endHour 24.
- **aimock §5 startCommand:** live GraphQL on prod + staging — `node
/app/dist/cli.js --proxy-only --fixtures /fixtures --provider-openai ...
--validate-on-load --host 0.0.0.0 --port 4010`.
Audit ref: claim-by-claim audit @ commit
|
||
|
|
a80b3515a9 | docs(shell-docs): sync OpenBox recipe to full-parity demo | ||
|
|
a6c22de23f | docs(showcase): complete Depot-runner footnote (all depot workflows + showcase/eval -16 runner) | ||
|
|
154b0d98bd | docs(showcase): address adversarial-review findings (QA count 19, single --fixtures ref, Depot runtime, autoUpdates schedule) | ||
|
|
3755ef34a7 | docs(showcase): correct factual drift in TESTING.md + RAILWAY.md docs (claim audit) | ||
|
|
b3530eac37 |
feat: CopilotDrawer — promote threads management to SDK (shadow-DOM web component + React vertical) (#5701)
## CopilotDrawer — promote threads management to the SDK Implements the hardened [CopilotDrawer design (Threads v2)](https://app.notion.com/p/3883aa3818528190b5d1f9b6ba26dca0). Promotes thread listing/selection/creation/rename/archive/delete out of the example apps and into the SDK as a framework-agnostic shadow-DOM web component plus a React vertical (Angular wrapper included; Vue/RN to follow). > Note: assumes [CopilotKit#5624](https://github.com/CopilotKit/CopilotKit/pull/5624) is already merged (built on top of its `unarchiveThread` surface). ### What's in this PR (by area) - **A — `@copilotkit/core`**: thread store with optimistic add/rename/archive/delete (delete rolls back on reject), session-guarded results (a superseded session can't leak an error/rollback), `startNewThread` (non-explicit), `refetchThreads`, `getServerState`/`getServerSnapshot`, per-store memoized selectors, and **non-fatal** realtime diagnostics (a channel-join or metadata-credential failure warns and keeps the already-loaded list rather than blanking it). Registers a `"threads"` feature for catalog gating. - **B — `@copilotkit/web-components` (new package)**: the shadow-DOM `<copilotkit-drawer>` Lit element — self-contained styles with **build-time token sync** from react-core's `@theme` (checked-in `generated-tokens.ts`, drift-guarded), slot projection for custom rows (`slot="row:{id}"`), license/upsell gating, active/all filtering, and a mobile modal. The element owns view-state; the host owns domain state. Multi-format build: ESM/CJS externalize `lit` (peer dep); the **UMD bundle inlines `lit`** so the advertised `<script>`/CDN path is self-contained. - **C/E/F — `@copilotkit/react-core`**: `<CopilotDrawer>` wrapper (custom-element interop, two-pronged license gate, scoped chat-input focus-return, registration-gated header launcher); `CopilotChatConfigurationProvider` gains `drawerOpen` + `<768px` modal/drawer mutual-exclusion + a **non-explicit active-thread setter** so a bare `<CopilotDrawer>` connects to the picked thread and shows the welcome screen on "+ New" with no host wiring; `useThreads` gains an `{enabled}` gate and a list-only error channel. - **D — `@copilotkit/angular`**: `injectThreads` signal wrapper — reactive runtime context (re-dispatches when `wsUrl`/`threadEndpoints` arrive), enabled-gated single-slot registration, bound result methods, and error/loading parity with react-core. ### Quality - Tests: core, web-components (46), react-core (1370), angular (106) — all green; `tsc --noEmit` clean across all four; full builds pass; oxfmt clean. - **Code review:** 4 rounds of an unbiased 7-slot review fleet (15 agents/round) — 3 fix rounds + 1 confirming round that converged with zero load-bearing findings. The convergence lever was applied per the requester: the deep focus/overlay-accessibility rework, pagination wiring, and Angular parity gaps are deferred to the documented follow-ups below rather than blocking this PR. ### Follow-ups (intentionally deferred) - **Accessibility (own PR + a11y test harness):** the mobile-modal Tab trap now runs at the host level and contains the backdrop (fixed in review); remaining a11y work — focus-restore to the opener on close, scroll-lock reference-counting, confirm-delete `alertdialog` focus management, the default-open-on-mobile scroll-lock, and a dedicated a11y test harness. - **Pagination wiring:** the element exposes `hasMore`/fetch-more UI but nothing triggers it and the wrapper doesn't pass a `limit`; also clear a recovered next-page error and cancel an in-flight next-page on filter change. - **Error surfacing:** wire `onError` to a transient toast and separate the mutation-error channel from the list-load error (the list no longer blanks on a failed mutation — the rollback restores the row — but mutation failures are currently silent beyond the rejected promise). - **Angular parity:** mirror react-core's `startNewThread` config-error dismissal, add a list-only `listError`, and refresh intelligence/threadEndpoints in `updateRuntime` on a runtime-URL change. - **Reach:** Vue + React Native wrappers; de-fork the ~18 example apps onto the SDK drawer; nested-provider active-thread sharing. - **Minor:** migrate the react-core/angular wrappers to per-store `store.selectors`; honor-or-drop `ThreadEnvironment.fetch` (RxJS `fromFetch` ignores it; both wrappers pass `globalThis.fetch`, so no behavior change today); `threadId=""` controlled-predicate edge; reveal animation for slotted rows; empty-string-timestamp coalescing; `_visibleThreads` recompute; empty-name `aria-label`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
225a3fc646 |
fix(web-components): contain mobile-modal focus including the backdrop
The mobile drawer is a modal dialog, but the Tab trap was bound to .root only, while the backdrop button renders as a sibling OUTSIDE .root — so Tab from the backdrop (or any out-of-root node) could escape the modal. Move the trap to the host keydown listener (keydown is composed, so it catches the backdrop too) and include the backdrop in the focusable cycle; keep initial focus on the first real control. Adds a regression test for backdrop/outside-root Tab containment. |
||
|
|
d07888c611 |
fix(web-components): make the UMD lit-inlining CI-safe
The UMD bundle inlines lit for a self-contained CDN <script>; inlining lit also pulls in its runtime deps (@lit/reactive-element, lit-html, lit-element, @lit-labs/ssr-dom-shim). tsdown flags that transitive bundling as 'unintended' and promotes the warning to a fatal error under CI, breaking @copilotkit/web-components:build (and every dependent). Declare all lit packages in noExternal and set inlineOnly: false to mark the bundling intentional. |