The agent/run handler logged `[CopilotKit Runtime] Warning: "agents"
feature is not licensed.` on every run whenever a licenseChecker was
present. The check is purely cosmetic (it never gated execution) and
fires for every customer because no license catalog defines an "agents"
feature id, so checkFeature("agents") can never return true. This
removes the misleading warning. The licenseChecker remains wired and is
still consumed by get-runtime-info for status reporting.
## Summary
Three-tier wiring on the CopilotKit side, mirroring `useThreads`, to
surface user UI signals into CopilotKit Intelligence's self-learning
loop. Companion change in `CopilotKit/Intelligence` (PR #192) lands the
connector + schema.
- **Runtime client** — `CopilotKitIntelligence.recordUserAction(...)`
hits the idempotent platform endpoint
`${apiUrl}/connector/user-actions/record/:clientEventId`. Auth via the
deployment-level Intel API key (Bearer); the Intel key never reaches the
browser.
- **Runtime handler** — `handleRecordUserAction` resolves the Intel user
via `resolveIntelligenceUser`, forwards to the platform client, returns
`{ id, duplicate }`.
- **Fetch router** — `POST /user-actions` wired in
(`user-actions/record` `RouteInfo` variant + dispatch case).
- **React hook** — `useRecordUserAction()` and
`useRecordUserActionInCurrentThread()` in `@copilotkit/react-core/v2`.
Auto-generates a UUID `clientEventId` per call so retries are idempotent
by default. Throws when `runtimeUrl` is absent.
Linear: CPK-7587
## Test plan
- [ ] CI green on this PR
- [ ] Companion Intelligence PR #192 merged or coordinated
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The A2UI middleware (@ag-ui/a2ui-middleware) forwards injectA2UITool on
forwardedProps; ag-ui-langgraph surfaces it into agent state at
state["ag-ui"]["inject_a2ui_tool"]. The CopilotKit LangGraph middleware
(py + js) now reads that flag and only injects generate_a2ui when it is
truthy (opt-in), drops the runtime's render_a2ui so the model sees one
A2UI tool, and skips if the agent already defines generate_a2ui. The
catalog only binds surfaces; it is no longer the gate.
Reverts the earlier runtime-forward + context-channel approach.
Deps: ag-ui-langgraph>=0.0.38 (py), @ag-ui/langgraph 0.0.37 (sdk-js),
@ag-ui/a2ui-middleware 0.0.6 + @ag-ui/langgraph 0.0.37 (runtime);
.npmrc min-release-age exclude for @ag-ui/a2ui-middleware. Showcase
langgraph pins bumped to copilotkit==0.1.94a3 / sdk-js 1.59.3-alpha.3 /
@ag-ui/langgraph 0.0.37.
Move enterprise-learning MCP attachment out of the BuiltInAgent-specific
path and the intelligence run handler into a single request-scoped hook:
- `attachIntelligenceEnterpriseLearning` (agent-utils) attaches
`@ag-ui/mcp-middleware` via `configureAgentForRequest`, gated on
`ɵisEnterpriseLearningEnabled()`, resolving the user via `identifyUser`
and the project apiKey.
- Called from `handleRunAgent`; the old `forwardedProps.auth` MCP plumbing
in `intelligence/run.ts` and the BuiltInAgent attach in `agent/index.ts`
are removed.
- Add released `@ag-ui/mcp-middleware@0.0.1` dependency (lockfile +
`@ag-ui/client` override). Drops the obsolete intelligence-mcp-helper test.
Replace the user-actions-specific path with a general thread-event
annotation endpoint:
- Route `PUT /connector/annotate/:clientEventId` via fetch-router /
fetch-handler / core hooks RouteInfo.
- `handleAnnotate` forwards `{ type, data, learningContainer }` to the
Intelligence platform.
- `CopilotKitIntelligence.annotate()` (idempotent PUT) supersedes the
prior `recordUserAction`; `AnnotateParams`/`AnnotateResponse` carry the
event `type` (`user_action`, `set_learning_containers`, ...).
Also renames the `mcpServer` config flag to `enableEnterpriseLearning`
(`ɵisEnterpriseLearningEnabled()`), consumed by the enterprise-learning
attach in the following commit.
## What
Bumps `@copilotkit/license-verifier` from an exact `0.4.0` pin to a
`~0.4.2` patch range across:
- `package.json` — root `pnpm.overrides`
- `packages/runtime/package.json` — `dependencies`
- `packages/shared/package.json` — `dependencies`
- `pnpm-lock.yaml` — regenerated, resolves to `0.4.2`
## Why
Aligns the runtime/shared deps with the newly published
`@copilotkit/license-verifier@0.4.2`. Switching from an exact pin to
`~0.4.2` (`>=0.4.2 <0.5.0`) means future `0.4.x` patches are picked up
automatically, while `0.5.0`+ still requires an intentional bump.
## Notes
- `.npmrc` `minimum-release-age` guard was **not** modified; the
lockfile was regenerated with a one-off override since `0.4.2` was
freshly published.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Move runtime and shared deps (and the root pnpm override) from an exact
0.4.0 pin to ~0.4.2, so future 0.4.x patches are picked up automatically.
Regenerate pnpm-lock.yaml to resolve 0.4.2.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Picks up the forwarded-headers fix from ag-ui PR #1798
(https://github.com/ag-ui-protocol/ag-ui/pull/1798), which injects
agent.headers as config.configurable.copilotkit_forwarded_headers so
the LG dev server's HTTP-to-configurable bridge is no longer required
for X-AIMock-Context propagation. Closes the header-propagation gap
for showcase D5/D6 langgraph-typescript probes.
Picks up per-request header forwarding (onRequest hook + headerFactory)
and the prepareStream configurable+context partition fix from
ag-ui-protocol/ag-ui#1763. Together with copilotkit==0.1.91 on the
Python side (R3a), this unblocks D6 LGP/LGT header propagation.
The mergeConfigs() change in 0.0.33 also fixes the HTTP 400 from
langgraph-api 0.7+ when both configurable and context are present.
Bumped in two files:
- packages/runtime/package.json: 0.0.31 -> 0.0.33
- packages/sdk-js/package.json: 0.0.31 -> 0.0.33
Added @ag-ui/langgraph to minimumReleaseAgeExclude in .npmrc.
pnpm-lock.yaml regenerated.
Showcase auto-redeploys on merge via showcase_build.yml.
Replace docs URLs that currently 301 through the legacy redirect catalog
with their canonical post-cutover destinations so users clicking links
from console warnings, JSDoc, and in-product help land in one hop.
URLs updated:
- /premium#how-do-i-get-access-to-premium-features
-> /premium/overview#getting-access
- /coagents/quickstart/langgraph -> /langgraph-python/quickstart
- /coagents/shared-state/predictive-state-updates
-> /langgraph-python/shared-state/predictive-state-updates
- /reference/v1/hooks/useCopilotChatHeadless_c
-> /reference/v2/hooks/useCopilotChatHeadless_c
- /coagents/troubleshooting/common-issues
-> /langgraph-python/troubleshooting/common-issues
- /quickstart#get-a-copilot-cloud-public-api-key
-> /built-in-agent/quickstart#create-a-free-account
- /premium -> /premium/overview
URLs left as-is because they already resolve 200 with no redirect:
/migration-guides/migrate-attachments, /migration/render-message,
/telemetry.
Hook bypassed: pre-commit test failed in @copilotkit/web-inspector due
to missing jsdom dependency in its package.json (unrelated to this
change; no overlap with edited files or URLs). Tests for the four
affected packages (react-core, react-ui, shared, runtime) pass.
- Rework shared helper: parseAndWarnTelemetryId returns parsed id AND
warns, so both v1 and v2 setLicenseToken call it once without
inlining duplicate code or double-parsing the JWT.
- Fix v1 sampleWeight bug: identified events bypass the sample gate
and ship at effective rate 1.0, so a single global sampleWeight =
1/sampleRate would overweight identified-customer counts by
1/sampleRate (20x at the 0.05 default). Move sample metadata
(sampleRate / sampleRateAdjustmentFactor / sampleWeight) out of
globalProperties and compute per-event using effectiveSampleRate.
- Guard setSampleRate against parseFloat("nonsense") = NaN slipping
past the range check. With the default now 0.05, env-var overrides
are more common and a typo would otherwise produce silent
always-drop.
- Add tests: sampleWeight differs for identified vs anonymous,
malformed JWT stays anonymous, license-token cache is overwritable,
NaN env override is rejected, v2 default sampleRate = 0.05 is pinned.
Cache parsed telemetry_id at setLicenseToken time and use it in capture()
to branch on identified vs anonymous. Identified callers (token with
telemetry_id) always send; anonymous callers are sampled at sampleRate.
Default sampleRate changes from 1.0 to 0.05 so the anonymous OSS-runtime
firehose is capped at the client. Identified customers continue to send
at full fidelity.
Operators currently get silent attribution loss if a license token is
configured but parses without a telemetry_id field — useful as a smoke
signal during the issuer rollout, when older licenses lack the field
entirely.
Each TelemetryClient setter (v1 shared, v2 singleton) now calls
parseTelemetryIdFromLicense at configuration time and emits a one-shot
console.warn when the result is null. No per-event spam.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The CopilotCloud customer API key (`ck_<env>_<id>.<secret>`) is unrelated
to telemetry attribution — it flows into Segment/PostHog only. The
attribution signal lives in the EIP / Intelligence license JWT, whose
payload carries `telemetry_id` (alongside license_id, owner.org_id,
features, etc.).
Rewires the lambda-client to base64url-decode the license JWT payload
and emit X-CopilotKit-Telemetry-Id from `telemetry_id`. No signature
verification — that's license-verifier's job, and the Lambda is
claim-only by design.
Plumbing:
- Shared TelemetryClient (v1) and v2 telemetry singleton each get a
`setLicenseToken` setter; the v1 client drops `apiKey:` from its
lambdaClient.send call, the v2 client drops the
cloud.public_api_key extraction from event properties.
- Both runtime constructors call `telemetry.setLicenseToken(...)` once,
resolving `options.licenseToken ?? process.env.COPILOTKIT_LICENSE_TOKEN`
to match license-verifier's own env-fallback. Without that, customers
who set only the env var would get a working licenseChecker but
anonymous telemetry.
Tests: v2 telemetry test refreshed — old "cloud api key extraction"
assertion replaced with one that confirms cloud.public_api_key rides
in properties (not as licenseToken), and a new test asserts that
setLicenseToken plumbs through to lambdaClient.send.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds @copilotkit/shared/telemetry/lambda-client which posts events to a
CopilotKit-controlled telemetry-sink endpoint, replacing the direct Scarf
calls in both v1 (shared) and v2 (runtime) telemetry clients. When the
configured CopilotCloud API key parses as the new ck_<env>_<id>.<secret>
format, the request is HMAC-signed (CK1, sha256 over ts/nonce/body) so
the sink can verify and enrich with the customer email; otherwise it
falls through to an unsigned send (legacy keys, OSS-only installs). v1
keeps its existing Segment path with 5% client sampling; v2 sends 100%
to the sink and lets the sink sample server-side.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The if (agent.headers) guard in configureAgentForRequest silently
skipped header forwarding when agent.headers was undefined (the
default for LangGraphAgent). This meant x-aimock-context, x-test-id,
and other x-* headers were never forwarded to agent backends.
Also wires install_httpx_hook in the Python SDK middleware so
forwarded headers propagate to outgoing LLM API calls.
Closes the gap documented in PR #4773 spec as out-of-scope.
Three follow-ups on top of PR #4837 that I had on the same branch but
didn't make it into the squash merge.
1. **packages/runtime: stamp `audio/webm` on empty-type Blobs in the
transcription handler.** Browser MediaRecorder writes the audio as
webm/opus, but the Blob's `type` field is often empty by the time it
hits the server. `isValidAudioType` lets empty / octet-stream through
for compatibility, but OpenAI Whisper then rejects the upload with
`502 Invalid file format. Supported formats: ['flac', 'm4a', 'mp3',
'mp4', 'mpeg', 'mpga', 'oga', 'ogg', 'wav', 'webm']` because it
can't pick a decoder. Reconstructing the File with an explicit
`audio/webm` type (and a `.webm` filename fallback) makes Whisper
accept the bytes that were already valid. Monorepo-wide — applies to
every integration using `/api/copilotkit-voice/transcribe`.
2. **showcase/aimock/feature-parity.json: port 12 subagents fixtures
from d5-all.json** so the three pills (cold-exposure blog, LLM
tool-calling explanation, reusable-rockets summary) work in
production. d5-all.json already has the full research → writing →
critique chain with substantive content; feature-parity only had the
single LP remote-work pill. Production aimock loads both files but
any case where feature-parity wins first-match needs the same
content. Verbatim port — no fabricated text. Net result: no more
`[sub-agent error] the writing agent...` on the demo's pills.
3. **showcase/aimock both files: scope shared-state-read-write Greet +
Plan-a-weekend fixtures with a true all-defaults systemMessage
gate.** The PR #4837 gate (`systemMessage: "tone: casual"`) only
caught tone changes — name / language / interests changes still hit
the canned fixture. Replaced with a two-element array gate (aimock
supports all-present substring matching, verified in
`/app/dist/router.js`):
- `preferences:\n- Preferred tone: casual\n` — breaks if name is
set (Name line inserts between signature and tone) or tone changes.
- `- Preferred language: English\nTailor every response` — breaks
if language changes or interests are added (Interests line
inserts between language and Tailor).
With `--provider-gemini` already wired in both local docker-compose
and Railway prod, any state change now proxies to real Gemini and
returns a personalised reply.
4. **showcase/aimock/feature-parity.json: re-remove bare 'plan' /
'steps' / 'mars' / 'dashboard' / 'report' substring catch-alls + the
bare 'alice' / 'Alice' fixtures.** These were removed in commit
`ddc2e179` on the PR #4837 branch but didn't survive the squash
merge, so they're back in main and still hijacking hitl-in-app
downgrade-#12346 ('plan'), shared-state-rw weekend pill ('plan'),
subagents 'rockets' pills, hitl-in-chat Schedule-1:1 with Alice
('alice'). Replace the alice pair with a single scoped
`Hi, my name is Alice` fixture for the showcase-assistant
introduction flow.
Local verification:
- `bin/showcase test google-adk --d5` → 38/38 green, 165s.
- Paired curl on shared-state-read-write:
- Default state → canned fixture ("Hi — I'm your shared-state co-pilot…")
- `name=alem` → real Gemini ("Hi there! …")
- `interests=[Cooking, Travel]` weekend pill → real Gemini ("Hey
there! Since you're into cooking and travel, how about a weekend
plan that combines both?")
Production deploys this PR will pick up the aimock fixture changes
(prod loads feature-parity.json from GitHub raw at boot — no image
rebuild needed for that file) plus the runtime change once the
packages/runtime build is republished.
Three layers of regression guards for the runtime reasoning-role filter and
the chained demo behavior:
1. Runtime unit test — packages/runtime/.../run-message-filtering.test.ts:
- Verifies `LangGraphAgent.run` strips `role:"reasoning"` from
`input.messages` before delegating to super.run.
- Verifies user/assistant/system/tool messages pass through in order.
- Verifies empty + missing messages arrays are tolerated.
- Verifies pre-existing forwardedProps.streamSubgraphs default + override
behavior is preserved.
- 6/6 tests pass against the runtime package's vitest config.
2. D5 harness probe — showcase/harness/.../d5-tool-rendering-reasoning-chain.ts:
- Expanded from one chained turn (flights→weather) to all three chained
pills in a single thread (stocks AAPL→MSFT, dice d20→d6, flights→weather).
- This is the canonical multi-pill regression at the harness layer:
without the runtime reasoning-role filter, the second pill would crash
before the model was called.
- Each turn asserts the per-turn delta of reasoning-block mounts (idx+1),
the minimum card count for each tool group, and unique transcript
substrings that scope to that turn.
3. Playwright e2e spec — showcase/integrations/langgraph-python/tests/e2e/
tool-rendering-reasoning-chain.spec.ts:
- Mirrors the pattern of the sibling tool-rendering-default-catchall spec
(notably its multi-pill regression at lines 162-212).
- Page-loads test verifies the 3 pills mount and no cards leak from a
prior session.
- One test per chained pill (stocks, dice, flights+weather) asserts the
full chain renders with reasoning-block + correct per-tool cards +
narration matching the aimock fixture text.
- Sequential-pills regression test clicks all 3 pills in one thread,
asserts each chain renders independently AND the reasoning-block count
increases monotonically across turns.
Agent: extend `get_stock_price` to accept optional `price_usd` and
`change_pct` arguments (mirrors the basic tool-rendering agent's signature
introduced in #4770). The aimock fixtures script the chained AAPL/MSFT
comparison by passing deterministic prices via these args; without the
wider signature, pydantic rejects the tool call and the card never mounts.
The runtime unit test is the strongest guard — it would catch any
regression on the role-filter logic without depending on the full Docker
stack. The harness probe and Playwright spec catch end-to-end regressions
in the canonical CI environment.
`@ag-ui/langgraph`'s message converter handles only user/assistant/system/tool
roles and throws `"message role is not supported."` on anything else. Agents
that stream reasoning summaries (OpenAI Responses API + `reasoning={summary:
"detailed"}`) emit AG-UI messages with `role: "reasoning"` that the AG-UI
client replays in the next turn's `input.messages`; the converter then crashes
before the model is ever called and the second pill click in a multi-turn
thread produces an `INCOMPLETE_STREAM` error.
Strip `role: "reasoning"` from `input.messages` inside CopilotKit's
LangGraphAgent.run subclass before delegating to super. This is the narrowest
fix at the runtime/AG-UI boundary — only the inbound message list is filtered,
the outbound event stream still carries reasoning summaries to the client, so
the `<ReasoningBlock>` slot continues to render on the active turn.
- Add telemetryDisabled to RuntimeInfo from COPILOTKIT_TELEMETRY_DISABLED/DO_NOT_TRACK env vars
- Mirror through AgentRegistry and expose via CopilotKitCore getter
- Guard track calls, URL param appending, and console disclosure on core.telemetryDisabled
- Move maybeShowDisclosure() to onRuntimeConnectionStatusChanged (fires after core attaches)
- Update docs to replace localStorage toggle description with env var approach
- Add telemetryDisabled test suite to get-runtime-info tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move `package` from properties string to top-level `{ name }` object per
Ben's confirmed IngestPayload schema (telemetry-sink-ingest/index.ts:127-134)
- Add typed per-event helpers trackBannerViewed/trackBannerClicked/trackThreadsTabClicked
to enforce property shapes at call sites and prevent PII leakage under wrong keys
- Add trackBannerClickedOnce guard in index.ts (per-mount Set keyed by
banner_id + cta) to prevent banner_clicked inflation on repeated clicks
- Fix handleTelemetryOptOutToggle: replace ?? true fallback with
instanceof HTMLInputElement guard (wrong fallback was a privacy bug)
- Add threadsTabClicked re-selection guard (skip if already on threads tab)
- Replace getTelemetryDistinctIdForUrl() call on mount with ensureTelemetryDistinctId()
- Add inMemoryFallbackId in persistence.ts for funnel coherence when
localStorage is unavailable (same UUID returned per page load)
- Add _resetTelemetryPersistenceForTesting() for test isolation
- Remove @copilotkit/shared dep from telemetry-disclosure.ts (inline
env-var check; keeps module self-contained and testable in isolation)
- Add clearMocks: true to web-inspector vitest config (fixes spy call
history accumulating across tests)
- Expand telemetry.test.ts to 22 tests covering wire body shape, opt-out,
5 error-resilience paths, typed helpers, distinct ID lifecycle (SSR +
localStorage-throws + funnel coherence), maybeShowDisclosure, and
getTelemetryDistinctIdForUrl
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three V1 funnel events from the inspector — oss.inspector.banner_viewed,
oss.inspector.banner_clicked, oss.inspector.threads_tab_clicked — plus a
privacy panel for opt-out, a first-run console disclosure on inspector
mount and runtime startup, and inspector content added to the canonical
/telemetry docs page on main.
Inspector POSTs directly from the browser to telemetry.copilotkit.ai/ingest
(per ticket: URL is intentionally clearly named for transparency in DevTools).
Inline fetch POST in lib/telemetry.ts — no @copilotkit/shared dep on the
inspector, no dependency on any non-main branch.
Wire body shape (conservative; needs Ben confirmation):
POST https://telemetry.copilotkit.ai/ingest
{ event, properties: { ...caller, distinct_id, package }, ts }
If the lambda expects a richer envelope, update the single JSON.stringify
in lib/telemetry.ts.
Privacy invariants:
- Opt-out toggle short-circuits before any network call (verified by test).
- Properties are scoped to event metadata only — no message content, agent
state, prompts, completions, banner markdown. Negative test pins the wire.
- Anonymous distinct ID (UUID v4 in localStorage) set on inspector load and
propagated onto banner CTA links as ?posthog_distinct_id=<uuid> so the
destination site can posthog.alias() and close the
banner_viewed → banner_clicked → signup_attributed funnel. URL param
suppressed when opted out.
- Console disclosure on first inspector mount and runtime startup. Both
link to https://docs.copilotkit.ai/telemetry.
Plan gaps addressed:
- CTA name on banner_clicked: cta:'body'|'dismiss' (click location) plus
optional cta_label read defensively. Sam: confirm dismiss treatment.
- De-anon opt-out folded into the single toggle. Docs say so explicitly.
- banner_viewed dedup: per-instance Set<string> keyed by timestamp.
- EPIC consent / pixel review: out of scope for this PR; flagged at merge.
Deferred for V1.1:
- Wire body shape (Ben).
- Event-type allowlist for oss.inspector.* (Ben — oss-path-to-production).
- posthog_distinct_id URL-param key name (Ben/Tyler/website team).
Refs https://linear.app/copilotkit/issue/OSS-96
Two CR comments addressed:
- Rename the local destructure of forwardedProps.auth.copilotkitIntelligence
from 'cki' to 'cpki' so it matches the project-wide abbreviation already
used in metadata fields (cpki_event_id, cpki_event_seq, etc).
- Replace the inline 'X-Cpki-User-Id' string literal with the existing
INTELLIGENCE_USER_ID_HEADER constant exported from intelligence-platform/client.
Applies to the runtime auto-attach in agent/index.ts and to the three
test sites in intelligence-mcp-helper.test.ts so the user-side and
runtime-side stay in sync.
Move the per-request Intelligence MCP bag from
forwardedProps.copilotkitIntelligence to
forwardedProps.auth.copilotkitIntelligence so the Intelligence-side
redaction policy strips it. The 'auth' namespace is the convention for
credentials; persistence sinks (Postgres, Redis, S3) and FE replay
paths in apps/realtime-gateway already strip everything under it.
Updates:
- Emitter (handlers/intelligence/run.ts): merge the bag into a single
forwardedProps.auth object alongside any upstream auth keys, and
only emit the auth namespace when there is something to put in it.
- Reader (agent/index.ts): read from forwardedProps.auth.copilotkitIntelligence
instead of forwardedProps.copilotkitIntelligence.
- Tests (intelligence-mcp-helper.test.ts): three fixtures rewritten
to the nested shape.
Four cases at the agent layer (BuiltInAgent reading forwardedProps):
* attaches when `copilotkitIntelligence` carries all three strings
(userId, apiKey, mcpUrl) — outbound headers carry Authorization +
X-Cpki-User-Id.
* does NOT attach when the bag is absent (no Intelligence wiring on
this run).
* does NOT attach when the bag is partial (e.g. mcpUrl missing).
* does NOT attach when the user has already configured an MCP server
pointing at the same URL — explicit user config wins, with the
user's headers and resolver hitting the wire.
Runtime side (intelligence/run.ts): when `runtime.intelligence.mcpServer`
is enabled, build a `copilotkitIntelligence` bag carrying the resolved
user-id, project apiKey, and the platform MCP URL, and pass it through
on `RunAgentInput.forwardedProps` to the agent. Skipped when the flag
is off — runs that don't go through this Intelligence path simply
don't see the bag.
Agent side (BuiltInAgent's run code): if `forwardedProps.copilotkitIntelligence`
contains all three string values AND the user's static `config.mcpServers`
doesn't already include the same URL, append a per-request
`MCPClientConfigHTTP`. Its `options.fetch` closes over apiKey + userId
and stamps `Authorization: Bearer <apiKey>` and `X-Cpki-User-Id:
<userId>` on every outbound MCP call. The custom fetch is the MCP
TypeScript SDK's documented extension point for per-request header
injection — no extra wrapper class, no separate framework concept.
The agent class is otherwise untouched: no new fields, no per-request
side channels, no typed reference to `CopilotKitIntelligence`. Other
agents that don't read `forwardedProps.copilotkitIntelligence` ignore
the bag.
Pulls in the AI SDK's stable `createMCPClient` export (rename from
`experimental_createMCPClient`) — the experimental name was deprecated;
`mcp-clients.test.ts`'s mock setup follows.
Adds `mcpServer?: boolean` to `CopilotKitIntelligenceConfig` (default
`false`). When true, the runtime emits the per-request bag the agent
needs to attach the platform's MCP server.
Internal accessors `ɵisMcpServerEnabled()` and `ɵgetApiKey()` round
out the existing `ɵgetApiUrl()`. Used by the runtime layer in the
forthcoming auto-attach commit; not part of the public surface.
Convenience re-exports so consumers wiring custom MCP clients (via
`mcpClients`) or custom transports don't need to add `@ai-sdk/mcp`
to their dependencies just to type the values.