Commit Graph

15424 Commits

Author SHA1 Message Date
copilotkit-qa-bot[bot] 7a24a2d855 fix(ms-agent-python): migrate starter to stable APIs 2026-08-26 12:09:08 -07:00
Ben Taylor 61a67e716b fix(runtime): keep thread naming task after transcript (#6722)
## Summary

- place the thread-title task after the embedded conversation transcript
- explicitly tell the reused agent not to answer the conversation
- add a regression test that locks the prompt ordering

## Why

LangGraph starter agents can interpret the final embedded `user:` line
as the active request when the transcript comes last. They answer the
conversation instead of returning title JSON, causing retries and
eventual `Untitled` thread names.

## Validation

- reproduced with the latest LangGraph starter and
`@copilotkit/runtime@1.69.2`
- original prompt: 0/12 direct calls returned title JSON; 4/4 targeted
threads fell back to `Untitled`
- reordered prompt: 12/12 direct calls returned title JSON; 4/4 targeted
threads received generated titles
- runtime thread-name unit suite: 28/28 passing
- pre-commit affected package checks passing under the repository Node
22 toolchain
2026-08-26 13:44:47 -05:00
Martha Kelly Schumann 7bb8610030 fix(react-core): honor predictive state tool arguments (#6608)
## Summary

- honor the predictive-state `tool_argument` source field instead of
indexing tool arguments by destination `state_key`
- preserve the agent's existing state and combine every matching
same-tool configuration into one replacement-safe `setState` call
- preserve the SDK contract by storing the complete tool arguments when
`tool_argument` is omitted
- tolerate incremental JSON strings while ignoring unrelated tools and
malformed `PredictState` configuration
- merge current `main` and keep the final diff limited to the
PredictState implementation and regression tests in React Core's
`v1-deprecated` source boundary

## Validation

- predictive-state regression tests: 8/8 passed
- full React Core suite: 1,532 passed, 2 skipped; script tests: 47/47
passed
- dependency-aware React Core type-check: passed
- Web Inspector type-check from current `main`: passed
- React Core build: passed
- Python predictive-state control tests: 5/5 passed
- focused lint: 0 errors; formatting and `git diff --check`: passed

The final PR diff is limited to two React Core files.

FAC-122 is the canonical report; this also covers its existing
duplicates FAC-90 and FAC-107.

Linear: FAC-122
2026-08-26 11:39:00 -07:00
Maximiliano Korp eeb01fc33f fix(runtime): keep thread naming task after transcript 2026-08-26 11:28:30 -07:00
copilotkit-qa-bot[bot] 1ddc8d5454 Merge remote-tracking branch 'origin/main' into codex/fac-72-google-adk-stop-callback 2026-08-26 11:11:28 -07:00
copilotkit-qa-bot[bot] ad8f3f0a1b Merge remote-tracking branch 'origin/main' into codex/fac-122-predict-state-tool-argument 2026-08-26 11:00:48 -07:00
Benjamin Taylor 314f1ca55d fix(examples): stop overriding the managed Intelligence URL defaults (closes OSS-981)
CopilotKitIntelligence resolves apiUrl/wsUrl to the managed hosts when they are
omitted, and its own docstring says leaving both unset is always correct against
the managed service. Every starter's runtime route supplied
`?? "http://localhost:4201"` instead, so a managed reader who copied the block
got a runtime aimed at a local stack that is not running -- the failure the
starter's own .env.example warns about two files away.

Replace the fallbacks with the conditional spread these same starters already use
in channel-host.mts, so a self-hosted override still works and the managed
default applies when it is absent. Three .env.example files also set the values
uncommented, two of them directly under a comment telling the reader to leave
them unset; comment those out to match the other nineteen starters.

Guard both shapes in validate-intelligence-env-names.ts, which already polices
the canonical Intelligence key name and hosts and runs unfiltered on every PR.
The rule is the pattern rather than the literal, so a staging host substituted
for localhost fails the same way. Local e2e harnesses and demo stacks that
genuinely target a local deployment are allowlisted with their reasons.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 12:56:36 -05:00
Tyler Slaton d917fef833 fix(web-inspector): restore Playground surface styling (#6721)
## Problem

The Inspector Playground rendered without its intended surface styling
in the React demo, leaving its header, welcome state, composer, and dark
mode looking flat and unfinished.

## Why

The Playground rules lived in `CpkThreadInspector.styles`, but the
Playground itself renders inside `WebInspectorElement`'s separate Shadow
DOM. Those selectors therefore could not reach the rendered Playground.

## Fix

- Move the Playground animation, responsive, and surface rules into the
owning Web Inspector Shadow DOM.
- Add explicit light/dark surface treatments for the header, welcome
state, and composer.
- Add a regression test that verifies the styles live in
`WebInspectorElement.styles`.
- Repair the current-main Inspector verification baseline for eager
message loading and strict indexed access.

Verified with the React demo and the root OpenAI API key, including a
successful Playground response. The Inspector package build, typecheck,
and all 608 tests pass.
2026-08-26 10:53:16 -07:00
copilotkit-qa-bot[bot] b04b47c932 fix(react-core): preserve predictive state updates 2026-08-26 10:44:39 -07:00
Tyler Slaton d44178a8f0 fix(web-inspector): match Playground composer surface 2026-08-26 19:31:36 +02:00
Tyler Slaton 76f3e9fff4 fix(web-inspector): repair Inspector verification baseline 2026-08-26 19:23:38 +02:00
Tyler Slaton edbbdbdf80 fix(web-inspector): restore Playground surface styling 2026-08-26 19:22:45 +02:00
copilotkit-qa-bot[bot] 6d6f59ee77 Merge main into FAC-122 predictive state fix
# Conflicts:
#	packages/react-core/src/v1-deprecated/components/__tests__/CopilotListeners.predictState.test.ts
2026-08-26 10:14:07 -07:00
Martha Kelly Schumann e1d31696f7 Fix blank Slack manifest usage hints (#6604)
## Summary
- omit blank optional `usage_hint` values from both distributed Slack
app manifests
- preserve the meaningful hints on `/agent` and `/preview`
- parse and validate both YAML and JSON manifests in a focused
regression suite
- assert their slash-command definitions stay in sync

## Validation
- `pnpm exec vitest run app/slack-app-manifest.test.ts` (7 tests)
- `pnpm exec nx run slack-example:test` (70 tests)
- `pnpm exec nx run slack-example:check-types`
- affected-package pre-commit matrix
- `git diff --check`

## Linear
-
[FAC-128](https://linear.app/copilotkit/issue/FAC-128/slack-example-manifest-yaml-rejects-due-to-empty-usage-hint-fields)
2026-08-26 10:07:58 -07:00
copilotkit-qa-bot[bot] e21af7f5c2 Merge main into FAC-128 Slack manifest fix
# Conflicts:
#	pnpm-lock.yaml
2026-08-26 09:53:09 -07:00
Alem Tuzlak 1a92a179f7 fix(web-inspector): polish inspector chrome, threads, and dark mode (#6713)
Open Inspector in dark mode and check Agent → Current Messages. Row
lines must be dark gray, not light.

This PR fixes Inspector chrome that was hard to read in dark mode. It
also shows live user turns on the Threads Messages tab, puts Collapse
under the Intelligence card, and sizes the collapsed sidebar.

## Testing

1. **Commands run.** `pnpm exec vitest run` on `thread-detail`,
`view-in-app`, `web-inspector`, and `inspector-navigation` in
`packages/web-inspector`. 128 tests passed after a 200ms wait on the
icon-rail agent flyout. The repo pre-commit Nx graph failed because
`.worktrees/` defines duplicate projects. That hook was skipped.

2. **Manual test.**
   1. Run `examples/v2/react-router` and open Inspector.
   2. Switch to dark mode.
3. Open Agent and look at Current Messages. Dividers must match the dark
card, not light gray.
4. Collapse the sidebar. The agent icon must match the other 36px tabs
and stay centered.
5. Open Settings → Privacy. There must be no extra line above Anonymous
usage analytics.

3. **How this PR makes testing easy.** Updated tests in
`thread-detail.spec.ts`, `view-in-app.spec.ts`, `web-inspector.spec.ts`,
and `inspector-navigation.spec.ts`. The react-router example publishes
app context for the Context tab.

## Risk / rollback

Low. UI and Inspector-only. Revert the PR to undo.
2026-08-26 18:40:01 +02:00
Alem Tuzlak 5a188d6783 fix(web-inspector): animate launcher hover scale and color 2026-08-26 09:25:41 -07:00
Alem Tuzlak 93861b428d fix(web-inspector): polish inspector chrome, threads, and dark mode 2026-08-26 09:25:41 -07:00
Tyler Slaton 51dc79268f feat(web-inspector): preview launcher HUD on load (#6715)
## Summary

Automatically preview the existing launcher HUD on every Inspector
mount, then fade it away after the feature statuses resolve.

## Why

The closed Inspector launcher did not draw attention to the feature
availability and enablement links already exposed on hover.

## How

- open the HUD after a short page-settle delay
- stagger the four rows and connected checks with compositor-only motion
- let hover or focus take control without an automatic close
- respect reduced motion and clean up mount timers
- cover the lifecycle with focused tests
2026-08-26 09:25:26 -07:00
Benjamin Taylor 84dd86f2ed test(examples): gate the starters' Intelligence wiring block on one shape (closes OSS-982)
The marked block that wires managed Intelligence is the region a hosted reader
copies verbatim, and nothing checked it. Both gaps were deliberate: the parity
manifest lists `src/app/api/copilotkit/**` under `allowedDivergence` for every
instance it tracks, and no `docker-compose.test.yml` sets
`COPILOTKIT_LICENSE_TOKEN`, so every smoke-tested starter takes the else arm and
the `intelligence:` arm has never run in CI.

The cost was already visible. The block's code was byte-identical in 21 of 22
starters, but its warning comment had drifted into five variants and the two
`ms-agent-framework-*` starters shipped the `demo-user` stub with no warning at
all. That drift is how the localhost default of OSS-981 survived in all 22
copies at once.

Add `scripts/validate-intelligence-wiring-block.ts`, which greps the opening
marker, compares every site against the north-star starter, and fails on the
first line that differs. Two normalisations keep it usable: the block is
dedented, because `agentcore` nests it deeper, and the else arm's runner name is
masked, because `agentcore` runs `AgentCoreRunner` in front of a Bedrock session
where an in-process runner has nothing to run. Everything else, comment text
included, must match to the byte.

Then unify the warning at all 22 sites on the fullest wording, which also says
the id must exist in Intelligence or thread operations can fail.

The check passes on day one, so it is a ratchet rather than a migration. It is a
shape gate, not a content gate: 22 identically wrong copies still pass. What it
guarantees is that a fix reaches all of them or none.

Not covered: enrolling the `intelligence:` arm in the smoke path. That needs a
license token in CI and a reachable endpoint from the compose network, and is
tracked separately.
2026-08-26 11:16:00 -05:00
Tyler Slaton e8a5e00e8b feat(web-inspector): preview launcher HUD on load 2026-08-26 17:41:32 +02:00
Benjamin Taylor 8483f434f7 fix(examples): stop overriding the managed Intelligence URL defaults (closes OSS-981)
CopilotKitIntelligence resolves apiUrl/wsUrl to the managed hosts when they are
omitted, and its own docstring says leaving both unset is always correct against
the managed service. Every starter's runtime route supplied
`?? "http://localhost:4201"` instead, so a managed reader who copied the block
got a runtime aimed at a local stack that is not running -- the failure the
starter's own .env.example warns about two files away.

Replace the fallbacks with the conditional spread these same starters already use
in channel-host.mts, so a self-hosted override still works and the managed
default applies when it is absent. Three .env.example files also set the values
uncommented, two of them directly under a comment telling the reader to leave
them unset; comment those out to match the other nineteen starters.

Guard both shapes in validate-intelligence-env-names.ts, which already polices
the canonical Intelligence key name and hosts and runs unfiltered on every PR.
The rule is the pattern rather than the literal, so a staging host substituted
for localhost fails the same way. Local e2e harnesses and demo stacks that
genuinely target a local deployment are allowlisted with their reasons.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 09:59:23 -05:00
David McKay 21ac364918 fix(runtime): stop reporting every connect failure as a 404 (closes OSS-971) (#6708)
## Summary

The Intelligence connect handler classified a handful of platform
rejections and flattened everything else into `404 "Connect plan not
available"`, writing the real cause only to server-side stderr.

So a 500 from app-api, a socket timeout, a connection reset and a bug in
our own code all produced the same misleading answer, and the only way
to learn what actually happened was to read the runtime container's
logs.

## Why it matters

This came out of a production incident on a self-hosted deployment.
Redis filled, app-api returned 500 on a join-code write, and the
operator saw a 404 naming a "connect plan" that had nothing to do with
the failure. The platform was up and the request was retryable; the
reported status communicated neither, and pointed the investigation at a
missing thread instead. Diagnosing it took about a day, almost all of it
spent on the wrong hypothesis.

## What changes

| Case | Before | After |
| --- | --- | --- |
| Platform 400/401/403/404/409 | rejection + message | unchanged |
| Platform 500, 503, any other status | `404` "Connect plan not
available" | that status, with the message |
| No status: timeout, ECONNRESET, DNS, our own bug | `404` "Connect plan
not available" | `502`, with the message |

`502` for the no-status case says "the thing behind me is unreachable",
which is true and actionable. `404` asserts the thread does not exist,
which sends the caller to investigate the wrong thing entirely.

Every branch now returns the underlying message rather than burying it
in a log line the caller cannot see.

## Verification

- `handle-connect.test.ts` 20 passed, including three new cases:
platform 500 passes through, platform 503 passes through, and an
unreachable platform reports 502. The existing genuine-404 case is
asserted unchanged, so widening the pass-through does not blur a real
not-found with an unreachable platform.
- Red/green checked: reverting the handler while keeping the tests fails
3 of 20.
- `packages/runtime` v2 suite goes 1270 to 1273 passing, the three
additions. The 13 test files that fail to collect and the 26 `tsc`
errors are identical with and without this change; they are unbuilt
workspace deps in a fresh worktree, not related to it.
- Full lefthook pre-commit suite passes.

## Not addressed here

`handlers/shared/sse-response.ts` has the same shape: it returns `200`
and `text/event-stream` before the run starts, so a later throw closes
the stream with zero events and no error frame. From the client that is
indistinguishable from a silent no-op send. Fixing it means changing the
streaming contract, so it wants its own PR.

Related, same family of bug, different file: #6697.
2026-08-26 07:58:51 -07:00
Benjamin Taylor 6669b3a487 docs(react-core): state the blast radius of key-remount and the provisional agent (refs OSS-979)
An Intelligence integration lost a request-to-row correlation map partway
through a user interaction, with no error and no warning. It surfaced as
"our response routing is flaky". OSS-979 filed it as CopilotKitProvider
remounting its children.

The provider does nothing of the kind. It renders `{children}`
unconditionally, unkeyed, with no early return and no Suspense boundary
anywhere in v2. The remount was app-side, and it was app-side because this
skill told it to be:

* `references/threads.md` teaches `useThreads()` -> select ->
  `<CopilotChat key={activeId}>`, and that recipe is only reachable once
  Intelligence is wired.
* `references/switching-agents.md` teaches "`key={activeAgent}` forces
  remount so thread state doesn't leak" without saying what else that
  discards.
* `examples/showcases/reskinnable-demo/src/app/[skin]/layout.tsx:223`
  models `<SubagentActivityProvider key={threadId}>` above `{children}`,
  commented "Remounting is deliberate".

Follow all three and you key a layout-level provider on a thread id that
changes asynchronously after mount. Everything below it dies
mid-interaction.

Two properties made it invisible. Durable threads exist only in
Intelligence mode, so in OSS-only development the selected thread never
changes and the remount never fires. And whether state survives depends on
whether the user acted before the thread list resolved.

Both traps now carry their blast radius, in the four places an agent
actually reads:

* `SKILL.md` -- two invariants in the load-once section, so they land
  before any reference is opened.
* `references/threads.md` -- a HIGH entry on keying above app state, plus a
  note that `activeId` in the switcher recipe settles asynchronously.
* `references/switching-agents.md` and `switching-agents-recipes.md` --
  keep the `key` on `<CopilotChat>`, never on a wrapper or a layout
  provider.
* `references/agent-access.md` -- the second route to the same symptom.
  `useAgent` swaps a provisional stand-in for the real agent when `/info`
  resolves, so an effect keyed on `agent` re-runs once, mid-interaction.
  Adds an `isReady` pattern and a HIGH entry. `isReady` appeared in zero
  shipped skills before this; it was documented only in shell-docs and in
  JSDoc.

Also corrects a factual error: the skill claimed `useAgent` returns
`{ agent }` only. It returns `{ agent, isReady }`.

No library change. The provider behaves correctly; the guidance did not
describe what it costs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 09:58:28 -05:00
David McKay b4145f42fc fix(runtime): stop reporting every connect failure as a 404 (closes OSS-971)
The Intelligence connect handler classified a handful of platform rejections and
flattened everything else into HTTP 404 "Connect plan not available", writing the
real cause only to server-side stderr.

That made every unrecognised failure look like a missing thread. A 500 from
app-api, a socket timeout, a connection reset and a bug in our own code all
produced the same misleading answer, and the only way to find out what actually
happened was to read the runtime container's logs.

It cost a customer a day. Their Redis filled, app-api returned 500 on a
join-code write, and their engineer saw a 404 naming a "connect plan" that had
nothing to do with the failure. The platform was up and the request was
retryable; the reported status said neither.

Now:

  - a status we already special-case (400, 401, 403, 404, 409) still reports as
    a rejection with its message, unchanged;
  - any other status from the platform passes through as itself, so a 503 stays
    a 503 and the caller knows to retry; and
  - an error carrying no status never reached the platform, so it reports 502
    rather than 404, which says "the thing behind me is unreachable" instead of
    asserting the thread does not exist.

Every branch now returns the underlying message rather than burying it in a log
line the caller cannot see.

The SSE run path in handlers/shared/sse-response.ts has the same shape and is not
addressed here: it returns 200 and text/event-stream before the run starts, so a
later throw closes the stream with no events and no error frame. That needs its
own change to the streaming contract.
2026-08-26 07:55:31 -07:00
Alem Tuzlak b5d037545f fix(examples): add missing zod for the OpenRouter demo-server (#6704)
The Angular OpenRouter demo-server crashed on chat because
`@ai-sdk/openai` imports `zod/v4` and the server did not declare `zod`.
This PR adds that dependency so pnpm can put `zod` next to the server on
Windows.

## What does this PR do?

Adds `zod` to `examples/v2/angular/demo-server`. That is the OpenRouter
`createOpenAI` path. The React demo already had this dependency.

## Related PRs and Issues

- Inspector test path: local `main` plus the React Router example also
has `zod` linked so chat can run while this PR is open.

## Checklist

- [ ] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [ ] If the PR changes or adds functionality, I have updated the
relevant documentation
- [x] "Allow edits by maintainers" is checked (lets us help iterate on
your PR directly — faster turnaround for everyone)

## Testing

1. **Commands run.** I did not run the monorepo test suite. This change
is one private example `package.json` plus the lockfile importer line. I
did confirm Node can resolve `zod/v4` from `@ai-sdk/openai` after `zod`
is linked into the example `node_modules`.
2. **Manual test.**
1. On current `main` (before this change), run
`examples/v2/angular/demo-server` with `OPENROUTER_API_KEY` set. Send a
chat message. The process fails with `Cannot find module 'zod/v4'`.
   2. Check out this branch and run `pnpm install`.
3. Start the demo-server again and send a chat message. The OpenRouter
call starts. No `zod/v4` error.
3. **How this PR makes testing easy.** The OpenRouter example now
declares the same `zod` peer the React demo already had.

## Risk / rollback

Low. The change is a private example dependency. Revert the PR to undo
it.
2026-08-26 12:38:26 +02:00
Alem Tuzlak 1983c07ccf fix(examples): add missing zod for the OpenRouter demo-server
@ai-sdk/openai imports zod/v4 at runtime. The Angular OpenRouter
demo-server did not declare that peer, so chat failed on Windows pnpm.
2026-08-26 12:20:08 +02:00
Tyler Slaton 5547d625a9 fix(web-inspector): match HUD arrow and tighten Inspector chrome (#6691) 2026-08-26 02:58:40 -07:00
Tyler Slaton dbd7801872 docs: remove the open-a-PR-up-front agent rule (#6703) 2026-08-26 02:58:04 -07:00
Alem Tuzlak fd7f2fa683 Merge origin/main into alem/hud-arrow-color 2026-08-26 11:36:50 +02:00
Alem Tuzlak c4d27b7815 docs: remove the open-a-PR-up-front agent rule
Agents must not open a PR after the first commit.

Open a PR only when the work is ready.
2026-08-26 11:30:13 +02:00
Alem Tuzlak 3759fb7ae1 docs: stop telling agents to open PRs as drafts
Agents still open a PR after the first commit.

The create command no longer uses --draft, and there is no ready-flag step.
2026-08-26 11:27:14 +02:00
Tyler Slaton 7998cf2c32 fix(web-inspector): align launcher HUD availability (#6699)
## Problem

The closed Inspector launcher derived its status from raw transport
flags, so an unentitled runtime could show Threads, Intelligence, and
Learning as enabled while Home correctly showed them unavailable.

## Why

The hover HUD and Home were evaluating different sources of truth for
feature availability.

## Fix

Derive HUD statuses from the same Home feature projection and add
coverage for an Intelligence transport with a `none` license.
2026-08-25 18:47:42 -07:00
Tyler Slaton a6751df682 fix(web-inspector): theme launcher HUD with inspector 2026-08-26 03:40:30 +02:00
Tyler Slaton d2e0c35cae fix(web-inspector): align launcher HUD shell styling 2026-08-26 03:38:32 +02:00
Tyler Slaton d2a6eee07a fix(web-inspector): align launcher HUD availability 2026-08-26 03:29:38 +02:00
Tyler Slaton 786f4f525c chore: release monorepo v1.69.2 (#6698)
## Release monorepo v1.69.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.69.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.69.2`
   - Creates git tag `monorepo/v1.69.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.69.2
2026-08-25 17:40:24 -07:00
tylerslaton 9629e930d1 chore: release monorepo v1.69.2 2026-08-26 00:18:42 +00:00
Tyler Slaton 2ad03320fe fix(inspector): clarify local action availability (#6696)
## Problem

Event Snippets adds bookmark controls across assistant, thinking,
tool-call, A2UI, and generative UI surfaces, but snippet saving is not
ready to ship.

## Why

The Inspector needs one clear local-development entry point without
exposing unfinished save or replay behavior.

## Fix

Revert the complete Event Snippets feature in one mainline rollback,
removing every snippet button, the Inspector pane, storage/replay
support, framework wiring, and related documentation. Retain the direct
🪁 `View in Inspector (local only)` control with its explicit
local-development hover explanation.
2026-08-25 17:16:52 -07:00
Tyler Slaton b3b339f544 Revert "feat(web-inspector): add Event Snippets and save-as-snippet (#6649)"
This reverts commit ba4260ad66, reversing
changes made to 47c5510b49.
2026-08-26 02:11:19 +02:00
Tyler Slaton dc916484ab fix(inspector): clarify local action availability
fix(inspector): group local developer actions

fix(inspector): refine local tools menu

fix(inspector): use direct local action
2026-08-26 02:11:19 +02:00
Mike Ryan b3c3cb0d7b test(web-inspector): stop the gesture tests racing the beat on real timers (#6693)
## Problem

`packages/web-inspector/src/__tests__/launcher-error-signal.spec.ts` is
failing intermittently on `test / unit`, across unrelated branches and
on `main`. It is currently red on the **v1.69.1 release PR**.

```
FAIL  src/__tests__/launcher-error-signal.spec.ts >
      the whole gesture completes on its own and leaves the resting state behind
AssertionError: expected 'opening' to be 'closed'
```

| When | Branch | Shard | Run |
|---|---|---|---|
| 19:29Z | `release/publish/monorepo/v1.69.1` | Node 24 / React 18 |
[32886136479](https://github.com/CopilotKit/CopilotKit/actions/runs/32886136479)
|
| 14:09Z | `ben1/oss-924-agui-core-058` (#6687) | Node 24 / React 19 |
[32857130830](https://github.com/CopilotKit/CopilotKit/actions/runs/32857130830)
|
| 12:41Z | `lukas/oss-903-presentation-…` | Node 20 / React 18 |
[32848616256](https://github.com/CopilotKit/CopilotKit/actions/runs/32848616256)
|

Neither #6687 (an `@ag-ui/core` version bump) nor the presentation
branch touches gesture timing, and no shard fails consistently — it
follows runner load, not code.

## Cause

Both real-timer tests in this suite asserted a *pre-beat* state 200ms
after breaking the connection:

```ts
const context = await setup({ realTimers: true });
await context.breakConnection();
await context.advance(200);
expect(pillPhase(context.inspector)).toBe("closed");   // ← races the beat
```

Under `realTimers`, `advance(ms)` is a literal `setTimeout(resolve, ms)`
(spec L656–664). The pill's `closed → opening` transition fires at
`ERROR_GESTURE_MS.beat = 400` (`index.ts` L330–339, scheduled at
L18998). So the assertion had a **200ms margin against a 400ms boundary
on a wall clock**. On a loaded runner the 200ms sleep overshoots 400ms,
the beat has already fired, and the phase reads `opening`.

The comment directly above the test already says phase boundaries are
asserted on the fake clock "because real timers would make this suite
slow and flaky" — and then this was a phase-boundary assertion on real
timers.

The same 200-vs-400 race sat in the adjacent test (`pulsing` is true
only for the beat's 400ms), so both are fixed here.

## Fix

Remove the two racy preconditions. Both claims are already pinned
deterministically on the fake clock at spec L411–427, which asserts
`pillPhase === "closed"` **and** `pulsing === true` right after arming,
then walks every phase boundary. These real-timer tests exist only to
show the beat and the gesture run to their end on their own — which the
loops and their closing assertions still prove.

This also matches the idiom the sibling `launcher-signal.spec.ts`
already uses (L667–676): assert at t≈0, then poll for the end.

## Testing

**1. Reproduced the CI failure locally.** Injected a 250ms stall before
the assertion on the unmodified test, simulating a loaded runner (200ms
sleep + 250ms ≈ 450ms > the 400ms beat):

```
FAIL  src/__tests__/launcher-error-signal.spec.ts > the whole gesture completes on its own …
AssertionError: expected 'opening' to be 'closed' // Object.is equality
Expected: "closed"
Received: "opening"
 ❯ src/__tests__/launcher-error-signal.spec.ts:2666:40
```

Byte-for-byte the CI assertion.

**2. The fix survives that same simulation.** With a 700ms stall (well
past the beat) injected into both tests:

```
 ✓ the beat ends and leaves the resting dot behind  750ms
 ✓ the whole gesture completes on its own and leaves the resting state behind  3435ms
 Tests  2 passed | 88 skipped (90)
```

**3. Mutation-checked that the remaining assertions still have teeth.**
Three separate breaks to `src/index.ts`, each caught:

| Mutation | Result |
|---|---|
| Gesture opens but never closes (drop the `closing` phase +
`endGesture`) | `FAIL … AssertionError: expected <span …> to be null` |
| Pill never opens at all (`openPill` returns early) | `FAIL …
AssertionError: expected false to be true` (`sawOpenPill`) |
| Beat never ends (`beat: 400` → `999_999`) | `FAIL … AssertionError:
expected true to be false` (`pulsing`) |

Source restored afterwards; `git status` confirms this PR touches only
the spec file.

**4. Full `@copilotkit/web-inspector` suite:**

```
 Test Files  29 passed (29)
      Tests  625 passed (625)
```

**5. Pre-commit gate** (`test-and-check-packages`: test, publint, attw
across 5 projects + 22 dependencies, incl. `@copilotkit/react-core`,
`@copilotkit/angular`, `@copilotkit/runtime`) passed on the committed
tree.

## Note

`main` also has a second, unrelated flake I did not touch here —
`CopilotChatToolRerenders.e2e.test.tsx > should not re-render a
completed tool call when subsequent text is streamed` (`expected 4 to be
3`), which reddened `main` at `0943c519` ([run
32750372113](https://github.com/CopilotKit/CopilotKit/actions/runs/32750372113)),
a runtime `.d.ts` change that touches no react-core chat code. Different
mechanism, worth its own issue.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-25 14:22:03 -07:00
Tyler Slaton cc74d650ca fix(docs): stop shipping stale Intelligence config claims, and gate the dead hosts (refs OSS-961) (#6694)
Follow-up to OSS-961. The reported failure — a `both-oss` conversion
cell that turned a working OSS dashboard into a 502 — was caused by the
packaged runtime skill in **v1.62.2** prescribing `api.copilotkit.ai` /
`realtime.copilotkit.ai`:

```
{ status: 404, body: '', path: '/api/threads' }
```

That signature is exact. `api.copilotkit.ai` and
`api.cloud.copilotkit.ai` resolve to the **same three IPs** — the legacy
Copilot Cloud ALB. With `Host: api.cloud.copilotkit.ai` a listener rule
matches and Express answers; with `Host: api.copilotkit.ai` nothing
matches, so the ALB returns its default action, a 404 with
`content-length: 0`. `realtime.copilotkit.ai` has no DNS record at all.

The hosts themselves were already fixed in **v1.64.0** (`3c1c85937b`
corrected the reference, `3f0bbe4a7b` made `apiUrl`/`wsUrl` optional
with managed defaults), so the ticket's "the fix regressed or never
reached that surface" is not what happened — the failing run was on a
runtime released three and a half weeks before the fix. Bumping the
fixture pins is the actual unblocker and lives in the Intelligence repo,
not here.

What this PR fixes is the residue that survived on `main`, plus the
missing gate.

## Changes

**`skills/copilotkit-debug/references/runtime-debugging.md`** — the line
"Requires `CopilotKitIntelligence` configuration with `apiUrl`, `wsUrl`,
`apiKey`, `tenantId`" carried three errors:
- `apiUrl`/`wsUrl` have been optional with managed defaults since
v1.64.0
- `tenantId` has never existed on `CopilotKitIntelligenceConfig` (it is
a Teams adapter concept)
- there is no org or tenant field to pass at all — the API key's token
format is `cpk-{projectId}_{shortToken}_{longToken}` and app-api
resolves `organizationId` + `projectId` from the key row server-side.
`ThreadSummary.organizationId` is a *response* field, not config.

This mattered because it is the one remaining page that would send a
reader back to hand-filling URLs, which is how the dead host got picked
up in the first place.

**`CopilotKitIntelligence` TSDoc** — the class's own hover docs showed
only `https://intelligence.internal` /
`wss://realtime.intelligence.internal`, so they never named the pair
that serves prod. Now names the managed pair explicitly, and the
override example uses `*.example.com` so it can't be mistaken for a real
host.

**`scripts/validate-intelligence-env-names.ts`** — extended to fail on
either dead host anywhere in the repo. This validator is already the
unfiltered drift guard for exactly this config surface (added by
OSS-881), and both its CI workflow and its lefthook step are
deliberately un-globbed, so the new rule needed no new wiring.

`packages/channels-intelligence/src/realtime-gateway.test.ts` is
allowlisted rather than rewritten: it needs a hostname that genuinely
does not resolve, because `getaddrinfo ENOTFOUND` is the condition under
test. Swapping in a live host would silently void the assertion.

## Testing

**The gate fires (mutation check).** Appended the dead pair to a shipped
skill, then reverted:

```
$ printf 'apiUrl: "https://api.copilotkit.ai"\nwsUrl: "wss://realtime.copilotkit.ai"\n' \
    >> skills/runtime/references/intelligence-mode.md
$ tsx scripts/validate-intelligence-env-names.ts
Found 2 non-canonical Intelligence references:

  skills/runtime/references/intelligence-mode.md:366  api.copilotkit.ai — routes nothing (empty-body 404); use api.intelligence.copilotkit.ai
  skills/runtime/references/intelligence-mode.md:367  realtime.copilotkit.ai — does not resolve; use realtime.intelligence.copilotkit.ai
exit=1
```

**Clean tree passes, and the real hosts are not false-positived**
(`--fixed-strings` means `api.copilotkit.ai` does not match
`api.intelligence.copilotkit.ai`; the tree contains many mentions of the
latter):

```
$ tsx scripts/validate-intelligence-env-names.ts
Intelligence env var names and hosts are canonical.
```

**Host claims re-verified against prod, 2026-08-25:**

```
$ curl -D- -X POST https://api.intelligence.copilotkit.ai/api/threads
HTTP/2 401    x-powered-by: Express    content-length: 241

$ curl -D- -X POST https://api.copilotkit.ai/api/threads
HTTP/2 404    server: awselb/2.0    content-length: 0

$ curl -D- https://api.cloud.copilotkit.ai/          # same ALB, matching host
HTTP/2 404    x-powered-by: Express    content-length: 63

$ dig +short api.copilotkit.ai        -> 3.220.212.155 54.211.52.23 35.168.142.32
$ dig +short api.cloud.copilotkit.ai  -> 54.211.52.23 3.220.212.155 35.168.142.32
$ dig +short realtime.copilotkit.ai   -> (no record)
```

**Other gates:**

```
$ tsc --noEmit --strict scripts/validate-intelligence-env-names.ts     # clean
$ oxfmt --check <changed files>            All matched files use the correct format.
$ tsx scripts/sync-plugin-skills.ts --check    plugin skill mirror in sync
$ vitest run scripts/__tests__/public-skill-drift.test.ts \
             scripts/__tests__/sync-plugin-skills.test.ts
  Test Files  2 passed (2)    Tests  19 passed (19)
```

Committed with `core.hooksPath=/dev/null` — this worktree borrows
`node_modules` by symlink, so lefthook's native deps can't load. The
hooks' own checks were run by hand above.

## Not in this PR

- **The fixture pins.** 45 `both-oss` cells still resolve a pre-1.64.0
runtime; that's in the Intelligence repo.
- **Whether `api.copilotkit.ai` should stop answering.** It is not an
unclaimed name — it is aliased onto the live Copilot Cloud ALB. Making
it the public Intelligence alias would mean adding an Intelligence
listener rule to the *legacy Cloud* balancer, which straddles two
products. Worth its own issue.
- **Compiling the packaged skill snippets.** `scripts/doc-tests` only
extracts from `showcase/shell-docs/src/content`, so the 601 TS snippets
across 75 shipped skill files — the surface an agent-driven install
actually follows — are never type-checked. That is the structural reason
a non-existent config property survived in the runtime skill until
OSS-881 caught it by hand. Too large for this PR; it deserves its own.
2026-08-25 13:55:40 -07:00
Benjamin Taylor b8283ef4e1 refactor(scripts): match dead hosts case-insensitively, carry each reason with its host
DNS is case-insensitive, so a capitalized host in prose would have slipped the
literal match. Env var names stay case-sensitive — `ignoreCase` is opt-in per
rule. The per-host reason moves onto the constant so adding a third host cannot
silently inherit the wrong message.

Also restores the TSDoc's original framing of what an override is for
("non-production or future self-hosted"), matching the runtime skill's wording
rather than diverging from it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 15:45:27 -05:00
Benjamin Taylor b057744684 fix(docs): stop shipping stale Intelligence config claims, and gate the dead hosts (refs OSS-961)
The packaged runtime skill up to v1.62.2 prescribed `api.copilotkit.ai` /
`realtime.copilotkit.ai`. The first host is a CNAME onto the legacy Copilot
Cloud ALB, where no listener rule matches it, so every request gets the ALB
default action: a 404 with an empty body. The second has no DNS record at all.
A reader who followed that page converted a working OSS install into a 502.

The hosts themselves were corrected in v1.64.0, but two shipped surfaces still
carried stale claims about the same step, and nothing stopped the hosts from
coming back a third time:

- The debug skill said Intelligence "requires ... `apiUrl`, `wsUrl`, `apiKey`,
  `tenantId`". Three errors in one line: `apiUrl`/`wsUrl` have been optional
  with managed defaults since v1.64.0, and `tenantId` has never existed on
  `CopilotKitIntelligenceConfig` — the API key carries the project (its token
  format is `cpk-{projectId}_...`) and the platform resolves the organization
  server-side, so there is no org or tenant field for a caller to pass.
- `CopilotKitIntelligence`'s own TSDoc showed only `*.internal` placeholders,
  so the class's hover docs never named the pair that actually serves prod.

`validate-intelligence-env-names` — already the unfiltered guard for this same
config surface (OSS-881) — now also fails on either dead host. The
channels-intelligence realtime test is allowlisted: it needs a hostname that
genuinely does not resolve, since `getaddrinfo ENOTFOUND` is the condition
under test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 15:35:55 -05:00
Tyler Slaton 79c02f0f02 style(web-inspector): fewer layers on the launcher, a lens for its dot (#6688)
## What does this PR do?

Design review on the launcher and its notification dot asked for three
things: a milder face than solid black, fewer borders and background
layers, and a subtle shadow in place of the dot's heavy border. This is
all three, plus the removal of six utilities that never had any effect.

Everything here was compared side by side at production size, on a light
*and* a dark host page, before it was chosen. Two of my own first
proposals were dropped after measuring them, both described below.

**Two concerns, three commits.** `4fa38d91f` and `cd711b369` are the
launcher itself — the package change, 2 files. `295f75495` gives the
react-router lab a dark mode, because a dark host page is what this
change has to be judged against and the lab could not produce one. If
you would rather review those separately, say so and I will split them.

## The face

`#181C1F` at 95%, which review asked for.

Worth recording so it does not come up again: the near-black the review
saw was `#010507`, 20.5:1 against white. What shipped yesterday was
already `#1C1F24` at 16.5:1, so this value is a hair *darker* than the
one it replaces (17.2:1) and the difference between them is a ΔE of 2.3,
at the floor of what an eye can separate. It settles the question rather
than changing the look.

## Fewer layers

Six Tailwind utilities on the launcher set properties the unlayered
`css` block sets again — `bg-slate-950/95`, `border-white/20`, `ring-1`,
`ring-white/10` and the two hover variants. Unlayered declarations beat
layered ones regardless of specificity or source order, so none of them
has ever had any effect. Each was the package's only use, so the
checked-in stylesheet drops 980 bytes.

Of the *visible* layers, two went:

**The outer hairline.** The launcher carried two concentric lilac rings:
the border, and a second one 1px outside it as a box-shadow. The outer
one also hardcoded the lilac rather than reading `--cpk-launcher-edge`,
so it silently could not follow the token. It is replaced by a one-pixel
light edge along the top, which is what keeps the face from reading flat
without drawing a frame.

**`backdrop-blur-md`.** It sat behind a 95%-opaque fill and bought close
to nothing, while mounting a permanent blur compositing layer over a
customer's page.

**The border stays, and this is the finding that changed my mind.** I
first proposed removing it too. Against a dark host page the face
measures 1.10:1 (GitHub dark), 1.04:1 (Tailwind slate-900) and 1.22:1
(black) — indistinguishable from the page. The border is the only thing
that gives the launcher an outline there. It is not decoration.

## The dot

The collar was `1.5px`, opaque, zero blur, and 21% of the dot's
footprint. Because the dot's centre sits *on* the rim, its outer half
painted a hard dark crescent onto the **host page** rather than onto the
launcher — which is what read as "heavy". A hairline plus a soft drop
separates it just as well.

The fill becomes a lens lit from the upper left. Both stops are derived
from `--cpk-launcher-signal`, so a new tone needs no new values;
verified for the rose error tone and the violet announcement tone.

**Dropped after looking at it:** a coloured glow around the dot. It was
the obvious reading of "more premium", but the launcher already pulses
in that same colour when a failure is new, and a permanent glow competes
with the thing that is supposed to draw the eye.

**Also dropped:** tinting the border in the signal colour, which was
suggested in review. On a dark page the border is the entire silhouette,
so tinting it recolours the whole launcher for a state that can persist
for hours.

## One non-obvious consequence

Removing the blur removed a side effect nobody had written down:
`backdrop-filter` promotes the element to its own compositing layer.
Without a layer, the hover `scale(1.05)` re-rasterises the mark every
frame and it visibly jitters — geometrically nothing moves, the mark's
centre holds to three decimals, but the vector is re-rendered at
fractional offsets. `will-change: transform` asks for the layer directly
and the jitter is gone. Confirmed by eye on the running demo before this
was chosen.

## Tests

`packages/web-inspector` stays at **28 files / 611 tests**, all passing.

No new tests. The colour tests here are deliberately token-shaped rather
than value-shaped — they assert the custom property and the *sharing* of
one face and one edge between the launcher and its pill, never a hex —
so face and edge values are free to move and this change is exactly the
kind they were written to allow. The one test that constrains it, `"the
pill and the launcher share one surface and one edge"`, still passes.

What is genuinely unguarded, and was before this PR too: the dot's
collar width, the double hairline, and the Tailwind class list.
Asserting rendered geometry would need a browser test runner, which this
package does not have — jsdom computes no layout.

## How to see it

`pnpm --filter react-router-example dev`, then `http://localhost:5173`.
The launcher is top right; `Break runtime` arms the error tone and
`Break run` the announcement one. Hover it to check the mark no longer
jitters.

One thing worth knowing while reviewing: the launcher anchors top-right
and is `position: fixed` on an element mounted directly under `<body>`,
so on this page it sits over the lab's toolbar. Drag it to the lower
right and it is out of the way.

## The lab's dark mode

A dark host page is where the launcher's border earns its place, and the
lab had no way to produce one, so reviewing this change was not possible
without it.

It follows `examples/v2/react/demo` rather than inventing anything: the
host owns a `theme` state, and `CopilotChat` gets `className="dark"` —
which is what makes the package swap its own variable set. The colours
are the demo's by another route; it writes the oklch literals
CopilotKit's variables use, and those are Tailwind's neutral steps
(`neutral-950` is `oklch(0.145 0 0)`, `neutral-50` is `oklch(0.985 0
0)`, `neutral-800` is `oklch(0.269 0 0)`). Measured identical on the
running lab.

`@custom-variant dark (&:is(.dark *))` is needed in the lab's stylesheet
because Tailwind v4 points `dark:` at `prefers-color-scheme` by default,
so the toggle would have lost to the OS. Same declaration the package
uses for its own sheet.

Two details that are decisions rather than oversights. The **error
banner keeps a rose tint** in dark mode instead of going neutral,
because an error banner that looks like every other surface is not an
error banner. And the **toolbar buttons keep a visible on/off contrast**
— active inverts to a light face, inactive sits on `neutral-800` —
because the lab's whole purpose is knowing which failure is currently
armed.

My first attempt stripped every background instead of theming, and that
is worth recording because it looked plausible: the chat bubble, the
send button, the button states and the banner all collapsed into one
flat grey. The chat paints its own surfaces and has to be told what
theme it is in, not undressed.

## A separate bug found on the way

`CopilotKitProvider` documents `inspectorDefaultAnchor` — *"Default
anchor corner for the inspector button and window"* — and it has no
effect. `defaultAnchor` is typed on the React wrapper and forwarded to
the element, but the string `defaultAnchor` does not occur anywhere in
`packages/web-inspector`, so it lands as `defaultanchor="[object
Object]"` and is ignored. The corner stays hardcoded `{ horizontal:
"right", vertical: "top" }` in two places.

Not fixed here, to keep this PR to one concern. It is worth fixing: any
host with a top navigation bar hits exactly this, finds exactly that
prop, and it does nothing.

## Related PRs and Issues

- Follows #6646
2026-08-25 13:27:06 -07:00
Mike Ryan 35a4820cec chore: release monorepo v1.69.1 (#6692)
## Release monorepo v1.69.1

**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.69.1`
   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.69.1`
   - Creates git tag `monorepo/v1.69.1`
   - 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.69.1
2026-08-25 12:57:50 -07:00
Benjamin Taylor 04684614ca test(web-inspector): stop the gesture tests racing the beat on real timers
The two real-timer tests in the launcher's error-signal suite each asserted a
pre-beat state 200ms after breaking the connection. The beat is 400ms
(ERROR_GESTURE_MS.beat), so both assertions had a 200ms margin against a wall
clock on a shared runner. When the runner is loaded the 200ms sleep overshoots
400ms, the beat has already fired, and the assertion reports the next phase.

Both claims are already pinned deterministically on the fake clock, where every
phase boundary of the gesture is asserted. The real-timer tests exist only to
show the beat and the gesture run to their end on their own, so the racy
preconditions are removed rather than retimed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 14:46:25 -05:00
MikeRyanDev 6053e4e262 chore: release monorepo v1.69.1 2026-08-25 18:50:37 +00:00
Ben Taylor bf2068734b docs: fix missing space in CONTRIBUTING heading (#6681)
Fix 1)Install -> 1) Install in CONTRIBUTING.md
2026-08-25 12:41:34 -05:00