dist/v2/runtime/endpoints/express.d.cts imports types from `cors` and `express`.
Neither ships its own declarations, and both @types packages were devDependencies,
so whether a consumer resolved them came down to whether something else in their
tree happened to hoist them.
Moving them to dependencies makes the published types self-contained.
`export type {} from "@whatwg-node/server"` worked around microsoft/TypeScript#42873
and its own comment says it was waiting on the TypeScript 5.5 stable release.
The repo is on 5.9.2 and typechecks without it.
It was reaching a devDependency, so it emitted an import of a module consumers
never install.
@copilotkit/channels is a devDependency and a pure re-export of
@copilotkit/channels-core, so tsdown treated it as bundleable and inlined its
prebuilt declarations into dist/channels/dist/index.d.cts -- along with a
rolldown helper chunk that ships JavaScript only. Consumers got a TS7016 for a
file they cannot see.
channels-core is a real dependency and stays external, so importing the types
from there drops the inlined copy entirely.
GraphQLContext was defined as `YogaInitialContext & {...}`. That single type
reference pulled the whole graphql-yoga barrel into every consumer's program,
and with it lru-cache@10, whose `implements Map` clause costs five TS2416
errors under strict + skipLibCheck: false.
Nothing in this package serves GraphQL any more -- every v1 integration entry
point delegates to the v2 Hono endpoint -- so the type is declared locally
instead. It is structurally identical, so a real Yoga context still satisfies it.
OSS-899 shipped 81 strict-mode errors to consumers because nothing checked what
the published .d.cts files reach for. validate-dts-ambient.ts checks their shape;
this checks their imports against the one thing that matters -- whether someone
who installed this package and nothing else can resolve them.
Flags devDependencies, optional peers, dependencies whose types live in a
devDependency @types package, relative imports of JS-only bundler chunks, and an
explicit ban on graphql-yoga, whose types drag lru-cache@10 into every consumer
program. Currently red on 18 real violations; the fixes follow.
The landing card ended in "The failed run event is highlighted below."
unconditionally, but the error carries no guarantee that such an item
exists. A code that reaches `run` through the catch-all without a run of
its own -- a locked thread, an agent that was never registered -- lands on
an empty AG-UI Events with the card still pointing at nothing, and
`applyEventErrorLanding` bails out silently rather than telling it. Same
for a tool error that arrives without a call id.
Split the copy by what it can promise: `advice` is about the reader's next
move and always holds, `highlight` is a claim about this view and renders
only once the item is there. Sending someone to look for something absent
is worse than saying nothing.
The mapping itself is unchanged. Whether a locked thread should be called
a failed run at all is a separate question for its author.
The thread-list-error route asserts that the first open lands on Threads
rather than Home. That only holds once the list request has been refused,
and the request is still in flight at that point: the helper waits two
microtask turns and a render, never the fetch. On a loaded machine the
fetch loses the race, the launcher carries no signal yet, and the open
lands on Home.
Green locally, red on all six CI matrix combinations. Reproduced here by
delaying the list fetch, which fails the assertion at 300ms and passes it
with this change at 1200ms.
Twelve quickstarts provisioned a license key in step 1 and then showed a
runtime constructed with `runner: new InMemoryAgentRunner()` — an option
that is mutually exclusive with `intelligence`, so the key was never read.
Threads showed "locked" and nothing indicated a choice had been made.
Each of those pages now constructs the runtime with `intelligence` and
`identifyUser`, names `INTELLIGENCE_API_KEY` where the route reads it, and
links /premium/connect-your-runtime — which had no inbound link from any
quickstart. The in-memory runner stays available as a labelled opt-out;
it was already the default, so passing it explicitly only added the steer.
Also adds the required `name` field to the `identifyUser` snippets in
connect-your-runtime.mdx and the runtime skill's agent-runners reference.
Both omitted it, so copying either was a type error.
Scope is every page that provisions a key and then shows a runtime that
cannot consume it. Pages that legitimately document the in-memory runner
(backend/agent-runner, deploy/agentcore) are unchanged.
Verified: all 15 doctest `component` snippets typecheck against the pinned
@copilotkit/runtime@1.68.3, and the gate goes red when `name` is removed.
`CopilotIntelligenceRuntime` hardcodes `IntelligenceAgentRunner` into its
`super()` call, so a caller-supplied `runner` could never be honored. The type
forbids it — `runner` is declared only on `CopilotSseRuntimeOptions` — but that
is an excess-property check: a JS, `as any`, or non-literal caller passing
`{ intelligence, runner }` reached the constructor and had `runner` silently
dropped with no diagnostic.
Add the runtime guard, mirroring the `channels` guard already in
`CopilotSseRuntime` for exactly this reason. Explicit `runner: undefined` still
constructs, matching how the sibling `identifyUser` / `channels` / `memory`
guards treat undefined.
This also resolves a contradiction inside the shipped runtime skill:
`SKILL.md:87` already asserted the rejection happens at construction, while
`references/agent-runners.md` correctly described the silent drop. The guard
makes SKILL.md true; agent-runners.md is updated to describe the throw and to
cite line numbers that match the current file.
Show agent, tool, and error on Inspector banners. Click the launcher to open the failed tool call or RUN_ERROR. Shorten the error pill wait to 400ms. Use dark gray titles on the rose tool-error card so the name stays readable.
useAgentNodeName stored the node name in a ref, so AG-UI STEP_STARTED
events never triggered a re-render and useLangGraphInterrupt's
agentMetadata.nodeName was stale by the time enabled() ran.
Replace the ref with a typed, pure state machine (useState + reducer):
- STEP_STARTED updates nodeName reactively
- RUN_FINISHED with outcome "interrupt" (standard) or a preceding
legacy on_interrupt custom event keeps the interrupting node instead
of advancing to "end"
- success/error still resolve to "end"; new runs and agent switches
reset to "start"
- share the on_interrupt event name via INTERRUPT_EVENT_NAME exported
from v2/types/interrupt so the protocol fact has a single definition
Fixes#1426
Five changes from looking at rendered variants side by side, none of them
behavioural.
The reveal animates a rounded clip. Without a round component the
revealing edge is a straight line sweeping sideways, which reads as a
wipe; with it the edge is the capsule's own cap travelling outward.
The text side of the padding is derived from the capsule's radius rather
than being a literal. Padding is measured from the bounding box, but the
first half-height of that side is the rounded cap, so a bare 14px left
the words sitting inside the curve — and the launcher size is itself a
clamp on the viewport, so no literal could have been right at every size.
The label is two lines now, a heading and a subline, at 12px and 10.5px.
It stays exactly as tall as the launcher; there was room above and below.
The pill takes pointer events while it is on screen, so a click opens the
Inspector as pressing the launcher does. That makes the subline's
instruction honest. It is deliberately not focusable: the launcher is
already a focusable control for the same action, and a second tab stop
for one action would be a regression.
The launcher's surface and edge are now two custom properties declared
once on the wrapper and resolved by both the button and the pill, so the
two cannot drift apart. The pill's own red-tinted border is gone — it read
as a second object rather than as the launcher opening. The surface moves
from near-black to a dark grey, softening the edge against a white page
from 20.5:1 to 16.5:1 while staying far above any legibility threshold.
Refs OSS-903
Once per outage the launcher beats, then opens sideways into a short pill
carrying the failure's name, holds long enough to read, and closes back to
the plain mark. Nothing stays behind: the dot keeps the state, the pill
carries only the moment.
The ticket this belongs to started with a user who could not tell what the
button was. A dot says something is wrong without saying who is asking, so
the pill names both the problem and the control raising it.
Errors only. The announcement feed's preview text is 54 characters against
a 36px launcher, so a pill carrying it would be ten times the width of the
control it grows from, sized by a feed we do not control. The label is read
from the signal rather than keyed off the tone, so a third signal can carry
one by declaring it.
The reveal animates a rectangular clip. Animating width would recompute
layout on every frame of someone else's page; scaling horizontally squashes
the mark itself, not merely the rounded ends. The contract is therefore
restated rather than broken: the rule was never "opacity and transform", it
is that nothing may force a layout per frame.
Direction comes from available room, because the launcher can be dragged to
within a margin of the left edge and that position persists. Left if it
fits, otherwise right, otherwise no pill at all -- the dot and the beat
still fire, so only the label is lost.
Recovery says nothing. The dot going out is already the message, and
announcing it would double the gestures across the break-and-fix cycle that
debugging consists of.
The whole gesture holds the single pending-beat slot, so an announcement
beat waits rather than cutting into it. Reduced motion gets the same words
and the same reading time without the movement. A polite live region speaks
the failure once, so the pill is not a sighted-only feature.
Refs OSS-903
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.
The launcher gains a red dot, and one beat, when the app's wiring is
broken: the runtime connection is in its error state, or a thread load
failed. It is a state indicator, so it clears itself when the problem
goes and there is nothing to dismiss.
A failed agent run deliberately does not raise it. A run failure is an
event, not a state, and an hour of iteration produces many of them; the
launcher would be red most of the time and carry no information. System
Health already reports run errors inside the panel, which is a
latest-activity readout rather than a health verdict.
The core error channel is unused for the same reason: all but one of its
emit sites are in the run handler, and its single wiring code already
sets the connection state. Memory failures are excluded because the
memory store subscribes lazily, so the state is unobservable until the
Learning view has been opened once.
Two signals now share a described table with a tone, a cadence, a
priority and two destinations. The generic version of this was removed
before the launcher-signal merge as speculative, which was right at the
time: it had one user. It has two now.
A pulse that cannot land is deferred rather than dropped, and only one
runs at a time. Starting a beat while one runs does not restart the CSS
animation, so the running beat would otherwise just change colour
mid-flight and the new one would be lost.
Telemetry gains oss.inspector.error_signal_viewed, modelled on the
announcement equivalent, plus has_error_signal and error_signal_source
on the open event. The failure message is never transmitted. The
catalogue moves to twenty-four names.
A runtime that dies mid-session is out of reach: the handshake runs once
and a transport-level run failure is reported through the run channel
without touching the connection state. Filed as OSS-904, to be fixed in
core so every reader benefits.
Refs OSS-903
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
## What does this PR do?
The Agent tab Current Messages list used `whitespace-pre-wrap` around
the message text.
The Lit template also had extra spaces and newlines around that value.
As a result, a short message such as `test` rendered with a large empty
block.
This change puts the message text on the same line as the opening tag,
so only the real message text is shown.
Verified in the browser: the user row is now the exact string `test`
(one line, 16px high).
Inspector tests: 485 passed, including a new regression test.
## Related PRs and Issues
None. Found while testing Playground on the react-router example.
## 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