Commit Graph

419 Commits

Author SHA1 Message Date
Tyler Slaton 001dda539a chore(channels-intelligence): complete channel terminology sweep 2026-07-10 14:47:06 -07:00
Tyler Slaton 68e43fefe1 refactor(channels-intelligence): rename remaining channel APIs 2026-07-10 14:39:56 -07:00
Benjamin Taylor b394f06fdc refactor(channels): rename @copilotkit/bot* packages to @copilotkit/channels* (OSS-438)
Renames the Bots SDK to the Channels SDK. Names only — no behavior change.

- 8 packages @copilotkit/bot* -> @copilotkit/channels* (git mv dirs, names,
  workspace: cross-deps). Now includes @copilotkit/bot-intelligence ->
  @copilotkit/channels-intelligence (landed on main via #5761; unpublished, so
  renamed fresh with the family).
- release.config.json scope keys + versionSource; ReleaseScope union;
  canary/stable-release/publish-release scope dropdowns; verify script
- examples/slack (Kite) + examples/teams: deps, jsxImportSource, imports
- showcase/shell-docs: content dirs docs/bots->docs/channels and
  reference/bot->reference/channels, nav registry, redirects

createBot and other API names unchanged. Old @copilotkit/bot* to be deprecated
after the new packages publish (bot-intelligence was never published).

