Commit Graph

155 Commits

Author SHA1 Message Date
davidmckayv 692e52244c chore: release monorepo v1.60.2 2026-06-17 16:59:44 +00:00
ranst91 02c3a8aef0 chore: release monorepo v1.60.1 2026-06-12 13:03:45 +00:00
Ran Shem Tov 7805a9a57b fix: update cpk to use latest agui core packages 2026-06-12 11:16:19 +02:00
MikeRyanDev a6e8000c94 chore: release monorepo v1.60.0 2026-06-11 16:27:00 +00:00
Mark Fogle 47c993ccbb fix(core): scope context entries per agent and preserve the a2ui agent list (#5369) 2026-06-11 05:06:15 +00:00
Ran Shem Tov e5d3963db6 fix(core): bump @ag-ui core packages to 0.0.56 and adapt runHttpRequest
Bump @ag-ui/core, @ag-ui/client, @ag-ui/encoder from 0.0.53 to 0.0.56
across all packages.

@ag-ui/client 0.0.56 changed runHttpRequest from (url, requestInit) to a
fetch-thunk signature (() => Promise<Response>). Update the single-route
and connect transport paths in ProxiedCopilotRuntimeAgent to wrap the
request in () => this.fetch(url, init), restoring the broken envelope
transports.

Add @ag-ui/core, client, encoder, proto to minimum-release-age-exclude
in .npmrc so the freshly published 0.0.56 (under the 24h release-age
gate) installs in CI.
2026-06-09 17:47:43 +02:00
contextablemark 9bfeb74bc9 chore: release monorepo v1.59.5 2026-06-05 05:21:08 +00:00
ranst91 47fcb30d0f chore: release monorepo v1.59.4 2026-06-04 17:26:49 +00:00
lukasmoschitz 5de391fa27 fix(core): make setRuntimeTransport idempotent on the requested transport mode (#5179)
## Summary

`setRuntimeTransport` was not idempotent on the **requested** transport
mode. Auto-detect resolves the requested `"auto"` to a concrete
transport (`"rest"`/`"single"`) and writes that back to
`_runtimeTransport`; the guard then compared against that **resolved**
value. So re-applying the same requested mode — which the provider
effect does on **every render** — compared unequal and **re-ran the
entire `/info` handshake**, rebuilding the runtime agents mid-session.

When that re-sync lands during a turn, `useAgent` hands the UI a
freshly-rebuilt (empty) agent for a render, **blanking the whole
transcript** (and any per-message UI bound to it — e.g. the intelligence
indicator) until it replays.

## Fix

Track the **requested** mode separately (`_requestedTransport`) and
guard on it. Re-applying an unchanged requested transport — including
`"auto"` after auto-detect has resolved it — is now a no-op, so no
redundant `/info` re-sync fires.

## Tests

`packages/core/src/__tests__/agent-registry-resync.test.ts`: re-applying
`"auto"` after auto-detect resolves it does **not** refetch `/info`.

## Scope / risk

Touches only `packages/core` (`core/agent-registry.ts` + the test). **No
public API change.** Genuine transport *changes* still re-sync exactly
as before; only redundant re-applications of the same requested mode are
skipped.

> **Verified live:** this idempotency guard *alone* eliminates the
mid-turn re-sync in the e-commerce intelligence demo — instrumentation
shows no `/info` handshake fires during a turn, and the
transcript/indicator no longer flickers. (An earlier draft also made the
re-sync itself non-destructive as defense-in-depth; that was dropped
because, with this guard, no mid-turn re-sync occurs for it to protect
against.)
2026-06-04 11:56:47 +02:00
Jordan Ritter 32551f2bdb fix(react-core): type the active-run completion contract and serialize the suggestion send path
Replace the brittle `"activeRunCompletionPromise" in agent` probe + double
`as unknown as` casts in CopilotChat with a typed `RunCompletionAware`
contract plus an `isRunCompletionAware` type guard, both exported from core.
IntelligenceAgent now declares the property and implements the interface, so
the in-flight-run await is reachable without a cast and non-Intelligence
agents still degrade safely. Cast sites in the attachments/e2e tests and the
MockStepwiseAgent helper are updated to the typed accessor.

Factor the await-then-send logic into a shared `waitForActiveRunToSettle`
helper and call it from BOTH onSubmitInput and handleSelectSuggestion. This
closes the suggestion-path in-flight gap: selecting a suggestion mid-run no
longer pre-empts/aborts the active run (e.g. an interrupt RESUME) — the same
regression PR #5195 fixed for the typed-Enter path. Adds a red-green
regression test that parks the suggestion send on the in-flight promise.
2026-06-03 19:47:38 -07:00
jpr5 be20a389cf chore: release monorepo v1.59.3 2026-06-03 14:53:22 -07:00
Lukas Moschitz 8a79dca3b4 fix(core): make setRuntimeTransport idempotent on the requested transport mode (RD-4)
Auto-detect resolves the requested transport ("auto") to a concrete value
("rest"/"single") and writes it back to _runtimeTransport. setRuntimeTransport
then compared against that resolved value, so re-applying the same requested
mode — which the provider effect does on every render — compared unequal and
re-ran the entire /info handshake, rebuilding the runtime agents mid-session
and blanking the transcript (and any per-message UI bound to it).

Track the requested mode separately (_requestedTransport) and guard on it, so
re-applying an unchanged requested transport is a no-op.

Adds coverage: re-applying "auto" after auto-detect resolves it does not
refetch /info.
2026-06-03 16:28:35 +02:00
jpr5 3b78ae7551 chore: release monorepo v1.59.2 2026-05-30 17:34:06 +00:00
Tyler Slaton 8eb339e3e6 feat(ci): bundle-size tracking + ES-compat checks (OSS-123, OSS-121) (#5051) 2026-05-30 09:21:25 -07:00
David McKay c6ca283e96 feat(ci): bundle-size tracking + ES-compat checks (OSS-123, OSS-121)
Adds two CI signals for keeping the published packages small and broadly compatible:

- Bundle size: size-limit file-mode config across packages plus a
  CopilotChat import-size regression signal (gzip) so growth in the
  headline consumer entrypoint is visible on every PR. A bundle-size
  workflow comments results on the PR (Phase 1: no hard-fail).
- ES compatibility: a compat-check (es-check) script across 9 packages
  with a root .browserslistrc, validating built .mjs/.cjs against the
  es2022 build target.

The measure script is importable (measureBundle) and unit-tested. Dev
docs live under dev-docs/ (bundle-size.md, browser-compat.md). All
action refs are pinned to full commit SHAs for supply-chain safety.
2026-05-29 16:44:35 -07:00
BenTaylorDev 28f6264dd4 chore: release monorepo v1.59.1 2026-05-29 15:19:08 +00:00
BenTaylorDev 94b1f61cc3 chore: release monorepo v1.59.0 2026-05-27 22:31:30 +00:00
BenTaylorDev ebc09ea5c0 chore: release monorepo v1.58.0 2026-05-26 15:40:57 +00:00
github-actions[bot] 47d3a0d442 style: auto-fix formatting 2026-05-21 22:14:54 +00:00
Jordan Ritter 56f3477e09 fix(ci): add repository.url to packages missing it + one-shot publish workflow
Packages without repository.url fail npm OIDC provenance verification.
Adds the field to agentcore-runner, core, sqlite-runner, voice, and
web-inspector. Includes a one-shot workflow to publish the 14 remaining
v1.57.4 packages (a2ui-renderer already published via OIDC).
2026-05-21 15:14:02 -07:00
tylerslaton 938803e6f4 chore: release monorepo v1.57.4 2026-05-21 14:25:58 +00:00
tylerslaton efae3dfb5b chore: release monorepo v1.57.3 2026-05-19 15:59:44 +00:00
tylerslaton 1b14504788 chore: release monorepo v1.57.2 2026-05-13 00:40:27 +00: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
Dusty aa08743c7e fix(core): only reset agent state on actual thread switch (supersedes #4720)
When the chat's connect effect re-fires due to React effect-dep churn, it
calls copilotkit.connectAgent({ agent }) on the same thread again. The
RunHandler previously called agent.setMessages([]), agent.setState({}),
and (transitively) clearReconnectCursor on every such call. That forced
the realtime gateway to replay the topic's full event history on every
churn re-connect — sending the same persisted cpki_event_ids 2-3 times
per thread switch — and produced both halves of Tyler's bug: duplicate
rows in the inspector AG-UI Events tab plus "Message not found" toasts
when the next runAgent fired with an empty agent.messages.

This change makes the orchestrator detect actual thread switches:

- RunHandler tracks _lastConnectedThreadId across connectAgent calls.
  On a fresh restore (different threadId from last call) it does the
  reset and clears the replay cursor. On churn (same threadId) it
  skips the reset entirely so local messages/state are preserved and
  the gateway resumes from lastSeenEventId instead of replaying.

- IntelligenceAgent.connect() no longer auto-clears the cursor;
  cursor management is the caller's decision now. clearReconnectCursor
  is made public so RunHandler (and tests) can call it explicitly.

- ProxiedCopilotRuntimeAgent exposes a clearReplayCursor(threadId)
  method that delegates to the IntelligenceAgent. Non-Intelligence
  runtime modes are a safe no-op.

This supersedes #4720, which attempted to solve only the inspector
duplicate-row symptom by adding a dispatcher dedup. That approach
introduced a blocking regression in the A → B → A restore path: the
dedup persisted across thread switches and suppressed the gateway's
restore replay, leaving agent.messages at [] and triggering the very
"Message not found" toast we were trying to prevent. Mike's review
caught it. The dispatcher dedup is removed entirely; the
orchestrator-level gate is sufficient and a smaller surface change.

Tests:
- intelligence-agent.test.ts: updated 4 tests that codified the old
  "always clear cursor" semantics. Added an explicit test that
  clearReconnectCursor() empties the cursor for the next connect, and
  Mike's regression — replay the same cpki_event_id after A → B → A
  and verify the rehydrate still produces the user message.
- core-connect-thread-switch.test.ts (new): four tests for the
  RunHandler gate covering churn, single switch, A → B → A, and the
  no-clearReplayCursor non-Intelligence path.

All 430 core tests pass.
2026-05-08 14:31:49 -06:00
tylerslaton 5164ae303f chore: release monorepo v1.57.1 2026-05-07 16:41:22 +00:00
Markus Ecker 79fb5af452 Merge remote-tracking branch 'origin/main' into mme/register-proxied-agent
# Conflicts:
#	packages/react-core/src/v2/hooks/__tests__/use-agent-thread-isolation.test.tsx
#	packages/react-core/src/v2/hooks/use-agent.tsx
#	packages/web-inspector/src/styles/generated.css
2026-05-07 14:20:55 +02:00
Markus Ecker 77dab3766a refactor(core,docs): rename ProxiedCopilotRuntimeAgent.remoteAgentId → runtimeAgentId
Renames the proxy-config field, the field on the agent instance, and
all matching references in tests and the useCopilotKit reference page.
"runtime" reads more naturally now that the proxy concept is documented
as "a local agent that delegates to a runtime agent" rather than
"remote agent" — the latter conflates with `remoteAgents` (the
registry of agents fetched from the runtime), which keeps its name.

No behavioral change; the field still controls the outbound REST URL
used by the proxy.
2026-05-06 11:46:44 +02:00
tylerslaton 490440a0e4 chore: release monorepo v1.57.0 2026-05-04 17:33:59 +00:00
Markus Ecker 8436b29205 fix(core): round-2 cleanup — guard cleanup loops, drop dead code
Round-2 review surfaced two cheap improvements; this commit lands them.
All 7 round-1 findings were confirmed fixed by round 2.

- core.ts onAgentsChanged: each iteration of the unregister loops is
  now wrapped in try/catch. A throw on iteration [0] no longer stalls
  cleanup for [1..n]; both registries' unregister paths are
  idempotent so re-attempts on the next onAgentsChanged are safe.

- agent.ts abortRun: removed dead `if (!routedId) return;` after
  `routedAgentId()` — that method now throws (or returns a non-empty
  string), so the guard is unreachable.

Tests: core 425 — all green.
2026-05-04 17:25:27 +02:00
Markus Ecker 9abce2c9bc fix(core,react-core): address CR-loop findings on registerProxiedAgent + cloning revert
Round-1 review found seven actionable items; this commit lands fixes for
all of them. Tests: core 425, react-core 1151, web-inspector 7 — all green.

Real bugs fixed:

- run-handler.ts: dropped the stale `agent` argument on the
  `reloadSuggestions(agentId, agent)` call. The signature was tightened
  to `(agentId)` when the consumerAgent parameter was removed; the call
  site wasn't updated, leaving a TS-2554 build break.

- agent.ts: tightened `routedAgentId(): string` to throw when both
  `agentId` and `remoteAgentId` are unset, instead of returning
  `string | undefined`. Removes two `!` non-null asserts in
  `#runViaHttp` / `#connectViaHttp` and the silent
  `/agent/undefined/connect` URL path.

- agent.ts: marked `remoteAgentId` `readonly`. The field was publicly
  mutable but `super.url` is baked at construction — mutating
  `remoteAgentId` post-construction silently desyncs the REST run URL
  from the routing decision elsewhere. `readonly` prevents.

- agent-registry.ts: the registerProxiedAgent collision check now uses
  `Object.prototype.hasOwnProperty.call(this._agents, agentId)` instead
  of `agentId in this._agents`. The `in` operator walks the prototype
  chain, so an agentId of `"__proto__"`, `"constructor"`, etc. would
  falsely test as already-registered.

- core.ts: the onAgentsChanged handler now mirrors the thread-store
  unregister loop with a parallel
  `stateManager.unsubscribeFromAgent(agentId)` for any agentId in
  previousAgentIds but absent from the current snapshot. Without this,
  `unregister()`'s state-manager subscription leaked.

Comment / test cleanup:

- CopilotChatView.tsx:92: stale "empty cloned agent" reference in the
  `isConnecting` JSDoc rewritten to "empty agent instance" — clones are
  gone.

- core-register-proxied-agent.test.ts: split the misleading "registering
  before runtime connects yields a proxy in pending runtimeMode" test
  (which exercised the no-runtimeUrl path, never the pending path) into
  two: one for the no-runtimeUrl case, one that actually constructs a
  core with a runtimeUrl and asserts `runtimeMode === "pending"`.
2026-05-04 17:20:37 +02:00
github-actions[bot] 7fe0ffd602 style: auto-fix formatting 2026-05-04 12:08:05 +00:00
Markus Ecker 0332c3c697 test(core,react-core): port isolation regression tests to registerProxiedAgent
Re-adds the isolation coverage that the per-thread cloning revert deleted,
rewritten against the explicit-registration model:

- 9 new core-level tests in core-register-proxied-agent.test.ts cover the
  cases from the deleted use-agent-thread-isolation.test.tsx — distinct
  instances when two proxies target the same remoteAgentId, message and
  state isolation between proxies, independent threadId per proxy, shared
  outbound URL (both route to remoteAgentId), getAgent identity, pending
  registration before runtime connect, registration with a remote id the
  runtime doesn't yet know, and re-register-after-unregister yielding a
  fresh proxy.

- 1 new react-core test in CopilotChatActivityRendering.e2e.test.tsx
  replaces the deleted "passes the per-thread clone to activity message
  renderers" regression test. The clone-vs-registry trap is gone in the
  new model, so the test is reframed: the renderer must receive the agent
  registered under the local agentId, not any other agent in the registry
  (e.g. the runtime-side id a proxy might route to).

Total: +10 tests. Core 415 → 424, react-core 1150 → 1151.
2026-05-04 13:57:58 +02:00
Markus Ecker e576bc16b7 feat(core): add registerProxiedAgent for mounting frontend agents against runtime agents
Adds a public CopilotKitCore.registerProxiedAgent({ agentId, remoteAgentId }) API
that mints a ProxiedCopilotRuntimeAgent under a local registry id and routes its
outbound HTTP requests to the named runtime agent. Returns { agent, unregister }
so React callers can clean up via useEffect.

Throws when agentId is already taken — collisions with agents__unsafe_dev_only or
a previous registerProxiedAgent are loud, not silent.

ProxiedCopilotRuntimeAgent gains a remoteAgentId field used only for outbound
routing — URL paths (/agent/<id>/run, /connect, /stop), single-route envelopes,
and the IntelligenceAgent delegate's agentId. The local agentId remains the
registry key and the source of truth for state-manager subscriptions, useAgent
caching, and onAgentsChanged. So multiple proxies (e.g. chat-1, chat-2) can
target the same runtime agent ("default") without cross-talk in any subscriber
bookkeeping.

Use case: replaces the implicit per-thread cloning previously offered by
useAgent({ threadId }). Callers now opt into multiple frontend agents
explicitly.

Includes 6 unit tests covering routing, duplicate-throw, idempotent unregister,
onAgentsChanged notification, and header inheritance.
2026-05-04 13:37:20 +02:00
Markus Ecker 762370a4e5 refactor: remove per-thread agent cloning, restore single registry agent per id
Reverts the cloning design from #3525 (useAgent per-thread clones, getThreadClone,
globalThreadCloneMap, cloneForThread) and #3630 (clone routing in activity renderers),
plus the inspector machinery that existed only to handle clones (onAgentRunStarted
subscriber + run-handler emissions from #3869, the connect-time emission from #3872,
and the agentRunThreadId map that read from it).

State-manager isClone composite-key path and SuggestionEngine consumerAgent param —
both added in #3525 to keep clones visible to bookkeeping — are gone too.

Restores agent.threadId = resolvedThreadId in CopilotChat (pre-#3525 behavior) and
swaps the inspector's agentRunThreadId map for a direct agent.threadId read.

Removes the DemoButtonAgent and /a2ui-demo page from the demo (added by #3630 as a
clone-fix repro).

Re-opens the original issue #2957 (CPK-7155): two CopilotChat instances with the same
agentId and different threadIds will share message state again. The follow-up is a
public registerProxiedAgent API so callers can opt into multiple frontend agents
proxying to the same runtime agent, without implicit per-thread cloning.
2026-05-04 13:27:04 +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 cf51841d18 test(core): cover onAgentsChanged auto-unregister + seed previousAgentIds from constructor
Pins three integration cases for the thread-store auto-unregister branch
in CopilotKitCore's internal onAgentsChanged subscriber:

1. agent removed from agents → store IS unregistered; subscriber receives
   the previous store via prevStore.
2. FIRST onAgentsChanged({ agents: {} }) on a published-style core where
   the store was registered before any agents arrived → store SURVIVES.
   Reproduces the race that the "previously had" guard exists to
   prevent.
3. add → register → remove cycle → store IS unregistered. Complements (2)
   by exercising the same code path's positive branch.

Test (1) surfaced a real bug in the seed: agentRegistry.initialize does
NOT emit onAgentsChanged, so the constructor's internal subscriber never
saw the agents__unsafe_dev_only set, and `previousAgentIds` stayed empty.
A later removeAgent__unsafe_dev_only call would then be guarded into a
no-op because the agentId looked "new". Fixed by seeding
`previousAgentIds = new Set(Object.keys(agents__unsafe_dev_only))` right
before wiring the subscriber.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 12:52:02 -07:00
Martha Schumann bd9fe0169a fix(core): guard thread-store auto-unregister against initial empty agents
CopilotKitCore subscribes to onAgentsChanged and unregisters thread
stores for any agentId not in the new agents map. For published cores,
core.agents is asynchronously populated, so the FIRST
onAgentsChanged({ agents: {} }) notification fires BEFORE published
agents are merged in. Without a guard, that empty notification rips out
a thread store that a consumer (e.g. useThreads) just registered.

Track previousAgentIds and only unregister an agentId that was present
in the previous snapshot AND missing from the new one. The first
empty-agents notification (where the agentId was never previously
present) becomes a no-op.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 12:44:13 -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 9b7c0517b6 fix(core): forward prevStore on unregister and freeze getAll snapshot
R1's notify-ordering fix only made the sync subscriber path safe; async
subscribers still race against the next register(). When notifySubscribers
awaits inside Promise.all, control returns to register() which assigns the
new store to the same agentId before the async handler resumes — at which
point registry.get(agentId) returns the new store, not the unregistered one.

Carry the previous store on the onThreadStoreUnregistered payload so
subscribers tearing down state don't need to consult the registry. Same
treatment for unregister(). Doc the "do not call registry.get(agentId) in
this callback" contract on the subscriber type itself.

Also tighten getAll(): cache the snapshot, freeze it (so the Readonly<>
claim is honest at runtime), and return the same reference between
mutations. Stable identity matters for useSyncExternalStore consumers that
compare snapshots to skip re-renders. Tests updated to assert prevStore
delivery, frozen-snapshot mutation throws, and reference stability across
calls. Mock notifySubscribers now uses Promise.all to mirror production
parallel dispatch.
2026-04-30 11:49:13 -07:00
Tyler Slaton 0bdc798854 Merge branch 'main' into tyler/kind-mendeleev-636369 2026-04-30 10:48:28 -07:00
Martha Schumann ed4be71076 test(core): narrow invocationCallOrder access for tsc strict mode
vitest types invocationCallOrder as `number[]`, but TS narrows
indexed access to `number | undefined` under noUncheckedIndexedAccess.
Capture the entries first, assert each is defined, then compare —
keeps the ordering check intact while satisfying tsc.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 10:25:50 -07:00
Martha Schumann 9fdfe23b39 refactor(core): export ɵMAX_SOCKET_RETRIES for thread-store tests
Tests in @copilotkit/react-core need the WebSocket retry budget so they
can validate teardown semantics without hardcoding the threshold
separately from production. Exposing it under the ɵ-prefixed internal
namespace keeps it out of the public API surface while letting the
test bench import a single source of truth.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 10:20:25 -07:00
Tyler Slaton 7a0e2f532e fix(suggestions): show available:"always" pills on welcome screen with runtimeUrl
Two-part welcome-screen regression for `available: "always"` suggestion configs
when the chat connects to agents via `runtimeUrl` instead of registering them
locally:

1. SuggestionEngine.reloadSuggestions bailed early when the agent wasn't yet
   in the registry. With runtimeUrl, the registry is empty during the initial
   /info fetch, so the very first reload (fired by useConfigureSuggestions on
   mount) skipped every config — static pills never appeared on the welcome
   screen, dynamic pills never started generating.  Now: don't bail, default
   `messageCount` to 0, run static configs anyway. Dynamic configs still need
   a real agent and skip until one arrives.

2. useConfigureSuggestions's global-config path only iterated the current
   agents map, which compounded the problem above — the empty map meant zero
   reloads. Now: also reload for the chat's resolved consumer agent (covers
   the empty-map case), and subscribe to onAgentsChanged for dynamic configs
   only, firing exactly once when the target agent first appears (so dynamic
   pills catch up after the runtime fetch completes, without piling up
   overlapping generations as multiple useConfigureSuggestions hooks mount).

`hasSuggestions` keeps the `!isConnecting && !isRunning` UI gate. `available:
"always"` controls eligibility windows (welcome screen vs. after first
message), not whether to render through connect/replay or through a run —
those still hide and the end-of-run reload regenerates against the new
context.

Tests:
- Engine: added unit coverage for reloadSuggestions when no agent is present.
- React: 4 integration tests covering welcome screen (specific + global
  consumerAgentId) and the run lifecycle (hide during run, reappear after) in
  default and pin-to-send modes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-30 10:14:45 -07:00
Martha Schumann f5787e0b5d fix(core): tighten ThreadStoreRegistry notify ordering and snapshot isolation
- register() now snapshots and clears the previous slot before queuing the
  unregister notification, then assigns the new store before queuing the
  register notification. The unregister microtask is queued first so
  subscribers tear down stale subscriptions before receiving the
  replacement.
- All notifySubscribers calls now attach .catch handlers so notification
  failures surface via console.error instead of being silently swallowed
  by `void`.
- getAll() returns a shallow copy so callers cannot mutate the registry's
  internal state through the returned reference.
- Test mock now mirrors the real notifySubscribers signature (handler +
  errorMessage) and wraps each subscriber call in try/catch, exercising
  the same error-isolation behaviour as production.
- Replaced the `as unknown as ɵThreadStore` cast with a satisfies-typed
  minimal stub matching the real interface.
- Added tests for ordering (unregister before second register), throwing
  subscriber isolation, and getAll() snapshot isolation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 10:06:16 -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
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
ranst91 5686889567 chore: release monorepo v1.56.5 2026-04-29 11:57:11 +00:00