CR-loop final polish (non-behavioral):
- tsdown-exports.test.ts: the "maps over fallback-array targets" case left an
unspied console.warn (b.mjs has no adjacent declaration) leaking to stderr.
Wrap it in withWarnSpy and assert the warning fired — the declaration-less
element is reported, not silently dropped.
- tsdown-exports.d.mts / validate-package-exports-types.ts: cross-reference the
two hand-mirrored ExportsEntry definitions so they cannot drift silently.
No source behavior change; 36 tests pass, validate:exports exits 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CR-loop confirmation-round #4 findings:
- validator: `resolvesForMode` (renamed from `typesCoversMode`) now applies to
EVERY condition, not just `types`. A partial object under `node`/`default`
(active in both modes) no longer silently covers the mode it lacks —
resolution falls through to the sibling, catching the untyped-JS #3324 shape
there. This completes the per-mode model.
- helper: `withTypesConditions` throws on a non-object `exports` (bare string /
array) instead of iterating it into a corrupt map; `typedTarget` now warns
loudly when a JS target has no adjacent declaration (was silent, contradicting
the helper's own fail-loud contract); idempotency skips only the helper's own
`{ types: <string>, ... }` output, so a hand-authored types-first PARTIAL
object is normalized (its untyped sibling gets a `types`). UMD / `.d.mts` docs
corrected; `ctx.pkg: unknown` documented as intentional (weak-type constraint).
- tests: node/default partial fall-through regression; types-first-partial
normalization; a helper->validator round-trip; a console.warn assertion for
the missing-declaration path; split the untouched-target test into silent
(non-JS) and warning (JS) cases.
- react-native: dropped a pre-existing duplicate `@ag-ui/client` external.
Call sites: resolvesForMode (internal, walk + recursion; grep-confirmed no
`typesCoversMode` refs); withTypesConditions signature unchanged, and the new
throw/warn never fire on tsdown's real input (object map, packageJsonPath
present, dts:true).
36 tests pass; validator typechecks; validate:exports exits 0; native-loader
builds of react-core + react-native unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CR-loop confirmation-round #3 findings:
- validator: a `types` condition now "wins" a resolution mode only when it
actually covers that mode (new `typesCoversMode`). A partial object `types`
(e.g. only `import`) no longer silently covers `require` — resolution falls
through to the sibling, catching a real #3324 shape the checker previously
missed. normalizeExports doc + a "read or parse" message tidied.
- helper: idempotency now keys on the FIRST condition being `types` (its own
output shape) rather than mere presence, so a hand-authored `types`-last
entry is normalized instead of passed through — keeping helper output
consistent with what the validator accepts. JSDoc corrected (acts on every
condition target, requires a subpath map, only ADDS a missing `types`).
- tests: made the runtime-only test discriminating (a `browser`-only target
must not be flagged — the prior fixture couldn't catch a regression);
exercised `node`; added partial/well-formed object-`types`, top-level
fallback array, and types-last normalization cases.
Call sites: typesCoversMode (new internal, called by walk); walk/withTypes
signatures unchanged; isActiveCondition now also used by typesCoversMode.
32 tests pass; validator typechecks; real 25-package validate:exports exits 0;
native-loader react-core build unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CR-loop confirmation-round findings on the #3324 guard:
- validator: replace the single-`typesIndex` model with per-mode resolution.
import- and require-mode resolve independently (each picks the first present
of {types, <mode>, node, default} in object order), so a trailing
`default`/`node` shadowed by a typed `import` + `require` is no longer a false
positive, while a genuinely misordered/absent `types` is still flagged.
- helper: make `withTypesConditions` idempotent (skip an object that already
carries a `types` key, so re-application never re-nests `default`) and
optional-chain `ctx` so a missing context hits the fail-loud message.
- CI: run scripts/__tests__/tsdown-exports.test.ts in the guard workflow (the
helper is the mechanism that injects the types conditions, and its test ran
in NO workflow) and add it to the trigger `paths`; note the standalone gate
validates committed artifacts, not build output.
- tests: assert `types` is emitted FIRST via serialization (toEqual is
key-order-insensitive and missed a types-last regression); add idempotency and
trailing-default regression cases.
- react-native: document why its exports map is hand-maintained (no tsdown
`exports` hook) and guarded by validate:exports.
Call sites enumerated:
- walk / findExportsTypeViolations — internal to the validator + its tests;
signatures unchanged. JS_CONDITIONS removed (grep-confirmed no refs); replaced
by RESOLUTION_MODES + isActiveCondition.
- withTypesConditions — 13 tsdown configs via customExports; signature
unchanged, tsdown always supplies ctx.pkg.packageJsonPath and never re-applies
or emits null/array, so the new guards never fire on current input.
28 tests pass; validator typechecks; real 25-package validate:exports exits 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CR-loop findings on the #3324 regression guard:
- validator: normalize bare-string and conditions-only `exports` sugar to the
"." subpath (previously a false negative / false positive); flag a `types`
condition shadowed by an earlier import/require/node/default — a misordered
`types` is the exact #3324 failure a strict resolver hits; recurse into
object-valued `types`; ignore runtime-only conditions (browser/deno/...) so a
typed import/require sibling is not falsely flagged; attach the file path and
`cause` when a package.json fails to parse.
- helper: guard `null` (blocked subpath) and array (fallback) targets — the old
code crashed on null and corrupted arrays into objects; fail loud if
`packageJsonPath` is missing rather than silently emitting a types-less map.
- tests: hermetic fixture coverage for package discovery/filtering + the new
exports shapes, and a new tsdown-exports.test.ts covering the previously
untested `withTypesConditions` helper.
Call sites enumerated:
- withTypesConditions — 13 tsdown configs via customExports. tsdown always
supplies packageJsonPath and generates no null/array targets, so the new
throw/guards never fire on current input (verified: native-loader build of
react-core unchanged, exports byte-identical).
- findExportsTypeViolations / validateAllPackages — signature unchanged;
callers are the validator CLI and the tests. Real 25-package
`validate:exports` still exits 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up to the .ts -> .mjs rename: update the validate-package-exports
workflow trigger paths and the validator's comment/error message that still
named scripts/tsdown-exports.ts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CI build runners use a Node with native TypeScript stripping enabled
(process.features.typescript), so tsdown selects its native ESM config
loader. That loader cannot resolve the extensionless relative import
"../../scripts/tsdown-exports" in each tsdown.config.ts, so every package
build failed with "Cannot find module" — cascading into the unit, runtime,
and all integration jobs (which run the build first). Local builds used
tsdown's bundler loader instead, which is why this passed pre-push.
Ship the shared helper as scripts/tsdown-exports.mjs (real ESM) plus a
hand-written tsdown-exports.d.mts, imported with the explicit .mjs
extension. This resolves under the native loader, tsdown's bundler loader,
and tsc alike. The generated exports are unchanged — package.json maps stay
identical.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Under moduleResolution bundler/node16/nodenext, TypeScript ignores the
top-level "types" field once an "exports" map exists, so strict-exports
tooling (e.g. the Backstage CLI) resolves no type declarations and reports
every named export as "has no exported member". tsdown does not emit a
types condition in the exports map it generates.
Add one — first, per import/require, ESM to .d.mts and CJS to .d.cts — via
a shared withTypesConditions helper (scripts/tsdown-exports.ts) wired into
each tsdown config, and directly in the hand-maintained react-native map.
Add a regression guard, since attw and publint do NOT catch this (TS's
adjacent-file fallback masks it): scripts/validate-package-exports-types.ts,
unit + all-package tests, a validate:exports npm script, and a CI workflow
that fail if any publishable package's exports map lacks a types condition.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## What does this PR do?
Adds two clonable starter templates —
`examples/integrations/claude-sdk-python` and
`examples/integrations/claude-sdk-typescript` — that show CopilotKit
driving a Claude Agent SDK agent over AG-UI, mirroring the
`langgraph-python` showcase (todos canvas, charts, A2UI flight cards +
dynamic dashboards, human-in-the-loop, theme toggle, threads drawer).
Each agent is a thin, idiomatic layer on the official `ag-ui-claude-sdk`
/ `@ag-ui/claude-agent-sdk` adapters: three backend tools (`query_data`,
`search_flights`, `generate_a2ui`) live in per-tool modules and are
wired into `ClaudeAgentAdapter`, while the shared todo board is driven
by the adapter's built-in `ag_ui_update_state` tool. The default model
is `claude-sonnet-5` and local dev uses a real `ANTHROPIC_API_KEY`
(matching the official AG-UI dojo). Both instances are registered in the
`_parity` manifest so their frontends stay synced with the north-star.
## Related PRs and Issues
- Complements the Claude Agent SDK quickstart docs (#5840)
## Checklist
- [ ] 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
- [ ] "Allow edits by maintainers" is checked
🤖 Generated with [Claude Code](https://claude.com/claude-code)
CodeRabbit flagged that the form onSubmit added to headless-chat could submit
empty/whitespace messages. The handler now returns early on blank input and the
submit button is disabled when the message is empty. Applied to the claude-sdk
starters (where the form lives); headless-chat.tsx is already declared
allowedDivergence for these instances, so parity stays green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Revert the cross-demo parity sync from b34a9a348: langgraph-python (north-star)
and the langgraph-js / langgraph-fastapi / strands-python instances are restored
to their origin/main state — this PR should not mutate the canonical demo or its
siblings.
Instead, keep the CodeRabbit fixes on the claude-sdk-* starters and declare the
affected shared files as per-instance `allowedDivergence` in the parity manifest,
so `pnpm parity:check` passes without touching the other demos. The starters
carry fixes the north-star has not caught up to yet:
- border-3 -> border-[3px]; bg-[--x] / text-[--x] -> [var(--x)] (Tailwind v4)
- JSX.IntrinsicElements -> React.JSX.IntrinsicElements; Recharts <Bar shape> type
- tool-rendering args?: unknown; mode-toggle a11y; headless-chat <form>
- docker-route-override AGENT_URL trailing-slash normalization
next.config.ts is left matching the north-star template (its ignoreBuildErrors is
a shared build-config concern and the Docker build re-adds it regardless).
parity:check green (5/5 instances, 0 errors); claude-sdk tsc + oxfmt clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The CodeRabbit fixes in d4ef84f2c edited verbatim parity-tracked files
directly in the claude-sdk-* instances, diverging them from the north-star
(langgraph-python) and failing `pnpm parity:check`.
Move the shared-surface fixes to the north-star and propagate to every
tracked instance via `pnpm parity:sync --all`:
- border-3 -> border-[3px] (border-3 is not a Tailwind utility)
- bg-[--x] / text-[--x] -> [var(--x)] (Tailwind v4 CSS-variable syntax)
- JSX.IntrinsicElements -> React.JSX.IntrinsicElements (@types/react 19)
- Recharts <Bar shape> callback typing
- tool-rendering args?: unknown
- mode-toggle aria-pressed/type/role, headless-chat <form> + aria-label
- docker-route-override AGENT_URL trailing-slash normalization
Also revert the next.config.ts `ignoreBuildErrors` removal: next.config.ts is
a north-star template file shared across all integration demos, so the
suppression can't be dropped on a subset without breaking parity, and dropping
it template-wide would need every demo verified to build clean without it. The
two real type errors it was masking are now fixed at the north-star, so the
shared surface is type-clean regardless.
parity:check green (5/5 instances, 0 errors); claude-sdk tsc + oxfmt clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve the real issues surfaced by CodeRabbit on the new claude-sdk-python /
claude-sdk-typescript starters (frontend files are shared, so most fixes apply
to both):
- Fix two type errors that `ignoreBuildErrors` was masking: `JSX.IntrinsicElements`
-> `React.JSX.IntrinsicElements` (@types/react 19) and the Recharts `<Bar shape>`
callback type; then drop the blanket `typescript.ignoreBuildErrors` from
next.config.ts so source type-checks. The Dockerfile's build-time patch still
re-adds it for the `next@latest` Docker build, so deploy behavior is unchanged.
- Fix Tailwind v4 CSS-variable syntax: `bg-[--x]` -> `bg-[var(--x)]`, and
`border-3` -> `border-[3px]` (border-3 is not a utility -> invisible spinner).
- Harden the agent tools: omit the empty ANTHROPIC_API_KEY, wrap the Anthropic
call in try/catch (TS + Python), normalize the AGENT_URL trailing slash, and
make the Flight schema's id/airlineLogo/statusIcon required to match the
"must have" tool description.
- Minor a11y: mode-toggle `aria-pressed`/`type`/`role`, headless-chat `<form>`
+ `aria-label` (Enter-to-submit).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add claude-sdk-python and claude-sdk-typescript to the Integrations table
in examples/README.md (17 → 19; total 48 → 50). The two starters were added
to examples/integrations/ but were missing from the index.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two clonable starter templates showing CopilotKit driving a Claude Agent SDK
agent over AG-UI, mirroring the langgraph-python showcase (todos canvas, charts,
flight cards, dynamic dashboards, HITL, theme, threads drawer).
Each agent is a thin, idiomatic layer on the official ag-ui-claude-sdk /
@ag-ui/claude-agent-sdk adapters: three backend tools (query_data, search_flights,
generate_a2ui) live in per-tool modules and are wired into ClaudeAgentAdapter,
while the shared todo board is driven by the adapter's built-in ag_ui_update_state
tool. The default model is claude-sonnet-5 and local dev uses a real
ANTHROPIC_API_KEY (matching the official AG-UI dojo). Both instances are
registered in the _parity manifest so their frontends stay synced with the
langgraph-python north-star.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Summary
SDK-emit half of **OSS-446** (lease-token fencing for hosted-bot
render/complete). The `fail` path already sends the delivery lease
token; **render-accept** and the **completion intent** did not — so
app-api fell back to the weaker instance-id + expiry check on those two
paths.
This makes the SDK send `leaseToken` on both, on **both transports**:
- **HTTP** — `HttpRenderEventSink.push` now includes `leaseToken`, via a
new `leaseTokenFor(deliveryId)` accessor on `HttpDeliverySource`
(mirrors the existing `scopeFor`). (`ack` already sent it.)
- **Phoenix** — `push` (render-accept) and `complete_requested` now
carry `leaseToken` from `DeliveryState` (`fail` already did).
## Why it's safe to ship now (ahead of the app-api "require" flip)
Verified end-to-end against the live Intelligence server:
- **Gateway** `validate_render_payload` allows `leaseToken` (optional,
`maybe_put_lease_token`) and forwards it via `accept_render_event`;
`validate_complete_payload` merges the payload through and forwards it
via `complete_delivery`.
- **app-api** fences render-accept (`$N IS NULL OR lease_token_hash =
$N`) and complete (`$N IS NULL OR ...`) **optionally** today — so
supplying the token starts fencing on it immediately, and omitting it
still works. No breakage; strictly a security improvement on the paths
that were previously unfenced.
## Scope
This is **half A** (SDK emit). **Half B** — flipping app-api's
render-accept + complete fences from optional to *required* and dropping
the instance-id/expiry fallback — is deferred until #511 (managed Teams)
settles `managed-bots/service.ts`, and follows from OSS-446's "require
*once the SDK sends it*" premise.
## Tests
- HTTP: render-accept POST asserts `leaseToken` from the claimed lease
flows into the body.
- Phoenix: render-accept + `complete_requested` payloads assert
`leaseToken`.
- Build + 95 tests green.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
SDK-emit half of OSS-446 (lease-token fencing for hosted-bot render/complete).
The fail path already sends the lease token; render-accept and the completion
intent did not, so app-api fell back to instance-id + expiry there.
- HTTP: HttpRenderEventSink.push now includes leaseToken (new leaseTokenFor()
bridge on HttpDeliverySource, mirroring scopeFor). (ack already sent it.)
- Phoenix: push (render-accept) and complete_requested now carry leaseToken
from DeliveryState (fail already did).
Optional/forward-compatible: app-api + gateway already accept and fence on the
token when present (verified render/complete validators + fencing SQL), falling
back to the old check when absent — so this deploys safely ahead of the app-api
flip-to-required (OSS-446 half B), which waits on #511 settling managed-bots
service.ts.
## Summary
Adds **Open Generative UI (OGUI)** to the Northwind banking demo: the
agent can author sandboxed, interactive UI (rendered in an isolated
iframe) that pulls **real, read-only banking data** via sandbox-function
callbacks — so every figure it shows is real app data (fetched on
demand), never fabricated, and no secret ever crosses the boundary.
- **New `src/opengen/` module** — a stable, module-scope
`sandboxFunctions` array
(`getTransactions`/`getPolicies`/`getCards`/`getKpis`) whose handlers
read a module snapshot kept fresh by a headless `<SandboxDataSync/>`
mirroring the app's live (role-filtered) `useCreditCards` view. Handlers
return **projection DTOs** — `getCards` drops `pin`/`expiry`, guarded by
a no-leak unit test.
- **Shared over-limit derivation** — extracted to
`src/lib/over-limit.ts` so the chat readable, the A2UI report renderers,
and the OGUI sandbox all agree on which charges are over limit
(behavior-preserving).
- **OGUI enabled on both runtime paths** (Intelligence + OSS) with an
**artifact-type routing fence** in the agent prompt:
`generateSandboxedUi` only for interactive/custom UI, explicitly
excluded from reports/charts *even when the user says "build"* (protects
the existing "Build a spend report on the canvas" pill), and never part
of the teach/recall arc.
- **Provider wiring** — `openGenerativeUI={{ sandboxFunctions,
designSkill: NORTHWIND_DESIGN_SKILL }}` plus two OGUI-only pills ("Build
an interactive spend explorer", "Prototype a cash-flow what-if
calculator").
- **Deterministic routing guard** — `e2e/ogui-routing.spec.ts`
(dedicated OSS-mode config, isolated ports, no docker) pins both
boundary sides: the 5 visualization pills still route to their curated
tool (no iframe), and the 2 OGUI pills render an iframe. Also fixes
stale pill assertions in `smoke.spec.ts`.
Additive only — no changes to existing curated charts, the A2UI report
canvas, or the teach/recall arc beyond the behavior-preserving
over-limit extraction.
## Test Plan
- [x] Unit suite green (65 tests) — incl. `over-limit`,
`sandbox-functions` (no-`pin`/`expiry` leak + over-limit flag + KPI
counts)
- [x] `tsc --noEmit` clean · eslint clean · `nx build` success
- [x] OGUI routing e2e: **7 passed** (OSS mode, no docker) — curated
pills + boundary + OGUI pills
- [ ] **CI**: license-gated `smoke.spec.ts` + docker-backed
`memory-learning.spec.ts` (blocked locally: no license token /
Intelligence stack; must pass unchanged in CI)
- [ ] **Manual**: render "Build an interactive spend explorer", confirm
iframe figures match the dashboard in light/dark, and no PIN is ever
shown
## Notes
- Follow-up (out of scope): additional over-limit derivation copies
remain in `proactive-notice.tsx`, `transactions-list.tsx`,
`pending-approvals-chat.tsx` — candidates to migrate onto the new shared
helper.
- The deterministic e2e pins the tool call (aimock), so it proves the
tool-rendering plumbing and that curated surfaces still work — real LLM
routing is the manual check above.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The .first() guard on the 'rendered on the canvas' handoff-pill assertion
was justified by an inaccurate comment (accumulation across exchanges). The
real cause is intra-turn: generateSandboxedUi has followUp:true, so aimock
re-serves the same fixture on the unchanged-userMessage follow-up turn in
replay -> a second identical pill. A terminating sequenceIndex follow-up
fixture was attempted but destabilized the suite (title-generation requests
substring-match the pill text and consume the sequence counter before the
real leg-1 turn), so the .first() guard remains. Test/fixtures only; no
source changes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Release channels-slack v0.1.1
**Scope:** `channels-slack` | **Bump:** `patch`
---
### How this release process works
1. **This PR was created automatically** by the "release / create-pr"
workflow.
It bumped the `channels-slack` packages to `0.1.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 `channels-slack` packages to npm at version `0.1.1`
- Creates git tag `channels-slack/v0.1.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.
## Problem
Claude Agent SDK docs could render malformed or missing extracted
snippets across the generated Python and TypeScript integration docs.
The generative UI pages had stale duplicate regions and generic setup
leakage, while the custom look-and-feel reasoning and slots pages
referenced demo cells or regions that did not exist for every Claude
integration.
## Why
The docs pipeline treated accidental duplicate region names across files
as intentional multi-file regions, and several authored docs pages
drifted from the actual generated showcase demo IDs/regions. That left
some pages visually correct at a glance but broken when users opened
specific extracted code snippets.
## Fix
- Move the shared `bar-chart-renderer` regions to the complete
`useComponent` call and delete stale duplicate snippet files.
- Add an explicit duplicate-region guard and verifier coverage for
accidental cross-file region collisions.
- Add line-emphasis support for extracted `<Snippet>` blocks and setup
`<DemoCode>` output.
- Scope generative UI feature pages away from generic `agent-setup`
boilerplate.
- Repair the shared reasoning-messages docs to use the generated
`reasoning-default` and `reasoning-custom` demo cells.
- Add the missing Claude Python chat-slots teaching snippet regions and
keep both Claude slot snippets self-contained.
- Broad-audit both Claude integration docs locally, then targeted-audit
the repaired reasoning/slots pages in light and dark mode.
## Problem
The published declaration files for `@copilotkit/react-core`,
`@copilotkit/react-ui`, and `@copilotkit/react-textarea` contain imports
that TypeScript cannot resolve, so **`attw` (Are The Types Wrong)
reports `InternalResolutionError` across every resolution mode**
(`node10` / `node16` / `bundler`). In `@copilotkit/react-core` this was
being **masked in CI** by `--ignore-rules internal-resolution-error` on
the package's `attw` script — so the existing `check:packages` gate
looked green while consumers under `moduleResolution:
bundler`/`node16`/`nodenext` got broken types (the symptom reported in
#3324: `has no exported member 'useAgent'`, etc.).
Two distinct artifacts leaked into the emitted `.d.ts` / `.d.cts` /
`.d.mts` (neither affects the JS bundles):
1. **Side-effect CSS imports** — `import "./index.css"` is intentionally
kept in the JS so styles auto-load for bundler consumers, but
`rolldown-plugin-dts` also left it in the declarations, where TypeScript
can't resolve a `.css` as a typed module.
2. **Extensionless relative `./context` import** —
`@copilotkit/react-core/v2/headless` re-exports the externalized context
module; the JS bundle correctly externalizes it to
`@copilotkit/react-core/v2/context`, but the declaration kept the
relative `./context`, which is invalid in ESM declarations.
> Note: this is **not** the missing-`exports.types`-condition theory
from #3324. tsdown deliberately relies on co-located `.d.mts`/`.d.cts`
siblings; `@copilotkit/core` already resolves cleanly. The real defects
are the two leaked imports above.
## Fix
A small tsdown `build:done` hook post-processes the emitted declarations
**on disk** (after every format is written, so it catches both `.d.mts`
and `.d.cts`):
- strips side-effect CSS imports from declarations (JS keeps them);
- rewrites the relative `./context` import to the
`@copilotkit/react-core/v2/context` package path (matching how the JS
bundle externalizes it).
Also:
- **Removed the `--ignore-rules internal-resolution-error` band-aid**
from `react-core`'s `attw` script so the existing CI gate validates for
real.
- **Dropped the dead `codeSplitting` option** from the UMD configs —
tsdown never reads it (it's a rolldown-only key), and it was failing
`tsc` in the configs that type-check themselves. UMD output is unchanged
(single file).
## Verification
- All three packages build; **no CSS or relative-`./context` imports
remain in any declaration**, while the JS bundles still contain them
(styles auto-load preserved).
- `attw` + `publint` pass for all packages **with no suppression**
(`react-core`'s `/v2`, `/v2/headless`, `/v2/context` are green for
node16-cjs/esm/bundler).
- Unit tests pass.
- A standalone consumer project (real tarball install, `skipLibCheck:
false`) type-checks the public APIs — including `useAgent` /
`useFrontendTool` / `useConfigureSuggestions` — cleanly under **both
`bundler` and `nodenext`**, and the headless↔context class is nominally
identical.
## Out of scope (follow-ups)
- `@copilotkit/react-native`: its `--ignore-rules
internal-resolution-error` currently suppresses nothing (no IRE) and it
has a separate `NoResolution` flag.
- `@copilotkit/vue`: a large, genuine set of `.vue`/relative-import
declaration errors unrelated to this change.
Relates to #3324.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- Add the new `copilotkit import` command to the shared CLI docs
rendered at `/cli` and integration CLI pages.
- Document ADK and LangGraph examples, scripted flags, source credential
env vars, `--dry-run`, and `--replace`.
- Update the shell-docs nav test expectation for de-duped `Threads`
placement in authored framework nav.
## Test Plan
- `cd showcase/shell-docs && npm run lint` (passes with existing
warnings)
- `cd showcase/shell-docs && npm run typecheck`
- `cd showcase/shell-docs && npm test`
- `cd showcase/shell-docs && npm run build`
## Release channels-teams v0.1.1
**Scope:** `channels-teams` | **Bump:** `patch`
---
### How this release process works
1. **This PR was created automatically** by the "release / create-pr"
workflow.
It bumped the `channels-teams` packages to `0.1.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 `channels-teams` packages to npm at version `0.1.1`
- Creates git tag `channels-teams/v0.1.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.
## Summary
Adds a `./render` subpath export to `@copilotkit/bot-teams` surfacing
`renderAdaptiveCard`, `isPlainText`, `collectPlainText`,
`ADAPTIVE_CARD_CONTENT_TYPE`, and `createRunRenderer`, so the managed
(Intelligence-hosted) Teams egress path can reuse the adapter's Adaptive
Card rendering without deep-importing `dist/*`.
Paired with the managed Microsoft Teams work in Intelligence (OSS-441
slice ②): CopilotKit/Intelligence#511.
## Notes
- Additive only — a new `exports["./render"]` entry +
`src/render/index.ts` re-export barrel. No behavior change to existing
exports.
- Not strictly on the critical path yet: the managed runtime currently
resolves the renderer via a runtime deep-dist import of the published
package, so this export is the clean forward path rather than a hard
dependency.
- Coordinate the `bot-teams` → `channels-teams` rename (OSS-438) before
merge.
## Problem
The Intelligence realtime loop exposed two SDK-side ownership gaps while
testing managed Coworkers against the Intelligence PR:
- Phoenix channel auth should use the SDK socket auth token path
expected by the gateway.
- Delivery handling needs to carry the app-api lease token and
authoritative delivery scope through render/fail/complete handling
instead of rebuilding ownership from local defaults.
- Runtime integration needs to pass the render sink into
`intelligenceAdapter` so managed runtimes stream rich render frames over
the realtime path.
## Why
The target Coworker path is websocket-first: Intelligence app-api owns
durable delivery state, realtime-gateway owns live transport, the SDK
receives leased delivery over Phoenix, streams render events, waits for
durable receipt coverage, and sends completion intent without taking
over app-api ack authority.
This PR is stacked on Alem's SDK realtime branch so the dependency trail
is explicit:
- Base SDK branch: `codex/oss-402-sdk-render-events`
- Intelligence PR: https://github.com/CopilotKit/Intelligence/pull/466
- Linear: OSS-402 / OSS-406
## Fix
- Use Phoenix socket `authToken` for the managed bot channel.
- Preserve `leaseToken` and delivery `scope` in
`PhoenixRealtimeTransport` state.
- Send fail/nack payloads with the correct lease token, delivery status,
and optional accepted-through pointer.
- Pass `renderSink` from `startManagedBots` into `intelligenceAdapter`.
- Add regression coverage for render-sink propagation and lease-token
fail payloads.
## Testing Methodology
Local verification used an external pnpm store to avoid repo-local
`.pnpm-store` churn:
`PNPM_CONFIG_STORE_DIR=/private/tmp/pnpm-store-codex`.
- `PNPM_CONFIG_STORE_DIR=/private/tmp/pnpm-store-codex
PNPM_CONFIG_CONFIRM_MODULES_PURGE=false corepack pnpm --dir
/private/tmp/copilotkit-oss-402-20260701 --filter
@copilotkit/bot-intelligence test`
- Passed: 5 files, 48 tests.
- Commit hook also ran the package gate for
`@copilotkit/bot-intelligence`:
- `test`: passed, 5 files / 48 tests.
- `publint`: passed with repository URL suggestion only.
- `attw --pack . --profile esm-only`: passed with the existing ignored
CJS-to-ESM warning profile.
- `git diff --check -- packages/bot-intelligence/src/phoenix-channel.ts
packages/bot-intelligence/src/phoenix-transport.ts
packages/bot-intelligence/src/render-events.test.ts
packages/bot-intelligence/src/runtime.test.ts
packages/bot-intelligence/src/runtime.ts`
- Passed.
Scope control: only the five `packages/bot-intelligence/src/*` files
above are committed. Existing local `pnpm-lock.yaml` and image/LFS dirt
in the SDK worktree were left unstaged and are not in this PR.
Reconcile #5878 with the @copilotkit/bot*→@copilotkit/channels* rename (#5849):
- relocate the new render barrel (render/index.ts, render/index.test.ts) into
packages/channels-teams; relative imports (./adaptive-card, ../event-renderer)
resolve unchanged. package.json ./render export auto-merged.
- fix a stale @copilotkit/bot-teams comment ref to @copilotkit/channels-teams.
Review follow-ups for the managed delivery-ownership change:
- add an optional `log` seam to PhoenixTransportConfig; the transport was
otherwise silent, so the two new drop paths were invisible failure modes.
- log the leaseToken-missing drop distinctly (the gateway/SDK version-skew
hazard: without it, every delivery silently re-loops on lease lapse) and the
nack no-delivery-state drop, instead of bare returns.
- refresh TSDoc: toIngressEnvelope's new return shape + drop semantics, and the
DeliveryState.leaseToken/scope fields.
- tests: leaseToken-required drop, nack no-state no-op, and per-delivery scope
stamping on render + fail (the scope field previously had no coverage).
Tracked follow-up to #5882 (D4 first-token SSE turn-complete fix). These
are the deferred **bucket-(b)** items from that PR's CR —
non-load-bearing polish/hardening on the D4 probe driver
(`showcase/harness/src/probes/drivers/d4-chat-roundtrip.ts` +
`.test.ts`). No re-architecture; each change is tight and scoped.
## Items
### 1. Budget-exhaustion retry guard (behavioral)
A late non-completion retry resend could floor its `type`/`press` action
timeout to ~1ms (when the remaining budget ≈ 0), throwing a
page-fault-shaped error. That throw was red-classified indistinguishably
from a real page fault — a spurious-red flap source. Fix: skip the
resend when the remaining budget is below `RETRY_MIN_BUDGET_MS` (750ms);
the stall then reds on its own terms.
**Red-green:** with the guard disabled, the doomed resend attempts a
second `type` (typeAttempts=2); with the guard, `type` fires exactly
once (typeAttempts=1). RED observed (2), GREEN observed (1).
### 2. Degraded-path floor when interceptor silently no-ops (behavioral)
When `sseAttachFailed` is true, the page-side turn-lifecycle globals
were never seeded, so the poll could only fall into the never-observed
branch and pin the deadline to the base `textPollTimeoutMs` floor —
reintroducing the slow-first-token false-red #5882 targets. Fix: consult
`sseAttachFailed` to widen the never-observed wait to the per-attempt
ceiling.
**Red-green:** degraded page + late-but-present token (800ms, base floor
bites at the ~500ms poll before the token) → RED (pre-fix, base-floor
fast-fail) vs GREEN (post-fix, widened to ceiling captures the token).
RED observed (`red`), GREEN observed (`green`). A genuinely-empty
degraded run still reds (over-correction guard).
### 3. Retry edge-header re-attribution (telemetry)
On a retry-rescued GREEN turn, `messageSendEdge` / `messageSendEdge` /
`lastMessagePostResp` stayed latched to the first (stalled) attempt,
mis-attributing `edge_interference_signal` / the DEBUG raw-byte sample.
Fix: re-arm the capture latches before the resend so the winning
attempt's response re-captures them.
**Focused test:** stalled attempt carries `cf-mitigated: stalled`,
winning resend `cf-mitigated: winning`; the final `probe.message.send`
boundary now carries `winning` (pre-fix it reported `stalled`).
### 4. Coverage (tests only, no prod change)
- FIFO-cap `CVDIAG_MAX_OUTSTANDING_STARTS_PER_URL` eviction backstop
(guard-verified: fails if the cap is removed).
- DEBUG-auto-disarm fail-closed negative test (disarmed → no raw-byte
capture).
- Alternate-content / raw-byte block SKIPPED on the container-success
(non-empty) path.
- Fixed the `makeLateTokenBrowser` shared-page state leak: each
`newContext().newPage()` now mints its own state, so L3 and L4 each run
an independent stall+retry cycle (was a singleton that leaked
`sendCount` from L3 into L4, so the L4 retry path was never genuinely
exercised).
### 5. `lastStoppedAtMs` documentation (cleanup)
Write-only in d4; documented that it is retained for shared-global
parity with the `attachSseInterceptor` global shape the d6 run-signal
snapshot mirrors, so it does not read as dead code.
## Verification
- `tsc --noEmit`: clean
- `tsc -p tsconfig.build.json` (build): clean
- Full harness vitest: **3213 passed, 0 failing** (72 in the d4 file,
all green)
- oxlint: **0 errors** (only pre-existing warnings, none new)
- Diff hygiene: only the driver + test file (no `repro_*` / `baseline`)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---
## CR follow-up round (commit 13a636b67)
CR found the follow-up left `readTurnState()` error handling
**inconsistent** — the exact false-red/flap class this effort fights.
Addressed, plus completed two of the PR's own items.
### (a) Harmonized `readTurnState()` error handling
One guarded `safeReadTurnState()` wrapper now backs all three consumers
(`readBaseline`, `readTurnComplete`, `readDegraded`). A mid-poll
`readTurnState()` throw now means ONE thing everywhere: "no reliable
signal" → return a well-defined degraded sentinel (`sseAttachFailed:
true`, zeroed counters) → route onto the degraded **widen** path (not
base-floor fast-fail, not a spurious `level-error`), and it is
**observable** via a one-shot greppable marker. Previously:
`readDegraded` swallowed the throw into `false` (silent base-floor
false-red, no telemetry) while `readBaseline`/`readTurnComplete` let it
escape → generic `level-error` (spurious red). A genuinely-empty
degraded turn still reds at the widened ceiling (no masking).
### Folds
- **item-1 first-send cap:** guard the in-send `press` against
`SEND_PRESS_MIN_BUDGET_MS` so a near-hang `type` can't floor `press` to
~1ms and yield a generic `level-error`; classify distinctly as
`send-budget-exhausted`. Only `press` is guarded (`type` opens the
envelope) so a legitimately-small `pageTimeoutMs` still issues a healthy
first send.
- **item-3 null-header:** suppress the `finally`-block fallback
`probe.message.send` once a real-header boundary already fired, so a
retry whose winning resend lands no POST no longer emits a second
NULL-header boundary (mis-attributed `edge_interference_signal`).
- errorDesc JSDoc: added `abort` to the enumeration (zero-risk).
### Red-green (verbatim, against the real runLevel/readTurnComplete
path)
- **readTurnState throw:** RED `expected 'red' to be 'green'` (pre-fix
spurious red on a late-but-present token) → GREEN (degraded widen; late
token passes; genuinely-empty degraded still reds).
- **first-send cap:** RED `expected 'level-error' to be
'send-budget-exhausted'` → GREEN (distinct classification, no
1ms-floored `press`).
- **item-3 null-header:** RED `expected 2 to be 1` (second null-header
emit) → GREEN (single correct attribution).
### Gates
`tsc --noEmit` clean · `npm run build` clean · full d4 vitest **76/76
pass** · oxlint **0 errors** (warnings pre-existing). Diff: driver +
test only (no repro_*/baseline).
The aggregate e2e-smoke:<slug> red signal omitted errorDesc on the normal
return path, so an abort/timeout/send-budget-exhausted red that runLevel
RETURNS (not throws) showed on the PRIMARY dashboard tick as an unclassified
content-shaped red — only the side chat:/tools: rows kept the classifier.
Thread the failing level's errorDesc (L3 precedence, L4 fallback) onto the
aggregate so the primary tick matches the side row and the launcher-phase
abort path. Does not change red/green — only carries the classifier.
Also reorder the aborted-and-empty short-circuit ABOVE the alternate-content
/ raw-byte evaluate reads: an aborted run's page is tearing down, so those
reads were swallowed against a dead page and emitted an ambiguous empty
histogram. Non-aborted runs still perform the alternate-content salvage.
A mid-poll abort — the external ctx.abortSignal firing, or the driver's
own hard-timeout landing during the first-token poll — makes runAttempt
return empty WITHOUT throwing. The retry loop breaks and control falls to
the clean-exit path, where the level was misclassified as a generic
content red ("empty assistant response", probe.exit outcome "err", no
errorDesc). That masqueraded a teardown/abort/timeout as a CONTENT
failure on the dashboard + CVDIAG.
Add an aborted-AND-empty guard before the content-red gate that
short-circuits to the same abort classification the other paths use
(errorDesc "abort", probe.exit outcome "timeout"). Discriminator is
abortSignal.aborted, not emptiness alone: a genuinely-completed-empty
turn (not aborted) stays the content-red "empty assistant response".
Harmonize the three readTurnState() consumers in the d4 chat-roundtrip probe
through one guarded safeReadTurnState() wrapper so a mid-poll readTurnState()
throw is handled consistently everywhere: it means "no reliable signal" ->
degraded widen + observable telemetry, never a silent false-red (the prior
readDegraded swallow) nor a spurious level-error (the prior unguarded
readBaseline/readTurnComplete escape). A genuinely-empty degraded turn still
reds at the ceiling.
Folds completing the PR's own items:
- item-1 first-send cap: guard the in-send press against SEND_PRESS_MIN_BUDGET_MS
so a near-hang type can't floor press to ~1ms and produce a generic
level-error; classify distinctly as send-budget-exhausted. Only press is
guarded (type opens the envelope), so a legitimately-small pageTimeoutMs still
issues a healthy first send.
- item-3 null-header: suppress the finally-block fallback probe.message.send once
a real-header boundary already fired, so a retry whose winning resend lands no
POST no longer emits a second null-header boundary (mis-attributed
edge_interference_signal).
Also add "abort" to the errorDesc JSDoc enumeration (zero-risk).
Red-green covered for all three behavioral items against the real
runLevel/readTurnComplete path with a faithful fake.