Re-derived onto latest main (was conflicting after #5761 landed).

Refs OSS-438
2026-07-08 13:27:35 -05:00
Ben Taylor 9fa925bd95 feat(bot,runtime): managed bots SDK — run the bot SDK from Intelligence-delivered events (OSS-360/361) (#5761)
## Summary

Lets the `@copilotkit/bot` SDK run from **Intelligence-delivered
events** without a second programming model, and adds the runtime `bots`
declaration API. A managed event (delivered by Intelligence) runs the
*same* customer handlers, tools, context, commands, Bot UI, and agents
as local/custom adapters — the managed path is "just another
`PlatformAdapter`," fed by injected transports.

This is the **OSS / SDK slice** of the Hosted Managed Bots work. The
credentialed transports (Realtime Gateway, Connector Outbox) and the
frozen shared contracts live elsewhere (see *Out of scope*); this PR
ships the seams they plug into, fully runnable headless.

Relates to **OSS-360** (runtime bots API), **OSS-361** (run the SDK from
Intelligence events), **OSS-363** (Slack render/codec reuse).

## What's in here

- **`intelligenceAdapter()` bridge** (`@internal`, not publicly
documented) — implements `PlatformAdapter` over two injected transports:
`DeliverySource` (inbound) + `EgressSink` (outbound). Ingress →
`onTurn`/`onCommand`/`onInteraction`/`onThreadStarted`/`onReaction`; ack
on success / nack on throw (at-least-once). Egress emits generic
operations carrying `BotNode[]` IR with **deterministic ids**
(`turnId:seq`, reset per turn) so a redelivered turn reproduces the same
ids for the Connector Outbox to dedupe. Idempotency lives at egress, so
the managed path skips ingress dedup (`skipIngressDedup`) — a redelivery
re-runs rather than being dropped.
- **Runtime `bots` API** — `new CopilotRuntime({ intelligence, bots })`,
accepted by TypeScript **only when `intelligence` is configured**
(discriminated union). `createBot({ name })`; `startManagedBots()`
validates names (required, identifier-style, unique — fail-loud), builds
activation metadata, and wires each bot to its resolved transport.
- **`PlatformCodec` seam** + Slack egress codec (`slackCodec`) composing
the existing pure `renderSlackMessage`, so IR→native rendering is shared
(no Bolt/creds) instead of duplicated.
- **Backwards-compatible SDK foundations**: `bot.addAdapter()` +
optional `adapters`, deferred backend resolution at `start()` with
`stateStore`-provider precedence (+ multi-provider warning),
`bot.transcripts` throws pre-start, optional
`eventId`/`turnId`/`deliveryId` on ingress + handler context. Existing
`createBot` callers and every `PlatformAdapter` implementer are
unaffected.
- **In-memory transports + fixture tests** — the full dispatch path
(envelope in → handler runs → egress op out) runs with zero
Slack/Intelligence/network.

## Out of scope (external / separate tickets)

- **Realtime Gateway + Connector Outbox transports** — implemented in
the closed-source repo against the `DeliverySource`/`EgressSink`
interfaces shipped here.
- **Shared contracts freeze (OSS-377)** — consumed here via a minimal,
isolated placeholder (`managed/contracts.ts`, marked `TODO(OSS-377)`);
swaps in via one import change.
- **OSS-363 ingress normalization** — the egress codec is done;
extracting the pure Slack event→neutral mapping out of the Bolt listener
(so local + Intelligence ingress share it) is the remaining, higher-risk
half and is left to that ticket (`TODO(OSS-363)`).

## Testing

TDD throughout (RED→GREEN per behavior). New: managed adapter
dispatch/ack-nack/ids/run-renderer/exclusivity, all-kinds routing, name
validation + metadata + lifecycle, runtime `bots` option, Slack codec.
Full suites green: `bot` 147, `bot-slack` 256, `runtime` 1574. All
builds typecheck (`bot`/`bot-slack`/`bot-discord`/`runtime`);
oxlint/oxfmt clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-08 12:49:17 -05:00
Alem Tuzlak dff80a2a9a fix(bot,bot-slack,bot-intelligence,runtime): address managed-bots SDK review findings
Correctness:
- C1 create-bot: start() is now idempotent — a second start() no longer
  re-resolves the backend / rebuilds Transcripts+Telemetry+ActionRegistry or
  re-connects adapters (which would wipe MemoryStore state and double-bind real
  adapters). stop() clears the flag so start→stop→start is still a real restart.
- S1 bot-slack ingress: a threaded reply that @-mentions the bot is now skipped
  (app_mention handles it) so the managed path no longer double-responds. Matches
  both the plain <@U…> and labeled <@U…|handle> mention forms.
- S2 runtime: CopilotSseRuntime throws if `bots` is passed without intelligence
  instead of silently dropping them (guards a JS/as-any caller past the type).
- S3 bot-intelligence: startManagedBots rolls back — stops already-started bots —
  when a later bot fails to start, instead of leaking listeners/connections.

Lower:
- S4 ingress: stripMentions handles the labeled <@U…|handle> form; DM turns strip
  mentions too (parity with app_mention/thread_reply).
- S5 bot-intelligence: bot-name uniqueness is now case-insensitive.
- S6 runtime: fail fast at construction when a declared bot has no name (full
  shape/uniqueness validation stays at the activation seam — assertValidBotNames —
  because it can't cross into this CJS package from pure-ESM bot-intelligence).
- S7 bot-intelligence: buildActivationMetadata throws on a nameless bot instead of
  silently filtering it out of the activation set.
- S8 bot-intelligence: startManagedBots warns on an empty bots array.
- M1 intelligence-adapter: the per-turn egress seq Map entry is deleted after each
  turn so it can't grow unbounded over a long-running bot.
- M2 intelligence-adapter: an inbound file that fails to fetch degrades to a
  fail-visible text note instead of being silently dropped from model context.
- I2 contracts: dropped the now-dead `duplicate_skipped` RenderAccepted value
  (Intelligence returns duplicate_accepted or a 409 conflict).

Changelog (C2/C3, intended behavior after moving init into start()):
- bot.transcripts now throws before start() (was a concrete property).
- telemetry `oss.bot.configured` now fires at start() rather than construction, so
  a constructed-but-never-started bot no longer emits it.

Not addressed here (cross-repo, tracked on the Intelligence side):
- I1 realtime render-event kind:"file" clause on the gateway validator.
- I3 lease-token fencing on the render-accept path.
2026-07-08 19:18:19 +02:00
tylerslaton 4394f9c81d chore: release monorepo v1.62.3 2026-07-08 16:17:36 +00:00
Alem Tuzlak 2330dca267 fix(bot-slack,runtime): align tests with renderer status + AbstractAgent.run
Two pre-existing test failures on this branch, surfaced by CI's unit +
check-types jobs once main was merged:

- bot-slack event-renderer: the non-pane thread tool-call test still
  asserted the old "no composer status" behavior. Commit 13248dda0b
  deliberately drove setStatus on ANY thread anchor (not just panes), so
  the test now expects both the 🔧 row and the "is using…" status.
- runtime in-memory-runner: HangingAgent/AbortableAgent extended
  AbstractAgent but omitted the abstract run() member (@ag-ui/client
  0.0.57), failing tsc on the test tsconfig (TS2515). Add the same
  run() => EMPTY stub the sibling test agents use.
2026-07-08 18:15:29 +02:00
Benjamin Taylor 71a4ac42e4 Merge origin/main into alem/oss-360-sdk-foundations
Brings the 499-commit-stale foundations branch up to date with main so #5761
has a clean diff and no stale reverts (e.g. forwardHeaders). Conflicts:
- CopilotThreadsDrawer.tsx: took main's (main renamed CopilotDrawer -> ThreadsDrawer
  + added the collapse feature; the branch's edit was a no-op import-type split).
- pnpm-lock.yaml: regenerated with the pinned pnpm 10.33.4 (adds @copilotkit/bot-intelligence).
2026-07-08 11:01:58 -05:00
Benjamin Taylor 5994bfe482 Merge origin/main into ben1/ent-1018-stateless-suggestions
Syncs the branch with main (304 commits) to resolve CI type-check failure.
main changed extractForwardableHeaders to require a forwarding policy and
added the mergeForwardableHeaders helper (#5712); handle-suggest now uses
mergeForwardableHeaders(agent.headers, request, runtime.forwardHeadersPolicy ??
resolveForwardHeadersPolicy(undefined)) to match the run handler — fixing the
drift and adopting the server-headers-win / infra-header denylist behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 17:40:50 -05:00
Benjamin Taylor 9f938e89cf refactor(suggestions): stream stateless /suggest over SSE and forward consumer state
Rework the stateless /suggest transport to reuse the AG-UI SSE pipeline
instead of a buffered JSON response, resolving the streaming + state review
feedback:

- server runs the provider agent directly and streams its events via
  createSseEventResponse (the runner's event pipeline minus GLOBAL_STORE
  persistence), gated with captureTelemetry:false so suggestions stay out of
  run telemetry
- client drives a stock HttpAgent against /agent/:id/suggest, so chips stream
  progressively via onMessagesChanged and the run never routes through the
  Intelligence websocket delegate (still no thread persistence)
- forward the consumer's deep-cloned messages + state onto the suggestion run
  (was state: {}), matching the clone fallback

Net -68 LOC of production code; the stateless and fallback paths now share one
runAgent flow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 17:24:46 -05:00
Mark f452699510 Merge branch 'main' into fix/issue-5533-agentid-runtime-sync 2026-07-07 11:21:43 -07:00
Jordan Ritter 7897be4a95 feat(runtime): configurable inbound-header forwarding policy with default infra/platform denylist (#5783)
## Problem — the leak

The v2 runtime's `shouldForwardHeader` forwarded `authorization` **and
any header whose name starts with `x-`** onto the outgoing agent call.
In a real deployment the inbound request has already traversed a
browser, CDN/edge, load balancer, and hosting platform — each stamping
its own `x-*` headers — so the wide `x-*` wildcard silently forwarded:

- **Hop-by-hop / topology:** `x-forwarded-for`, `x-real-ip`,
`x-forwarded-proto/host/port`
- **Cloud / CDN tracing:** `x-amzn-trace-id`, `x-amz-cf-id`,
`x-cloud-trace-context`, `x-azure-*`, `x-fastly-*`, `x-request-id`
- **Platform-injected:** `x-vercel-*`, `x-middleware-*`
- **CopilotKit Cloud platform credential:**
`x-copilotcloud-public-api-key`

The last item is a real credential-exfiltration concern: a platform key
scoped to Copilot Cloud reaching a third-party agent URL. This is the
**breadth** half of #5712 (option 3); the **precedence** half was fixed
in #5782.

## Design — denylist default + config knob, both paths

- **Default denylist (safe default).** Keep the `authorization` + `x-*`
base eligibility, but strip a curated, greppable set of known
infra/proxy/platform headers (exact names + prefix families) before
forwarding. Legitimate custom `x-*` application headers (`x-tenant-id`,
`x-api-key`, …) keep flowing untouched. The authoritative list is a
single exported constant in `header-utils.ts`.
- **Configurable policy (`forwardHeaders` runtime option).**
- `useDefaultDenylist?: boolean` (default **true**) — `false` restores
the previous wide-open behavior.
  - `deny?` / `denyPrefixes?` — extend the default denylist.
- `allow?` — opt into strict allowlist mode (only listed headers
forward).
- **Resolve once.** The constructor resolves `forwardHeaders` into a
`forwardHeadersPolicy: ResolvedForwardHeadersPolicy` field (mirroring
the existing `debug` → `ResolvedDebugConfig` resolve-once), exposed on
`CopilotRuntimeLike` / `BaseCopilotRuntime` with a passthrough getter on
the `CopilotRuntime` shim.
- **Both paths.** The resolved policy is read at **/run**
(`configureAgentForRequest`) and **/connect** (`handleSseConnect`) via
`mergeForwardableHeaders`, so the two can never diverge. Server-wins
precedence and server-self case-dedup from #5782 are untouched.

## Semver

**Minor with an opt-out.** Removing a leak is a fix, not a contract
change, and we ship a documented escape hatch: `new CopilotRuntime({
agents, forwardHeaders: { useDefaultDenylist: false } })` restores the
prior behavior. Custom-header forwarders (the common case) are
unaffected.

## Red-green proof (real surface, both paths)

RED — with the predicate reverted to the old wide-open `authorization ||
x-*` (policy ignored), the new behavior assertions fail; the leak
reproduces (`x-forwarded-for: 203.0.113.7` forwards on both /run and
/connect):

```
 ❯ header-utils.test.ts (19 tests | 8 failed)
   × strips known infra/proxy/platform headers by exact name → expected true to be false
   × strips known infra/platform header families by prefix   → expected true to be false
   × strips denylisted headers case-insensitively            → expected true to be false
   × deny extends the default set                            → expected true to be false
   × denyPrefixes extends the default set                    → expected true to be false
   × allow switches to allowlist mode                        → expected true to be false
   × extractForwardableHeaders drops denylisted x-* infra    → expected {…4} to deeply equal {…1}
 ❯ agent-utils-header-forwarding.test.ts (/run) (10 tests | 1 failed)
   × strips denylisted infra/platform headers (#5712 breadth) → expected '203.0.113.7' to be undefined
 ❯ sse-connect-agent-id.test.ts (/connect) (5 tests | 1 failed)
   × strips denylisted infra/platform headers                → expected '203.0.113.7' to be undefined
```

GREEN — with the real policy in place:

```
 ✓ header-utils.test.ts (19 tests)
 ✓ agent-utils-header-forwarding.test.ts (10 tests)   # /run path
 ✓ sse-connect-agent-id.test.ts (5 tests)             # /connect path
 ✓ agent-header-precedence.test.ts (2 tests)
 Test Files  4 passed (4)
      Tests  36 passed (36)
```

Full `@copilotkit/runtime` suite: **113 files / 1593 tests passed.**
Typecheck, oxlint (0 errors), oxfmt, and build all green.

## Builds on #5782

This branches off #5782's head (`636bcad05`) and reuses that PR's
`mergeForwardableHeaders` (server-wins precedence + server-self
case-dedup). It should land **after #5782**. It addresses the
**forwarding-breadth half of #5712** — #5712's precedence core is fixed
by #5782; this is the breadth follow-up (not `Fixes #5712`).
2026-07-06 09:41:07 -07:00
Alem Tuzlak d8928c445a fix(runtime): server-configured agent headers take precedence over forwarded inbound headers (#5782)
## Problem

When a self-hosted v2 `CopilotRuntime` is configured with a server-side
agent (an `@ag-ui/client` `HttpAgent` with static `headers` for
service-to-service auth), the runtime forwards inbound
`authorization`/`x-*` request headers onto the agent's outgoing call
**and lets them override the headers the server configured** — silently
breaking service-to-service auth to a secured backend (e.g. a private
Cloud Run agent behind IAM).

`Fixes #5712`

## Root cause


`packages/runtime/src/v2/runtime/handlers/shared/agent-utils.ts:125-128`
merged forwarded inbound headers **last**, so they won on collision:

```ts
agent.headers = {
  ...agent.headers,                      // server-configured
  ...extractForwardableHeaders(request), // inbound — overrode the above
};
```

There are actually **two** failure modes:

1. **Same-case collision** — inbound `authorization` overwrites a server
`authorization` (last-write-wins).
2. **Case-mismatch collision** — `extractForwardableHeaders` lowercases
inbound keys (`authorization`), while the server typically configures
canonical casing (`Authorization`). A plain spread treats those as
*distinct* keys and emits **both** — which undici downstream comma-joins
into a single invalid `"Bearer A, Bearer B"` ("multiple JWTs") value.
Flipping the spread order alone does **not** fix this case.

## Fix

In `agent-utils.ts`, make server-configured `agent.headers`
authoritative on collision, matched **case-insensitively**: drop any
forwarded inbound header whose name (case-insensitively) is already set
on the agent, and let non-colliding inbound headers pass through
unchanged. This preserves the existing forward-for-auth behavior for
headers the server does *not* set, while guaranteeing a server-set token
is never overridden or duplicated.

The merge logic lives in a shared
`mergeForwardableHeaders(serverHeaders, request)` helper in
`packages/runtime/src/v2/runtime/handlers/header-utils.ts` so the
precedence semantics are defined in exactly one place.

### Scope note

This is the conservative precedence + case-insensitive-dedup fix (the
issue's suggested fix #1). I did **not** tighten the default allowlist
to drop hop-by-hop/platform `x-*` headers (`x-serverless-*`,
`x-forwarded-*`, …) or add an opt-out — those alter existing forwarding
behavior and are worth a separate, deliberate change. The precedence fix
alone resolves the reported breakage (the server-set token now wins
regardless of what the platform injects on a colliding header name).

A documented workaround already exists for users on released versions:
pass a custom `fetch` to the `HttpAgent` that builds outgoing headers
from scratch (it runs after `configureAgentForRequest` and survives the
per-request `agent.clone()`).

## Red-green proof (the real fix — `/run` path)

The load-bearing assertion: there must be exactly **one** authorization
header carrying the **server** value.

### RED (fix stashed, against unmodified `agent-utils.ts`)

```
 ❯ src/v2/runtime/__tests__/agent-header-precedence.test.ts (2 tests | 1 failed)
   × configureAgentForRequest — header precedence (#5712) > server-configured agent headers win over a colliding inbound header
AssertionError: expected [ 'Authorization', 'authorization' ] to have a length of 1 but got 2
     81|     expect(authKeys).toHaveLength(1);
 Test Files  1 failed (1)
      Tests  1 failed | 1 passed (2)
```

The pre-existing `agent-utils-header-forwarding.test.ts` also failed,
because it explicitly encoded the buggy behavior
(`expect(...["x-aimock-context"]).toBe("new-context")` — inbound
winning):

```
 FAIL  src/v2/runtime/__tests__/agent-utils-header-forwarding.test.ts > ... > request forwardable headers override matching pre-existing agent headers
AssertionError: expected 'old-context' to be 'new-context'
```

### GREEN (fix applied)

```
 ✓ src/v2/runtime/__tests__/agent-header-precedence.test.ts (2 tests) 2ms
 ✓ src/v2/runtime/__tests__/agent-utils-header-forwarding.test.ts (8 tests) 3ms
 Test Files  2 passed (2)
      Tests  10 passed (10)
```

The colliding test (`agent-utils-header-forwarding.test.ts`) was updated
from asserting the old bug to asserting corrected precedence + a new
case-insensitive-dedup guard. The non-colliding-forward test is retained
unchanged as a regression guard.

## Quality gates

```
NX  Successfully ran target check-types for project @copilotkit/runtime
NX  Successfully ran target test for project @copilotkit/runtime  — Test Files 113 passed (113), Tests 1576 passed (1576)
```

---

## `/connect`-path change — forward-looking plumbing, inert today

The original issue and a prior eval flagged the same forwarding pattern
at `handlers/sse/connect.ts`. To keep the two paths' merge semantics
consistent, the `/connect` path now builds the same server-wins merged
headers (via the shared `mergeForwardableHeaders` helper) and passes
them into `runner.connect()`.

**This is not an active auth fix, and it is not red-green-proven as one
— because there is no live bug to fix on the connect path today.** No
shipped runner consumes the `headers` field of
`AgentRunnerConnectRequest`: the in-memory, intelligence, telemetry, and
sqlite runners all destructure only `threadId` from the connect request
and ignore `headers` entirely. Connect is a thread replay/reconnect, not
a fresh outgoing agent call. So whatever headers we pass into
`runner.connect()` are dropped on the floor by every runner that ships.

What this change actually does:

- Threads the per-request agent clone through `handle-connect.ts →
handleSseConnect` so the connect path *has access to* the
server-configured `agent.headers` (it previously did not).
- Passes `mergeForwardableHeaders(agent?.headers, request)` into
`runner.connect()` — the correct, server-wins argument **shape** for a
future outbound-connecting runner that *would* consume connect-path
headers.
- Rewrites the comments/JSDoc on this path to say this plainly, rather
than implying an active auth fix. It also documents that the
connect-site `cloneAgentForRequest` call is the sole `agentId`-existence
guard (the intelligence branch never re-validates the id), and documents
`cloneAgentForRequest`'s `AbstractAgent | Response` (404) dual-return
contract that both callers depend on.

The real outbound header forwarding — the thing that fixes #5712 — is
the `/run` path's `agent.headers` mutation described above. The connect
change is staged plumbing so that if/when a runner starts honoring
connect-path headers, it inherits the same server-wins precedence
without a second fix.

### Tests on the `/connect` path

The connect tests assert the *merge shape* that reaches
`runner.connect()` (server value wins on collision, exactly one
`authorization` key, non-colliding `x-*` still forwards) and that the
agent-undefined case (no server `agent.headers`) degrades to forwarding
allowlisted inbound headers only and does not crash. These verify the
argument we construct is correctly shaped — not that any shipped runner
consumes it.

## Files

- `packages/runtime/src/v2/runtime/handlers/header-utils.ts` — shared
`mergeForwardableHeaders` helper (case-insensitive, server-wins).
- `packages/runtime/src/v2/runtime/handlers/shared/agent-utils.ts` —
`/run` path uses the helper so server headers win on collision (**the
real fix**).
- `packages/runtime/src/v2/runtime/handlers/sse/connect.ts` — `/connect`
path uses the helper; forward-looking plumbing, inert until a runner
consumes connect-path headers.
- `packages/runtime/src/v2/runtime/handlers/handle-connect.ts` — threads
the per-request agent clone into `handleSseConnect`.
-
`packages/runtime/src/v2/runtime/__tests__/agent-header-precedence.test.ts`
— `/run` regression test exercising the real `configureAgentForRequest`
surface with a real `HttpAgent`.
-
`packages/runtime/src/v2/runtime/__tests__/agent-utils-header-forwarding.test.ts`
— updated the test that encoded the old (buggy) precedence; added a
case-mismatch dedup guard.
-
`packages/runtime/src/v2/runtime/handlers/sse/__tests__/sse-connect-agent-id.test.ts`
— connect-path merge-shape + agent-undefined coverage.

### Notes

- A documented `@ag-ui/client` `HttpAgent` `fetch` workaround already
exists for attaching service-to-service auth the runtime can't override
(see the issue). This change makes the workaround unnecessary for the
`/run` precedence case.
- Conservative scope: this is the **precedence flip on `/run`** plus
forward-looking connect plumbing. Tightening the default allowlist
(dropping hop-by-hop / platform `x-serverless-*`, `x-forwarded-*`,
`x-cloud-trace-context`, …) and an opt-out switch — issue suggestions
#2/#3 — are intentionally left as a follow-up to keep the
security-policy change minimal.
2026-07-06 18:26:38 +02:00
Markus Ecker 7b72bd491a Merge branch 'main' into mme/memory-core 2026-07-03 10:20:47 +02:00
BenTaylorDev a2cabd9455 chore: release monorepo v1.62.2 2026-07-02 22:23:11 +00:00
Markus Ecker 01469359a8 merge: integrate origin/main into mme/memory-core
Resolve the single conflict in packages/web-inspector/src/index.ts by keeping
both additions: this branch's CpkMemoryList memory-tab element and main's
ɵCpkThreadDetails back-compat alias (independent top-level declarations).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 17:26:01 +02:00
Benjamin Taylor 95345f10ef test(suggestions): cover header/credential forwarding, multi-route dispatch, 405, explicit-false; fix comments 2026-07-02 09:32:30 -05:00
Benjamin Taylor 7cfbfe4df3 fix(runtime): log suggest failures server-side, harden route exhaustiveness, prove marker round-trip 2026-07-02 09:32:29 -05:00
Benjamin Taylor f94cc45a76 fix(runtime): make stateless suggest side-effect-free and cancel on client abort
The suggest path called configureAgentForRequest, which conditionally
attaches A2UI/MCPApps/OpenGenerativeUI middleware. MCPApps setup can
incur a listTools network round-trip per suggestion under
available:"always" — contradicting the handler's side-effect-free
contract. Since suggest runs force toolChoice: copilotkitSuggest, those
middleware-injected tools are dead weight. Replace the call with only
the header forwarding it needs (extractForwardableHeaders).

Also wire request.signal to agent.abortRun() so an aborted client
request cancels the server-side provider run instead of letting it run
to completion (best-effort; the listener never throws).

Tests: assert no middleware is attached (agent.use never called),
forwarded headers land on agent.headers, and aborting the signal calls
agent.abortRun(). Runtime mock is now typed (no as any); runner spies
kept to prove the direct-run path is preserved.
2026-07-02 09:32:29 -05:00
Benjamin Taylor cb7c8be1cb test(runtime): lock in stateless /suggest no-thread-leak + cross-mode coverage 2026-07-02 09:32:29 -05:00
Benjamin Taylor e9c4eec9e2 feat(runtime): route POST /agent/:agentId/suggest to handleSuggestAgent 2026-07-02 09:32:28 -05:00
Benjamin Taylor 52e4f6823f feat(runtime): add stateless handleSuggestAgent handler 2026-07-02 09:32:28 -05:00
Benjamin Taylor 7922e31e2c feat(runtime): advertise suggestions capability on /info 2026-07-02 09:32:28 -05:00
Nathan 🔶 Tarbert dedb3183d6 Merge branch 'main' into fix/issue-5533-agentid-runtime-sync 2026-07-01 16:08:55 -04:00
Alem Tuzlak 9cb929b40d feat(runtime): opt-in supersede for concurrent same-thread runs
InMemoryAgentRunner gains an opt-in { onConcurrentRun: "throw" | "supersede" }
(default "throw", so existing consumers are unchanged). In "supersede" mode a
new run for an already-running thread aborts the prior run (agent.abortRun(),
mirroring stop()) instead of throwing "Thread already running" — so a fast
follow-up turn, or one after a dropped/aborted run, cleanly replaces the
previous one.

Superseding overlaps two runs on the module-global per-thread store, so all
four finalization sites (both resets and both historicRuns.push) are guarded on
store.currentRunId === request.input.runId and stamp the run's own id. A
superseded run therefore drops its partial events rather than resetting or
mislabeling the new run's state/history.

Used by the Intelligence-hosted (managed) Slack listener. (OSS-417)
2026-07-01 19:10:54 +02:00
tylerslaton 617e88a069 chore: release monorepo v1.62.1 2026-07-01 17:07:44 +00:00
Austin Merrick f54e99697b fix(build): replace Unix-only commands in package.json scripts with Node.js equivalents (#5602)
## What does this PR do?

This PR fixes build failures on Windows by replacing Unix-only shell
commands (`rm -rf`, `cp`, `mkdir -p`) in `package.json` scripts with
cross-platform Node.js `fs` built-in commands.

This follows the project's existing codebase pattern for cross-platform
operations, as seen in `packages/react-ui/package.json` (line 45).

### 🛠️ Changes:
- **`packages/runtime`**: Replaced `rm -rf` in `generate-graphql-schema`
with `fs.rmSync`.
- **`packages/vue`**: Replaced `cp` in `build:types` and `rm -rf` in
`clean` with `fs.cpSync` and `fs.rmSync`.
- **`packages/angular`**: Replaced `mkdir -p` and `cp` in `build:css`
with `fs.mkdirSync` and `fs.cpSync`.
- **`examples/v1/next-openai`, `next-pages-router`, `state-machine`**:
Replaced `rm -rf` clean commands with a single Node.js loop that deletes
`.turbo`, `node_modules`, `dist`, and `.next`.

All modified packages now build successfully on Windows.

## Related PRs and Issues

- Closes #5601

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [ ] If the PR changes or adds functionality, I have updated the
relevant documentation
- [x] "Allow edits by maintainers" is checked (lets us help iterate on
your PR directly — faster turnaround for everyone)
2026-07-01 09:56:00 -07:00
MikeRyanDev ca836ff920 chore: release monorepo v1.62.0 2026-07-01 15:45:54 +00:00
Alem Tuzlak a03cd7db8b test(runtime): supply bots:[] in the intelligence runtime-like factory
CopilotIntelligenceRuntimeLike.bots became required with the managed-bots
runtime work (OSS-360); the get-runtime-info test factory still omitted it,
failing check-types. Add the missing field.
2026-07-01 12:28:20 +02:00
Jordan Ritter 7d6a2b2a9a docs(runtime): correct forwardHeaders allowlist JSDoc accuracy
Empty/whitespace-only allow/deny/denyPrefixes entries are trimmed and
dropped before the allowlist-mode decision, so allow: [""] stays in
denylist mode rather than switching on exclusive allowlist mode. Also
document that allowlist mode bypasses the built-in default denylist, so
integrators must not allow-list protected/platform headers unintentionally.
2026-06-30 17:23:05 -07:00
Jordan Ritter dde79d1c89 fix(runtime): make forwardHeaders deny authoritative in allowlist mode
`shouldForwardHeader` returned early on `policy.allow` and silently ignored
the integrator's `deny`/`denyPrefixes`, so a header listed in BOTH `allow`
and `deny` still forwarded — a footgun on a security feature.

Rework the predicate so the integrator's own `deny`/`denyPrefixes` (exact,
case-insensitive, and prefix) always strip, including in allowlist mode:
`allow` selects the candidate set, `deny` subtracts from it. The built-in
default denylist is unchanged and still applies only in denylist mode (an
explicit `allow` is a deliberate opt-in), so only the integrator's OWN deny
subtracts from an allowlist.

Also harden `resolveForwardHeadersPolicy`: trim and drop empty/whitespace-only
entries from `deny`/`denyPrefixes`/`allow`. A stray `denyPrefixes:[""]` made
`startsWith("")` true for every header (silently denying ALL forwarding), and
`allow:[""]`/`allow:[" "]` seeded the exclusive allowlist with an entry that
could never match — both integrator typos that now can't silently break
forwarding. Entries are lowercased consistently with existing handling.
2026-06-30 17:12:55 -07:00
Jordan Ritter cf951e04f2 test(runtime): harden header-forwarding coverage — clone isolation, case-collision, allowlist boundary
- agent-utils: assert configureAgentForRequest does not mutate the shared
  registered agent; only the per-request clone carries merged inbound headers
  (guards against a cross-request bearer-token leak, #5712).
- header-utils: direct mergeForwardableHeaders unit test for server Authorization
  vs inbound lowercase authorization with a different value — exactly one
  authorization-family key survives carrying the server value (#5712).
- header-utils: shouldForwardHeader boundary tests for the bare 'x' name and the
  empty-string name under both denylist and allowlist policies.
2026-06-30 16:58:11 -07:00
Jordan Ritter 4cdc1e16f5 fix(runtime): make forwardHeadersPolicy optional on CopilotRuntimeLike
The published CopilotRuntimeLike interface (v2 export surface) added
forwardHeadersPolicy as a REQUIRED field, which breaks any external
implementor of the interface — inconsistent with the minor-release
classification. The /run (agent-utils) and /connect (sse/connect) read
sites dereferenced runtime.forwardHeadersPolicy with no coalesce, so a
policy-less object crashed with "Cannot read properties of undefined
(reading 'allow')".

Make the field optional on the interface and coalesce both read sites to
the default resolved policy (resolveForwardHeadersPolicy(undefined),
default-on denylist) when absent. Concrete runtimes (BaseCopilotRuntime)
still always resolve and set it, so behavior is identical for all real
runtimes; the interface is now non-breaking and crash-proof.

Adds a red-green test driving configureAgentForRequest with a runtime
whose forwardHeadersPolicy is undefined: asserts no throw and that the
default denylist applies (x-forwarded-for dropped, custom x-* and
authorization forwarded).
2026-06-30 16:58:06 -07:00
Jordan Ritter 62ed6fa045 test(runtime): cover header-forwarding denylist + config policy on both paths (#5712)
- header-utils.test.ts: new coverage for the default denylist (exact names +
  prefix families, case-insensitive), custom x-* still forwarding, config
  overrides (useDefaultDenylist:false, deny, denyPrefixes, allow allowlist mode),
  and the breadth/precedence interaction. Migrate the inverting assertions
  (x-request-id / X-Forwarded-For now stripped; extract result drops x-request-id)
  and add the new required policy arg to all call sites.
- agent-utils-header-forwarding.test.ts + sse-connect-agent-id.test.ts: /run and
  /connect integration coverage — denylisted infra/platform headers dropped,
  custom x-* + authorization still forward, and a runtime-supplied forwardHeaders
  policy is actually applied (plumb-through). Swap denylisted filler headers for
  non-denylisted custom headers in the precedence regression tests.
- handle-run / handle-connect / intelligence-run-telemetry / get-runtime-info:
  add the resolved forwardHeadersPolicy to mock runtimes that route through the
  header merge so they satisfy the now-required policy.
2026-06-30 16:40:51 -07:00
Jordan Ritter 462fa7ad58 feat(runtime): configurable inbound-header forwarding policy with default infra/platform denylist
Tighten which inbound HTTP headers the v2 runtime forwards onto the outgoing
agent call. The old `authorization` + `x-*` allowlist leaked infrastructure,
proxy, and platform headers (x-forwarded-*, x-real-ip, x-amzn-trace-id,
x-vercel-*, and the Copilot Cloud platform key x-copilotcloud-public-api-key)
to arbitrary configured agent URLs (#5712, breadth half).

- header-utils.ts: add DEFAULT_DENY_HEADER_NAMES + DEFAULT_DENY_HEADER_PREFIXES
  constants and a policy-aware shouldForwardHeader; thread ResolvedForwardHeadersPolicy
  through extractForwardableHeaders and mergeForwardableHeaders. Add the public
  ForwardHeadersConfig and resolveForwardHeadersPolicy (useDefaultDenylist defaults
  to true; deny/denyPrefixes extend the default; allow switches to allowlist mode).
  Server-wins precedence and server-self case-dedup are unchanged.
- runtime.ts: add forwardHeaders?: ForwardHeadersConfig to BaseCopilotRuntimeOptions,
  resolve it once in the constructor into forwardHeadersPolicy (mirroring the
  debug -> ResolvedDebugConfig resolve-once), expose it on CopilotRuntimeLike /
  BaseCopilotRuntime, and add a passthrough getter on the CopilotRuntime shim.
- Apply the resolved policy at both call sites: /run (configureAgentForRequest)
  and /connect (handleSseConnect), so the two paths can never diverge.

Default-on in a minor with { useDefaultDenylist: false } as the documented opt-out.
2026-06-30 16:40:27 -07:00
Jordan Ritter 636bcad058 fix(runtime): de-duplicate server-vs-server case-collision headers in mergeForwardableHeaders
When an agent is configured with both case-variants of the same header
in agent.headers (e.g. Authorization and authorization), the prior
{ ...base } spread kept both keys — the exact undici comma-join hazard
the function guards against for inbound collisions. Collapse server-self
case-collisions to a single first-occurrence-wins entry; server-wins-over
-inbound and case-insensitive inbound suppression are unchanged.
2026-06-30 15:00:16 -07:00
Jordan Ritter 8bdb3a1c3f test(runtime): cover header precedence — /run + /connect collision, x-* uniqueness, agent-undefined forwarding
Cover the #5712 header-precedence behavior across both paths:

- agent-header-precedence.test.ts: server-configured agent.headers win
  over forwarded inbound headers on collision (case-insensitive), with
  single-key uniqueness assertions for both authorization and the x-*
  family (exactly one surviving key carrying the SERVER value).
- agent-utils-header-forwarding.test.ts: the /run path merges via
  mergeForwardableHeaders so server values are authoritative and inbound
  headers fill only unset keys.
- sse/__tests__/sse-connect-agent-id.test.ts: the /connect path applies
  the same merge, plus the agent-undefined case (no server agent.headers)
  degrades to forwarding allowlisted inbound headers only and does not
  crash.
2026-06-30 14:39:51 -07:00
Jordan Ritter abb85c727d refactor(runtime): thread merged headers into runner.connect() as forward-looking plumbing
The /connect path now builds the same server-wins merged headers as the
/run path and passes them into runner.connect(). This does NOT fix
connect-path auth: no shipped runner consumes the headers field of
AgentRunnerConnectRequest today. The in-memory, intelligence, telemetry,
and sqlite runners all read only threadId from the connect request and
ignore headers entirely. The real outbound header forwarding lives on the
/run path, where agent.headers is mutated before the agent runs.

Passing merged headers here is the correct argument shape for a future
outbound-connecting runner, and keeps the connect path's merge semantics
consistent with /run. The comments and JSDoc are rewritten to state this
plainly rather than implying an active auth fix: the connect-site
cloneAgentForRequest call is documented as the sole agentId-existence
guard (the intelligence branch never re-validates the id), and
cloneAgentForRequest's AbstractAgent | Response (404) dual-return contract
that both callers depend on is now documented.
2026-06-30 14:39:38 -07:00
Jordan Ritter bc5e56a295 fix(runtime): server-configured agent headers take precedence over forwarded inbound headers
When a request hits the /run path, inbound headers are forwarded to the
agent. Previously, forwarded inbound headers could clobber the
server-configured agent.headers on a key collision, letting a client
override server-set values (e.g. authorization). This is the #5712 bug.

Introduce mergeForwardableHeaders (header-utils.ts): a case-insensitive
merge where server-configured agent.headers always win on collision,
regardless of header-name casing. agent-utils.ts now uses this helper on
the /run path so server-configured values are authoritative and inbound
headers only fill keys the server did not set.

Fixes #5712
2026-06-30 14:39:21 -07:00
Nathan 🔶 Tarbert e55c958393 Merge remote-tracking branch 'origin/main' into fix/issue-5533-agentid-runtime-sync 2026-06-30 16:33:10 -04:00
Markus Ecker 95ed596d3a fix(memory): tighten mutation-response and runtime boundary validation 2026-06-30 15:58:35 +02:00
Markus Ecker 8a7ac74e94 fix(runtime): validate sourceThreadIds elements are strings in memory body 2026-06-30 15:14:20 +02:00
Markus Ecker bb117b1ef7 Merge remote-tracking branch 'origin/main' into mme/memory-core
# Conflicts:
#	packages/core/src/index.ts
2026-06-29 13:37:52 +02:00
Alem Tuzlak c55aa134be docs(runtime): clarify managed bots are validated at activation, not construction 2026-06-29 12:05:34 +02:00
Alem Tuzlak 9046b241e6 feat(runtime): typed managed-bots option on the Intelligence runtime (OSS-360)
Expose new CopilotRuntime({ intelligence, bots }) -- the Mode B entry point
for managed bots:

- bots is accepted only on the Intelligence runtime variant (bots?: undefined
  on the SSE variant), so TypeScript rejects bots without intelligence
- CopilotIntelligenceRuntime stores the declared bots; the facade exposes them
  via the existing isIntelligenceRuntime getter pattern
- @copilotkit/bot is imported type-only (it is pure-ESM; a value import would
  break this package's CJS output). Name validation + transport wiring happen
  in startManagedBots (called by the managed-listener bootstrap), not here.

Adds a type-only @copilotkit/bot workspace dependency.
2026-06-29 11:50:41 +02:00
Jeel Gor 9632ca901c Merge branch 'main' into fix/5601-windows-cross-platform-scripts 2026-06-27 15:48:22 +05:30
Markus Ecker cd0739800e fix(runtime): identify memory-subscribe user via header, not body
ɵsubscribeToMemories mirrored ɵsubscribeToThreads and sent userId in the
body, but the platform's memory routes resolve the app user from the
x-cpki-user-id header (like listMemories/createMemory), not the body — so
POST /api/memories/subscribe returned 401 AUTH_UNAUTHENTICATED. Send the
user via the header instead; the body is now empty.

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

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 21:17:37 +02:00
Markus Ecker 4211bd58d8 chore: format memory SDK files with oxfmt 2026-06-25 19:49:34 +02:00
Markus Ecker 86f08d2ce3 fix(runtime): forward platform error status for memory ops; make scope optional
Memory handlers now forward client-actionable 4xx platform statuses
verbatim (404 not-found, 409 conflict, 422 unprocessable) so a useMemories
consumer can distinguish those from a server error, and map a platform 5xx
(or malformed status) to 502 rather than collapsing everything to 500 — the
runtime is healthy, its dependency failed. This also avoids a Response
RangeError on an out-of-range status.

parseMemoryBody and the platform client's createMemory/updateMemory now
treat scope as optional, deferring the default to the platform.
2026-06-25 14:20:53 +02:00