The original Math.random() key caused React to remount the CodeBlock on
every render. The PR's content-based key (language + content prefix) still
changed every streaming token, causing the same flickering. Removing the
key entirely lets React use positional identity, which is stable across
re-renders while content streams in.
Closes#2669
The V1 CopilotRuntime constructor did `{...endpointAgents, ...agents}`
which silently spread a factory function to `{}`, losing all agents.
Anyone using the V1 API with a factory function got zero agents and no
error. This wraps factory functions so endpoint agents are merged at
resolution time instead of construction time.
The p tag was changed to div to fix hydration errors, but CSS selectors
still targeted p.copilotKitMarkdownElement. Updated to use the
.copilotKitParagraph class selector so paragraph styling (line-height,
font-size, margin) applies correctly to the new div element.
- Restore getCapabilities() return type to Promise<AgentCapabilities>
with ?? {} fallback, honoring AbstractAgent base class contract
- Simplify type annotation in get-runtime-info.ts to explicit
AgentCapabilities | undefined instead of complex Awaited<ReturnType<...>>
- Add console.warn logging to per-agent capabilities error catch block
- Fix useCapabilities JSDoc to accurately describe behavior during
runtime handshake
- Add test for empty capabilities object {} (truthy, included in response)
- Update error isolation test to verify warning is logged
- Add per-agent error isolation in /info handler so a single
getCapabilities() failure doesn't 500 the whole endpoint
- Normalize getCapabilities() to return undefined (not {}) when no
capabilities are set, matching the sync getter
- Replace instanceof ProxiedCopilotRuntimeAgent with duck-type check
in useCapabilities hook for extensibility
- Add JSDoc warning about shallow-merge behavior on capabilities config
- Add useCapabilities hook tests (5 cases covering both branches)
- Add per-agent error isolation test for get-runtime-info
When useAgent subscribes to multiple update types (OnMessagesChanged,
OnStateChanged, OnRunStatusChanged), each event fires forceUpdate()
independently. During streaming this causes dozens of re-renders per
second, leading to brief content height fluctuations that trigger
scroll jumping in use-stick-to-bottom.
Coalesce OnStateChanged and OnRunStatusChanged notifications using
queueMicrotask so multiple synchronous events within the same tick
produce a single React re-render. OnMessagesChanged retains its
existing behavior (direct or throttled).
Add AgentsFactory type that accepts a function receiving request context,
enabling multi-tenant and request-scoped agent configuration. The agents
option now accepts a static record, a Promise, or a factory function.
When mode is null (unknown tool skipped), content_block_delta events
with input_json_delta would still call sendActionExecutionArgs using
the previous valid tool call ID, corrupting its arguments.
extractParametersFromSchema now preserves enum values as structured
data on string parameters, recursively extracts nested object
attributes, and converts object arrays to object[] type with
attributes. This ensures complex MCP tool schemas survive the
Parameter[] -> Zod conversion without losing nested structure.
The Markdown component accepted components but not urlTransform,
preventing users from customizing URL sanitization. Added urlTransform
as an optional prop with passthrough to MemoizedReactMarkdown.
Parse Retry-After as seconds or HTTP-date and use it as the retry delay
instead of exponential backoff. Throw if the server requests a wait
longer than maxRetryAfterSeconds (60s default) to avoid hanging.
When getLanguageModel() returns null and provider/model are undefined
(as with LangChainAdapter), the code constructed "undefined/undefined"
as a model string, causing a cryptic "Unknown provider" error.
Now checks each source of model info explicitly and throws a clear
error message directing users to provide an explicit agents config
when using adapters that don't expose model metadata.
applyCredentialsToAgents/applyCredentialsToAgent were defined but never
called from initialize(), setAgents__unsafe_dev_only(), or
addAgent__unsafe_dev_only(). Only remote agents received credentials
via constructor. Local agents now receive credentials alongside headers.
Skip keydown handling during IME composition (e.g. CJK input) by
checking isComposing/keyCode 229, and guard textarea measurement
from resetting value mid-composition.
The loading spinner only appeared when the last message had role "user".
During tool execution the last message can have role "tool", hiding the
indicator. Now also show it when the trailing message is a tool result.
langchain-mcp-adapters sends tool call result content as an array of
{type:"text", text:string} objects. Extract and join text parts so
downstream consumers always receive a plain string.
Radix UI's asChild pattern requires the child component to forward
refs. The Button component was a plain function, causing a React
warning when nested inside DropdownMenuTrigger. Converted to
React.forwardRef so the ref is properly passed through to the
underlying DOM element.
GoogleGenerativeAI returns "0" as lc_kwargs.id for every message,
which is truthy and bypassed the randomId() fallback. All streamed
messages then shared the same ID, breaking message deduplication.
Now we always use randomId() for new message streams.
checkForUpdates() fired unconditionally on mount, making a network
request even when the dev console was disabled. Now the useEffect
short-circuits when showDevConsole is false.
JSON schemas from tools like Zod can produce union types like
["string", "null"]. The switch statement only handles single type
strings, causing these to fall through silently. Now we detect
Array.isArray(schema.type), filter out "null", and recurse with
the resolved scalar type.
The onAfterRequest middleware callback was being called with an empty
object `{}` instead of the available hook parameters (threadId, runId,
messages, path). Extract and forward these from the hookParams that the
v2 runtime provides.
Providers like @ai-sdk/openai-compatible emit sequential IDs (txt-0,
reasoning-0, msg-0) that collide across requests. The existing check
only caught the literal "0". Expand the check to match the
^(txt|reasoning|msg)-0$ pattern and always use randomUUID() for these.
When @hono/node-server polyfills the Request class, instanceof fails
because the polyfilled object has a different prototype chain.
Replace `reqOrRequest instanceof Request` with a duck-type check
that verifies url, method, and headers properties exist, which
works with both native and polyfilled Request objects.
The routeError function returned early for ALL errors when
showDevConsole was false, suppressing user-visible errors
(TOAST and BANNER visibility) in production.
Now only DEV_ONLY and SILENT errors are suppressed in production.
TOAST and BANNER errors are always surfaced to the chat UI.
The .copilotKitMessagesFooter used width:97% which caused horizontal
overflow in constrained containers. Changed to width:100% with
box-sizing:border-box so padding is included in the width calculation.