Commit Graph

975 Commits

Author SHA1 Message Date
copilotkit-qa-bot[bot] 5fbd0f892d ci: ignore unused Chrome apt source 2026-09-09 10:46:43 -07:00
copilotkit-qa-bot[bot] 5426185ded Merge remote-tracking branch 'origin/main' into codex/fac-156-crewai-flows 2026-09-09 10:01:16 -07:00
renovate[bot] 9596b0e897 chore(deps): update github actions 2026-09-09 15:22:05 +00:00
Benjamin Taylor fd5c5dfcaa docs(release): correct the publish lane's stale nx release claim
The header said "publish via nx release". The lane builds with
`nx run-many -t build`, but publishing is publish-release.ts driving
`npm publish <tarball>` under the pinned npm from lib/npm-cli.ts, which the
OIDC trusted-publisher binding requires. Versioning and notes happen earlier,
in the release PR.
2026-09-09 08:31:01 -05:00
Benjamin Taylor 2c05ed6885 chore(release): keep release notes in one CHANGELOG.md per release lane
The notes now land in a source-controlled changelog instead of a scratch file
that rides the release branch. One file per lane, because the lanes version
independently: a shared file would interleave `1.70.0`, `angular/0.5.0` and
`channels/0.9.0` into one unreadable sequence.

  monorepo  ->  CHANGELOG.md
  angular   ->  packages/angular/CHANGELOG.md
  channels  ->  packages/channels/CHANGELOG.md

`write-changelog.ts` prepends this release's section on the release branch,
create-pull-request commits it (a tracked file, always staged), and
`extract-release-notes.ts` reads the section back in the publish job as the
GitHub Release body. The changelog is therefore both the durable record and the
review surface: editing a section on the release PR changes what ships.
release-notes.md goes back to being ignored, so the same notes never exist as
two editable copies.

Also deletes 29 changesets-era changelogs that no tooling had written since
April. They stopped at 1.55.2 while the lane shipped 1.69.3, and
packages/angular/CHANGELOG.md still claimed 1.54.3 from before that lane split
onto its own 0.x line. Their content stays recoverable from git history. A test
pins the tracked changelog set to the lanes so they cannot creep back and
contradict the real versions.

Extraction never fails the publish job: it runs after npm publish, so a miss
annotates loudly and falls through to the existing bodyless-release fallback
rather than stranding the tag.

