Commit Graph

12629 Commits

Author SHA1 Message Date
Jordan Ritter 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 5057efce1a brought the other 19 into conformance ("built-in-agent
already passes via its ChatErrorBoundary"). It rendered the post-sign-out
401 via a React ChatErrorBoundary (auth-demo-chat-boundary) + a local
401-regex onError, and defaulted to authenticated on first paint — so the
byte-identical auth.spec.ts (the CI conformance gate) failed every
unauth-first assertion.

Normalize to the langgraph-python gold shape:
- use-demo-auth.ts: unauth-first hook (hasEverSignedIn/signIn/signOut,
  localStorage-backed token, isAuthenticated/authorizationHeader).
- page.tsx: drop ChatErrorBoundary/lastError/local-401-regex; wire a shared
  handleAuthError onto BOTH <CopilotKitProvider onError> and the agent-scoped
  <CopilotChat onError>; clear-on-auth useEffect keyed off authError alone;
  unauth-first SignInCard gate; amber [data-testid="auth-demo-error"] surface.
- auth-banner.tsx / sign-in-card.tsx: align prop contract to gold
  (onSignIn, onSignIn(token)).

Forced divergences preserved: built-in-agent IS the built-in agent, so it
keeps <CopilotKitProvider> (runtime registers the agent under the default
key) rather than <CopilotKit agent="auth-demo">, and uses raw Tailwind
elements (no shadcn @/components/ui in this integration). The error-handling
shape, auth hook, and testid contract match gold exactly.

Proven RED->GREEN on the byte-identical auth.spec.ts (the discriminating
surface; the --d5 probe accepts both shapes and was green for the legacy
frontend): all unauth-first conformance assertions flip FAIL->PASS, and the
canonical built-in-agent:auth --d5 --isolate probe is green.
2026-06-25 22:50:30 -07:00
Jordan Ritter 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.
2026-06-25 22:50:30 -07:00
Jordan Ritter 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.
2026-06-25 22:46:32 -07:00
Jordan Ritter 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.
2026-06-25 21:12:36 -07:00
Jordan Ritter 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.
2026-06-25 20:34:01 -07:00
GeneralJerel 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>
2026-06-25 15:53:32 -07:00
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
Markus Ecker 99b221d9e5 fix(core): memory socket never joined its channel (no realtime deltas)
The memory store's socket effect created the `user_meta:memories:<code>`
channel and observed its events, but never subscribed the join-outcome
stream — and `ɵphoenixChannel$` only calls `channel.join()` when that stream
is subscribed. So the socket connected but no `phx_join` was ever sent: the
gateway saw the connection, the channel was never joined, and saved memories
never pushed to the client (REST snapshot worked; live deltas silently never
arrived). The thread store joins because its merged `joinOutcome$` subscribes
the stream.

Merge `ɵjoinPhoenixChannel$(channel$)` into the socket effect's output so the
join actually fires; swallow a failed/timed-out join so a transient rejection
doesn't tear down the metadata stream (Phoenix re-attempts on reconnect). Add
a `joinCount` spy to MockChannel and assert the realtime test actually joins —
this regression passed before because the mock delivered events without a join.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 23:38:33 +02: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
Markus Ecker cd0739800e fix(runtime): identify memory-subscribe user via header, not body
ɵsubscribeToMemories mirrored ɵsubscribeToThreads and sent userId in the
body, but the platform's memory routes resolve the app user from the
x-cpki-user-id header (like listMemories/createMemory), not the body — so
POST /api/memories/subscribe returned 401 AUTH_UNAUTHENTICATED. Send the
user via the header instead; the body is now empty.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 21:33:00 +02:00
Markus Ecker 4f50068244 fix(runtime): add missing /memories/subscribe route + handler
The memory store client POSTs /memories/subscribe to mint realtime join
credentials, but the runtime router had no such route — the path fell
through to /memories/:id (memories/mutate), so a POST returned 405 Method
Not Allowed. Threads had the full chain (route → handleSubscribeToThreads →
platform); memory was missing the runtime proxy between the client and the
platform's POST /api/memories/subscribe.

Add `memories/subscribe` to the route union, match it before /memories/:id
(and exclude "subscribe" from the :id rule, mirroring threads), add
handleSubscribeToMemories returning { joinToken, joinCode } (memory delivers
the join code here, unlike threads where it rides the thread-list response),
and ɵsubscribeToMemories on the platform client.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 21:17:37 +02: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
GeneralJerel a80b3515a9 docs(shell-docs): sync OpenBox recipe to full-parity demo 2026-06-25 10:55:39 -07:00
Markus Ecker 4211bd58d8 chore: format memory SDK files with oxfmt 2026-06-25 19:49:34 +02: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
Markus Ecker ca8e2fa064 refactor(core): drop thread-store user_meta sharing; memory owns its socket 2026-06-25 18:02:43 +02:00
Markus Ecker 250028469d feat(core): memory store opens its own user_meta:memories socket 2026-06-25 17:58:57 +02:00
Markus Ecker 3e04352d94 feat(core): memory store fetches its own subscribe credentials 2026-06-25 17:54:04 +02:00
Markus Ecker ce24806c1c feat(core): add wsUrl + credentials events to the memory store context 2026-06-25 17:47:48 +02: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
Markus Ecker 64078772a7 feat(core): export ɵNewMemory and ɵMemoryChanges mutation input types
ɵMemoryStore exposes addMemory(input: NewMemory) and
updateMemory(id, changes: MemoryChanges), but those input types were
internal — a thin framework binding (useMemories / injectMemories) had
no way to name them when wrapping the store. Export them alongside the
existing ɵMemory / ɵMemoryKind / ɵMemoryScope.
2026-06-25 15:40:57 +02:00