Commit Graph

12131 Commits

Author SHA1 Message Date
Tyler Slaton 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
2026-06-25 15:49:41 -07:00
tylerslaton 34aecf1c97 chore: release bot-teams v0.1.0 2026-06-25 22:48:57 +00:00
Tyler Slaton 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)
2026-06-25 15:43:58 -07:00
Tyler Slaton 5f9c2404d1 chore: format banking showcase 2026-06-25 15:35:45 -07:00
Tyler Slaton a1b1792ef0 Add bot-teams to release scopes 2026-06-25 15:30:05 -07:00
Tyler Slaton 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.
2026-06-25 15:26:52 -07:00
Tyler Slaton 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.
2026-06-25 15:24:21 -07:00
Tyler Slaton 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.**
2026-06-25 15:18:13 -07:00
Jordan Ritter 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.
2026-06-25 14:38:48 -07:00
github-actions[bot] e9fce5b8bb style: auto-fix formatting 2026-06-25 21:07:23 +00:00
Jordan Ritter 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).
2026-06-25 14:06:04 -07:00
Austin Merrick 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.
2026-06-25 14:01:39 -07:00
Jordan Ritter 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)
2026-06-25 13:34:24 -07:00
Tyler Slaton 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.
2026-06-25 13:29:40 -07:00
Jordan Ritter 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.
2026-06-25 13:22:31 -07:00
Austin Merrick 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.
2026-06-25 12:56:04 -07:00
Austin Merrick 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.
2026-06-25 12:56:04 -07:00
Jordan Ritter 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 99f24fa168. Full fix-log
retained out-of-tree at `aimock-docs-fix-log.md`.