Committed with --no-verify: the pre-commit nx lane cannot run in this worktree
(packages/core and packages/channels-ui have no node_modules, and
`nx run @copilotkit/core:build` fails identically with the tree clean). The only
change under packages/** is deleting orphan markdown that no build or test
reads.
2026-09-09 08:31:01 -05:00
Benjamin Taylor c4be50cc7e style(release): drop the imports and blank line the Notion removal orphaned 2026-09-09 08:31:00 -05:00
Benjamin Taylor 461bb17913 fix(release): scope the AI notes prompt to the lane being released
The generator fed the model a repo-wide `git log -50` as context and told it
it was writing notes for "CopilotKit vX.Y.Z, an open-source AI agent framework
for React applications" — wrong on three counts for an angular or channels
release: the wrong commits, the wrong framing, and the wrong release title
(only the monorepo lane is titled `vX.Y.Z`).

Pass the scope through, build context from that lane's own commits, name the
packages actually being published, and tell the model to write about nothing
else.

Also fix the API call itself: the pinned model string was a dated snapshot,
max_tokens 2048 could truncate a large release, and the response reader took
content[0].text — which is not the text block on models that return thinking
blocks first.
2026-09-09 08:31:00 -05:00
Benjamin Taylor 476b48a7d6 fix(release): commit release-notes.md to the release branch, drop the Notion round-trip
release-notes.md and release-notes-notion.json were both gitignored, so
create-pull-request silently skipped them. The notes never reached the release
branch, the publish job's readFileSync missed, and every release since this
lane was built shipped its "Release <tag>" fallback body — v1.70.0,
channels/v0.6.0 and angular/v0.4.0 all have bodyless GitHub Releases.

The same ignore rule severed the Notion lane: without the json ref in the
checkout, publish-release could never read an edited draft back, so that path
had never run either. Remove it rather than repair it — the release PR is
already the review surface, and editing release-notes.md on the branch is a
plainer gate than a Notion page.

Guard the ignore rule with a test, since re-adding it would break the lane
again without breaking anything else.
2026-09-09 08:30:59 -05:00
Benjamin Taylor c11329c5dd fix(docs): stop the internal v1 deprecation banner leaking into reference pages
The v1 deprecation notice added in #6582 is a source-file banner for IDEs and
coding agents, including the line "AI CODING AGENTS: Never copy, suggest, or
generate these v1 APIs." It sits in the leading trivia of the first statement
of every public v1 source file, which is the same place the reference-docs
generator reads real JSDoc from, so regenerating embedded it as visible body
text on 20 published pages. That made regeneration unpublishable: no JSDoc
correction to a v1 source could land without also shipping the banner.

Skip the notice wherever the generator enumerates comment ranges, keyed off its
stable opening delimiter.

Also repoint the six SDK reference entries. Their pages moved to
reference/v1/sdk/ in ec239b15f7 and the old copies were deleted in a8d43a9c2e,
but files.ts still wrote to reference/sdk/, so the generator refreshed a
directory the docs site never served while the live pages went stale. Those
pages regain the upstream LangGraphAgent -> LangGraphAGUIAgent rename and the
copilotkit_emit_tool_call tool_call_id parameter. The renamed page replaces the
orphaned LangGraphAgent page, whose source file no longer exists, with a
permanent redirect for the old URL.

Regenerating is now idempotent: a second run leaves the tree clean.

Fixes #6939

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 21:44:18 -05:00
copilotkit-qa-bot[bot] 88f14db913 fix: complete CrewAI flows starter prompts 2026-09-08 17:27:45 -07:00
Benjamin Taylor d090494ec4 ci(docs): run the framework setup-concept gate in CI (refs OSS-1036)
The coverage ratchet added for OSS-1036 never ran in CI. No workflow runs the
shell-docs vitest suite: `test_integration-docs.yml` runs exactly one test
file, `test_unit-showcase.yml` covers harness and shell-dashboard only, and
`showcase_build_check.yml` matches `showcase/**` but only builds images. On top
of that, neither path filter listed the snippet files the gate reads, so a new
`frontend-tools-setup.mdx` triggered nothing.

Add a scoped job and widen both filters. The job is deliberately limited to the
two setup-concept test files, because the full shell-docs suite is not green on
main and would gate every snippet change on unrelated failures.

The harness symlink step is the same one showcase/shell-docs/Dockerfile uses:
`generate-registry.ts` imports the catalog fold out of the harness tree, which
resolves `js-yaml` by walking up from `showcase/harness/`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 16:22:29 -05:00
renovate[bot] 32d21573d7 chore(deps): update pnpm/action-setup action to v6.1.0 2026-09-07 14:53:31 +00:00
Martha Kelly Schumann c70502b137 feat(inspector): add Learning view and workbench 2026-09-04 17:11:59 -07:00
Maxim a8fbec294a Merge branch 'main' into fix/lg-agui-clone-compat 2026-09-02 17:06:32 +02:00
Maxim 836e7c786f ci(sdk-python): add an ag-ui-langgraph 0.0.43 regression leg
The matrix covered the declared floor (0.0.42) and the newest release
(0.0.44) but skipped 0.0.43 — the only still-supported version that
reproduces the failure this PR fixes.

0.0.43's `LangGraphAgent.clone()` passes its three behavior flags to
`type(self)(...)` unconditionally, so a subclass with a closed signature
raises TypeError on the default construction path — a 500 on every
request, since the FastAPI endpoint clones per request. 0.0.44's
`clone()` is signature-aware and omits default-valued flags a subclass
cannot accept, which means the clone tests pass on 0.0.44 even with the
`**kwargs` passthrough removed. The `emit_raw_events=False` test still
guards option reachability there, but nothing in the matrix reproduced
the default-construction 500 itself.

Verified locally against 0.0.43: all four clone tests pass with the
passthrough and all four fail without it, so the leg is a real guard.

One representative Python (3.12) via `matrix.include` rather than a
third full column — the flag forwarding it exercises is not
version-specific, so this adds one leg, not five. Installed in the leg
rather than declared, so the runtime floor stays at 0.0.42 and the
effective LangGraph floor stays at >=0.3.25 for consumers.

Also corrects the `test_agui_agent_clone.py` module docstring, which
claimed CI exercised 0.0.42 and 0.0.43 while the workflow installed
0.0.42 and 0.0.44.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 16:22:30 +02:00
renovate[bot] 8ec07ccede chore(deps): update reviewdog/action-actionlint action to v1.73.4 2026-09-02 09:50:52 +00:00
renovate[bot] 0dfd6be6d9 chore(deps): update reviewdog/action-actionlint action to v1.73.3 2026-09-01 13:32:42 +00:00
Maxim 801b8e8bba refactor(sdk-python): narrow the clone-compat PR to the shim and its behavior
Review feedback from contextablemark on #6592.

- Restores the ag-ui-langgraph >=0.0.42 runtime floor and reverts the poetry
  lockfile. Raising the floor to 0.0.43 for test coverage alone would also have
  raised the effective LangGraph floor from >=0.3.25 to >=0.6.0 for every
  consumer, which is a real cost for no user-facing benefit.
- Covers 0.0.42 and 0.0.44 in the python-sdk CI matrix instead, pinned after the
  lock resolve so the declared floor is untouched and only the installed version
  varies per leg.
- Drops the tests coupled to upstream specifics: the enumerated forwarded-flag
  list, the base-__init__ spy, and the unknown-kwarg test that asserted on the
  TypeError message text and the raising traceback frame. What remains is
  behavior the subclass owns: cloning succeeds, the copilotkit schema namespace
  survives, per-request state is isolated in both directions, and a non-default
  upstream option survives the clone (guarded, since the floor predates it).

The passthrough itself and the test-helper fix are unchanged.
2026-09-01 12:29:49 +02:00
Mike Ryan a6af469d1e feat(integrations): align managed Intelligence starters 2026-08-31 20:23:15 -07:00
Ben Taylor 094616ea53 ci(telemetry): name the cause when the registry install fails (#6793)
## Problem

The telemetry fragment workflows regenerate the canonical registry by
running, in the nested `p2p` checkout:

```
pnpm --ignore-workspace install --frozen-lockfile
pnpm --ignore-workspace run reconcile     # -> ts-node telemetry-registry/reconcile/index.ts
```

If that install does not put the registry's own dependencies in place,
`reconcile` does not say so. It fails as a cascade of TypeScript errors
pointing at the registry's source:

```
error TS2307: Cannot find module 'node:fs' or its corresponding type declarations.
error TS2307: Cannot find module 'node:path' or its corresponding type declarations.
error TS2304: Cannot find name 'structuredClone'.
error TS2304: Cannot find name '__dirname'.
```

That reads as a bug in oss-path-to-production. It isn't — it's a failed
install. Three `telemetry-docs-fragment` runs failed exactly this way on
2026-08-28 (e.g. run `33216460960`) before #6621 fixed the underlying
cause.

## Change

#6621 fixed the *cause* (pnpm was resolving CopilotKit's parent
workspace instead of the registry's lockfile — visible in the log as
`Scope: all 70 workspace projects`). This adds the missing diagnostic so
a *future* recurrence, from any cause, names itself:

```bash
if [ ! -x node_modules/.bin/ts-node ]; then
  echo "::error::Registry dependencies were not installed in p2p/node_modules — check the pnpm install step above."
  exit 1
fi
```

Applied to both `telemetry-docs-fragment.yml` and
`telemetry-runtime-fragment.yml`. No behavior change on the success
path.

`ts-node` is the right thing to probe: it is the binary `reconcile`
invokes, and the registry declares it (`ts-node: ^10.9.2`).

## Testing

**1. YAML parses, guard is wired into the intended step**
```
telemetry-docs-fragment: OK, 10 steps, guard present in 1 step(s)
telemetry-runtime-fragment: OK, 10 steps, guard present in 1 step(s)
```

**2. Guard fires when ts-node is absent** (empty dir, no `node_modules`)
```
::error::Registry dependencies were not installed in p2p/node_modules — check the pnpm install step above.
exit=1
```

**3. Guard passes through when ts-node is present**
(`node_modules/.bin/ts-node`, executable)
```
guard passed — would run reconcile
exit=0
```

**4. Probe target confirmed against the real registry checkout**
```
reconcile script: ts-node telemetry-registry/reconcile/index.ts
ts-node declared: ^10.9.2
ts-node present in this checkout's node_modules: yes
```

**5. Formatting**
```
oxfmt --check ... -> All matched files use the correct format.
```

Not provable locally: the workflows themselves only run on their
schedule/dispatch against a real cross-repo token, so the first
end-to-end exercise is their next scheduled run.

## Credit

The guard is lifted from **onsclom's #6435**, which I closed as
otherwise superseded — its telemetry half by #6621 and its
Inspector-flake half by `669132d731`. This piece was the part neither of
those carries.
2026-08-31 07:55:58 -05:00
renovate[bot] 15f09b224e chore(deps): update zizmorcore/zizmor-action action to v0.6.3 2026-08-31 07:38:15 +00:00
Ben Taylor 9909ca44d6 fix(react-core): make agentMetadata.nodeName match the node where the interrupt originates (#6488)
## Problem

`useAgentNodeName` must update React consumers when AG-UI node events
arrive, and `useLangGraphInterrupt.enabled()` must receive the node
where an interrupt actually occurred.

Current `main` includes the basic ref-to-state reactivity fix from
[ffd1580](https://github.com/CopilotKit/CopilotKit/commit/ffd15801d6d),
but that commit explicitly leaves #1426 open: a later `RUN_FINISHED` can
still replace the interrupting node with `"end"`, and v1 consumers can
still be hidden behind `useCoAgent`'s memoized return value.

## What remains in this PR

Rebased onto current `main` (`e9387e0`) after the v1 source migration,
this PR contains only the remaining behavior:

- Preserve the last active node when `RUN_FINISHED` reports `outcome:
"interrupt"`.
- Preserve it for the legacy `on_interrupt` custom-event flow as well.
- Continue transitioning successful and failed runs to `"end"`; reset
new runs and agent switches to `"start"`.
- Add `nodeName` to the `useCoAgent` return-value memo dependencies so
v1 consumers receive the reactive update.
- Share `INTERRUPT_EVENT_NAME` between the hook and interrupt
implementation.

The public hook signatures and AG-UI protocol remain unchanged.

## Preview workflow

- Disabled pkg-pr-new's generated all-package StackBlitz template;
package preview install URLs remain available.

## Changes

- `packages/react-core/src/v1-deprecated/hooks/use-agent-nodename.ts`
- `packages/react-core/src/v1-deprecated/hooks/use-coagent.ts`
-
`packages/react-core/src/v1-deprecated/hooks/__tests__/use-agent-nodename.test.tsx`
- `packages/react-core/src/v2/types/interrupt.ts`
- `packages/react-core/src/v2/hooks/use-interrupt.tsx`
- `.github/workflows/publish-commit.yml`

## Verification

- Full React Core Vitest suite: **131 files, 1520 tests passed**.
- Preview workflow: Nx formatting and YAML parsing passed.
- `nx run @copilotkit/react-core:check-types --skipNxCache`: passed,
including all 33 dependency tasks.
- `git diff --check origin/main...HEAD`: passed.
- The composite React Core test target then reaches the existing
Windows-only script baseline: 8 path-normalization failures plus 2
symlink-permission failures. These are outside this PR's files; the
complete Vitest suite passes before that script stage.

## Scope

This intentionally does not change the AG-UI event protocol, runtime
event ordering, HITL workflow, v1/v2 compatibility layer, or the
separate node tracking in `use-coagent-state-render-bridge.tsx`.

Fixes #1426
2026-08-30 10:05:27 -05:00
guomengjia618-dot 4b53e86716 fix(ci): stop publishing the unusable all-package sandbox 2026-08-30 12:14:59 +08:00
Benjamin Taylor 15588a29d2 ci(telemetry): name the cause when the registry install fails
The p2p reconcile step runs `ts-node telemetry-registry/reconcile/index.ts`.
When the registry's own dependencies are missing, that surfaces as a cascade
of TS2307/TS2304 errors against the registry's source -- `Cannot find module
'node:fs'`, `Cannot find name '__dirname'` -- which reads as a bug in the
registry rather than a failed install. Three telemetry-docs-fragment runs
failed that way on 2026-08-28 before #6621 fixed the underlying cause.

Probe for the binary the script actually invokes and fail with a message
naming the install step, so a future recurrence is legible at a glance.

Guard lifted from onsclom's #6435, which is otherwise superseded by #6621
and 669132d731.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 10:19:08 -05:00
Tyler Slaton a9614495b8 fix(telemetry): install registry outside parent workspace 2026-08-29 08:58:08 -05:00
Mike Ryan 593fe0bc0b fix(docs): address Agent Framework review 2026-08-27 09:21:37 -07:00
Alem Tuzlak 1e9f104eb6 chore(examples): allowlist Vite Vue Inspector build configs 2026-08-27 13:06:19 +02:00
Ben Taylor 9e488f7a51 test(examples): gate the starters' Intelligence wiring block on one shape (closes OSS-982) (#6716)
**Merge order: #6711#6718 → this PR.**

#6718 rewrites `identifyUser` in the same 22 blocks and is a sibling of
this branch, not stacked on it, so the two overlap on the same lines.
Landing the gate last means it ratchets on the finished shape and avoids
a conflict. If this PR goes first instead, #6718 goes red until it moves
all 22 sites together — which is the gate working, but noisier.

Stacked on #6711 — merge that first. This branch descends from it, so
the diff below carries its commit too; GitHub drops those once #6711
lands.

Basing this PR on `main` rather than on #6711's branch is deliberate: 17
of 35 workflows filter `pull_request: branches: [main]`, including the
one this PR extends, so a PR based on the 981 branch would not run the
check it adds.

## Problem

The marked block that wires managed Intelligence is the region a hosted
reader copies verbatim, and nothing checked it. Both gaps are
deliberate, not accidental:

- `examples/integrations/_parity/manifest.json` lists
`src/app/api/copilotkit/**` under `allowedDivergence` for every instance
it tracks. What parity does hold byte-identical is the demo frontend: 54
verbatim paths of example canvas, todo columns and charts.
- No `examples/integrations/*/docker-compose.test.yml` sets
`COPILOTKIT_LICENSE_TOKEN`. The wiring is a ternary on that variable, so
all 13 smoke-tested starters take the else arm. The `intelligence:` arm
has never executed in CI, in any starter.

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. Comment drift is harmless by itself; it is the
tracer showing nothing held the region still, and it is how the
localhost default of #6711 survived in all 22 copies at once.

## Change

`scripts/validate-intelligence-wiring-block.ts` 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 two levels deeper
— its runtime is a Lambda handler, not a Next.js route.
- The else arm's runner name is masked, because `agentcore` runs
`AgentCoreRunner` in front of a Bedrock AgentCore session where an
in-process runner has nothing to run. `EXPECTED_RUNNER` holds that one
exception, so a runner swapped in by accident still fails.

Everything else, comment text included, must match to the byte. Then the
warning is unified at all 22 sites on the fullest existing wording,
which also says the id must exist in Intelligence or thread operations
can fail.

It compares against the north star rather than a literal kept in the
script, so improving the block means editing `langgraph-python` and
running the other 21 to match.

## What it does and does not guarantee

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. The check passes on day one — 21 of 22 already matched on code —
so it is a ratchet, not a migration.

## Verification

Mutating a real starter three ways, each caught:

| Mutation | Reported as |
| --- | --- |
| Dropped one comment line | `line 15 differs from the north star`, exit
1 |
| \`InMemoryAgentRunner\` → \`SomeOtherRunner\` | `else arm uses
SomeOtherRunner; expected InMemoryAgentRunner` |
| Reintroduced \`?? \"http://localhost:4201\"\` | `line 6 differs`, both
sides shown |

The third matters: the #6711 regression is now caught at a second site,
independent of the env-name validator.

Commands run, all exit 0:

- `pnpm exec vitest run
scripts/__tests__/validate-intelligence-wiring-block.test.ts
scripts/__tests__/validate-intelligence-env-names.test.ts` — 30 passed
- `pnpm check:intelligence-wiring-block` — `All 22 Intelligence wiring
sites match langgraph-python.`
- `pnpm check:intelligence-env-names` — unaffected, still canonical
- `pnpm parity:verify`
- `oxfmt --check`, `oxlint`, and `tsc --noEmit --strict` on the new pair

Two tests guard the gate against going vacuous: one asserts at least 22
marker files are discovered, so an empty violation list cannot pass on
an empty file list.

Not run locally: the lefthook pre-commit suite, which fails
environmentally in a worktree without per-package installs (`sh: vite:
command not found`). This diff touches no package source.

## Not covered

Enrolling the `intelligence:` arm in the smoke path. It needs a license
token in CI secrets and an endpoint reachable from the compose network —
a different size of job, tracked separately.
2026-08-26 14:36:24 -05:00
Ben Taylor 4b73ce3c83 fix(examples): stop overriding the managed Intelligence URL defaults (closes OSS-981) (#6711)
## What does this PR do?

`CopilotKitIntelligence` is built to be correct when the caller says
nothing: omitting `apiUrl`/`wsUrl` resolves to
`https://api.intelligence.copilotkit.ai` and
`wss://realtime.intelligence.copilotkit.ai`, and its docstring says so
outright — *"leaving both unset is always correct against it."*

