Commit Graph

260 Commits

Author SHA1 Message Date
Markus Ecker a0ad8649df Merge remote-tracking branch 'origin/lukas/cpk-7526-copilotkitruntimev2-configurable-mcp-server-on-builtinagent' into mme/integration-4629-4632-4420 2026-05-05 10:10:27 +02:00
github-actions[bot] 4da5b785a4 style: auto-fix formatting 2026-05-01 10:02:54 +00:00
Lukas Moschitz 8a2e6bd316 feat(runtime): requiresUser flag on MCPClientConfigHTTP
The thread-naming sub-run clones the agent without inheriting
agent.user, so any MCP server whose getHeaders resolver requires a
user-id (notably the one returned by CopilotKitIntelligence.toMCPServer)
threw MCPHeaderResolverError on every new chat — naming silently failed
and titles stayed "Untitled".

Opt-in `requiresUser?: boolean` on MCPClientConfigHTTP. When set and
`agent.user` is undefined, the agent skips the server at run-start with
a warn log instead of opening the transport. toMCPServer() sets the
flag by default; the existing resolver throw is retained as
defense-in-depth.
2026-05-01 12:01:22 +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
Tyler Slaton 52f8030f82 Merge branch 'main' into release/publish/monorepo/v1.56.5 2026-04-30 12:42:10 -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
Ran Shemtov e8061b707b Merge branch 'main' into release/publish/monorepo/v1.56.5 2026-04-30 17:59:09 +02:00
Tyler Slaton 5fe7ec55ca Merge branch 'main' into tyler/kind-mendeleev-636369 2026-04-29 22:46:51 -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
Tyler Slaton bd68aeda32 fix(deps): bump ag-ui packages to 0.0.53
Picks up ag-ui-protocol/ag-ui#1578 — `import * as jsonpatch from
"fast-json-patch"` produced an empty namespace under Node native ESM
because fast-json-patch@3.x populates exports via Object.assign, which
the CJS→ESM named-export detector cannot see. Result: every STATE_DELTA
and ACTIVITY_DELTA event threw "applyPatch is not a function", and
LangGraph generative UI streams floods the console with the failure on
each patch.

0.0.53 switches to a default import so the emitted bundle works under
both ESM and CJS consumers. Bumped @ag-ui/core and @ag-ui/encoder in
lockstep since they share the release.
2026-04-29 22:41:28 -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
Lukas Moschitz bc9d3a8862 test(runtime): cover per-call MCP headers and intelligence helper
- mcp-clients: switch to the stable `createMCPClient` import; type
  the user-managed-client compat case via
  `Awaited<ReturnType<typeof createMCPClient>>`.
- mcp-servers-integration: add a `vi.spyOn(globalThis, "fetch")`
  helper to read unredacted Authorization off each outbound request
  (aimock's journal redacts it). Use it to assert that
  - static `headers` still reach every outbound request
    (initialize, tools/list, tools/call),
  - per-call `getHeaders` runs on every request and is never cached,
  - `getHeaders` overrides static `headers` on key collision,
  - `getHeaders` errors surface as a `MCPHeaderResolverError`-tagged
    RUN_ERROR.
  Plus a regression test that an SSE server with static `headers`
  receives them on the wire — pre-existing latent bug from when SSE
  was constructed directly off `@modelcontextprotocol/sdk`.
- intelligence-mcp-helper (new): asserts that `toMCPServer()` emits
  Authorization (Bearer apiKey) AND X-Cpki-User-Id (from
  `agent.user`) on every outbound request, throws when no user has
  been resolved, and composes the URL by appending `/mcp` to
  `apiUrl` (normalizing a trailing slash).
2026-04-29 17:44:02 +02:00
Lukas Moschitz dd59dd8af0 feat(runtime): CopilotKitIntelligence.toMCPServer() shortcut
Pre-wires both auth axes for the Intelligence /mcp endpoint:
the project Bearer (from `apiKey`) as a static header, and the
per-call X-Cpki-User-Id (read off `MCPRequestContext.user.id`,
which the runtime populates via `identifyUser` →
`resolveIntelligenceUser` → `agent.user`). User-side code never
has to know the wire-level header name.

The resolver throws if no user has been resolved for the current
run. Silent fallthrough to an empty user-id would collapse every
browser session into one shared server-side state, which is exactly
the isolation guarantee the per-call header exists to enforce.
2026-04-29 17:43:41 +02:00
Lukas Moschitz f3c43c412a feat(runtime): plumb runtime user-identity to BuiltInAgent.user
After `resolveIntelligenceUser` runs `runtime.identifyUser` on the
incoming HTTP request, the resolved `{id, name}` is attached to the
agent so downstream MCP per-call resolvers can read it off
`MCPRequestContext.user`. Without this hop, an MCP getHeaders running
inside the agent has no way to see who the request was made for.

The user is set on the agent immediately before `agent.run()` is
invoked. `BuiltInAgent.run` then snapshots it (alongside the
forwardable request headers) so the value is stable for the lifetime
of the run, not mutable mid-stream.

`MiddlewareCapableAgent` gains an optional `user` field;
`configureAgentForRequest` stays synchronous (no second
`identifyUser` call) — the existing handler-level resolution remains
the single source of truth.
2026-04-29 17:43:25 +02:00
Lukas Moschitz 5ee569e0b5 feat(runtime): per-call MCP HTTP header resolution on BuiltInAgent
Adds the agent-side primitive that lets MCP HTTP servers receive
per-outbound-request headers — needed for things like a user-id, a
trace id, or any value that varies per agent run and must not be
cached at client construction time.

- New `MCPClientConfigHTTP.getHeaders` resolver, invoked on every
  outbound HTTP request (initialize, tools/list, tools/call, reconnects).
- New `CopilotKitMCPTransport` that implements `@ai-sdk/mcp`'s
  stable `MCPTransport` interface and wraps
  `StreamableHTTPClientTransport` with a fetch that applies
  static `headers` + per-call `getHeaders` results.
- New `MCPRequestContext` (carries `requestHeaders`, `input`,
  `mcpServerUrl`, `user`) and `MCPHeaderResolverError` (RUN_ERROR
  attribution when a resolver throws).
- `mcpClients` accepts any `Pick<MCPClient, "tools">` so a Vercel-built
  client passes through unchanged.
- Re-exports `MCPClient` / `MCPTransport` from
  `@copilotkit/runtime/v2` so consumers don't have to add
  `@ai-sdk/mcp` to type their wiring.

SSE servers continue to flow through `createMCPClient` directly;
the existing per-call resolver only applies to HTTP transports.
2026-04-29 17:43:07 +02:00
ranst91 5686889567 chore: release monorepo v1.56.5 2026-04-29 11:57:11 +00:00
Ran Shem Tov e504254792 chore: use latest ag-ui-langgraph 2026-04-29 11:30:43 +02:00
Jordan Ritter 8d7e850c22 chore: bump @copilotkit/aimock to latest (v1.16.1)
Needed for turnIndex/sequenceIndex fixture matching in D5 multi-turn
conversations. Updated in both packages/runtime (devDep) and
showcase/scripts (dep).
2026-04-28 22:21:02 -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
Jordan Ritter a23bdccb6c fix: use /* instead of * in express route for Express 4.20+ compat
Express 4.20+ uses path-to-regexp v8 which rejects bare * as a route
pattern. Use /* which is the forward-compatible syntax.
2026-04-28 10:33:05 -07:00
Alem Tuzlak b2d14a847e Merge remote-tracking branch 'origin/worktree-mossy-tumbling-unicorn' into worktree-mossy-tumbling-unicorn 2026-04-28 13:55:50 +02:00
Alem Tuzlak 46eeb21924 Merge remote-tracking branch 'origin/main' into worktree-mossy-tumbling-unicorn
# Conflicts:
#	showcase/scripts/__tests__/generate-catalog.test.ts
2026-04-28 13:54:22 +02:00
BenTaylorDev f19afade44 chore: release monorepo v1.56.4 2026-04-27 15:05:59 -05:00
github-actions[bot] 8334dcdde6 style: auto-fix formatting 2026-04-27 16:32:58 +00:00
Alem Tuzlak 18f6c1e2e5 fix(runtime): close open reasoning message in REASONING_END and on REASONING_START reopen 2026-04-27 18:30:01 +02:00
Alem Tuzlak a48e9a17b3 fix(runtime): align TanStack state-tool detection on rawContent ?? raw.result fallback 2026-04-27 18:16:15 +02:00
Alem Tuzlak 7bcd2f81ee fix(runtime): track REASONING message-open and run-open separately to avoid duplicate REASONING_MESSAGE_END 2026-04-27 18:01:54 +02:00
Alem Tuzlak d128257dd2 feat(runtime): auto-close open REASONING lifecycle in TanStack converter 2026-04-27 13:50:50 +02:00
Alem Tuzlak 6f77116aee feat(runtime): pass through REASONING_* chunks in TanStack converter 2026-04-27 13:47:02 +02:00
Alem Tuzlak d07c1d109e test(runtime): assert TanStack converter does not emit STATE_* for non-state tools 2026-04-27 13:42:24 +02:00
Alem Tuzlak a0be5455ab feat(runtime): emit STATE_DELTA on AGUISendStateDelta in TanStack converter 2026-04-27 13:40:38 +02:00
Alem Tuzlak 5f233a1987 feat(runtime): emit STATE_SNAPSHOT on AGUISendStateSnapshot in TanStack converter 2026-04-27 13:37:36 +02:00
Alem Tuzlak 71005252b8 test(runtime): add TanStack tool-result + reasoning chunk helpers 2026-04-27 13:17:09 +02:00
Mike Ryan ae978b95b1 test(runtime): align intelligence telemetry mock platform 2026-04-24 11:22:49 -07:00
github-actions[bot] 5bd77b35f0 style: auto-fix formatting 2026-04-24 11:22:49 -07:00
Mike Ryan ad7a383095 fix(runtime): tighten realtime startup handling 2026-04-24 11:22:49 -07:00
Mike Ryan 17bd39cd26 fix(runtime): finalize realtime connection responses 2026-04-24 11:22:49 -07:00
Mike Ryan 05aa15f908 fix(runtime): align intelligence realtime contracts 2026-04-24 11:22:49 -07:00
Benjamin Taylor b8e7b575c3 refactor(runtime): drop framework tag from v2 telemetry
The framework label was only meaningful for Segment's per-event
properties. Scarf receives only the event name, so the label has no
destination. Removes the `framework` option on createCopilotRuntimeHandler
and its pass-through from the hono/express adapters, plus the now-unused
setGlobalProperties/globalProperties/flattenObject on the telemetry
client.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:01:27 -05:00