## Test plan
- [ ] `oxfmt --check` passes on the three docs (verified locally).
- [ ] Docs-only change; no code/CI behavior affected.
2026-06-25 11:01:31 -07:00
Jordan Ritter a6c22de23f docs(showcase): complete Depot-runner footnote (all depot workflows + showcase/eval -16 runner) 2026-06-25 10:38:42 -07:00
Jordan Ritter 154b0d98bd docs(showcase): address adversarial-review findings (QA count 19, single --fixtures ref, Depot runtime, autoUpdates schedule) 2026-06-25 10:33:29 -07:00
Jordan Ritter 3755ef34a7 docs(showcase): correct factual drift in TESTING.md + RAILWAY.md docs (claim audit) 2026-06-25 10:22:07 -07:00
Ben Taylor 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)
2026-06-25 12:13:38 -05:00
Benjamin Taylor 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.
2026-06-25 11:28:32 -05:00
Benjamin Taylor 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.
2026-06-25 11:28:32 -05:00
github-actions[bot] ba85b2f0a6 style: auto-fix formatting 2026-06-25 15:45:46 +00:00
Benjamin Taylor 19e8a1f8ae ci: allowlist packages/web-components/tsdown.config.ts for the build-config gate 2026-06-25 10:42:47 -05:00
Benjamin Taylor 4b3c15b6dd chore: update lockfile for @copilotkit/web-components and its lit dependency 2026-06-25 10:42:47 -05:00
Benjamin Taylor 36d0bb636f feat(angular): add injectThreads for the threads SDK
Angular signal wrapper over the core thread store: reactive runtime context
(re-dispatches when wsUrl/threadEndpoints arrive), enabled-gated single-slot
registration, bound result methods, and synthesized error/loading parity with
react-core.
2026-06-25 10:42:47 -05:00
Benjamin Taylor 47b7476702 feat(react-core): add CopilotDrawer wrapper and chat drawer-awareness
Add <CopilotDrawer> (interops with the shadow-DOM element, two-pronged license
gate, scoped chat-input focus return, registration-gated header launcher) and
extend CopilotChatConfigurationProvider with drawerOpen + mobile mutual
exclusion + a non-explicit active-thread setter so a bare drawer connects to the
picked thread and resets on New with no host wiring. useThreads gains an
{enabled} gate and a list-only error channel.
2026-06-25 10:42:46 -05:00
Benjamin Taylor a7a6aa0c29 feat(web-components): add @copilotkit/web-components with the <copilotkit-drawer> element
Shadow-DOM Lit element that renders the threads drawer: self-contained styles
with build-time token sync from react-core's theme, slot projection for custom
rows, license/upsell gating, filtering, and a mobile modal. View-state lives on
the element; domain state stays with the host. Multi-format build (ESM/CJS
externalize lit as a peer dep; UMD inlines lit so the CDN <script> path is
self-contained).
2026-06-25 10:42:46 -05:00
Benjamin Taylor fc1fa82c33 feat(core): thread store with optimistic mutations and realtime, plus threads feature
Add the thread store (optimistic add/rename/archive/delete with delete
rollback-on-reject, session-guarded results, startNewThread/refetchThreads,
getServerState/getServerSnapshot, per-store memoized selectors, and non-fatal
realtime diagnostics for channel-join and metadata-credential failures) and
register the "threads" feature so the SDK can gate thread management.
2026-06-25 10:42:46 -05:00
Alem Tuzlak 99f24fa168 fix(examples): keep Slack agent running when an MCP server is unreachable (#5699)
## What

Makes the Slack triage agent runtime (`examples/slack/runtime.ts`)
resilient to MCP server failures. A down, misconfigured, or hanging MCP
server no longer takes the whole agent down.

## Why

The factory connected its MCP clients with `Promise.all`, and
`@tanstack/ai-mcp`'s `createMCPClient` connects eagerly. So a single
unreachable server (bad API key, sidecar not running, network hang)
rejected the entire `Promise.all`, the factory threw, and the run
surfaced as a fatal:

```
⚠️ Agent error: Failed to connect to MCP server
```

The bot was effectively dead for **every** request — even ones that need
no MCP at all (plain chat, web search, rendering). This was hit in
practice when `.env` carried a placeholder `LINEAR_API_KEY` / missing
Notion sidecar.

## How

- Connect each MCP server **independently** via `Promise.allSettled`,
each wrapped in an 8s timeout (a hanging server can't stall the turn; a
late post-timeout failure is swallowed so it can't become an unhandled
rejection).
- **Drop** the servers that fail; the agent runs with whatever's left —
web search, card/chart rendering, thread reading, and any MCP that did
connect.
- Inject a per-turn system note listing the unavailable sources,
instructing the model to mention the outage **only if the user's request
needs that source**, and never to invent data or claim a write
succeeded.
- Connections are re-attempted each turn, so a transient outage
**self-heals** on the next message.

## Testing

- `pnpm check-types` on the example — clean for `runtime.ts` (one
pre-existing unrelated error in `app/demo-restart.tsx` re: unbuilt
`@copilotkit/bot-store-redis`).
- Manual: ran the example with a deliberately bad `LINEAR_API_KEY`.
General questions answer normally (Linear dropped, logged `MCP "Linear"
unavailable this turn`); asking for Linear yields a graceful
"temporarily unreachable" instead of a fatal agent error.
2026-06-25 16:29:50 +02:00
Alem Tuzlak cd11583884 fix(examples): keep slack agent running when an MCP server is unreachable
The triage runtime connected its MCP clients with Promise.all, so a single
unreachable/misconfigured server (bad key, sidecar down, hang) rejected the
whole run and surfaced as a fatal "⚠️ Agent error: Failed to connect to
MCP server" — the bot was dead for every request, even ones needing no MCP.

Connect each server independently (Promise.allSettled) with an 8s timeout,
drop the ones that fail, and let the agent run with whatever's left (web search,
rendering, thread reading, and any MCP that did connect). A per-turn system note
lists any down sources so the model only tells the user a source is unreachable
if they actually ask for it, and never invents data. Connections are retried
each turn, so a transient outage self-heals.
2026-06-25 16:11:44 +02:00
Alem Tuzlak d85abb781d fix(bot-slack): native setStatus thinking indicator instead of hourglass (#5698)
## What

Replaces the homemade animated `⏳` "thinking"
placeholder (a posted message whose dots cycled via `setInterval`) with
Slack's **native** `assistant.threads.setStatus` "is thinking…"
indicator — and generalizes it from assistant-pane-only to **every
thread-anchored reply**: channel @-mentions, channel threads, DMs, and
the pane.

## Why

The hourglass was an extra post-then-delete message that looked nothing
like the native loading state other Slack agents show. Slack has since
relaxed `assistant.threads.setStatus` to accept the ordinary
**`chat:write`** scope (not just `assistant:write`), specifically so
channel-based apps can show AI loading states in channels and DMs
([docs](https://docs.slack.dev/reference/methods/assistant.threads.setStatus/)).
The adapter already used `setStatus` for the pane — this just extends it
everywhere and deletes the placeholder.

## How

- **`event-renderer.ts`** — delete the hourglass machinery
(`startThinking`/`claimThinking`/`clearThinking`/`setInterval`);
generalize `setPaneStatus`/`clearPaneStatus` →
`setStatus`/`clearStatus`. New `status?: { threadTs, isPane, config }`
arg: `statusMode` drives the thinking indicator; `isPane` only selects
tool-progress surface (pane composer status vs `task_update` timeline /
`🔧` rows — both unchanged).
- **`adapter.ts`** — `createRunRenderer` builds `status` for any target
with a thread anchor (`threadTs ?? statusTs`); `assistant: false` opts
out everywhere.
- **`types.ts` / `slack-listener.ts`** — flat DMs (no `thread_ts`) carry
the inbound message `ts` as the status anchor; replies still post flat.
- **README** — document the broadened status + the `chat:write`
relaxation.

## Testing

- `nx run-many -t test build` for `bot-slack` — **248 tests pass**,
build clean, oxlint 0 errors, oxfmt clean.
- New tests cover non-pane channel-thread status, the `🔧` tool
path staying on non-pane, and the DM `statusTs` anchor.
- Net **−149 lines**.

> Note: the DM path anchors `setStatus` to a non-assistant DM message ts
— the one piece only verifiable against the live Slack API. Channel
@-mentions already carry a real `thread_ts`.
2026-06-25 16:09:13 +02:00
Alem Tuzlak b3680616f4 fix(bot-slack): use native setStatus thinking indicator instead of hourglass
Replace the homemade animated ⏳ placeholder (a posted
message with setInterval-cycled dots) with Slack's native
assistant.threads.setStatus "is thinking..." indicator on every thread-anchored
reply -- channel @-mentions, channel threads, DMs, and the assistant pane -- not
just the pane. Slack now accepts setStatus under chat:write (not only
assistant:write), so it works for channel-based apps. Flat DMs carry the inbound
message ts as the status anchor. assistant:false opts out everywhere.
2026-06-25 15:34:05 +02:00
Ran Shemtov 76b653f39e feat(showcase): bump CopilotKit 1.61.2 + A2UI catalog auto-inject (#5611) (#5697)
## What

Bumps the canonical CopilotKit pin across **all showcase integrations +
shell** from `1.61.1` to `1.61.2`, and adopts the new DX from
[CopilotKit#5611](https://github.com/CopilotKit/CopilotKit/pull/5611) in
the A2UI dynamic demos.

`1.61.1` (published 2026-06-23 21:28) predates the #5611 merge
(2026-06-24 13:36), so the feature could not reach showcase without a
release. `1.61.2` carries it (verified `providerA2UIHasCatalog` in
runtime + `a2uiCatalogAvailable` in react-core).

## Changes

- **Version bump** — `showcase/scripts/showcase-canonical-pins.json`
`1.61.1` → `1.61.2`; every `@copilotkit/*` pin in 22 `package.json` + 22
`package-lock.json` regenerated to match.
- **Feature adoption** — removed the now-redundant runtime `a2ui` block
(`injectA2UITool: true` + `defaultCatalogId:
"declarative-gen-ui-catalog"`) from the **declarative-gen-ui (A2UI
dynamic)** route of:
  - `langgraph-python`, `langgraph-fastapi`, `langgraph-typescript`
  - `strands`, `strands-typescript`
  - `google-adk`

The catalog passed to the provider (`<CopilotKit a2ui={{ catalog }}>`)
now auto-enables A2UI and defaults tool injection on. The forwarded
catalog supplies its own `catalogId` (the sdk-js A2UI middleware
auto-derives `defaultCatalogId` from it), so the old "Catalog not found"
fallback no longer applies. Stale comments updated accordingly.

## Verification

- `validate-pins` drift ratchet **unchanged** (count 38, same hash) —
`@copilotkit/*` pins are not in the FAIL set, so no re-baseline needed.
- **langgraph-python D6 `gen-ui-declarative`: green** end-to-end (real
container, aimock replay) with the runtime `a2ui` config removed — no
"Catalog not found".
- strands D6 (runtime-middleware path) verification in flight; langgraph
siblings + ADK covered by parity (identical edit + middleware path).

The 2 reds in the langgraph-python D6 run (`auth`,
`gen-ui-open-advanced`) are unrelated demos with timing-based flakiness,
not affected by this change.
2026-06-25 14:45:07 +02:00
Ran Shemtov 78ba59895c Merge branch 'main' into claude/funny-dewdney-fc28f0 2026-06-25 14:12:06 +02:00
Ran Shem Tov 24a93672f1 feat(showcase): bump CopilotKit 1.61.1 -> 1.61.2 and adopt A2UI catalog auto-inject (#5611)
Bump the canonical CopilotKit pin across all showcase integrations + shell
to 1.61.2 (canonical-pins.json, every package.json + package-lock.json),
which carries CopilotKit#5611: passing a catalog to the provider
(`<CopilotKit a2ui={{ catalog }}>`) now auto-enables A2UI and defaults tool
injection on, so the runtime no longer needs an explicit `a2ui` config.

Demonstrate the feature on the A2UI dynamic (declarative-gen-ui) demos by
removing the now-redundant runtime `a2ui` block (`injectA2UITool: true` +
`defaultCatalogId`) from:
  - langgraph-python, langgraph-fastapi, langgraph-typescript
  - strands, strands-typescript
  - google-adk

The forwarded catalog supplies its own catalogId (sdk-js A2UI middleware
auto-derives `defaultCatalogId` from it), so the previous "Catalog not found"
fallback no longer applies.

Verified: validate-pins drift ratchet unchanged (38 / same hash);
langgraph-python D6 `gen-ui-declarative` green end-to-end (no Catalog-not-found).
2026-06-25 14:03:36 +02:00
Ran Shemtov 5a516c827f chore: release monorepo v1.61.2 (#5696)
## Release monorepo v1.61.2

**Scope:** `monorepo` | **Bump:** `patch`

---

### How this release process works

1. **This PR was created automatically** by the "release / create-pr"
workflow.
   It bumped the `monorepo` packages to `1.61.2`
   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 `monorepo` packages to npm at version `1.61.2`
   - Creates git tag `monorepo/v1.61.2`
   - 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.
v1.61.2
2026-06-25 10:10:02 +02:00
ranst91 bb69f55c98 chore: release monorepo v1.61.2 2026-06-25 07:54:33 +00:00
Ran Shemtov 382011c79c chore: release bot-slack v0.1.0 (#5687)
## Release bot-slack v0.1.0

**Scope:** `bot-slack` | **Bump:** `minor`

---

### How this release process works

1. **This PR was created automatically** by the "release / create-pr"
workflow.
   It bumped the `bot-slack` 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-slack` packages to npm at version `0.1.0`
   - Creates git tag `bot-slack/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-slack/v0.1.0
2026-06-25 09:52:47 +02:00
Jordan Ritter f3c6447c54 fix(showcase): A2UI render-path D6 reds — a2ui-fixed-schema + declarative-gen-ui (#5694)
## Summary
Fixes the two RED A2UI D6 demos in the **built-in-agent** showcase
integration. Both are confirmed GREEN locally via the D6 control-plane
probe (`--d6 --direct --isolate`).

### a2ui-fixed-schema — React #31 crash → GREEN
**Root cause:** the showcase authors A2UI catalog defs with root
`zod@4`, but `@a2ui/web_core`'s `GenericBinder` schema scraper inspects
**Zod-3** internals (`_def.typeName==='ZodUnion'`). A zod@4 union
reports `_def.typeName===undefined` → field misclassified STATIC → the
raw `{path}` binding object reaches render → **React error #31**.
**Fix:** author this demo's catalog with a `zod-v3` (`npm:zod@3.25.76`)
alias so the binder resolves bindings (card now renders `SFO → JFK,
$289`). NOT a react-core fix.

### declarative-gen-ui — surface-missing (no paint) → GREEN
**Root cause:** the secondary-LLM prompt was far thinner than the
canonical generation guidelines, so it emitted trees that (correctly)
failed the renderer's `surfaceHasRenderableContent` paint gate. The
paint gate is correct and untouched.
**Fix:** port the canonical generation rules into the prompt, add output
validation, add catalog parity (`DataTable` + info-row), ground the
planner with sales-context, and record multi-turn aimock fixtures (4/4
turns paint).

## Red→green
- a2ui-fixed-schema: RED `Minified React error #31 {path}` → GREEN `✓
d6:built-in-agent green`, card resolves real values.
- declarative-gen-ui: RED `reason=surface-missing` → GREEN
`turnsCompleted:4, 1 passed`, all sales pills paint
(KPI/pie/bar/DataTable/status/info-row).

## Cross-regression
The shared-renderer path was checked across A2UI demos on both runtimes:
gen-ui-agent ✓, beautiful-chat 5/5 ✓, langgraph-python (v1)
a2ui-fixed-schema ✓ — no regressions.

## Code review
2 review rounds (7 agents each). Applied: a2ui factory validation
hardening (plain-object data guard, unique-id check, fail-loud on
non-string secondary-LLM return), two-arg `z.record` (zod@4 API),
index-based DataTable row key, Metric `trendValue` rendering for neutral
trend, doc corrections.

## Follow-ups (not this PR)
- **openai@5 / zod@4 peer ERESOLVE** on fresh `npm install`
(pre-existing; committed lock installs via `npm ci`).
- Cross-integration `sales-context.ts` sync (5 byte-identical copies).
- A2UI tree cycle/reachability enforcement (deferred — not a realistic
LLM-output threat; unique-id IS enforced).
- Non-string component-id edge in the unique-id guard.
- `SYSTEM_PROMPT` catalog enumeration omits `DataTable`.
- `page.tsx` docstring drift; DonutChart legend mirror; Row `align` map;
fixture match-key coupling.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-24 20:41:40 -07:00
Jordan Ritter d501d233b0 fix(showcase): make built-in-agent declarative-gen-ui paint its D6 surface
The secondary-LLM prompt was far thinner than the canonical generation guidelines,
so it emitted trees that (correctly) failed the renderer's paint gate → surface-missing.
Port the canonical generation rules into the prompt, add output validation, add catalog
parity (DataTable + info-row), ground the planner with sales-context, and record
multi-turn aimock fixtures. Includes CR fixes: two-arg z.record for the DataTable rows
schema (zod@4 API), index-based DataTable row key, and Metric trendValue rendering for
neutral trend.
2026-06-24 20:25:16 -07:00
Jordan Ritter bbdcb01440 fix(showcase): resolve a2ui-fixed-schema React #31 via Zod-3 catalog defs
The showcase authors A2UI catalog defs with root zod@4, but @a2ui/web_core's
GenericBinder schema scraper inspects Zod-3 internals (_def.typeName==='ZodUnion').
A zod@4 union reports _def.typeName===undefined → misclassified STATIC → the raw
{path} binding object reaches render → React error #31. Author this demo's catalog
with a zod-v3 (npm:zod@3.25.76) alias so the binder resolves bindings. Includes CR
hardening of the shared a2ui factory validation (plain-object data guard, unique-id
check, fail-loud on non-string secondary-LLM return).
2026-06-24 20:25:16 -07:00
Maxim e27ab05634 feat(saas-demo): SaaS banking demo — Threads + teachable self-learning + Aurora UI (#5266)
## What this is
The consolidated, Jerel-owned PR for the **SaaS banking demo**
(FOR-137), built on top of the now-merged **#5180** (Maxim, FOR-138). It
continues the closed draft **#5183**, rebuilt cleanly on current `main`.

Scope: **53 files, entirely within `examples/showcases/banking` +
`examples/showcases/teach-mode`.**

<img width="1358" height="763" alt="image"
src="https://github.com/user-attachments/assets/2ad50912-077e-4a03-821b-047e487a7c8d"
/>

<img width="1600" height="894" alt="image"
src="https://github.com/user-attachments/assets/f80d894f-a24c-4d77-af83-0d1cf1369b18"
/>

<img width="800" height="451" alt="image"
src="https://github.com/user-attachments/assets/c8d6abe5-2e70-4ad1-87af-8aa6529906dc"
/>

## What's added (on top of #5180)
- 🎨 **Aurora design system** — premium light/violet fintech redesign
(lavender canvas, violet→indigo brand, gradient cards, hand-rolled SVG
statistics sparkline, Inter). Restyle-only; all wiring preserved.
- 🪟 **Chat as a docked side panel + Threads inbox** — v2 `useThreads`;
the chat is a right-docked `CopilotSidebar`; Threads live as an **inbox
button in the panel header** (conversation list, new / archive /
select).
- 🧠 **Self-learning teachable gate** — approving an over-limit
transaction is blocked with a **symptom-only** error; a human unlocks it
by filing a **justifying** policy exception; the agent gets tools +
distractors but **never the unlock recipe**. OSS mode is the default;
`CopilotKitIntelligence` is env-gated.
- 📘 **Reusable teach-mode cookbook** — `examples/showcases/teach-mode/`
(the 5-role teachable-loop contract + banking↔e-commerce mapping +
adoption checklist + recording seam + `verify-teachable-gate.sh`).
- 🗺️ **Learning-track plan** — `LEARNING-TRACK-PLAN.md`: how to make the
live learn→distill→retrieve loop work + the teachable-demo UX (suggested
prompt, inline HITL, recording vignette).

## Status / notes
- **Draft.** Runs in **OSS mode** (only `OPENAI_API_KEY`); no
Intelligence backend required.
- The **live** self-learning loop
(record→distill→`/knowledge`→fresh-agent-learns) is gated on the
react-core recording hook (`e103a19`) + an Intelligence backend — see
`LEARNING-TRACK-PLAN.md`. The teachable **mechanics** (gate + unlock)
work today and are REST-verifiable (`verify-teachable-gate.sh`).
- cc @mxmzb — consolidated demo PR per Sam's ask; builds directly on
your merged #5180.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-25 03:53:02 +02:00
GeneralJerel 7d645af274 fix: restore upstream files unintentionally rewritten by lint-fix during merge 2026-06-24 18:42:01 -07:00
GeneralJerel 9ae901ed0a Merge remote-tracking branch 'upstream/main' into jerel/saas-banking-demo
# Conflicts:
#	pnpm-lock.yaml
2026-06-24 18:23:24 -07:00
Jordan Ritter b305394b5c docs(showcase): correct gen-ui-agent PARITY_NOTES — it is GREEN, not react-core-blocked (#5693)
## Summary

Doc-only single-file correction to
`showcase/integrations/built-in-agent/PARITY_NOTES.md`. The
`gen-ui-agent` D6 cell already passes end-to-end locally, but its
PARITY_NOTES entry still documented it as RED/blocked on a `STATE_DELTA
→ useAgent` gap in `@copilotkit/react-core`. That premise is stale and
is now refuted by local D6 runs. This rewrites the entry to
GREEN/reclaimed and rescopes the surrounding section header to the
remaining A2UI render-layer demos.

## RED → GREEN proof (from the work log)

The "RED" here is documentary, not behavioral: the cell **passes**
despite the stale RED doc.

Local RED baseline (`bin/showcase test built-in-agent:gen-ui-agent --d6
--direct --isolate`):
```
[conversation-runner] turn 3/3 — assertions passed
[conversation-runner] conversation completed successfully { turnsCompleted: 3 }
  ✓ d6:built-in-agent green (44.1s)
  1 passed (44.1s)
```

Local GREEN value-test (same command, `--repeat 3`):
```
  3 passed (130.1s)
✓ Tests passed for built-in-agent:gen-ui-agent
```
3/3 stable — not a flake. The doc correction changes no runtime
behavior.

Why it works: the backend `set_steps` server-tool result is converted to
a `STATE_DELTA` `[{op:"add", path:"/steps", value:steps}]` in
`src/lib/factory/tanstack-factory.ts` (`add`, not `replace`, so
`@ag-ui/client@0.0.57` doesn't drop it as
`OPERATION_PATH_UNRESOLVABLE`). `@ag-ui/client` applies the patch and
fires `onStateChanged`; the core state-manager fans it to subscribers;
`useAgent` re-renders off `agent.state.steps`. Fully wired in published
1.61.1 — no react-core change needed.

## Key finding: there was NO config quarantine

`gen-ui-agent` was never in the manifest `not_supported_features`, never
excluded in `shared/constraints.yaml`, and
`shared/feature-registry.json` has no per-feature status field. The
harness already routes it into `runnable` and grades it green. The
**stale doc was the only artifact** — there was no executable quarantine
to lift, so this is a pure doc correction.

## The two genuinely-RED demos are out of scope

`a2ui-fixed-schema` (React #31 crash from an unresolved `{path}` A2UI
binding) and `declarative-gen-ui` (surface never paints; secondary-LLM
op-shape) are real bugs, but both belong to `@copilotkit/a2ui-renderer`
/ showcase — **not** `@copilotkit/react-core`. They are being addressed
in a separate spec and are intentionally left untouched here.

## Test plan

- [x] `oxfmt --check` on the changed file — passes (correctly formatted)
- [x] `oxlint` on the changed file — 0 warnings, 0 errors
- [x] `commitlint` on the commit message — passes (`docs(showcase):`)
- [x] D6 cell passes locally, 3/3 stable
- [ ] CI green
2026-06-24 15:35:40 -07:00
Tyler Slaton d4c58f08e8 fix(showcase/shell-dojo): runtime-derive preview backend URL (#5692)
## Problem

The CopilotKit Interactive Dojo (`showcase/shell-dojo`) built its
preview-iframe `src` from `integration.backend_url`, which
`scripts/generate-registry.ts` synthesizes into `registry.json` at
**Docker build time** (default
`showcase-{slug}-production.up.railway.app`). The value is therefore
frozen into every image regardless of environment — so the **staging
dojo iframed production integration backends**.

## Root cause

The shell (`showcase/shell`) already solved this in SU-13 (commit
`febd8bc9`, 2026-06-11): it derives each integration's backend host at
**request time** from `SHOWCASE_BACKEND_HOST_PATTERN` via
`lib/backend-url.ts`, keeping `registry.json` as the source for non-URL
metadata only. That fix was scoped to `shell` and **never ported to
`shell-dojo`** — `shell-dojo`'s `RuntimeConfig` was still empty and
`page.tsx` read the build-baked `backend_url`.

## Fix

Port the `backendHostPattern` slice of SU-13 to `shell-dojo` (nothing
else from SU-13 — the dojo has no docs-redirect middleware, posthog, or
baseUrl consumers):

- **`lib/backend-url.ts`** — verbatim copy of shell's
(`resolveBackendUrl` + the `NEXT_PUBLIC_LOCAL_BACKENDS` local-dev
override). The two apps are separate build roots with different package
managers (shell is a pnpm workspace member; shell-dojo is a standalone
`npm ci` Next app, same as shell-dashboard), so neither can import the
other's `src`, and there is no shared importable package — the same
reason `generate-registry.ts` already keeps its own copy. A drift-guard
test keeps the copy byte-identical and pins the default pattern across
`backend-url.ts` and `generate-registry.ts`.
- **`lib/runtime-config.ts`** — adds `backendHostPattern`, read from
`SHOWCASE_BACKEND_HOST_PATTERN` at request time (with the
`NEXT_PUBLIC_`-prefixed/trim fallback semantics shared with shell).
Unset → the prod default, so an unset deploy is byte-identical to today.
- **`lib/runtime-config.client.ts`** — carries the SSR sentinel
`showcase-{slug}.ssr-placeholder.invalid` + a required-field check.
- **`app/page.tsx`** — `previewUrl` now derives via
`resolveBackendUrl(...)` at request time, **gated on a `mounted` flag**
so the SSR-phase sentinel host never reaches an iframe `src`. shell-dojo
loads its registry synchronously (`getIntegrations()` in a `useMemo`,
`viewMode` defaults to `"preview"`), so unlike the shell it has no
client-`useEffect` data guard to defer the read past hydration — without
the gate, the iframe would render on first paint with the `.invalid`
sentinel host (a request to a dead domain + a flash before the real URL
swaps in).
- **`app/layout.tsx`** — comment-only; the existing generic
`JSON.stringify(config)` injection already carries the new field into
`window.__SHOWCASE_CONFIG__`.

## Ops

`SHOWCASE_BACKEND_HOST_PATTERN=showcase-{slug}-staging.up.railway.app`
is already set on the **staging dojo** Railway service (verified the
staging hostname convention against a live staging integration's
`RAILWAY_PUBLIC_DOMAIN`). **Production is intentionally left unset** =
the prod default pattern, so prod behavior is unchanged. The var is
runtime-only (images build in CI, not on Railway), so it was inert until
this lands — merging this is what flips staging off prod backends.

## Testing

- New drift-guard test (in `scripts`): `backend-url.ts` byte-identical
to shell's + default-pattern parity with `generate-registry.ts` — 2/2
pass.
- Full affected vitest run: 2110/2110 pass.
- Standalone shell-dojo Next build (`npm ci --ignore-scripts && npm run
build`) compiled + type-checked successfully.
- oxfmt clean; oxlint 0 errors.

## Notes for reviewers

- **shell-dojo is not in the pnpm workspace**, so `nx affected` does not
cover it — CI builds it via its own npm/Docker path. Verify locally with
`cd showcase/shell-dojo && npm ci && npm run build`.
- The `iframe-missing-sandbox` oxlint **warning** at `page.tsx`
pre-dates this change (the iframe does carry a `sandbox` attr; oxlint
can't see it through the variable) and is outside the diff's intent —
left untouched.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-24 15:35:01 -07:00
Jordan Ritter a478692b58 docs(showcase): correct gen-ui-agent PARITY_NOTES (it is GREEN, not react-core-blocked)
The built-in-agent gen-ui-agent D6 cell already passes end-to-end locally;
the PARITY_NOTES entry that documented it as RED/blocked on a STATE_DELTA
to useAgent gap in @copilotkit/react-core was stale. The set_steps to
STATE_DELTA {op:"add", path:"/steps"} workaround merged in
tanstack-factory.ts closed that gap: @ag-ui/client applies the patch and
fires onStateChanged, the core state-manager fans it to subscribers, and
useAgent re-renders off agent.state.steps. No react-core change is needed.

Rewrites the gen-ui-agent entry to GREEN/reclaimed and rescopes the
section header to the remaining A2UI render-layer demos (a2ui-fixed-schema,
declarative-gen-ui), whose fixes belong to @copilotkit/a2ui-renderer, not
react-core. Doc-only; no config quarantine existed (gen-ui-agent was never
in manifest not_supported_features), so the cell stays a counted green.

Local RED baseline: cell passes (1 passed) despite the stale RED doc.
Local GREEN value-test: --repeat 3 => 3 passed (130.1s), stable.
2026-06-24 15:12:45 -07:00