Every starter's runtime route defeated that default:

```ts
apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201",
wsUrl:
  process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401",
```

With the variables unset — the correct configuration for a managed user
— the `??` supplies localhost and the runtime aims at a local stack that
is not running. This is the artifact `copilotkit init` clones, so it is
the first thing a new managed user runs.

The starter's own `.env.example` already warns about exactly this
failure, two files away:

> `INTELLIGENCE_API_URL` and `INTELLIGENCE_GATEWAY_WS_URL` point at a
self-hosted or local Intelligence deployment only — leave them unset
when using managed Intelligence, or the channel host and runtime will
try to reach a local stack that usually is not running.

So the documentation was right and the code contradicted it.
`channel-host.mts`, in the same directories, already had the correct
shape.

### The fix

**22 runtime wiring sites** (20 route handlers, `adk-angular/server.ts`,
and the AgentCore Lambda) now use the conditional spread these starters
already use in `channel-host.mts`, so a self-hosted override still works
and the managed default applies when absent:

```ts
...(process.env.INTELLIGENCE_API_URL
  ? { apiUrl: process.env.INTELLIGENCE_API_URL }
  : {}),
```

No hosted URL is written into the examples — the library already owns
them, so this is a deletion.

**3 `.env.example` files** (`agent-spec`, `llamaindex`, `mcp-apps`) set
the same values *uncommented*. Two do it directly beneath a comment
telling the reader to leave them unset, and an `.env.example` is copied
to `.env`, so these were the remaining route to a localhost value once
the code default was gone. Commented out to match the other nineteen
starters; `agent-spec` had no explanation at all and gets the standard
one.

**A guard**, added to the existing
`scripts/validate-intelligence-env-names.ts` rather than a new script —
it already polices the canonical Intelligence key name and the two dead
hosts, and its workflow is deliberately unfiltered so it sees every
README, example and skill. Two rules: `managedUrlFallback` (a `??`/`||`
default on either variable) and `managedUrlEnvFileAssignment` (an
uncommented env-example assignment). The rule is the *pattern*, not the
literal, so a staging host substituted for localhost fails the same way.

Five files legitimately want a local target and are allowlisted with
their reasons: the `playwright.config.ts` and `.env.example` of the
banking and reskinnable-demo showcases (own vendored compose ports
7050/7053 and 7250/7253, own seeded org keys) and
`agentcore/docker/.env.example` (the documented local development
stack). Resolving those to the managed hosts would aim an offline test
suite at production.

`scripts/__tests__` has no general runner, so the workflow runs this
test file explicitly, following the `plugin-skills-check.yml` precedent
— otherwise a rule that silently stopped matching would leave the check
passing on an empty result.

## Related PRs and Issues

