Commit Graph

237 Commits

Author SHA1 Message Date
Mike Ryan 05cb21d325 fix(runtime): pass user ids to intelligence thread reads 2026-06-11 08:27:50 -07:00
Benjamin Taylor 8d68a95bc9 docs(packages): drop client license-key prop references; Angular no longer needs a key
Follow-up correction. The client publicLicenseKey/publicApiKey prop is the
header→cloud path and is NOT what activates the Intelligence runtime (that's
the server-side COPILOTKIT_LICENSE_TOKEN). So:

- Remove the `npx copilotkit@latest license` guidance from all client-prop
  contexts — that CLI yields the server-side license token, not the client
  prop value.
- Revert the client-prop docstrings (copilotkit-props, v2 CopilotKitProvider)
  to bare one-liners; drop the premium/"requires a license key" framing from
  the headless hook, react-ui observability docs, and runtime logging/onError
  JSDoc rather than reframing.
- Angular: remove all `licenseKey` mentions from the README — it is no longer
  a premium feature (the license watermark is disabled) and the key is not
  needed to function.

Server-side license-token documentation remains deferred to the example/runtime
setup pass (Bucket B).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 07:22:37 -05:00
Benjamin Taylor 8956c668bc docs(packages): retire Copilot Cloud framing in SDK doc references, point to the license key
Cloud is no longer promoted; the Intelligence license key is its replacement.
Scrub the old Copilot Cloud system from SDK JSDoc / doc-comments / console
messages / README prose so code references reflect how the license key is
obtained and used, mirroring examples/integrations/*:

- publicApiKey/publicLicenseKey docstrings (react-core props + v2 provider,
  vue legacy types, copilot-context) describe the CopilotKit public license
  key, acquired via `npx copilotkit@latest license` or the dashboard;
  publicApiKey framed as the legacy alias of publicLicenseKey.
- Premium-feature docs (headless hook, react-ui Chat/Popup/Sidebar
  observability, runtime logging/onError) drop "Copilot Cloud"/"requires a
  publicApiKey" wording and the publicApiKey examples in favor of the public
  license key + publicLicenseKey.
- console-styling messages and the angular README point at the license key
  and the `npx copilotkit@latest license` command.

Defunct features (guardrails_c, authConfig_c, useCopilotAuthenticatedAction_c)
keep their code but lose their JSDoc (marked @internal defunct).

Functional surfaces untouched: api.cloud.copilotkit.ai endpoint, the
X-CopilotCloud-Public-Api-Key header, prop names, gating logic, tests,
CHANGELOGs. Example-app migration (Bucket B) deferred.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 07:22:37 -05:00
Mark Fogle b9df2ecaa9 fix(runtime): forward per-agent a2ui scoping in the runtime info response (#5369) 2026-06-11 05:05:54 +00:00
Alem Tuzlak f611667fe1 fix(runtime): make MCP server failures non-fatal (graceful degradation)
A single unavailable MCP server (down, 5xx, timeout, bad auth) no longer
fails the whole run - it is skipped with an error log and the run
continues with healthy servers and the agent's own tools.
2026-06-10 10:46:08 -07:00
Benjamin Taylor f20157aa88 Merge remote-tracking branch 'origin/main' into ben1/intelligence-threads-examples-rollout 2026-06-04 16:46:57 -05:00
Maximiliano Korp d13f944c3c fix(runtime): drop spurious "agents" license warning on agent run
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.
2026-06-04 13:29:25 -07:00
Benjamin Taylor e2cb8e3d1c Merge remote-tracking branch 'origin/main' into ben1/intelligence-threads-examples-rollout
# Conflicts:
#	examples/integrations/crewai-crews/package-lock.json
#	examples/integrations/crewai-crews/package.json
2026-06-04 15:27:53 -05:00
Markus Ecker c3f7961242 feat(runtime): attach enterprise-learning MCP middleware on real agent runs
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.
2026-06-04 17:56:29 +02:00
Markus Ecker c68be7ecbe feat(runtime): add /annotate endpoint and generalize intelligence client
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.
2026-06-04 17:56:29 +02:00
Mike Ryan 1f3c5fb007 fix(runtime): filter generated thread titles 2026-06-01 17:51:50 -07:00
Sam Julien 33f669ba7b fix(packages): canonicalize docs.copilotkit.ai URLs in user-facing messages
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.
2026-05-22 16:37:21 -07:00
Benjamin Taylor b684fae377 review(telemetry): address CR findings on client-side sampling
- 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.
2026-05-21 14:45:20 -05:00
Benjamin Taylor ad94ceb254 feat(telemetry): gate anonymous v2 events client-side, bypass for identified
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.
2026-05-21 14:45:20 -05:00
Benjamin Taylor e03de792c3 review(telemetry): address PR feedback on sink migration
- Mark v1 licenseToken private to match v2 visibility
- Extract shared warnIfLicenseTokenLacksTelemetryId helper to keep v1
  and v2 setLicenseToken bodies in lockstep
- Remove dead v2 scarf-client and its test block (migration leftover)
- Add v1 shared TelemetryClient test coverage: lambda always-send,
  segment sample gating, setLicenseToken warn paths, cloud config,
  telemetryDisabled gate, sample-rate range, env-var matrix
2026-05-21 14:45:20 -05:00
Benjamin Taylor 3be4c6b1e7 feat(telemetry): warn when license token yields no telemetry_id
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>
2026-05-21 14:45:20 -05:00
Benjamin Taylor ff33123c93 feat(telemetry): source telemetry-id from EIP license JWT
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>
2026-05-21 14:45:19 -05:00
Benjamin Taylor ce3697aad8 feat(telemetry): route runtime telemetry through CopilotKit telemetry sink
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>
2026-05-21 14:45:19 -05:00
Jordan Ritter 9107e00a9a fix(runtime): close header propagation gap for LangGraphAgent
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.
2026-05-21 01:39:28 -07:00
Alem Tuzlak 6d49ecbb7b fix(showcase, runtime): subagents fixtures, voice mic format, fine-grained shared-state gating
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.
2026-05-15 15:57:31 +02:00
github-actions[bot] fd5e8e7c20 style: auto-fix formatting 2026-05-12 16:33:44 +00:00
Alem Tuzlak cdb5f44fb5 test(reasoning-chain): add regression coverage at runtime, harness, and e2e layers
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.
2026-05-12 18:29:27 +02:00
Alem Tuzlak e4764324c5 fix(runtime/langgraph): filter reasoning-role messages before AG-UI converter
`@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.
2026-05-12 17:21:21 +02:00
Martha Schumann ce35cba85e feat(inspector/telemetry): propagate telemetryDisabled from runtime env var through inspector
- 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>
2026-05-11 15:43:17 -05:00
Martha Schumann db17476883 fix(inspector): update wire body shape + typed helpers + full test coverage (OSS-96)
- 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>
2026-05-11 15:43:17 -05:00
Claude 7914275c99 feat(inspector): add anonymous interaction telemetry (OSS-96)
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
2026-05-11 15:43:17 -05:00
github-actions[bot] 657077b4f2 style: auto-fix formatting 2026-05-07 12:35:14 +00:00
Markus Ecker 12f2c0e734 refactor(runtime): rename cki → cpki and use INTELLIGENCE_USER_ID_HEADER constant
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.
2026-05-07 14:31:59 +02:00
Markus Ecker 3e7449dc4c Merge remote-tracking branch 'origin/main' into lukas/cpk-7526-copilotkitruntimev2-configurable-mcp-server-on-builtinagent 2026-05-07 14:28:27 +02:00
Markus Ecker 6ed400c776 refactor(runtime): nest Intelligence MCP credentials under forwardedProps.auth.copilotkitIntelligence
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.
2026-05-07 10:55:02 +02:00
Markus Ecker faa2a556d0 test(runtime): cover Intelligence MCP auto-attach via forwardedProps
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.
2026-05-06 16:16:14 +02:00
Markus Ecker a46acc3f94 feat(runtime/agent): auto-attach Intelligence MCP server via input.forwardedProps
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.
2026-05-06 16:15:57 +02:00
Markus Ecker 1de2fae369 feat(runtime): CopilotKitIntelligence.mcpServer opt-in flag + ɵ-accessors
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.
2026-05-06 16:15:21 +02:00
Markus Ecker 44cb538856 feat(runtime/v2): re-export MCPClient and MCPTransport from @ai-sdk/mcp
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.
2026-05-06 16:15:06 +02:00
github-actions[bot] 56524a7282 style: auto-fix formatting 2026-05-01 01:08:19 +00:00
Martha Schumann 0721414fbb Merge remote-tracking branch 'origin/main' into feat/CPK-7193-inspector-threads-clean
# Conflicts:
#	examples/integrations/langgraph-python-threads/apps/app/package.json
#	examples/integrations/langgraph-python-threads/apps/bff/package.json
#	examples/integrations/langgraph-python-threads/package-lock.json
#	pnpm-lock.yaml
2026-04-30 18:01:51 -07:00
Martha Schumann c9e1bd8e34 feat(runtime): wire intelligence /_inspect/threads/:id/{events,state} endpoints
Replaces the 501 stubs in handleGetThreadEvents and handleGetThreadState
with real delegation to CopilotKitIntelligence. Adds two new client
methods (`getThreadEvents`, `getThreadState`) on intelligence-platform/
client.ts that hit the new `/api/_inspect/threads/:id/{events,state}`
routes shipped in Intelligence PR #144 (CPK-7453). Auth flows through
the existing `resolveIntelligenceUser` path; threadId scoping happens
server-side via the API key's org/project resolution.

Wire shapes match the in-memory branch so the inspector consumes both
runtimes identically:
- events: `{ events }` (platform-internal `decodeErrorRowIds` and
  `truncated` flags are stripped at the runtime boundary)
- state: `{ state }` where the platform's discriminated `ThreadStateResult`
  flattens to the snapshot value for `kind: "snapshot"` and to `null` for
  both `no-snapshot` and `snapshot-decode-error`

Replaces the 501 regression-protection tests with real delegation tests
that mock the platform methods, assert call args, and exercise the
no-snapshot / decode-error / throw paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:33:02 -07:00
Martha Schumann 2e498341ad test(runtime): pin per-thread routes, GET-only enforcement, and agentId tagging
Three new test areas covering surfaces this PR introduced or relies on:

- fetch-router: matchRoute tests for `/threads/:id/events`,
  `/threads/:id/state`, and `/threads/clear` (with and without URL
  encoding). Critically pins that "/threads/clear" resolves to
  `threads/clear` and does NOT fall through to the more permissive
  `threads/update` arm with threadId="clear" — the explicit guard in
  the router exists for this reason.
- fetch-handler validation: 405 enforcement for POST/PATCH/DELETE on
  the read-only `/threads/:id/events` and `/threads/:id/state`
  endpoints, with `Allow: GET` header. Complementary positive case
  asserts GET is NOT a 405.
- handle-run: end-to-end test that handle-run.ts:40
  (`agent.agentId = agentId`) propagates the registry key onto historic
  runs. Without this stamp, InMemoryAgentRunner falls back to "default"
  and the agentId filter on `GET /threads?agentId=...` breaks for the
  local-dev fallback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 12:52:35 -07:00
Martha Schumann 3d6166611e refactor(runtime,inspector): tighten types and align JSDoc with reality
Type quality:
- intelligence/threads.ts handleGetThreadMessages: switch on the Message
  discriminant (role) and read narrowed fields directly. Removes
  `as Record<string, unknown>` laundering and chained `as` casts on
  toolCalls/function/arguments. AssistantMessage's toolCalls always have
  `function: { name, arguments }`, so the prior fallbacks (`tc.name`,
  `tc.args`) were dead branches.
- in-memory.ts getThreadState: import StateSnapshotEvent and use it
  instead of `(event as { snapshot?: unknown }).snapshot`.

Comment / API alignment:
- intelligence/threads.ts handleClearThreads JSDoc no longer claims the
  inspector calls this; the actual caller is the demo button.
- in-memory.ts clearThreads JSDoc updated to match.
- in-memory.ts getThreadEvents JSDoc no longer references a SQLite
  runner that does not exist; just describes the compaction logic.
- web-inspector lint fix: rename unused `changed` parameter to
  `_changed` per oxc no-unused-vars.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 12:45:07 -07:00
Martha Schumann 7dcaa15063 test(core,runtime): strengthen error-log assertions and stub identity
- thread-store-registry: makeStore now attaches a __testId via
  intersection so callers can distinguish stubs at a glance during
  debugging instead of relying on identity-by-allocation alone.
- thread-store-registry subscriber-isolation test: assert the
  diagnostic content ("Subscriber onThreadStoreRegistered error") and
  Error argument, not just that some error was logged.
- handle-threads identifyUser-throws test: assert
  "Error identifying intelligence user" with an Error argument, since
  the throw originates inside resolveIntelligenceUser which logs and
  returns 500 before subscribeToThreads is reached.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 12:13:43 -07:00
Martha Schumann 7d525d62aa test(runtime): align MessagePopulatingTestAgent with TestAgent contract
- Add the missing protected connect() override returning EMPTY so the
  mock matches TestAgent and ThrowingAgent. Without it, a clone() that
  ever exercised connect() would fall through to AbstractAgent.connect()
  and may try to open a real transport in tests.
- clone() now forwards this.agentId directly instead of coercing
  undefined to "". The constructor accepts string | undefined to make
  this type-safe — coercion would silently turn "no agent id" into
  "empty agent id", a different state per AgentConfig.agentId?: string.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 12:13:22 -07:00
Martha Schumann d5e5da6dce test(runtime): tighten thread-handler and in-memory-runner assertions
handleClearThreads is intentionally synchronous, but neither test
asserted the return type. Add `expect(response).not.toBeInstanceOf(Promise)`
on both branches so a regression that starts awaiting I/O updates the
synchronous call sites.

The handleGetThreadEvents/handleGetThreadState 501 tests asserted the
status code but not that intelligence stayed untouched. Stub spies for
both `listThreads` and a hypothetical `getThreadEvents`/`getThreadState`,
then assert neither was called — so a regression that drops the early
return and falls through to platform calls fails this test even after
the response code changes.

The handleSubscribeToThreads 500 test created an `errorSpy` to silence
console output but never asserted on it. Add `expect(errorSpy).toHaveBeenCalled()`
so a regression that quietly drops the diagnostic log is caught.

The InMemoryAgentRunner getThreadEvents test claimed the synthetic
terminal event was present but only asserted on TEXT_MESSAGE_*. Add an
explicit assertion on `RUN_ERROR` with `code: "INCOMPLETE_STREAM"` so
finalizeRunEvents' contract is locked in — a regression that stops
appending the synthetic event would leave the inspector showing an
in-progress thread forever.

Declare `onNewMessage` on `MessagePopulatingTestAgent.runAgent`'s options
type to match the runner's call site and `TestAgent` above. Without it,
a regression that starts depending on `onNewMessage` here would compile
cleanly even though the mock would silently drop the call.
2026-04-30 11:54:14 -07:00
Martha Schumann 7421bf12b1 test(runtime): tighten thread-handler and in-memory-runner tests
handle-threads.test.ts:
- handleGetThreadMessages intelligence-path test now asserts the response
  body verbatim, so a regression that swaps in a stub body is caught.
- 422-no-intelligence test issues a real DELETE request for the delete
  path instead of cloning a POST request.
- handleClearThreads block carries a comment explaining why the handler
  is intentionally synchronous (no I/O on either branch).
- The identifyUser-throws test now silences console.error for the
  duration of the assertion, matching the pattern already used by the
  subscribe-throws test.

in-memory-runner.test.ts:
- ThrowingAgent test asserts RUN_ERROR is the last emitted event and
  that no RUN_FINISHED is emitted, locking in terminal-event semantics.
- getThreadEvents test asserts the full TEXT_MESSAGE triple is present
  in the persisted event log, and the comment now reflects the real
  finalizeRunEvents behaviour (it appends a synthetic terminal event
  when the agent does not emit one, so terminal events ARE persisted).
- getThreadState multi-run test gains a cross-thread isolation
  assertion: a snapshot on a different thread must not bleed into the
  original thread's state.
- Bumped the inter-thread sort delay from 5ms to 20ms to absorb timer
  jitter on slow CI runners.
- Removed four redundant `agent.agentId = "test-agent"` reassignments
  (the constructor already sets it via super({ agentId })).
- Aligned MessagePopulatingTestAgent.runAgent with TestAgent: `onEvent`
  is now required so the runner contract is exercised consistently.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 10:14:09 -07:00
Jordan Ritter b355acd56c fix(runtime): stop converting TanStack stream after RUN_FINISHED
TanStack's chat() engine runs a multi-turn agent loop: after the model
returns tool calls, it tries to execute them via processToolCalls().
Frontend-only tools (like render_pie_chart) are unknown to TanStack, so
executeToolCalls() treats them as errors and buildToolResultChunks()
re-emits TOOL_CALL_END without a preceding TOOL_CALL_START. The ag-ui
verify middleware rejects this duplicate.

Fix: track a runFinished flag in convertTanStackStream and discard all
events after the first RUN_FINISHED, which marks the boundary between
the streaming pass and TanStack's internal tool execution loop.

Also adds built-in-agent to docker-compose.local.yml and local-ports.json.
2026-04-29 22:41:58 -07:00
Jordan Ritter 497b205d1e fix: auto-format 16 files with pre-existing oxfmt violations
These files accumulated formatting drift across recent PRs. Fixes the
format CI check on main.
2026-04-29 19:12:22 -07:00
Jordan Ritter f2bc4a8dcb fix: default streamSubgraphs to true in LangGraph agent wrapper (#4446)
## Summary

- Explicitly defaults `streamSubgraphs: true` in `LangGraphAgent.run()`
forwardedProps so subagent streaming works with `@ag-ui/langgraph`
0.0.31+, which changed the default from `true` to `undefined`
- Uses nullish coalescing (`??`) so explicit user overrides (including
`false`) are preserved
- Fixes 6 showcase integrations failing E2E probes on the `subagents`
feature

## Why

`@ag-ui/langgraph` 0.0.31 removed the `?? true` fallback on
`streamSubgraphs` in `handleStreamEvents()`. The CopilotKit runtime
never explicitly set this prop, so subgraph event forwarding became
silently disabled. This caused all subagent-dependent demos to stop
working.

A previous v1 fix (commit `21e12afca`) handled this in the now-deleted
`agui-action.ts`, but the logic was lost during the v2 migration.

## Additional context

Investigation identified a second issue: `@ag-ui/langgraph` 0.0.34
includes state snapshot fixes needed for shared-state-read/write
features, but 0.0.34 is not yet published on npm (0.0.31 is latest). The
ag-ui team needs to cut a release. Full analysis: [Notion
write-up](https://app.notion.com/p/3513aa381852812a9ecef5fbf1e71739)

## Test plan

- [x] Runtime tests pass (1412/1412)
- [x] 7-agent CR converged Round 1 (0 findings)
- [ ] Verify showcase E2E probes for subagent features turn green after
merge
2026-04-29 16:57:58 -07:00
Jordan Ritter 1baeb9940a fix: use RegExp routes in Express adapter for Express 4/5 compatibility
Express 4 does not support the {*splat} wildcard syntax introduced in
Express 5. Replace string-based wildcard patterns with RegExp routes
that work across both major versions.
2026-04-29 16:50:19 -07:00
Jordan Ritter c0c97b4bf1 fix: default streamSubgraphs to true in LangGraph agent wrapper
The @ag-ui/langgraph 0.0.31+ changed the default for streamSubgraphs
from true to undefined, breaking subagent streaming. This enriches the
run() input's forwardedProps with streamSubgraphs: true as a default,
while preserving any explicit user override via the nullish coalescing
operator.
2026-04-29 15:59:08 -07:00
Martha Schumann d55d5495c7 Merge remote-tracking branch 'origin/main' into feat/CPK-7193-inspector-threads-clean
# Conflicts:
#	pnpm-lock.yaml
2026-04-29 13:31:32 -07:00
Jordan Ritter 9c9af7665f fix: update express wildcard routes for Express 4.20+ compat
Express 4.20+ uses path-to-regexp v8 which requires named wildcard
params. Bare * is no longer valid — use {*splat} syntax instead.

Updated production code (express.ts) and test (express-fetch-bridge).
Restores express >=4.20.0 override now that code is compatible.
2026-04-28 10:33:05 -07:00