- Closes OSS-981.
- Supersedes the canceled ENT-922, whose blocker ("do not invent hosted
URLs; rewrite once the managed env contract is final") no longer
applies: the contract shipped as `MANAGED_INTELLIGENCE_API_URL` /
`MANAGED_INTELLIGENCE_WS_URL`, and the fix removes a fallback rather
than adding a URL.
- ENT-949 shipped a warning for this class of mistake, but
`warnOnPartialHostOverride` only fires on a *partial* override — both
values defaulting to localhost together is not partial, so nothing
warned.

## Verification

- `pnpm exec vitest run
scripts/__tests__/validate-intelligence-env-names.test.ts` — 13 passed.
Written first: the rules were red before they existed, then reported
**48 violations across 24 files** for the code rule and **10 across 5**
for the env-file rule; the fixes took both to green.
- `pnpm check:intelligence-env-names` — exit 0.
- `oxfmt --check` and `oxlint` over all 25 touched files — clean.
- The spread typechecks under `strict` + `exactOptionalPropertyTypes`,
the setting that would reject `apiUrl: string | undefined`.
- The marked wiring block stays byte-identical across 21 of 22 starters
(`agentcore` differs only in its runner), and no `localhost` remains
inside any marked block.
- Not run locally: the 13 starter Next builds. `test_smoke-starter.yml`
typechecks the route handlers in CI on this PR.

### Out of scope

`agentcore/docker/docker-compose.yml` keeps its
`${INTELLIGENCE_API_URL:-http://localhost:4201}`: it is compose
substitution in the documented local-dev stack, not shipped runtime
code. Separately that default cannot work anyway — inside the bridge
container `localhost` is the container's own loopback — but that is a
different bug.

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation
- [x] "Allow edits by maintainers" is checked

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-26 14:28:28 -05: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
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
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
Maxim e3dafff825 chore(examples): remove banking showcase in favor of reskinnable-demo
The banking showcase is superseded by `examples/showcases/reskinnable-demo`,
which ships the same banking experience as one of its runtime-swappable skins
(alongside airline) on top of a shared shell. Keeping both means maintaining
two copies of the same demo, so banking is sunset here.

Removes the app and the things that referenced it:

- `pnpm-workspace.yaml` — drops the workspace entry. Also fixes the adjacent
  NOTE, which attributed the canary AG-UI pin to "banking's agent" when it is
  reskinnable-demo's own Python deep agent that needs it.
- `pnpm-lock.yaml` — regenerated. Only the removed importer and the peer-suffix
  re-keying it caused; no dependency version changes.
- `examples/README.md` — the banking row becomes a reskinnable-demo row, so the
  successor is listed and the showcase count is unchanged.
- `showcase/shell-docs/.../faq.mdx` — the Banking Assistant link retargets to
  reskinnable-demo instead of 404ing.
- `.github/config-allowlist.txt` — drops the deleted `next.config.mjs`.

Not changed: `scripts/migrate-demos.sh` and `scripts/archive-demo-repos.sh`
still name `examples/showcases/banking`. Those are the already-executed one-shot
manifests for the repo consolidation; the path is a historical record there, not
a live reference. reskinnable-demo's `.env.example` and `docker-compose.yml`
likewise still explain their +200 port offset in terms of banking's stack — the
offset stays real, and "was cloned from banking" stays true.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 12:11:48 +02:00
renovate[bot] bf44b367b8 chore(deps): update github actions 2026-08-24 16:59:00 +00:00
Mike Ryan 6b9aadf025 fix(runtime): stop emitting a require() statement in published .d.cts (#6644)
Fixes the part of OSS-899 that is hard to defend: every `.d.cts` file we
publish from `@copilotkit/runtime` starts with a `require()` call.

## The bug

A consumer whose only source file is `import { CopilotRuntime } from
"@copilotkit/runtime";`, compiled with `strict` and `skipLibCheck:
false`, gets **81 errors** on a bare install of 1.68.3. **71 of them are
`TS1036` "Statements are not allowed in ambient contexts"**, raised
inside our own shipped declarations.

Cause is in `packages/runtime/tsdown.config.ts`. The banner that
guarantees `reflect-metadata` loads before `type-graphql` was returned
as a **string**. tsdown's `resolveChunkAddon` routes an *object* return
by chunk kind (`js` / `dts` / `css`) but applies a *string* return to
**every** emitted chunk — declarations included. So all 87 published
`.d.cts` files began:

```ts
require("reflect-metadata");
import { CopilotRuntimeLogger, ... } from "./lib/logger.cjs";
```

A `require()` call is a statement, and a `.d.ts` is an ambient context.
One error per file.

Two reasons this went unnoticed for so long:

- Every scaffolder sets `skipLibCheck: true`. Verified in genuine `ng
new` and `create-next-app` output. A developer who scaffolds normally
never sees it.
- The `.d.mts` flavour got `import "reflect-metadata";`, which is a
legal side-effect import in a declaration file. **ESM-resolving
consumers saw zero `TS1036`.** Only CJS resolution is affected.

## The fix

Return an object so tsdown routes by chunk kind — JS keeps its
`reflect-metadata` prologue, declarations get nothing.

The `fileName.includes("_virtual/_rolldown/runtime")` condition is
dropped as well, and that is the more interesting half.
`resolveChunkAddon` reassigns its own closure variable on the first
call:

```js
if (typeof chunkAddon === "function") chunkAddon = chunkAddon({ format, fileName: chunk.fileName });
```

so a function banner is evaluated **once** and its result reused for
every later chunk. The old config's comment ("propagates to all output
files per format") described that as intended behaviour, but it was
really a condition deciding the banner for the entire build based on
whichever chunk happened to be emitted first. Keying on `format` alone —
fixed per build — is order-independent.

The object form is tsdown's declared API, not a workaround:
`ChunkAddonFunction` returns `ChunkAddonObject | string | undefined`
where `ChunkAddonObject` is `{ js?, css?, dts? }`. `tsc --noEmit
--strict` on `tsdown.config.ts` against tsdown's own types is clean —
worth stating because the config is in no tsconfig `include`, so nothing
else typechecks it.

## The guard

`scripts/validate-dts-ambient.ts` parses each built declaration with the
TypeScript compiler API and fails on any top-level node that is not a
declaration, import, or export. Wired as a `check-dts` nx target shaped
exactly like the existing `publint` / `attw` / `compat-check` targets
(`dependsOn: ["build"]`, `inputs` on `dist/**`), and folded into the
`check:packages` script that the `package-quality` CI job already runs.
That job already builds runtime for `publint`, so the added cost is one
177-file parse.

Only `@copilotkit/runtime` opts in, because it is the only offender.
Running the validator itself over the built declarations of all 32
packages: **87 of runtime's 177** bad on the published 1.68.3 artifact,
and **0** in every other package. Others can opt in with the same
one-line script.

## Testing

**1. Reproduce the reported defect on the published package.** Bare `npm
install @copilotkit/runtime@1.68.3 typescript`, `probe.ts` importing
only `CopilotRuntime`, tsconfig with `strict`, `skipLibCheck: false`,
`module`/`moduleResolution` `nodenext`:

```
$ npx tsc --noEmit ; echo exit=$?
exit=1
$ grep -oE 'error TS[0-9]+' tsc.out | sort | uniq -c | sort -rn
  71 error TS1036
   5 error TS2416
   2 error TS7016
   2 error TS2307
   1 error TS2694
```

81 errors, matching the issue. All 71 `TS1036` are at line 1, column 1
of a `.d.cts`.

**2. Confirm the mechanism.** Every published declaration's first line,
before the fix:

```
-- *.d.cts --  total: 87
  87 require("reflect-metadata");
-- *.d.mts --  total: 90
  90 import "reflect-metadata";
```

**3. Same probe across every public subpath, before and after.** Built
`packages/runtime` at 1.68.3 with this change and swapped the result
into the probe's `node_modules`. `total` is all errors; `1036` is the
subset this PR addresses.

| subpath | CJS before | CJS after | ESM before | ESM after |
| --- | --- | --- | --- | --- |
| `@copilotkit/runtime` | 81 (71×1036) | **10** (0) | 10 (0) | 10 (0) |
| `/v2` | 32 (29×1036) | **3** (0) | 3 (0) | 3 (0) |
| `/langgraph` | 15 (4×1036) | **7** (0) | 7 (0) | 7 (0) |
| `/v2/express` | 21 (18×1036) | **3** (0) | 3 (0) | 3 (0) |
| `/v2/hono` | 21 (19×1036) | **2** (0) | 2 (0) | 2 (0) |
| `/v2/node` | 22 (20×1036) | **2** (0) | 2 (0) | 2 (0) |

Zero `TS1036` on every subpath in both module modes, and **after the fix
each subpath's CJS count equals its ESM count** — the CJS-only penalty
is gone and nothing else moved. Every ESM column is untouched, which is
the expected result since `.d.mts` never carried the bad banner.

The errors that remain are the separate items catalogued on OSS-899
(optional-peer SDK types, `@types/cors`, a `lru-cache` variance error
from `graphql-yoga`, a zod namespace skew in
`@copilotkit/license-verifier`) and are not touched here.

**4. `reflect-metadata` still runs first in every JS output.** This is
what the banner exists for, so it is the thing most at risk from the
change:

```
cjs files with require("reflect-metadata") as line 1: 131  / total 131
mjs files with import "reflect-metadata" as line 1: 132  / total 132
```

**5. Nothing but the banner line changed.** Diffed every one of the 87
built `.d.cts` files against the published 1.68.3 artifact from line 2
onward. Exactly one file differs, and it is unrelated source drift — a
JSDoc env-var rename from `6f58b2c6a4` (`COPILOTKIT_API_KEY` →
`INTELLIGENCE_API_KEY`, refs OSS-881) that landed on main after 1.68.3
shipped. Line counts are also identical, so declaration sourcemaps do
not shift.

The `_virtual/_rolldown` reference count in declarations is 2 before and
2 after — that item is deliberately out of scope here.

**6. The guard catches the regression it exists for.** Reverted the
banner to its pre-fix string form, rebuilt, and ran the new target:

```
$ pnpm exec tsx ../../scripts/validate-dts-ambient.ts dist
Found 87 statement(s) in published declarations.
A .d.ts is an ambient context: only declarations, imports, and exports are
allowed. Each of these is a TS1036 error for consumers on skipLibCheck: false.

  dist/agent/converters/aisdk.d.cts:1  require("reflect-metadata");
  ...
exit=1
```

Restored the fix and rebuilt:

```
$ pnpm exec tsx ../../scripts/validate-dts-ambient.ts dist
validate-dts-ambient: dist clean (177 files).
exit=0
```

**7. Validator unit tests, mutation-checked.**
`scripts/__tests__/validate-dts-ambient.test.ts`, 7 tests covering the
exact OSS-899 banner, the legal ESM form, every declaration form a real
`.d.ts` uses, line-number reporting, and ignoring sibling `.cjs`/`.map`
files.

```
 Test Files  1 passed (1)
      Tests  7 passed (7)
```

Then broke the mechanism three ways to confirm the tests are not
self-fulfilling:

| mutation | result |
|---|---|
| allow `ExpressionStatement` in the kind allowlist | 2 failed / 5
passed |
| drop the `line + 1` conversion | 2 failed / 5 passed |
| scan only `.d.ts`, not `.d.mts` / `.d.cts` | 3 failed / 4 passed |
| restored | 7 passed |

**8. Runtime suite and packaging targets, on a clean `pnpm install
--frozen-lockfile` in this worktree.**

```
$ nx run @copilotkit/runtime:test
 Test Files  143 passed (143)
      Tests  2073 passed (2073)

$ nx run-many -t publint,attw,check-dts --projects=@copilotkit/runtime
NX   Successfully ran targets publint, attw, check-dts for project @copilotkit/runtime
```

`attw --profile node16` reports 🟢 from both CJS and ESM; the `node10`
failure is pre-existing and ignored by the profile.

**9. Formatting and types.** `oxfmt --check` clean on all three source
files; `tsc --noEmit --strict` clean on the new script.

## Overlap with #6476

#6476 (`adopt TypeScript 7 and tsdown 0.22`) bumps tsdown to 0.22.14 but
does **not** touch `packages/runtime/tsdown.config.ts`, so it does not
fix this. The two PRs conflict only textually — both add lines to
runtime's `scripts` block and to the root `package.json`. This fix uses
tsdown's documented object-banner form, so it holds whether or not 0.22
changed `resolveChunkAddon`'s memoization.

No changeset: this ships through the normal release scopes.
2026-08-21 10:44:36 -07:00
Tyler Slaton 7bff49739e fix(runtime): replace retired Anthropic model IDs (#6616) 2026-08-21 09:06:33 -07:00
Benjamin Taylor 8951232a0f fix(runtime): stop emitting a require() statement in published .d.cts
A consumer who imports @copilotkit/runtime and compiles with strict +
skipLibCheck: false gets 81 errors from our published declarations, 71 of
them TS1036 "Statements are not allowed in ambient contexts". Cause: the
tsdown banner that guarantees reflect-metadata loads before type-graphql
was returned as a string, and tsdown applies a string banner to every
emitted chunk -- declarations included. So all 87 published .d.cts files
began with `require("reflect-metadata");`, which is a statement and
illegal in an ambient context.

Returning an object instead lets tsdown route the banner by chunk kind, so
JS keeps its reflect-metadata prologue and declarations get nothing. The
fileName condition is gone too: tsdown's resolveChunkAddon reassigns its
own closure variable on the first call, so a function banner is evaluated
once and reused, meaning that condition was really deciding the banner for
the entire build from whichever chunk was emitted first. Keying on format
alone is order-independent.

This was invisible to us because every scaffolder sets skipLibCheck: true,
and because .d.mts got the legal `import "reflect-metadata";` form -- ESM
consumers never saw a single TS1036.

Adds a check-dts target that parses the built declarations and fails on any
top-level statement, wired into the existing package-quality job so the
class cannot come back silently.

Refs OSS-899
2026-08-21 08:24:46 -05:00
Mark fc14045c0a fix(showcase): register CrewAI flows production instance 2026-08-20 17:06:09 -07:00
Tyler Slaton 0ae1e188a8 ci: reject retired Anthropic model references 2026-08-20 16:40:52 -07:00
Mark 0298616223 docs: add Claude Managed Agents cookbook (#6430)
## Summary

- add an independently runnable Claude Managed Agents finance-assistant
example
- add a cookbook recipe that explains the CopilotKit runtime,
managed-session mapping, and tool rendering flow
- add the recipe to cookbook navigation, the overview grid, sidebar icon
mapping, and render coverage
- use the real Claude vector mark for the cookbook instead of the
text-placeholder SDK asset
- register the example's Vite configuration and managed-agent model with
the repository CI allowlists
- include a compact architecture diagram and links to the relevant
rendering and CopilotKit Intelligence documentation
- disable Claude's complete built-in toolset and expose only the scoped
`show_growth_projection` runtime tool
- make the provisioning model configurable through `ANTHROPIC_MODEL`,
defaulting to `claude-fable-5`
- bound CopilotKit request bodies to 256 KB and managed-agent turns to
90 seconds, while relying on the adapter's per-thread serialization
- cap public run traffic at 20 provider-like attempts per client IP per
minute and 2,000 successful starts per process per 24-hour window
- restrict browser runtime requests with an exact Origin allowlist that
supports same-origin or separately hosted frontends, and restrict iframe
parents with CSP `frame-ancestors`
- validate persisted managed-agent IDs at startup so malformed local
configuration fails immediately
- publish the interactive example on Railway and embed the live
deployment in the cookbook
- align the demo with the existing cookbook chat styling and show the
`Project monthly investing` starter on first load

## Demo

![Claude Managed Agents finance assistant rendering an interactive
growth
projection](https://github.com/CopilotKit/CopilotKit/blob/f36cb2b8eee8084aa6e37a200175532f4ecdca02/examples/showcases/claude-managed-agents/demo.png?raw=1)

## Why

This gives developers a focused example of connecting CopilotKit to
Anthropic Claude Managed Agents without the extra surface area of a
larger analyst application. The recipe follows the existing cookbook
structure and keeps the live demo compact enough for the standard
cookbook pane. Its managed environment has no outbound network or
package-manager access, and its agent cannot use bash, filesystem,
search, or fetch tools.

The request, turn, per-IP, and process-wide limits bound public demo
traffic without adding authentication or user friction. The traffic
counters are intentionally in memory, reset on process restart, and are
not shared across replicas, so the dedicated Anthropic workspace spend
limit remains the durable cost backstop. The exact-Origin browser check
reduces drive-by use but is explicitly documented as a control rather
than authentication. The model override allows operators to select a
lower-cost supported model during provisioning without editing source
code.

## Validation

- scoped formatting: passed
- scoped lint: 0 warnings, 0 errors
- shell-docs typecheck: passed
- standalone example typecheck: passed
- docs render tests: 26/26 passed
- standalone example tests: 23/23 passed
- shell-docs tests: 375/375 passed
- shell-docs production build: passed (222/222 pages)
- standalone example production build: passed
- standalone npm lockfile validation: passed
- build-config allowlist validator: passed
- docs model-name validator: passed
- exact-Origin regression coverage for run requests plus headerless
same-origin runtime discovery: passed
- malformed persisted agent-ID regression coverage: passed
- live Railway root and iframe CSP: passed
- live Railway runtime discovery, exact welcome copy, and first-load
starter pill: passed
- live three-turn AG-UI managed-agent run with `show_growth_projection`:
passed
- cookbook verified in the browser at desktop and narrow widths with no
console errors or horizontal overflow
2026-08-20 12:07:52 -07:00
Mike Ryan b8b19834a2 fix(runtime): unify the Intelligence key name and publish the wiring (refs OSS-881) (#6595)
## What does this PR do?

Closes the naming and documentation half of
[OSS-881](https://linear.app/copilotkit/issue/OSS-881). Paired with
CopilotKit/Intelligence#890, which adds `copilotkit verify` and tightens
the evaluation rubric.

### 1. One name for the Intelligence key

**Three** names for one value were live in CopilotKit's own
documentation, and following the wrong one with a CLI-provisioned
project yields an undefined key:

| Name | Where | Code readers |
| --- | --- | --- |
| `INTELLIGENCE_API_KEY` | what `copilotkit project select` writes; all
34 integration examples; the docs site | 34 |
| `COPILOTKIT_INTELLIGENCE_API_KEY` | 7 Channels package READMEs +
packaged skills | **0** |
| `COPILOTKIT_API_KEY` | `examples/slack`, `examples/teams`, and the
TSDoc on `CopilotKitIntelligence` itself | 2 |

`INTELLIGENCE_API_KEY` wins — it is the name the CLI provisions, and
changing it would break every scaffolded project in the wild.

- `COPILOTKIT_INTELLIGENCE_API_KEY` is **retired outright**. Nothing
ever read it, so there is nothing to keep compatible.
- `COPILOTKIT_API_KEY` stays **readable as a deprecated alias** in the
two examples that consume it, so an existing `.env` keeps working, and
is documented as deprecated everywhere it appears.

The third name was the worst placed: it was in the TSDoc on
`CopilotKitIntelligence`, which is what an IDE shows on hover.

This was not only untidy. The CLI's own `channels-preflight` accepts
`INTELLIGENCE_API_KEY` or `COPILOTKIT_API_KEY` — **not**
`COPILOTKIT_INTELLIGENCE_API_KEY`, the name the Channels READMEs told
people to set. So following a Channels README verbatim made `copilotkit
channels` warn that no runtime API key was present while the key sat
visibly in `.env`. After this PR the documented name is one preflight
accepts.

> [!NOTE]
> `NEXT_PUBLIC_COPILOTKIT_API_KEY` is a **different value** — the legacy
Copilot Cloud public key — and is deliberately left alone.

### 2. A real defect, not just naming skew

`skills/runtime/references/intelligence-mode.md` documented
`organizationId` as a `CopilotKitIntelligence` option, sourced from two
further env names (`COPILOTKIT_INTELLIGENCE_ORG_ID`,
`COPILOTKIT_ORG_ID`).

`CopilotKitIntelligenceConfig` has no such field — the copy-pasteable
sample it appeared in **would not compile**. Removed from the samples,
and the prose telling readers to fetch a value for it corrected. That
file is the only place those two names ever existed, which is very
likely why the failing validation run reported that "the runtime reads
`COPILOTKIT_INTELLIGENCE_API_KEY` and `COPILOTKIT_INTELLIGENCE_ORG_ID`".

### 3. Publish the Intelligence wiring

The wiring instructions existed only inside
`node_modules/@copilotkit/runtime/skills/`, and the only docs pages
mentioning `CopilotKitIntelligence` at all were the two Channels
frontends — so a developer on the plain web path had no page to reach it
from.

Adds **`/premium/connect-your-runtime`**: the wiring itself, how to
confirm the credential is actually consumed, the self-hosted
both-URLs-or-neither rule, and a troubleshooting table. Linked into both
navs, and the skills reference now points at the published page.

### 4. A guard so it cannot drift back

`scripts/validate-intelligence-env-names.ts` (`pnpm
check:intelligence-env-names`), wired to lefthook and a new workflow.

The workflow is **intentionally unfiltered**. The two workflows that
would otherwise cover this both filter: `plugin-skills-check` by
`paths:`, and `static/quality` by `paths-ignore: examples/**` — which is
exactly where the deprecated alias lives. Scoping the job would re-open
the hole it exists to close. Legitimate alias sites live in
`ALIAS_ALLOWLIST`.

## Related PRs and Issues

- [OSS-881](https://linear.app/copilotkit/issue/OSS-881) — needs
**both** PRs; neither closes it alone
- CopilotKit/Intelligence#890 — items 1 and 4 (`copilotkit verify` +
rubric contract 1.3.0)

## Verification

- Full lefthook pre-commit ran green: `check-plugin-skills`, `lint-fix`,
the new `check-intelligence-env-names`, and `test`/`publint`/`attw`
across **25 projects**.
- `examples/slack` `managed.test.ts` extended to cover **both** the
canonical name and the alias fallback, and proven non-vacuous — removing
the fallback turns the new test red.
- The drift guard proven non-vacuous the same way: reintroducing a
retired name fails it, exit 1.
- `oxfmt` and `oxlint` clean on every file touched (0 errors).

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-20 12:00:52 -07:00
Mark d50e8d7e7c Merge branch 'main' into codex/claude-managed-agents-cookbook 2026-08-20 11:40:54 -07:00
David McKay 35ea9eee36 feat(reskinnable-demo): run banking on a nested LangChain deep agent with a streaming CLI console (#6581)
Replaces the Codex CLI harness with **LangChain deep agents (Python)**:
sandboxed shell execution, parallel research subagents, no external
harness binary, our own API key.

All ten beats have been walked in a browser, and the demo now has its
own CI job. **Ready for review.**

## What banking's agent is now

```
banking                       gpt-5.4, temp 0
  │  banking's own prompt · the browser's frontend tools · Intelligence
  │  memory tools · render_report for the canvas
  └─ expense-analyst          gpt-5.6-sol, reasoning_effort=high
       │  sandboxed shell (LocalShellBackend) · submit_expense_report
       └─ merchant-researcher  gpt-5.4, one per merchant
            search_merchant (Tavily)
```

Six skins still run in-process as `BuiltInAgent`s. `banking` is an
`HttpAgent` pointed at `agent/`.

Each level exists because something about it must differ from its parent
— prompt, model, reasoning effort, tool set. A single agent had nowhere
to put any of that.

### Why the whole agent moved, not just the expense beat

The obvious design was a second agent id for the long-running beat,
leaving banking's `BuiltInAgent` alone. That was built first, and it
does not survive the requirement *"start the analysis, switch threads,
run other pills, come back to it."*

**Threads are scoped per agent.** `listThreads` takes `agentId` as a
required parameter, and measured against the running app the two lists
were disjoint — `banking` 46 threads, `banking-expenses` 10. The v2
runtime has no handoff mechanism, and `defineTool`'s `execute` takes
`(args)` with no emitter, so a tool cannot stream a multi-minute run
into the conversation. One conversation list means one agent.

### Why the beat is a subagent rather than prose in one prompt

It started as a section appended to banking's prompt. That left per-beat
configuration homeless (model, effort and recursion limit are all
agent-level, and there was one agent), and made banking's
~21,000-character rulebook ride every one of the ~20 model calls the run
makes — re-sending rules about markdown tables while the agent read a
CSV.

Reached as a `CompiledSubAgent`, because a raw `SubAgent` spec has no
`subagents` field and this one needs its own: the per-merchant fan-out
is a headline of the beat, and a flat subagent could only research
serially. A probe confirmed nesting survives — the analyst's `task`
dispatches, the researchers' `search_merchant` calls and the final
report tool all reach `astream_events`.

## The console

One CLI window in the transcript carries the whole run: narration, `$
execute`, `search "…"`, `→ merchant-researcher: …`, results, indented by
depth so a ten-way fan-out reads as a fan-out. Subagent narration is
suppressed from the conversation so the console is the single place the
harness is visible; banking's own replies still render normally.

`shell/subagents/subagent-activity.tsx` subscribes to the agent's
**event stream**. Reading `agent.messages` — the first design — was
wrong twice: messages materialise at the `MESSAGES_SNAPSHOT` (two per
run), so the pane sat still for minutes then filled at once; and
persisted messages carry no `subagentRunId`, so the harness's narration
could not be told from banking's reply. The fold is pure and idempotent,
so the same code serves the live subscription and a replay of the
thread's stored events when a conversation is reopened.

Three heuristics were deleted along the way, each replaced by identity
the protocol already carried:

| heuristic | replaced by |
|---|---|
| console anchored on "the first tool call" | the run's first `task`
call, from **message order** (durable; the event-derived version
rendered one console *per delegation* on a restored thread — six,
measured) |
| `CONSOLE_TOOL_NAMES` allowlist | presence of `subagentRunId` |
| `disable_streaming` on the researchers | the canary's per-lane state |

The message filter suppresses **prose** and keeps **tool calls**: an
agent routinely narrates and calls a tool in one message, and returning
`null` for the whole message hid the report card — the run looked
perfect and ended with nothing to show.

## Canary stack, contained to this app

The subagent surface only exists on the canary line, and a released
`@ag-ui/client` ≤ 0.0.57 rejects `SUBAGENT_*` events **in the HTTP
transport before any middleware runs**, killing the stream. So the demo
leaves the root pnpm workspace and ships its own lockfile:

- `@copilotkit/* 1.68.3-canary.1786716392`
- `@ag-ui/* 0.0.59-canary.1786716392.0`
- `ag-ui-langgraph 0.0.43.dev1786716392` → `ag-ui-protocol
0.1.20.dev1786716392`

That keeps an unreleased protocol out of every other package in the
monorepo.

A 1.62.2-based canary was tried first and **could not compile the app**:
`workspace:*` is not a version, so the demo had no recorded lower bound
on the CopilotKit API it needs, and pointing it at 1.62.2 silently
rewound that API five minors. `OpenGenerativeUIActivityRenderer` — a
public `/v2` export since ~1.63 — was the first thing to break, and
there was no reason to think it was the only one. Rebasing the canary on
1.68.3 collapsed that whole class of risk.

## Verified against the running app

Every row is a measurement, not a claim.

| | |
|---|---|
| Registry shape | `/info`: `banking` = HttpAgent, other six =
BuiltInAgent |
| Sandboxed execution | 8 `execute` calls; agent writes and runs its own
Python |
| Parallel subagents | 6 `SUBAGENT_STARTED`/`FINISHED` pairs, 10
`search_merchant` |
| Canvas beat | `render_report` → a2ui middleware emits `activityType:
a2ui-surface` |
| Frontend tools | given two, picks `showTransactions`, emits no result,
emits **no prose** — the prompt's restraint rule surviving the port |
| Memory (Intelligence) | run is handed `recall_memory`, `save_memory`,
`forget_memory`, `knowledge_base_shell` |
| HITL round-trip | tool call out, answer back in, agent continues |
| Durable background run | client disconnected at 8s; run finished
unattended; thread replayed |
| Thread restore | 61 messages persisted incl. the report tool (an
earlier flat-subagent attempt collapsed to 4) |
| Report correctness | 14 rows, 9 researched, 6 filed with ids read out
of real 201 bodies, totals reconciling against their own rows |
| Run duration | 86s reported vs 98s wall clock |

## Correctness bugs found by running it

Each produced a confident, complete-looking wrong answer rather than an
error — the characteristic failure of a multi-minute agentic beat.

- **Beat 3d was dead, and it took the whole thread with it.** The bet
against this one was right. `ag_ui_langgraph` routes *every* attachment
to the model as an `image_url` block, documents included, so the Q2
invoice was rejected before the first token: `400 Invalid MIME type.
Only image types are supported`. The exception is raised inside the
model node, which kills the SSE stream — the runtime sees `RUN_ERROR:
terminated` with no cause and the browser renders **nothing**: no error
bubble, no failed message. And the crashed run is still checkpointed, so
every later message on that thread replays the rejected content and dies
the same way. One click on the pill killed the conversation permanently;
only restarting the service cleared it. Fixed here by a
`wrap_model_call` middleware that rewrites those blocks into LangChain
standard `file` blocks, and upstream in
[ag-ui#2476](https://github.com/ag-ui-protocol/ag-ui/pull/2476) (both
adapters, plus the return leg so a non-image attachment survives
`MESSAGES_SNAPSHOT`). The middleware is a stopgap with its deletion
condition written into its header — this service installs the adapter
from PyPI, so the upstream fix cannot reach it until published.
- **Totals did not match their own rows.** Every per-row amount matched
the CSV while the headline totals came back $1.00 and $0.20 high: the
model authored them instead of adding them. The card prints the total
directly above the rows it is the sum of. Fixed structurally — derived
in `submit_expense_report`, removed as parameters.
- **`amount` arrived as a string** on all 14 rows, silently defeating
`toLocaleString(…currency…)` so it printed `842.10` with no currency.
- **`merchantKind` non-answers.** With no search tool the model wrote a
bare `"unclear"`; with Tavily live it hedges in prose (`"unknown (likely
bookbindery/bookshop retail, but not established for this exact
merchant)"`), which an exact-match filter passed into a 60-character
label glued to the merchant name. Now rejected on leading token, hedging
language, and a 40-character cap.
- **The run clock reported 333s for a two-minute run.** It took the
oldest open stamp across the process because the tool could not name its
own run; model calls *after* the report re-stamped it and the leftover
became the next run's start. Now keyed per run via an injected
`ToolRuntime`.
- **`graph.with_config({"recursion_limit"})` is silently dropped** by
the AG-UI adapter. The agent completed the entire analysis, streamed
every argument of the final report, then died at LangGraph's default of
25 supersteps.
- **A sync `wrap_model_call` under `astream`** surfaces as a bare
`RUN_ERROR: terminated`, cause only in the service log.
- **`emit_raw_events` defaults to `True`**, piggybacking LangChain's
internal events onto the stream: 27,950,261 bytes → 374,086 with it off,
identical report. Matters because the thread *persists* those events for
replay.
- **`gpt-5.6-sol` + `reasoning_effort` + function tools 400s** on
`/v1/chat/completions`; needs `use_responses_api=True`. The first model
probe missed it by binding no tools — a model probe for an agent has to
bind one.

## Upstream findings (reported separately, not fixed here)

1. **`@copilotkit/runtime` drops `subagentRunId` when persisting
messages.** 2888 of 3026 stream events carry it; 0 of 53 persisted
messages do. Reproduced with Intelligence removed entirely, so it is the
runtime's message shape rather than the platform store — and
`@copilotkit/runtime`'s dist contains no occurrence of the field, while
`@ag-ui/core`, `ag-ui-protocol` and `@copilotkit/core` all model it. One
field threaded through would let the console rebuild from message
history and delete the event-replay seeding here.
2. **`copilotkit` 0.1.95 × `ag-ui-langgraph` 0.0.43** — the FastAPI
endpoint calls `agent.clone()` per request; 0.0.43's base `clone()`
hard-passes three kwargs the SDK subclass does not accept, so **every
request 500s**. Verified with a minimal repro on stock classes and by
reading published wheels (0.0.41/0.0.42 are fine — the window is 0.0.43
only). `sdk-python` requires `>=0.0.42` unbounded, so fresh installs
break, and `examples/showcases/deep-agents{,-finance-erp,-job-search}`
are one `uv lock --upgrade` away. Now open as #6592 (`**kwargs`
passthrough + 9 regression tests incl. a forward-compat guard); it needs
a `0.1.96` bump to reach PyPI before `main.py`'s
`BankingAGUIAgent.clone()` workaround can go — and note `agent/uv.lock`
pins `copilotkit 0.1.95`, so removing the workaround is a re-lock as
well as a delete. That branch also fixes 4 pre-existing failures in the
sdk-python suite from the same root cause — **`test_unit-python-sdk` may
currently be red on `main`; worth checking independently.**

3. **Both LangGraph AG-UI adapters send non-image attachments as
`image_url`**, so a PDF, audio clip or video is rejected on the block
kind — see beat 3d in *Correctness bugs*. Fixed in
[ag-ui#2476](https://github.com/ag-ui-protocol/ag-ui/pull/2476); carried
here as a middleware until that publishes.

Also verified and **not** a problem: pnpm 10.33.4 still applies
`package.json` `pnpm.overrides` despite warning that it ignores them —
the ~70 root overrides including the security pins are live, nothing was
silently unpinned. (Migration to `pnpm-workspace.yaml` is worth doing
anyway, since the installed pnpm is 11.21.0 where the field genuinely is
dropped; branch exists, byte-identical lockfile.)

## What is not done

1. **Docs are stale.** `CLAUDE.md` still says `AgentRegistration` is `{
createAgent: () => BuiltInAgent }` and that the route "builds one
`BuiltInAgent` per registered skin"; the reskin skill says the same in
three places, and `templates.md` scaffolds it. The real type is `() =>
AbstractAgent`. This app has a standing rule that every change answers
whether the skill went stale. It did — the skill's launcher step is
fixed, the `BuiltInAgent` claims are not.
3. **Pre-release dependencies**, now on both halves: the JS canaries in
this app's own `pnpm-lock.yaml`, and `ag-ui-langgraph` /
`ag-ui-protocol` pinned `==` to the matching `.dev` canaries in
`agent/pyproject.toml`. Both move to stable when the subagent work
ships.
4. `pnpm test:e2e` has not been run.

### Closed since this PR was opened

- **The demo has its own CI job** —
`.github/workflows/test_reskinnable-demo.yml`. Nx discovers projects
*through* the pnpm workspace (no `workspaceLayout` in `nx.json`), so
leaving it also left the repo-wide `nx run-many -t build`
(`static_compat.yml`), `-t check-types` (`static_quality.yml`) and `-t
test` (`test_unit.yml`) sweeps, and both static workflows carry
`paths-ignore: ["examples/**"]` besides. One job runs the four gates
(lint, typecheck, 2460 unit tests, build); a second syncs
`agent/uv.lock` with `--frozen` and asserts the subagent *capability*
rather than a version string, so it keeps meaning something after the
pin moves.

The build gate is the one a developer can least run locally: `next
build` corrupts a concurrently running dev server's PostCSS/Turbopack
cache — measured, `globals.css` transforms to garbage, every route 500s,
and a dev-server restart does not clear it because the corruption is on
disk.

**Its first two runs failed, both usefully**, and both bugs were
pre-existing:

1. `pnpm/action-setup@v6.0.10` with `package_json_file:` pointed at this
app still installed the **root's** pnpm 10.33.4 rather than this app's
pinned 10.10.0 — the resolver that ignores `pnpm.overrides`, which is
the only place three of the five `@ag-ui/*` canaries are pinned. Caught
only because the job asserts `pnpm --version` against the pin; it would
otherwise have installed under the wrong resolver and stayed green. Now
activated through corepack, which reads the nearest `package.json`.
2. `pnpm install` in this directory **never installed this app**. pnpm
walks *up* for a workspace root, found the repo's, and installed all 70
monorepo projects (4645 packages) while leaving this directory with no
`node_modules` — the next command failing as `sh: 1: eslint: not found`.
So the app that ships its own lockfile was uninstallable by its own
documented instruction. Fixed by giving it its own
`pnpm-workspace.yaml`; `ignore-workspace=true` in an `.npmrc` does
**not** work (CLI-only in pnpm 10.10, measured).

- **The canary `overrides` now live in `pnpm-workspace.yaml`**, their
supported home, as a side effect of that fix. They no longer depend on
`packageManager: pnpm@10.10.0` being the version that still reads
`package.json` — which matters because `@ag-ui/core`, `@ag-ui/encoder`
and `@ag-ui/proto` are pinned nowhere else and a released `@ag-ui/core`
rejects `SUBAGENT_*` in the HTTP transport.
- **All ten beats walked in a browser**, including 3d — which was
broken, for the reason in *Correctness bugs* above.
- **`./run-demo.sh` starts a complete demo.** It now brings up the
Python agent between the compose wait and `pnpm dev`, guarded on
`/health` so a re-run reuses a live one, and dies with `(cd agent && uv
sync)` when the venv is missing. `./stop-demo.sh` had the mirror gap and
now stops it too — that one mattered more than it looks: the start
script *reuses* a live :8124, so an orphan surviving teardown is
silently adopted by the next cold start, serving whatever code it was
launched with.
- **The agent's Python deps are pinned and locked.** They were not, and
the JS half was — so a colleague's fresh `uv sync` resolved the
*release* `ag-ui-langgraph 0.0.43`, which does not accept
`emit_subagent_events` and ships no subagent symbols. That is this PR's
headline feature, and it would have failed silently: `main.py` sets the
flag as an attribute, so the assignment succeeds against an object
nobody reads and the service starts clean. Now `==` pins plus a
committed `agent/uv.lock`, verified by building a venv from only the
tracked files and replaying a captured Q2-with-PDF payload through it.
- **The README told people to install from the repo root** "as a
workspace package". It is deliberately not one, so a root install did
nothing for this app — and installing in the right place did nothing
either, until the `pnpm-workspace.yaml` above.

## Relationship to #6501

Based on `b94e4bfb5d`, the last commit before Codex appears, which is
**not on `main`**. So this PR carries **9 commits: the 4 here plus the 5
foundation commits it shares with #6501** — harness types + fixture,
prompt/workspace, the OFFSITE-to-fixture invariant guard, the
summary-shape/filing-contract fix, and `POST /transactions`. Whichever
merges first shrinks the other. #6501 and #6565 are deliberately
untouched.

## A demo-design question, not a bug

The fixture's merchant names are invented, so `Cardinal & Ash` — the
prompt's own worked example of *"could be a restaurant or a law firm,
find out"* — cannot be resolved by real web search and stays `unclear`
alongside `Bluebonnet Provisions`. The beat's headline claim is that the
agent researches every merchant; real merchant names in the CSV would
make that land harder.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01JRubZT6AS6LCGkcE2KzcfA
2026-08-20 10:49:40 -07:00
Maxim 80e83c8186 ci(reskinnable-demo): activate the app's pnpm via corepack, key the import smoke
First run (32398188492) failed twice, both usefully:

  * `pnpm/action-setup@v6.0.10` with `package_json_file:` pointed at this
    app still installed the ROOT's pnpm 10.33.4 rather than the app's pinned
    10.10.0, and it cannot take a `version:` alongside a `packageManager`
    field. The version assertion caught it instead of the job silently
    installing under a resolver that ignores the three override-only
    `@ag-ui/*` canary pins. corepack reads the nearest package.json, so it
    resolves the app's pin and honours its +sha512 hash. The pnpm store is
    now cached explicitly, keyed on this app's lockfile, since setup-node's
    implicit pnpm cache needs pnpm installed before it runs.

  * The import smoke needs OPENAI_API_KEY. `main.py` calls `build_agent()`
    at import time and that raises without it. A placeholder is enough —
    constructing a ChatOpenAI does not validate the key or touch the network.
2026-08-20 19:34:30 +02:00
Maxim a36bef985d ci(reskinnable-demo): gate the demo's four gates and its agent's lockfile
Nothing in CI built or type-checked this app. Leaving the root pnpm
workspace — which was right, it contains the canary line — also removed it
from every repo-wide sweep, because Nx discovers projects THROUGH the
workspace and there is no `workspaceLayout` in `nx.json`:

    nx run-many -t build         static_compat.yml    does not see it
    nx run-many -t check-types   static_quality.yml   does not see it
    nx run-many -t test          test_unit.yml        does not see it

Both static workflows also carry `paths-ignore: ["examples/**"]`, so this
is a new workflow rather than an edit to either. It follows
`test_unit-showcase.yml`, which exists for the same reason applied to a
different directory.

Two jobs.

`gates` runs lint, typecheck, unit (2460 tests) and build, cheapest first,
inside the app because no root task reaches it. Measured locally at ~2
minutes of gates; the 20m budget is install headroom.

The build gate is the one that matters most and the one a developer can
least run: `next build` corrupts a concurrently running dev server's
PostCSS/Turbopack cache — measured, `globals.css` transforms to garbage
and every route 500s, and a dev-server restart does not clear it because
the corruption is on disk. CI is the only safe home for it. It needs no
env: the route constructs one agent per skin at module load and none of
them requires a key (banking's is an `HttpAgent` whose URL is never called
during a build).

`agent-resolve` syncs `agent/uv.lock` with `--frozen` and then asserts the
subagent surface is actually present. That is not hypothetical: until this
branch pinned it, `ag-ui-langgraph>=0.0.43` resolved the RELEASE, which
accepts no `emit_subagent_events` and exports no subagent symbols — the
demo's headline feature, failing silently because the flag is set as an
attribute on an object nobody reads. The assertion is on the CAPABILITY
rather than the version string, since a version assertion goes stale the
moment the pin moves and the surface is what must stay true.

One uncertainty is made into a gate rather than left to trust: this app
pins pnpm@10.10.0 while the root pins pnpm@10.33.4, and the older resolver
is load-bearing here — it still reads `pnpm.overrides` from package.json,
the only place `@ag-ui/core`, `@ag-ui/encoder` and `@ag-ui/proto` are
pinned to the canary (`@ag-ui/client` is a direct dependency; those three
are not). `pnpm/action-setup` is pointed at the app's package.json, and
because an unexpected action input is a WARNING in Actions rather than an
error, a step then compares `pnpm --version` against the pin and fails
loudly if they differ.

No `continue-on-error` and no `|| true` anywhere in the file.
2026-08-20 19:32:26 +02:00
renovate[bot] d4f147aee8 chore(deps): update depot/setup-action digest to 91bc849 2026-08-20 13:58:50 +00:00
Benjamin Taylor 6f58b2c6a4 fix(runtime): unify the Intelligence key name and publish the wiring (refs OSS-881)
Three names for one value were live in CopilotKit's own documentation, and
following the wrong one with a CLI-provisioned project yields an undefined
key:

- `INTELLIGENCE_API_KEY` — what `copilotkit project select` writes, used by
  all 34 integration examples and the docs site.
- `COPILOTKIT_INTELLIGENCE_API_KEY` — the seven Channels package READMEs and
  the packaged skills. Nothing ever read it.
- `COPILOTKIT_API_KEY` — the Slack and Teams examples, and the TSDoc on
  `CopilotKitIntelligence` itself, which is what an IDE shows on hover.

`INTELLIGENCE_API_KEY` wins, because it is the name the CLI provisions and
changing it would break every scaffolded project in the wild.
`COPILOTKIT_INTELLIGENCE_API_KEY` is retired outright — no code read it.
`COPILOTKIT_API_KEY` stays readable as a deprecated alias in the two
examples that consume it, so an existing `.env` keeps working, and is
documented as deprecated everywhere it appears.

The skills reference also documented `organizationId`, sourced from a fourth
and fifth env name, as a `CopilotKitIntelligence` option. It is not one:
`CopilotKitIntelligenceConfig` has no such field, so the copy-pasteable
sample it appeared in would not compile. Removed from the samples, and the
prose that told readers to fetch a value for it corrected.

The Intelligence wiring itself was published only inside
`node_modules/@copilotkit/runtime/skills/`, and the only docs pages showing
`CopilotKitIntelligence` were the two Channels frontends — so a developer on
the plain web path had no page to reach it from. Adds
`/premium/connect-your-runtime`, which covers the wiring, how to confirm the
credential is actually consumed, and the self-hosted two-URL rule.

`scripts/validate-intelligence-env-names.ts` keeps this from drifting back.
It runs unfiltered in CI on purpose: the two workflows that would otherwise
cover it filter paths, and static/quality ignores `examples/**` — exactly
where the deprecated alias lives.
2026-08-19 17:50:09 -05:00
copilotkit-qa-bot[bot] 90d36a62c7 Merge main into codex/fac-126-strands-ts-starter 2026-08-19 11:46:47 -07:00
Benjamin Taylor 5c0150392f ci: stop Playwright browser installs shelling out to apt
Every Playwright install in CI passed `--with-deps`, which runs `apt-get
update` before downloading the browser. apt on the runners cannot always
reach azure.archive.ubuntu.com; when it can't it retries for many minutes,
which is long enough to burn a job's whole `timeout-minutes` budget before
a single test runs. GitHub renders that kill as "The operation was
canceled", so it reads as a test failure rather than an infrastructure hang.

Chromium's system libraries are already present on the Ubuntu runner
images, and every one of these steps installs chromium only, so the browser
download is all they need. Six jobs lose their apt dependency:
test_unit, test_e2e-legacy-v1, test_e2e-showcase-on-demand,
test_showcase-frontend-matrix, showcase_eval and showcase_capture-previews.

Ports CopilotKit/website#529 to this repo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 12:06:44 -05:00
copilotkit-qa-bot[bot] 20e481b749 fix: make Strands TypeScript starter smokeable 2026-08-18 16:01:36 -07:00