@copilotkit/core 1.62.2 → 1.62.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,10 +1,11 @@
1
1
  import { Throttler } from "@tanstack/pacer";
2
2
  import { AbstractAgent, EventType, HttpAgent, randomUUID, runHttpRequest, structuredClone_, transformChunks, transformHttpEventStream } from "@ag-ui/client";
3
3
  import { RUNTIME_MODE_INTELLIGENCE, RUNTIME_MODE_SSE, logger, partialJSONParse, phoenixExponentialBackoff, randomUUID as randomUUID$1, resolveDebugConfig, schemaToJsonSchema } from "@copilotkit/shared";
4
- import { BehaviorSubject, EMPTY, NEVER, Notification, Observable, Subject, Subscription, asapScheduler, combineLatest, concat, defer, dematerialize, firstValueFrom, from, lastValueFrom, merge, of, switchMap, throwError } from "rxjs";
5
- import { catchError, delay, distinctUntilChanged, endWith, filter, finalize, ignoreElements, map, mergeMap, observeOn, scan, share, shareReplay, switchMap as switchMap$1, take, takeUntil, tap, timeout, withLatestFrom } from "rxjs/operators";
4
+ import { BehaviorSubject, EMPTY, NEVER, Notification, Observable, Subject, Subscription, asapScheduler, combineLatest, concat, defer, dematerialize, firstValueFrom, from, lastValueFrom, merge, observeOn, of, switchMap, throwError } from "rxjs";
5
+ import { catchError, concatWith, delay, distinctUntilChanged, endWith, filter, finalize, ignoreElements, map, mergeMap, observeOn as observeOn$1, scan, share, shareReplay, switchMap as switchMap$1, take, takeUntil, tap, timeout, withLatestFrom } from "rxjs/operators";
6
6
  import { Socket } from "phoenix";
7
7
  import { zodToJsonSchema } from "zod-to-json-schema";
8
+ import { fromFetch } from "rxjs/fetch";
8
9
 
9
10
  //#region src/utils/phoenix-observable.ts
10
11
  /**
@@ -542,12 +543,12 @@ function hasCredentials(agent) {
542
543
  function isZodError(error) {
543
544
  return error !== null && typeof error === "object" && "name" in error && error.name === "ZodError";
544
545
  }
545
- function isAbortError(error) {
546
+ function isAbortError$1(error) {
546
547
  return (error instanceof DOMException || error instanceof Error) && error.name === "AbortError";
547
548
  }
548
549
  function withAbortErrorHandling(observable) {
549
550
  return observable.pipe(catchError((error) => {
550
- if (isZodError(error) || isAbortError(error)) return EMPTY;
551
+ if (isZodError(error) || isAbortError$1(error)) return EMPTY;
551
552
  throw error;
552
553
  }));
553
554
  }
@@ -895,6 +896,7 @@ var AgentRegistry = class {
895
896
  _runtimeMode = RUNTIME_MODE_SSE;
896
897
  _intelligence;
897
898
  _threadEndpoints;
899
+ _suggestions;
898
900
  _a2uiEnabled = false;
899
901
  _a2uiAgents;
900
902
  _openGenerativeUIEnabled = false;
@@ -944,6 +946,9 @@ var AgentRegistry = class {
944
946
  get threadEndpoints() {
945
947
  return this._threadEndpoints;
946
948
  }
949
+ get suggestions() {
950
+ return this._suggestions;
951
+ }
947
952
  get a2uiEnabled() {
948
953
  return this._a2uiEnabled;
949
954
  }
@@ -1131,6 +1136,7 @@ var AgentRegistry = class {
1131
1136
  this._runtimeMode = RUNTIME_MODE_SSE;
1132
1137
  this._intelligence = void 0;
1133
1138
  this._threadEndpoints = void 0;
1139
+ this._suggestions = void 0;
1134
1140
  this._a2uiEnabled = false;
1135
1141
  this._a2uiAgents = void 0;
1136
1142
  this._openGenerativeUIEnabled = false;
@@ -1178,6 +1184,7 @@ var AgentRegistry = class {
1178
1184
  this._runtimeMode = runtimeInfoResponse.mode ?? RUNTIME_MODE_SSE;
1179
1185
  this._intelligence = runtimeInfoResponse.intelligence;
1180
1186
  this._threadEndpoints = runtimeInfoResponse.threadEndpoints;
1187
+ this._suggestions = runtimeInfoResponse.suggestions;
1181
1188
  const a2uiInfo = runtimeInfoResponse.a2ui;
1182
1189
  this._a2uiEnabled = a2uiInfo?.enabled ?? runtimeInfoResponse.a2uiEnabled ?? false;
1183
1190
  this._a2uiAgents = a2uiInfo?.enabled ? a2uiInfo.agents : void 0;
@@ -1193,6 +1200,7 @@ var AgentRegistry = class {
1193
1200
  this._runtimeMode = RUNTIME_MODE_SSE;
1194
1201
  this._intelligence = void 0;
1195
1202
  this._threadEndpoints = void 0;
1203
+ this._suggestions = void 0;
1196
1204
  this._a2uiEnabled = false;
1197
1205
  this._a2uiAgents = void 0;
1198
1206
  this._openGenerativeUIEnabled = false;
@@ -1432,38 +1440,81 @@ var SuggestionEngine = class {
1432
1440
  };
1433
1441
  }
1434
1442
  /**
1435
- * Generate suggestions using a provider agent
1443
+ * Generate suggestions by running a provider agent and extracting the
1444
+ * `copilotkitSuggest` tool call from its streamed messages.
1445
+ *
1446
+ * Two transports, one set of mechanics: both seed an agent with the
1447
+ * consumer's messages + state and the instruction, then `runAgent` with
1448
+ * forced `copilotkitSuggest` tool choice, parsing suggestions as messages
1449
+ * stream in via `onMessagesChanged`.
1450
+ *
1451
+ * - **Stateless** (runtime advertises `suggestions` and transport isn't
1452
+ * `single`): a stock `HttpAgent` pointed at `/agent/:id/suggest`. That
1453
+ * endpoint runs the provider directly and streams AG-UI SSE **without**
1454
+ * persisting a thread, and a plain `HttpAgent` only ever speaks REST SSE —
1455
+ * it never routes through the Intelligence websocket delegate (which would
1456
+ * persist a thread). This is the path that removes the thread flood.
1457
+ * - **Fallback** (capability absent → old runtime, or `single` transport):
1458
+ * clone the provider agent and run it client-side, as before.
1459
+ *
1460
+ * Gated to non-`single` transports because this client only builds the
1461
+ * multi-route `/agent/:id/suggest` URL; single-route deployments (not the
1462
+ * persisting-thread/Intelligence case) fall through to the clone fallback.
1436
1463
  */
1437
1464
  async generateSuggestions(suggestionId, config, consumerAgentId) {
1438
- let agent = void 0;
1465
+ let runHandle = void 0;
1466
+ let aborted = false;
1439
1467
  try {
1440
- const suggestionsProviderAgent = this.core.getAgent(config.providerAgentId ?? "default");
1441
- if (!suggestionsProviderAgent) throw new Error(`Suggestions provider agent not found: ${config.providerAgentId}`);
1442
- const suggestionsConsumerAgent = this.core.getAgent(consumerAgentId);
1468
+ const friends = this.core;
1469
+ const resolvedProviderAgentId = config.providerAgentId ?? "default";
1470
+ const suggestionsProviderAgent = friends.getAgent(resolvedProviderAgentId);
1471
+ if (!suggestionsProviderAgent) throw new Error(`Suggestions provider agent not found: ${resolvedProviderAgentId}`);
1472
+ const suggestionsConsumerAgent = friends.getAgent(consumerAgentId);
1443
1473
  if (!suggestionsConsumerAgent) throw new Error(`Suggestions consumer agent not found: ${consumerAgentId}`);
1444
- agent = suggestionsProviderAgent.clone();
1445
- agent.threadId = suggestionId;
1446
- agent.messages = JSON.parse(JSON.stringify(suggestionsConsumerAgent.messages));
1447
- agent.state = JSON.parse(JSON.stringify(suggestionsConsumerAgent.state));
1474
+ const instructionContent = [
1475
+ `Suggest what the user could say next. Provide clear, highly relevant suggestions by calling the \`copilotkitSuggest\` tool.`,
1476
+ `Provide at least ${config.minSuggestions ?? 1} and at most ${config.maxSuggestions ?? 3} suggestions.`,
1477
+ `The user has the following tools available: ${JSON.stringify(friends.buildFrontendTools(consumerAgentId))}.`,
1478
+ ` ${config.instructions}`
1479
+ ].join("\n");
1448
1480
  this._suggestions[consumerAgentId] = {
1449
1481
  ...this._suggestions[consumerAgentId],
1450
1482
  [suggestionId]: []
1451
1483
  };
1452
- this._runningSuggestions[consumerAgentId] = [...this._runningSuggestions[consumerAgentId] ?? [], agent];
1453
- agent.addMessage({
1484
+ const seededMessages = JSON.parse(JSON.stringify(suggestionsConsumerAgent.messages));
1485
+ const seededState = JSON.parse(JSON.stringify(suggestionsConsumerAgent.state ?? {}));
1486
+ const useStateless = this.core.suggestions === true && this.core.runtimeTransport !== "single";
1487
+ let suggestionAgent;
1488
+ if (useStateless) {
1489
+ const suggestUrl = `${this.core.runtimeUrl}/agent/${encodeURIComponent(resolvedProviderAgentId)}/suggest`;
1490
+ const credentials = this.core.credentials;
1491
+ suggestionAgent = new HttpAgent({
1492
+ agentId: resolvedProviderAgentId,
1493
+ url: suggestUrl,
1494
+ headers: { ...this.core.headers },
1495
+ ...credentials ? { fetch: (url, requestInit) => fetch(url, {
1496
+ ...requestInit,
1497
+ credentials
1498
+ }) } : {}
1499
+ });
1500
+ } else suggestionAgent = suggestionsProviderAgent.clone();
1501
+ suggestionAgent.threadId = suggestionId;
1502
+ suggestionAgent.setMessages(seededMessages);
1503
+ suggestionAgent.setState(seededState);
1504
+ runHandle = { abortRun: () => {
1505
+ aborted = true;
1506
+ suggestionAgent.abortRun();
1507
+ } };
1508
+ this._runningSuggestions[consumerAgentId] = [...this._runningSuggestions[consumerAgentId] ?? [], runHandle];
1509
+ suggestionAgent.addMessage({
1454
1510
  id: suggestionId,
1455
1511
  role: "user",
1456
- content: [
1457
- `Suggest what the user could say next. Provide clear, highly relevant suggestions by calling the \`copilotkitSuggest\` tool.`,
1458
- `Provide at least ${config.minSuggestions ?? 1} and at most ${config.maxSuggestions ?? 3} suggestions.`,
1459
- `The user has the following tools available: ${JSON.stringify(this.core.buildFrontendTools(consumerAgentId))}.`,
1460
- ` ${config.instructions}`
1461
- ].join("\n")
1512
+ content: instructionContent
1462
1513
  });
1463
- await agent.runAgent({
1464
- context: this.core.getContextForAgent(consumerAgentId),
1514
+ await suggestionAgent.runAgent({
1515
+ context: friends.getContextForAgent(consumerAgentId),
1465
1516
  forwardedProps: {
1466
- ...this.core.properties,
1517
+ ...friends.properties,
1467
1518
  toolChoice: {
1468
1519
  type: "function",
1469
1520
  function: { name: "copilotkitSuggest" }
@@ -1474,18 +1525,15 @@ var SuggestionEngine = class {
1474
1525
  this.extractSuggestions(messages, suggestionId, consumerAgentId, true);
1475
1526
  } });
1476
1527
  } catch (error) {
1477
- console.warn("Error generating suggestions:", error);
1528
+ if (!isAbortError(error) && !aborted) console.warn("Error generating suggestions:", error);
1478
1529
  } finally {
1479
1530
  this.finalizeSuggestions(suggestionId, consumerAgentId);
1480
1531
  const runningAgents = this._runningSuggestions[consumerAgentId];
1481
- if (agent && runningAgents) {
1482
- const filteredAgents = runningAgents.filter((a) => a !== agent);
1483
- this._runningSuggestions[consumerAgentId] = filteredAgents;
1484
- if (filteredAgents.length === 0) {
1485
- delete this._runningSuggestions[consumerAgentId];
1486
- await this.notifySuggestionsFinishedLoading(consumerAgentId);
1487
- }
1488
- }
1532
+ const remaining = runHandle ? (runningAgents ?? []).filter((a) => a !== runHandle) : runningAgents ?? [];
1533
+ if (remaining.length === 0) {
1534
+ delete this._runningSuggestions[consumerAgentId];
1535
+ await this.notifySuggestionsFinishedLoading(consumerAgentId);
1536
+ } else this._runningSuggestions[consumerAgentId] = remaining;
1489
1537
  }
1490
1538
  }
1491
1539
  /**
@@ -1605,6 +1653,13 @@ var SuggestionEngine = class {
1605
1653
  }
1606
1654
  };
1607
1655
  /**
1656
+ * Detects an `AbortController`-driven cancellation so the stateless path can
1657
+ * treat it as an expected (non-error) outcome on clear/reload.
1658
+ */
1659
+ function isAbortError(error) {
1660
+ return typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
1661
+ }
1662
+ /**
1608
1663
  * Type guard for dynamic suggestions config
1609
1664
  */
1610
1665
  function isDynamicSuggestionsConfig(config) {
@@ -2564,162 +2619,1144 @@ var ThreadStoreRegistry = class {
2564
2619
  };
2565
2620
 
2566
2621
  //#endregion
2567
- //#region src/core/core.ts
2568
- let CopilotKitCoreErrorCode = /* @__PURE__ */ function(CopilotKitCoreErrorCode) {
2569
- CopilotKitCoreErrorCode["RUNTIME_INFO_FETCH_FAILED"] = "runtime_info_fetch_failed";
2570
- CopilotKitCoreErrorCode["AGENT_CONNECT_FAILED"] = "agent_connect_failed";
2571
- CopilotKitCoreErrorCode["AGENT_RUN_FAILED"] = "agent_run_failed";
2572
- CopilotKitCoreErrorCode["AGENT_RUN_FAILED_EVENT"] = "agent_run_failed_event";
2573
- CopilotKitCoreErrorCode["AGENT_RUN_ERROR_EVENT"] = "agent_run_error_event";
2574
- CopilotKitCoreErrorCode["TOOL_ARGUMENT_PARSE_FAILED"] = "tool_argument_parse_failed";
2575
- CopilotKitCoreErrorCode["TOOL_HANDLER_FAILED"] = "tool_handler_failed";
2576
- CopilotKitCoreErrorCode["TOOL_NOT_FOUND"] = "tool_not_found";
2577
- CopilotKitCoreErrorCode["AGENT_NOT_FOUND"] = "agent_not_found";
2578
- /**
2579
- * Emitted when an agent run fails because the thread is already locked
2580
- * by another active run.
2581
- *
2582
- * @example
2583
- * ```tsx
2584
- * <CopilotKitProvider
2585
- * onError={({ code, error, context }) => {
2586
- * if (code === "agent_thread_locked") {
2587
- * // Show "Agent is busy, retry?" UI
2588
- * }
2589
- * }}
2590
- * />
2591
- * ```
2592
- */
2593
- CopilotKitCoreErrorCode["AGENT_THREAD_LOCKED"] = "agent_thread_locked";
2594
- CopilotKitCoreErrorCode["TRANSCRIPTION_FAILED"] = "transcription_failed";
2595
- CopilotKitCoreErrorCode["TRANSCRIPTION_SERVICE_NOT_CONFIGURED"] = "transcription_service_not_configured";
2596
- CopilotKitCoreErrorCode["TRANSCRIPTION_INVALID_AUDIO"] = "transcription_invalid_audio";
2597
- CopilotKitCoreErrorCode["TRANSCRIPTION_RATE_LIMITED"] = "transcription_rate_limited";
2598
- CopilotKitCoreErrorCode["TRANSCRIPTION_AUTH_FAILED"] = "transcription_auth_failed";
2599
- CopilotKitCoreErrorCode["TRANSCRIPTION_NETWORK_ERROR"] = "transcription_network_error";
2600
- CopilotKitCoreErrorCode["SUBSCRIBER_CALLBACK_FAILED"] = "subscriber_callback_failed";
2601
- return CopilotKitCoreErrorCode;
2602
- }({});
2622
+ //#region src/utils/micro-redux.ts
2623
+ const INTERNAL_ACTION_TYPES = {
2624
+ boot: "@@micro-redux/boot",
2625
+ init: "@@micro-redux/init",
2626
+ stop: "@@micro-redux/stop"
2627
+ };
2628
+ const INTERNAL_BOOT_ACTION = { type: INTERNAL_ACTION_TYPES.boot };
2603
2629
  /**
2604
- * Runtime allowlist derived from {@link SUBSCRIBE_TO_AGENT_KEYS}. Hoisted
2605
- * to module scope so the Set is allocated once, not per-subscription.
2630
+ * Builds a typed action creator from a type string and payload factory.
2606
2631
  */
2607
- const ALLOWED_KEYS = new Set([
2608
- "onMessagesChanged",
2609
- "onStateChanged",
2610
- "onRunInitialized",
2611
- "onRunFinalized",
2612
- "onRunFailed",
2613
- "onRunErrorEvent"
2614
- ]);
2615
- let CopilotKitCoreRuntimeConnectionStatus = /* @__PURE__ */ function(CopilotKitCoreRuntimeConnectionStatus) {
2616
- CopilotKitCoreRuntimeConnectionStatus["Disconnected"] = "disconnected";
2617
- CopilotKitCoreRuntimeConnectionStatus["Connected"] = "connected";
2618
- CopilotKitCoreRuntimeConnectionStatus["Connecting"] = "connecting";
2619
- CopilotKitCoreRuntimeConnectionStatus["Error"] = "error";
2620
- return CopilotKitCoreRuntimeConnectionStatus;
2621
- }({});
2632
+ function createTypedActionCreator(type, factory) {
2633
+ const creator = ((...args) => ({
2634
+ ...factory(...args),
2635
+ type
2636
+ }));
2637
+ creator.type = type;
2638
+ creator.match = (action) => action.type === type;
2639
+ return creator;
2640
+ }
2622
2641
  /**
2623
- * Normalize a header map to the internal invariant: a `Record<string, string>`
2624
- * with no `null`/`undefined` values. Entries whose value is `null`/`undefined`
2625
- * are dropped (this is how a header is cleared). Shared by the constructor and
2626
- * `setHeaders` so both write paths into `_headers` enforce the same invariant.
2642
+ * Declares a payload-based action config for `createActionGroup`.
2643
+ *
2644
+ * @example
2645
+ * ```ts
2646
+ * const actions = createActionGroup("User", {
2647
+ * loaded: props<{ id: string }>(),
2648
+ * });
2649
+ * ```
2627
2650
  */
2628
- function normalizeHeaders(headers) {
2629
- return Object.fromEntries(Object.entries(headers).filter((entry) => entry[1] != null));
2651
+ function props() {
2652
+ return { kind: "props" };
2630
2653
  }
2631
- var CopilotKitCore = class {
2632
- _headers;
2633
- _credentials;
2634
- _properties;
2635
- _defaultThrottleMs;
2636
- _debug;
2637
- subscribers = /* @__PURE__ */ new Set();
2638
- agentRegistry;
2639
- contextStore;
2640
- suggestionEngine;
2641
- runHandler;
2642
- stateManager;
2643
- threadStoreRegistry;
2644
- /**
2645
- * Tracks the agent IDs from the most recent `onAgentsChanged` notification.
2646
- * Used to gate thread-store auto-unregister so the FIRST empty-agents
2647
- * notification (before published agents are merged in) does not rip out a
2648
- * store that was registered prior to that initial notification.
2649
- */
2650
- previousAgentIds = /* @__PURE__ */ new Set();
2651
- constructor({ runtimeUrl, runtimeTransport = "auto", headers = {}, credentials, properties = {}, agents__unsafe_dev_only = {}, tools = [], suggestionsConfig = [], debug }) {
2652
- this._headers = normalizeHeaders(headers);
2653
- this._credentials = credentials;
2654
- this._properties = properties;
2655
- this._debug = debug;
2656
- this.agentRegistry = new AgentRegistry(this);
2657
- this.contextStore = new ContextStore(this);
2658
- this.suggestionEngine = new SuggestionEngine(this);
2659
- this.runHandler = new RunHandler(this);
2660
- this.stateManager = new StateManager(this);
2661
- this.threadStoreRegistry = new ThreadStoreRegistry(this);
2662
- this.agentRegistry.initialize(agents__unsafe_dev_only);
2663
- this.runHandler.initialize(tools);
2664
- this.suggestionEngine.initialize(suggestionsConfig);
2665
- this.stateManager.initialize();
2666
- this.agentRegistry.setRuntimeTransport(runtimeTransport);
2667
- this.agentRegistry.setRuntimeUrl(runtimeUrl);
2668
- this.previousAgentIds = new Set(Object.keys(agents__unsafe_dev_only));
2669
- this.subscribe({ onAgentsChanged: ({ agents }) => {
2670
- Object.values(agents).forEach((agent) => {
2671
- if (agent.agentId) this.stateManager.subscribeToAgent(agent);
2672
- });
2673
- const currentAgentIds = new Set(Object.keys(agents));
2674
- for (const agentId of Object.keys(this.threadStoreRegistry.getAll())) if (this.previousAgentIds.has(agentId) && !currentAgentIds.has(agentId)) try {
2675
- this.threadStoreRegistry.unregister(agentId);
2676
- } catch (err) {
2677
- console.error(`CopilotKitCore.onAgentsChanged: threadStoreRegistry.unregister failed for "${agentId}":`, err);
2678
- }
2679
- for (const agentId of this.previousAgentIds) if (!currentAgentIds.has(agentId)) try {
2680
- this.stateManager.unsubscribeFromAgent(agentId);
2681
- } catch (err) {
2682
- console.error(`CopilotKitCore.onAgentsChanged: stateManager.unsubscribeFromAgent failed for "${agentId}":`, err);
2683
- }
2684
- this.previousAgentIds = currentAgentIds;
2685
- } });
2686
- }
2687
- /**
2688
- * Internal method used by delegate classes and subclasses to notify subscribers
2689
- */
2690
- async notifySubscribers(handler, errorMessage) {
2691
- await Promise.all(Array.from(this.subscribers).map(async (subscriber) => {
2692
- try {
2693
- await handler(subscriber);
2694
- } catch (error) {
2695
- console.error(errorMessage, error);
2696
- }
2697
- }));
2654
+ /**
2655
+ * Declares a no-payload action config for `createActionGroup`.
2656
+ *
2657
+ * @example
2658
+ * ```ts
2659
+ * const actions = createActionGroup("User", {
2660
+ * reset: empty(),
2661
+ * });
2662
+ * ```
2663
+ */
2664
+ function empty() {
2665
+ return { kind: "empty" };
2666
+ }
2667
+ /**
2668
+ * Creates a namespaced group of typed action creators.
2669
+ *
2670
+ * Action types are formatted as: `[Source] actionName`.
2671
+ */
2672
+ function createActionGroup(source, config) {
2673
+ const group = {};
2674
+ for (const eventName of Object.keys(config)) {
2675
+ const eventConfig = config[eventName];
2676
+ if (!eventConfig) continue;
2677
+ const actionType = `[${source}] ${eventName}`;
2678
+ if (eventConfig.kind === "props") {
2679
+ group[eventName] = createTypedActionCreator(actionType, (payload) => ({ ...payload }));
2680
+ continue;
2681
+ }
2682
+ group[eventName] = createTypedActionCreator(actionType, () => ({}));
2698
2683
  }
2699
- /**
2700
- * Internal method used by delegate classes to emit errors
2701
- */
2702
- async emitError({ error, code, context = {} }) {
2703
- await this.notifySubscribers((subscriber) => subscriber.onError?.({
2704
- copilotkit: this,
2705
- error,
2706
- code,
2707
- context
2708
- }), "Subscriber onError error:");
2684
+ return group;
2685
+ }
2686
+ /**
2687
+ * Registers one reducer handler for one or more action creators.
2688
+ *
2689
+ * @throws Error when called without at least one action creator and reducer.
2690
+ */
2691
+ function on(...args) {
2692
+ if (args.length < 2) throw new Error("on requires at least one action creator and one reducer");
2693
+ const reducer = args[args.length - 1];
2694
+ return {
2695
+ creators: args.slice(0, -1),
2696
+ reducer
2697
+ };
2698
+ }
2699
+ /**
2700
+ * Creates a reducer from an initial state and `on(...)` handler entries.
2701
+ *
2702
+ * Unknown action types return the current state unchanged.
2703
+ */
2704
+ function createReducer(initialState, ...entries) {
2705
+ const reducerMap = /* @__PURE__ */ new Map();
2706
+ for (const entry of entries) for (const creator of entry.creators) {
2707
+ const handlers = reducerMap.get(creator.type) ?? [];
2708
+ handlers.push(entry.reducer);
2709
+ reducerMap.set(creator.type, handlers);
2709
2710
  }
2710
- /**
2711
- * Log a message to the console and emit an error to subscribers.
2712
- * Catches failures from `emitError` itself to prevent unhandled rejections.
2713
- */
2714
- logAndEmitError(message, params, logLevel = "error") {
2715
- console[logLevel](message, params.error);
2716
- this.emitError(params).catch((emitErr) => {
2717
- console.error(message + " — emitError itself failed:", emitErr);
2718
- });
2711
+ return (state, action) => {
2712
+ const currentState = state ?? initialState;
2713
+ const handlers = reducerMap.get(action.type);
2714
+ if (!handlers || handlers.length === 0) return currentState;
2715
+ let nextState = currentState;
2716
+ for (const handler of handlers) nextState = handler(nextState, action);
2717
+ return nextState;
2718
+ };
2719
+ }
2720
+ /**
2721
+ * Creates a selector that caches and reuses the last computed result
2722
+ * when all input references are unchanged.
2723
+ */
2724
+ function createSelector(...args) {
2725
+ if (args.length === 1) {
2726
+ const projector = args[0];
2727
+ let hasCached = false;
2728
+ let lastState;
2729
+ let lastResult;
2730
+ return (state) => {
2731
+ if (hasCached && state === lastState) return lastResult;
2732
+ lastState = state;
2733
+ lastResult = projector(state);
2734
+ hasCached = true;
2735
+ return lastResult;
2736
+ };
2719
2737
  }
2720
- /**
2721
- * Snapshot accessors
2722
- */
2738
+ const projector = args[args.length - 1];
2739
+ const selectors = args.slice(0, -1);
2740
+ let hasCached = false;
2741
+ let lastInputs = [];
2742
+ let lastResult;
2743
+ return (state) => {
2744
+ const inputs = selectors.map((selector) => selector(state));
2745
+ if (hasCached && inputs.length === lastInputs.length && inputs.every((value, index) => value === lastInputs[index])) return lastResult;
2746
+ lastInputs = inputs;
2747
+ lastResult = projector(...inputs);
2748
+ hasCached = true;
2749
+ return lastResult;
2750
+ };
2751
+ }
2752
+ /**
2753
+ * RxJS operator that maps state emissions through a selector and suppresses
2754
+ * unchanged projected values via reference equality.
2755
+ */
2756
+ function select(selector) {
2757
+ return (source$) => source$.pipe(map(selector), distinctUntilChanged());
2758
+ }
2759
+ /**
2760
+ * RxJS operator that filters an action stream by action creators and narrows
2761
+ * the output action type to the matched creator union.
2762
+ *
2763
+ * @throws Error when called without at least one action creator.
2764
+ */
2765
+ function ofType(...creators) {
2766
+ if (creators.length === 0) throw new Error("ofType requires at least one action creator");
2767
+ const actionTypes = new Set(creators.map((creator) => creator.type));
2768
+ return (source$) => {
2769
+ return source$.pipe(filter((action) => {
2770
+ return actionTypes.has(action.type);
2771
+ }));
2772
+ };
2773
+ }
2774
+ /**
2775
+ * Creates an effect descriptor consumed by `createStore`.
2776
+ */
2777
+ function createEffect(factory, options = {}) {
2778
+ if (options.dispatch === false) return {
2779
+ run: factory,
2780
+ dispatch: false
2781
+ };
2782
+ return {
2783
+ run: factory,
2784
+ dispatch: true
2785
+ };
2786
+ }
2787
+ /**
2788
+ * Creates a small observable store with reducer + effects.
2789
+ *
2790
+ * Behavior:
2791
+ * - `init()` starts effects and dispatches `@@micro-redux/init`.
2792
+ * - `stop()` dispatches `@@micro-redux/stop` and unsubscribes all effects.
2793
+ * - Effect action observation is scheduled on `asapScheduler` to avoid
2794
+ * synchronous re-entrancy in the effect loop.
2795
+ * - Any effect error triggers fail-fast teardown and errors both `actions$`
2796
+ * and `state$`.
2797
+ */
2798
+ function createStore(options) {
2799
+ const reducer = options.reducer;
2800
+ const effects = options.effects ?? [];
2801
+ let hasFatalError = false;
2802
+ let isRunning = false;
2803
+ let effectSubscriptions = new Subscription();
2804
+ let currentState = reducer(void 0, INTERNAL_BOOT_ACTION);
2805
+ const stateSubject = new BehaviorSubject(currentState);
2806
+ const actionsSubject = new Subject();
2807
+ const dispatchInternal = (action) => {
2808
+ if (hasFatalError) throw new Error("Store is in a failed state due to an effect error");
2809
+ currentState = reducer(currentState, action);
2810
+ stateSubject.next(currentState);
2811
+ actionsSubject.next(action);
2812
+ };
2813
+ const failFast = (error) => {
2814
+ if (hasFatalError) return;
2815
+ hasFatalError = true;
2816
+ isRunning = false;
2817
+ effectSubscriptions.unsubscribe();
2818
+ effectSubscriptions = new Subscription();
2819
+ actionsSubject.error(error);
2820
+ stateSubject.error(error);
2821
+ };
2822
+ const startEffects = () => {
2823
+ for (const effect of effects) {
2824
+ const scheduledActions$ = actionsSubject.asObservable().pipe(observeOn$1(asapScheduler));
2825
+ const state$ = stateSubject.asObservable();
2826
+ if (effect.dispatch) {
2827
+ const subscription = effect.run(scheduledActions$, state$).subscribe({
2828
+ next: (effectAction) => {
2829
+ if (hasFatalError) return;
2830
+ dispatchInternal(effectAction);
2831
+ },
2832
+ error: (error) => {
2833
+ failFast(error);
2834
+ }
2835
+ });
2836
+ effectSubscriptions.add(subscription);
2837
+ continue;
2838
+ }
2839
+ const subscription = effect.run(scheduledActions$, state$).subscribe({ error: (error) => {
2840
+ failFast(error);
2841
+ } });
2842
+ effectSubscriptions.add(subscription);
2843
+ }
2844
+ };
2845
+ return {
2846
+ dispatch(action) {
2847
+ dispatchInternal(action);
2848
+ },
2849
+ getState() {
2850
+ return currentState;
2851
+ },
2852
+ get state$() {
2853
+ return stateSubject.asObservable();
2854
+ },
2855
+ get actions$() {
2856
+ return actionsSubject.asObservable();
2857
+ },
2858
+ select(selector) {
2859
+ return stateSubject.asObservable().pipe(select(selector));
2860
+ },
2861
+ init() {
2862
+ if (hasFatalError || isRunning) return;
2863
+ isRunning = true;
2864
+ startEffects();
2865
+ if (hasFatalError) return;
2866
+ dispatchInternal({ type: INTERNAL_ACTION_TYPES.init });
2867
+ },
2868
+ stop() {
2869
+ if (hasFatalError || !isRunning) return;
2870
+ dispatchInternal({ type: INTERNAL_ACTION_TYPES.stop });
2871
+ effectSubscriptions.unsubscribe();
2872
+ effectSubscriptions = new Subscription();
2873
+ isRunning = false;
2874
+ }
2875
+ };
2876
+ }
2877
+
2878
+ //#endregion
2879
+ //#region src/memory-errors.ts
2880
+ /**
2881
+ * The memory error registry: the single source of truth for memory error
2882
+ * metadata. Keyed by `code` so a code maps to exactly one category/retryable
2883
+ * default.
2884
+ *
2885
+ * Categories/retryable rationale:
2886
+ * - List/credentials/mutation failures are `dependency` (the runtime/platform
2887
+ * the store calls failed). A 5xx is transient (`retryable: true`); a 4xx is
2888
+ * caller-caused, so the helpers below downgrade those to non-retryable.
2889
+ * - Timeouts are `dependency` and `retryable: true` (the request may simply be
2890
+ * slow).
2891
+ */
2892
+ const MEMORY_ERROR_REGISTRY = Object.freeze({
2893
+ MEMORY_LIST_FAILED: {
2894
+ code: "MEMORY_LIST_FAILED",
2895
+ category: "dependency",
2896
+ retryable: true,
2897
+ message: "Failed to fetch memories",
2898
+ docsPath: "docs/errors/memory.md#memory_list_failed"
2899
+ },
2900
+ MEMORY_CREDENTIALS_FAILED: {
2901
+ code: "MEMORY_CREDENTIALS_FAILED",
2902
+ category: "dependency",
2903
+ retryable: true,
2904
+ message: "Failed to fetch memory subscribe credentials",
2905
+ docsPath: "docs/errors/memory.md#memory_credentials_failed"
2906
+ },
2907
+ MEMORY_MUTATION_FAILED: {
2908
+ code: "MEMORY_MUTATION_FAILED",
2909
+ category: "dependency",
2910
+ retryable: true,
2911
+ message: "Memory mutation request failed",
2912
+ docsPath: "docs/errors/memory.md#memory_mutation_failed"
2913
+ },
2914
+ MEMORY_REQUEST_TIMEOUT: {
2915
+ code: "MEMORY_REQUEST_TIMEOUT",
2916
+ category: "dependency",
2917
+ retryable: true,
2918
+ message: "Request timed out",
2919
+ docsPath: "docs/errors/memory.md#memory_request_timeout"
2920
+ }
2921
+ });
2922
+ /**
2923
+ * An `Error` carrying a stable memory error `code`, `category`, and `retryable`
2924
+ * flag so consumers and observability can branch on it without string-matching
2925
+ * the message.
2926
+ *
2927
+ * It is a real `Error` subclass: `instanceof Error` is true, so the public
2928
+ * `MemoryState.error: Error | null` contract (and the `error: Error | null`
2929
+ * surfaced by `useMemories` / `injectMemories`) holds unchanged. Existing
2930
+ * consumers that only read `.message` keep working; the richer fields are
2931
+ * additive.
2932
+ */
2933
+ var MemoryError = class MemoryError extends Error {
2934
+ /** Stable, machine-readable error identifier. */
2935
+ code;
2936
+ /** Coarse error category for grouping/observability. */
2937
+ category;
2938
+ /** Whether retrying the operation could plausibly succeed. */
2939
+ retryable;
2940
+ /**
2941
+ * @param code - Registry code identifying this error.
2942
+ * @param options - Optional overrides:
2943
+ * - `message`: human-readable message (defaults to the registry message;
2944
+ * callers pass a status-bearing string to preserve prior wording).
2945
+ * - `retryable`: overrides the registry default (e.g. a 4xx list failure is
2946
+ * not retryable even though the code's default is).
2947
+ * - `cause`: the underlying error, preserved for debugging.
2948
+ */
2949
+ constructor(code, options) {
2950
+ const entry = MEMORY_ERROR_REGISTRY[code];
2951
+ super(options?.message ?? entry.message, { cause: options?.cause });
2952
+ this.name = "MemoryError";
2953
+ this.code = code;
2954
+ this.category = entry.category;
2955
+ this.retryable = options?.retryable ?? entry.retryable;
2956
+ Object.setPrototypeOf(this, MemoryError.prototype);
2957
+ }
2958
+ };
2959
+ /**
2960
+ * Narrows an HTTP status to whether the failure is worth retrying: a 5xx (or a
2961
+ * missing/0 status) is a transient dependency failure and retryable; a 4xx is
2962
+ * caller-caused and not. Used so a `MEMORY_LIST_FAILED`/`MEMORY_MUTATION_FAILED`
2963
+ * carries an accurate `retryable` flag derived from the response status.
2964
+ */
2965
+ function isRetryableStatus(status) {
2966
+ return !(Number.isInteger(status) && status >= 400 && status <= 499);
2967
+ }
2968
+
2969
+ //#endregion
2970
+ //#region src/memory.ts
2971
+ const MEMORIES_PATH = "/memories";
2972
+ const MEMORIES_SUBSCRIBE_PATH = "/memories/subscribe";
2973
+ const REQUEST_TIMEOUT_MS$1 = 15e3;
2974
+ /** Consecutive socket errors tolerated before the realtime stream gives up. */
2975
+ const MAX_SOCKET_RETRIES$1 = 5;
2976
+ /**
2977
+ * HTTP status codes that indicate memory routes are not configured (non-fatal).
2978
+ * 404/501 mean the route is absent/unimplemented; 422 is the runtime's
2979
+ * MISSING_INTELLIGENCE signal (intelligence not configured on the deployment —
2980
+ * see packages/runtime/.../handlers/intelligence/memories.ts). All three map to
2981
+ * the graceful "not available" locked-teaser path, not a hard error.
2982
+ */
2983
+ const ROUTE_UNAVAILABLE_STATUSES = new Set([
2984
+ 404,
2985
+ 422,
2986
+ 501
2987
+ ]);
2988
+ /**
2989
+ * Thrown when a memory route returns a 404/422/501 — treated as "not
2990
+ * configured" (422 is the runtime's MISSING_INTELLIGENCE signal).
2991
+ */
2992
+ var MemoryRouteUnavailableError = class extends Error {};
2993
+ const initialMemoryState = Object.freeze({
2994
+ memories: Object.freeze([]),
2995
+ isLoading: false,
2996
+ inFlightMutationCount: 0,
2997
+ error: null,
2998
+ context: null,
2999
+ sessionId: 0,
3000
+ available: true,
3001
+ realtimeStatus: "connecting"
3002
+ });
3003
+ const memoryAdapterEvents = createActionGroup("Memory Adapter", {
3004
+ started: empty(),
3005
+ stopped: empty(),
3006
+ contextChanged: props(),
3007
+ addRequested: props(),
3008
+ updateRequested: props(),
3009
+ removeRequested: props()
3010
+ });
3011
+ const memoryRestEvents = createActionGroup("Memory REST", {
3012
+ listRequested: props(),
3013
+ listSucceeded: props(),
3014
+ listFailed: props(),
3015
+ listUnavailable: props(),
3016
+ mutationFinished: props(),
3017
+ credentialsRequested: props(),
3018
+ credentialsSucceeded: props(),
3019
+ credentialsFailed: props(),
3020
+ credentialsUnavailable: props()
3021
+ });
3022
+ const memoryDomainEvents = createActionGroup("Memory Domain", {
3023
+ memoryUpserted: props(),
3024
+ memoryInvalidated: props(),
3025
+ realtimeConnecting: props(),
3026
+ realtimeConnected: props(),
3027
+ realtimeUnavailable: props()
3028
+ });
3029
+ /** Projects a wire memory to the public {@link Memory} shape (drops tenant ids). */
3030
+ function toMemory(memory) {
3031
+ return {
3032
+ id: memory.id,
3033
+ kind: memory.kind,
3034
+ scope: memory.scope,
3035
+ content: memory.content,
3036
+ sourceThreadIds: memory.sourceThreadIds,
3037
+ invalidatedAt: memory.invalidatedAt
3038
+ };
3039
+ }
3040
+ /**
3041
+ * Maps a realtime `memory_metadata` event to the domain action that applies it:
3042
+ * `created`/`updated` upsert the projected memory, `invalidated` removes it by
3043
+ * id. Carries the current `sessionId` so the reducer's session guard can drop
3044
+ * deltas left over from a previous context.
3045
+ */
3046
+ function mapMemoryMetadataEvent(event, sessionId) {
3047
+ if (event.operation === "invalidated") return memoryDomainEvents.memoryInvalidated({
3048
+ sessionId,
3049
+ memoryId: event.invalidated.id
3050
+ });
3051
+ return memoryDomainEvents.memoryUpserted({
3052
+ sessionId,
3053
+ memory: toMemory(event.memory)
3054
+ });
3055
+ }
3056
+ /**
3057
+ * Inserts or replaces a memory by id. A new memory is prepended (newest first,
3058
+ * matching the REST list's `created_at DESC` ordering without needing a date
3059
+ * field on the public projection); an existing memory is replaced in place.
3060
+ */
3061
+ function upsertMemory(memories, memory) {
3062
+ const existingIndex = memories.findIndex((item) => item.id === memory.id);
3063
+ if (existingIndex === -1) return [memory, ...memories];
3064
+ const next = [...memories];
3065
+ next[existingIndex] = memory;
3066
+ return next;
3067
+ }
3068
+ const memoryReducer = createReducer(initialMemoryState, on(memoryAdapterEvents.contextChanged, (state, { context }) => ({
3069
+ ...state,
3070
+ context,
3071
+ sessionId: state.sessionId + 1,
3072
+ memories: [],
3073
+ isLoading: Boolean(context),
3074
+ inFlightMutationCount: 0,
3075
+ error: null,
3076
+ available: true,
3077
+ realtimeStatus: "connecting"
3078
+ })), on(memoryAdapterEvents.stopped, (state) => ({
3079
+ ...state,
3080
+ memories: [],
3081
+ isLoading: false,
3082
+ inFlightMutationCount: 0,
3083
+ error: null,
3084
+ available: true,
3085
+ realtimeStatus: "connecting"
3086
+ })), on(memoryRestEvents.listRequested, (state, { sessionId }) => {
3087
+ if (sessionId !== state.sessionId || !state.context) return state;
3088
+ return {
3089
+ ...state,
3090
+ isLoading: true,
3091
+ error: null,
3092
+ available: true
3093
+ };
3094
+ }), on(memoryRestEvents.listFailed, (state, { sessionId, error }) => {
3095
+ if (sessionId !== state.sessionId) return state;
3096
+ return {
3097
+ ...state,
3098
+ isLoading: false,
3099
+ error
3100
+ };
3101
+ }), on(memoryRestEvents.listUnavailable, (state, { sessionId }) => {
3102
+ if (sessionId !== state.sessionId) return state;
3103
+ return {
3104
+ ...state,
3105
+ memories: [],
3106
+ isLoading: false,
3107
+ error: null,
3108
+ available: false
3109
+ };
3110
+ }), on(memoryAdapterEvents.addRequested, memoryAdapterEvents.updateRequested, memoryAdapterEvents.removeRequested, (state) => ({
3111
+ ...state,
3112
+ inFlightMutationCount: state.inFlightMutationCount + 1
3113
+ })), on(memoryRestEvents.mutationFinished, (state, { outcome }) => {
3114
+ if (outcome.sessionId !== state.sessionId) return state;
3115
+ return {
3116
+ ...state,
3117
+ inFlightMutationCount: Math.max(0, state.inFlightMutationCount - 1),
3118
+ error: outcome.ok ? null : outcome.error
3119
+ };
3120
+ }), on(memoryRestEvents.listSucceeded, (state, { sessionId, memories }) => {
3121
+ if (sessionId !== state.sessionId) return state;
3122
+ return {
3123
+ ...state,
3124
+ memories,
3125
+ isLoading: false,
3126
+ error: null,
3127
+ available: true
3128
+ };
3129
+ }), on(memoryDomainEvents.memoryUpserted, (state, { sessionId, memory }) => {
3130
+ if (sessionId !== state.sessionId) return state;
3131
+ return {
3132
+ ...state,
3133
+ memories: upsertMemory(state.memories, memory)
3134
+ };
3135
+ }), on(memoryDomainEvents.memoryInvalidated, (state, { sessionId, memoryId }) => {
3136
+ if (sessionId !== state.sessionId) return state;
3137
+ return {
3138
+ ...state,
3139
+ memories: state.memories.filter((memory) => memory.id !== memoryId)
3140
+ };
3141
+ }), on(memoryDomainEvents.realtimeConnecting, (state, { sessionId }) => {
3142
+ if (sessionId !== state.sessionId) return state;
3143
+ return {
3144
+ ...state,
3145
+ realtimeStatus: "connecting"
3146
+ };
3147
+ }), on(memoryDomainEvents.realtimeConnected, (state, { sessionId }) => {
3148
+ if (sessionId !== state.sessionId) return state;
3149
+ return {
3150
+ ...state,
3151
+ realtimeStatus: "connected"
3152
+ };
3153
+ }), on(memoryDomainEvents.realtimeUnavailable, (state, { sessionId }) => {
3154
+ if (sessionId !== state.sessionId) return state;
3155
+ return {
3156
+ ...state,
3157
+ realtimeStatus: "unavailable"
3158
+ };
3159
+ }));
3160
+ const selectMemories = createSelector((state) => state.memories);
3161
+ const selectMemoriesIsLoading = createSelector((state) => state.isLoading);
3162
+ const selectMemoriesError = createSelector((state) => state.error);
3163
+ /**
3164
+ * Reports whether at least one mutation is in flight. Derived from
3165
+ * `inFlightMutationCount` so concurrent mutations stay correct: the boolean
3166
+ * only clears once every in-flight mutation has settled.
3167
+ */
3168
+ const selectMemoriesIsMutating = createSelector((state) => state.inFlightMutationCount > 0);
3169
+ /**
3170
+ * Reports whether the memory routes are available. Becomes `false` after a
3171
+ * 404, 422 (the runtime's MISSING_INTELLIGENCE signal), or 501 response during
3172
+ * an auto-activation read; defaults to `true`.
3173
+ */
3174
+ const selectMemoriesAvailable = createSelector((state) => state.available);
3175
+ /**
3176
+ * Reports the realtime-connection health (see {@link MemoryRealtimeStatus}).
3177
+ * Distinct from `available`/`error`: it reflects ONLY the live socket/channel,
3178
+ * so the UI can suppress a "live" indicator once realtime permanently dies even
3179
+ * while the REST list route stays healthy. Defaults to `"connecting"`.
3180
+ */
3181
+ const selectMemoriesRealtimeStatus = createSelector((state) => state.realtimeStatus);
3182
+ const MEMORY_METADATA_EVENT = "memory_metadata";
3183
+ function memoryFromFetch(input, init) {
3184
+ return fromFetch(input, init);
3185
+ }
3186
+ /**
3187
+ * Fetches the memory snapshot and maps it to a success/failure action. Keeps
3188
+ * only user-scoped memories (v1 surfaces user scope only; the realtime stream
3189
+ * is user-scoped too), so project-scoped rows visible to the caller are
3190
+ * dropped from the store.
3191
+ */
3192
+ function createMemoryFetchObservable(environment, context, sessionId) {
3193
+ return defer(() => {
3194
+ const qs = context.includeInvalidated ? "?includeInvalidated=true" : "";
3195
+ return memoryFromFetch(`${context.runtimeUrl}${MEMORIES_PATH}${qs}`, {
3196
+ selector: (response) => {
3197
+ if (!response.ok) {
3198
+ if (ROUTE_UNAVAILABLE_STATUSES.has(response.status)) throw new MemoryRouteUnavailableError(String(response.status));
3199
+ throw new MemoryError("MEMORY_LIST_FAILED", {
3200
+ message: `Failed to fetch memories: ${response.status}`,
3201
+ retryable: isRetryableStatus(response.status)
3202
+ });
3203
+ }
3204
+ return response.json();
3205
+ },
3206
+ fetch: environment.fetch,
3207
+ method: "GET",
3208
+ headers: { ...context.headers }
3209
+ }).pipe(timeout({
3210
+ first: REQUEST_TIMEOUT_MS$1,
3211
+ with: () => {
3212
+ throw new MemoryError("MEMORY_REQUEST_TIMEOUT");
3213
+ }
3214
+ }), map((data) => memoryRestEvents.listSucceeded({
3215
+ sessionId,
3216
+ memories: data.memories.filter((memory) => memory.scope === "user")
3217
+ })), catchError((error) => {
3218
+ if (error instanceof MemoryRouteUnavailableError) return of(memoryRestEvents.listUnavailable({ sessionId }));
3219
+ return of(memoryRestEvents.listFailed({
3220
+ sessionId,
3221
+ error: error instanceof Error ? error : new Error(String(error))
3222
+ }));
3223
+ }));
3224
+ });
3225
+ }
3226
+ /**
3227
+ * Fetches join credentials from the `/memories/subscribe` endpoint and maps
3228
+ * the response to a success/failure action. Requires both `joinToken` and
3229
+ * `joinCode` to be non-empty strings; throws otherwise so the `catchError`
3230
+ * path emits `credentialsFailed`.
3231
+ */
3232
+ function createMemoryCredentialsFetchObservable(environment, context, sessionId) {
3233
+ return defer(() => memoryFromFetch(`${context.runtimeUrl}${MEMORIES_SUBSCRIBE_PATH}`, {
3234
+ selector: async (response) => {
3235
+ if (!response.ok) {
3236
+ if (ROUTE_UNAVAILABLE_STATUSES.has(response.status)) throw new MemoryRouteUnavailableError(String(response.status));
3237
+ throw new MemoryError("MEMORY_CREDENTIALS_FAILED", {
3238
+ message: `Failed to fetch memory subscribe credentials: ${response.status}`,
3239
+ retryable: isRetryableStatus(response.status)
3240
+ });
3241
+ }
3242
+ return response.json();
3243
+ },
3244
+ fetch: environment.fetch,
3245
+ method: "POST",
3246
+ headers: {
3247
+ ...context.headers,
3248
+ "Content-Type": "application/json"
3249
+ },
3250
+ body: JSON.stringify({})
3251
+ }).pipe(timeout({
3252
+ first: REQUEST_TIMEOUT_MS$1,
3253
+ with: () => {
3254
+ throw new MemoryError("MEMORY_REQUEST_TIMEOUT");
3255
+ }
3256
+ }), map((data) => {
3257
+ if (typeof data.joinToken !== "string" || data.joinToken.length === 0) throw new Error("missing joinToken");
3258
+ if (typeof data.joinCode !== "string" || data.joinCode.length === 0) throw new Error("missing joinCode");
3259
+ return memoryRestEvents.credentialsSucceeded({
3260
+ sessionId,
3261
+ joinToken: data.joinToken,
3262
+ joinCode: data.joinCode
3263
+ });
3264
+ }), catchError((error) => {
3265
+ if (error instanceof MemoryRouteUnavailableError) return of(memoryRestEvents.credentialsUnavailable({ sessionId }));
3266
+ return of(memoryRestEvents.credentialsFailed({
3267
+ sessionId,
3268
+ error: error instanceof Error ? error : new Error(String(error))
3269
+ }));
3270
+ })));
3271
+ }
3272
+ /** Projects a REST mutation response to the public {@link Memory} shape. */
3273
+ function responseToMemory(data) {
3274
+ return {
3275
+ id: data.id,
3276
+ kind: data.kind,
3277
+ scope: data.scope,
3278
+ content: data.content,
3279
+ sourceThreadIds: data.sourceThreadIds,
3280
+ invalidatedAt: data.invalidatedAt
3281
+ };
3282
+ }
3283
+ const MEMORY_KINDS = new Set([
3284
+ "topical",
3285
+ "episodic",
3286
+ "operational"
3287
+ ]);
3288
+ const MEMORY_SCOPES = new Set(["user", "project"]);
3289
+ /**
3290
+ * Validates the required fields of a create/supersede REST response before it is
3291
+ * projected to a {@link Memory}. Returns an `Error` describing the first
3292
+ * missing/invalid field, or `null` when the body is well-formed. Without this a
3293
+ * malformed 200 body yields a Memory with undefined fields that gets upserted.
3294
+ */
3295
+ function validateMutationResponse(data) {
3296
+ if (!data || typeof data !== "object") return /* @__PURE__ */ new Error("memory mutation response missing/invalid body");
3297
+ if (typeof data.id !== "string" || data.id.length === 0) return /* @__PURE__ */ new Error("memory mutation response missing/invalid id");
3298
+ if (typeof data.kind !== "string" || !MEMORY_KINDS.has(data.kind)) return /* @__PURE__ */ new Error("memory mutation response missing/invalid kind");
3299
+ if (typeof data.scope !== "string" || !MEMORY_SCOPES.has(data.scope)) return /* @__PURE__ */ new Error("memory mutation response missing/invalid scope");
3300
+ if (typeof data.content !== "string") return /* @__PURE__ */ new Error("memory mutation response missing/invalid content");
3301
+ if (!Array.isArray(data.sourceThreadIds) || !data.sourceThreadIds.every((id) => typeof id === "string")) return /* @__PURE__ */ new Error("memory mutation response missing/invalid sourceThreadIds");
3302
+ if (data.invalidatedAt !== null && typeof data.invalidatedAt !== "string") return /* @__PURE__ */ new Error("memory mutation response missing/invalid invalidatedAt");
3303
+ return null;
3304
+ }
3305
+ /**
3306
+ * Request body for create/supersede. `scope` is forwarded only when the caller
3307
+ * supplies it: the platform owns the default (`scope` defaults to `"user"` in
3308
+ * the `/api/memories` schema), so the store does not second-guess it here —
3309
+ * one default, at the contract owner. `sourceThreadIds` defaults to `[]`.
3310
+ */
3311
+ function toMutationBody(input) {
3312
+ return {
3313
+ content: input.content,
3314
+ kind: input.kind,
3315
+ ...input.scope !== void 0 ? { scope: input.scope } : {},
3316
+ sourceThreadIds: input.sourceThreadIds ?? []
3317
+ };
3318
+ }
3319
+ /**
3320
+ * Builds the actions a successful mutation dispatches (server-authoritative):
3321
+ * the REST response is applied to local state immediately, and the realtime
3322
+ * event reconciles idempotently (upsert-by-id). `mutationFinished` resolves the
3323
+ * caller's promise.
3324
+ */
3325
+ function buildMutationSuccessActions(request, data) {
3326
+ const { requestId, sessionId } = request;
3327
+ if (request.kind === "remove") return [memoryDomainEvents.memoryInvalidated({
3328
+ sessionId,
3329
+ memoryId: request.id
3330
+ }), memoryRestEvents.mutationFinished({ outcome: {
3331
+ requestId,
3332
+ sessionId,
3333
+ ok: true,
3334
+ memory: null
3335
+ } })];
3336
+ const validationError = validateMutationResponse(data);
3337
+ if (validationError) return [memoryRestEvents.mutationFinished({ outcome: {
3338
+ requestId,
3339
+ sessionId,
3340
+ ok: false,
3341
+ error: validationError
3342
+ } })];
3343
+ const memory = responseToMemory(data);
3344
+ if (request.kind === "update") {
3345
+ const retiredId = data.retiredId;
3346
+ if (typeof retiredId !== "string" || retiredId.length === 0) return [memoryRestEvents.mutationFinished({ outcome: {
3347
+ requestId,
3348
+ sessionId,
3349
+ ok: false,
3350
+ error: /* @__PURE__ */ new Error("supersede response missing retiredId")
3351
+ } })];
3352
+ return [
3353
+ memoryDomainEvents.memoryInvalidated({
3354
+ sessionId,
3355
+ memoryId: retiredId
3356
+ }),
3357
+ memoryDomainEvents.memoryUpserted({
3358
+ sessionId,
3359
+ memory
3360
+ }),
3361
+ memoryRestEvents.mutationFinished({ outcome: {
3362
+ requestId,
3363
+ sessionId,
3364
+ ok: true,
3365
+ memory
3366
+ } })
3367
+ ];
3368
+ }
3369
+ return [memoryDomainEvents.memoryUpserted({
3370
+ sessionId,
3371
+ memory
3372
+ }), memoryRestEvents.mutationFinished({ outcome: {
3373
+ requestId,
3374
+ sessionId,
3375
+ ok: true,
3376
+ memory
3377
+ } })];
3378
+ }
3379
+ /** Performs a create/supersede/retire HTTP call and emits its resulting actions. */
3380
+ function createMemoryMutationObservable(environment, context, request) {
3381
+ const method = request.kind === "add" ? "POST" : request.kind === "update" ? "PATCH" : "DELETE";
3382
+ const path = request.kind === "add" ? MEMORIES_PATH : `${MEMORIES_PATH}/${encodeURIComponent(request.id)}`;
3383
+ return defer(() => memoryFromFetch(`${context.runtimeUrl}${path}`, {
3384
+ selector: async (response) => {
3385
+ if (!response.ok) throw new MemoryError("MEMORY_MUTATION_FAILED", {
3386
+ message: `Request failed: ${response.status}`,
3387
+ retryable: isRetryableStatus(response.status)
3388
+ });
3389
+ return request.kind === "remove" ? null : await response.json();
3390
+ },
3391
+ fetch: environment.fetch,
3392
+ method,
3393
+ headers: {
3394
+ ...context.headers,
3395
+ "Content-Type": "application/json"
3396
+ },
3397
+ body: request.kind === "remove" ? void 0 : JSON.stringify(request.body)
3398
+ }).pipe(timeout({
3399
+ first: REQUEST_TIMEOUT_MS$1,
3400
+ with: () => {
3401
+ throw new MemoryError("MEMORY_REQUEST_TIMEOUT");
3402
+ }
3403
+ }), mergeMap((data) => of(...buildMutationSuccessActions(request, data))), catchError((error) => of(memoryRestEvents.mutationFinished({ outcome: {
3404
+ requestId: request.requestId,
3405
+ sessionId: request.sessionId,
3406
+ ok: false,
3407
+ error: error instanceof Error ? error : new Error(String(error))
3408
+ } })))));
3409
+ }
3410
+ /**
3411
+ * Creates the framework-agnostic memory store: a REST snapshot on `setContext`,
3412
+ * server-authoritative add/update/remove mutations, and realtime
3413
+ * `memory_metadata` deltas off the injected `user_meta` event source — all
3414
+ * reduced into observable state.
3415
+ */
3416
+ function createMemoryStore(environment) {
3417
+ let memoryRequestId = 0;
3418
+ const createMemoryRequestId = () => {
3419
+ memoryRequestId += 1;
3420
+ return `memory-request-${memoryRequestId}`;
3421
+ };
3422
+ const bootstrapEffect = createEffect((actions$, state$) => actions$.pipe(ofType(memoryAdapterEvents.contextChanged), withLatestFrom(state$), filter(([, state]) => Boolean(state.context)), map(([, state]) => memoryRestEvents.listRequested({ sessionId: state.sessionId }))));
3423
+ const credentialsBootstrapEffect = createEffect((actions$, state$) => actions$.pipe(ofType(memoryAdapterEvents.contextChanged), withLatestFrom(state$), filter(([, state]) => Boolean(state.context)), map(([, state]) => memoryRestEvents.credentialsRequested({ sessionId: state.sessionId }))));
3424
+ const credentialsFetchEffect = createEffect((actions$, state$) => actions$.pipe(ofType(memoryRestEvents.credentialsRequested), switchMap$1((action) => state$.pipe(map((state) => state.context), filter((context) => Boolean(context)), take(1), map((context) => ({
3425
+ action,
3426
+ context
3427
+ })), takeUntil(actions$.pipe(ofType(memoryAdapterEvents.contextChanged, memoryAdapterEvents.stopped))), switchMap$1(({ action: currentAction, context }) => createMemoryCredentialsFetchObservable(environment, context, currentAction.sessionId))))));
3428
+ const store = createStore({
3429
+ reducer: memoryReducer,
3430
+ effects: [
3431
+ bootstrapEffect,
3432
+ credentialsBootstrapEffect,
3433
+ createEffect((actions$, state$) => actions$.pipe(ofType(memoryRestEvents.listRequested), switchMap$1((action) => state$.pipe(map((state) => state.context), filter((context) => Boolean(context)), take(1), map((context) => ({
3434
+ action,
3435
+ context
3436
+ })), takeUntil(actions$.pipe(ofType(memoryAdapterEvents.contextChanged, memoryAdapterEvents.stopped))), switchMap$1(({ action: currentAction, context }) => createMemoryFetchObservable(environment, context, currentAction.sessionId)))))),
3437
+ credentialsFetchEffect,
3438
+ createEffect((actions$, state$) => actions$.pipe(ofType(memoryAdapterEvents.addRequested, memoryAdapterEvents.updateRequested, memoryAdapterEvents.removeRequested), withLatestFrom(state$), mergeMap(([action, state]) => {
3439
+ const context = state.context;
3440
+ const sessionId = state.sessionId;
3441
+ if (!context?.runtimeUrl) return of(memoryRestEvents.mutationFinished({ outcome: {
3442
+ requestId: action.requestId,
3443
+ sessionId,
3444
+ ok: false,
3445
+ error: /* @__PURE__ */ new Error("Runtime URL is not configured")
3446
+ } }));
3447
+ if (memoryAdapterEvents.addRequested.match(action)) return createMemoryMutationObservable(environment, context, {
3448
+ requestId: action.requestId,
3449
+ sessionId,
3450
+ kind: "add",
3451
+ body: toMutationBody(action.input)
3452
+ });
3453
+ if (memoryAdapterEvents.updateRequested.match(action)) return createMemoryMutationObservable(environment, context, {
3454
+ requestId: action.requestId,
3455
+ sessionId,
3456
+ kind: "update",
3457
+ id: action.id,
3458
+ body: toMutationBody(action.changes)
3459
+ });
3460
+ return createMemoryMutationObservable(environment, context, {
3461
+ requestId: action.requestId,
3462
+ sessionId,
3463
+ kind: "remove",
3464
+ id: action.id
3465
+ });
3466
+ }))),
3467
+ createEffect((actions$, state$) => actions$.pipe(ofType(memoryRestEvents.credentialsSucceeded), withLatestFrom(state$), filter(([action, state]) => {
3468
+ return action.sessionId === state.sessionId && Boolean(state.context?.wsUrl);
3469
+ }), switchMap$1(([action, state]) => {
3470
+ const context = state.context;
3471
+ const { joinToken, joinCode } = action;
3472
+ const sessionId = action.sessionId;
3473
+ const shutdown$ = actions$.pipe(ofType(memoryAdapterEvents.contextChanged, memoryAdapterEvents.stopped));
3474
+ return defer(() => {
3475
+ const socket$ = ɵphoenixSocket$({
3476
+ url: context.wsUrl,
3477
+ options: {
3478
+ params: { join_token: joinToken },
3479
+ reconnectAfterMs: phoenixExponentialBackoff(100, 1e4),
3480
+ rejoinAfterMs: phoenixExponentialBackoff(1e3, 3e4)
3481
+ }
3482
+ }).pipe(shareReplay({
3483
+ bufferSize: 1,
3484
+ refCount: true
3485
+ }));
3486
+ const channel$ = ɵphoenixChannel$({
3487
+ socket$,
3488
+ topic: `user_meta:memories:${joinCode}`
3489
+ }).pipe(shareReplay({
3490
+ bufferSize: 1,
3491
+ refCount: true
3492
+ }));
3493
+ const fatalSocketShutdown$ = ɵobservePhoenixSocketHealth$(ɵobservePhoenixSocketSignals$(socket$).pipe(share()), MAX_SOCKET_RETRIES$1).pipe(catchError(() => {
3494
+ console.warn(`[memory] WebSocket failed after ${MAX_SOCKET_RETRIES$1} attempts, giving up`);
3495
+ return of(void 0);
3496
+ }), share());
3497
+ const fatalStatus$ = fatalSocketShutdown$.pipe(map(() => memoryDomainEvents.realtimeUnavailable({ sessionId })));
3498
+ const metadata$ = channel$.pipe(switchMap$1(({ channel }) => ɵobservePhoenixEvent$(channel, MEMORY_METADATA_EVENT)), map((event) => mapMemoryMetadataEvent(event, action.sessionId)));
3499
+ const join$ = ɵjoinPhoenixChannel$(channel$).pipe(concatWith(of(memoryDomainEvents.realtimeConnected({ sessionId }))), catchError((error) => {
3500
+ console.warn(`[memory] failed to join user_meta:memories:${joinCode}`, error);
3501
+ return of(memoryDomainEvents.realtimeUnavailable({ sessionId }));
3502
+ }));
3503
+ return merge(of(memoryDomainEvents.realtimeConnecting({ sessionId })), metadata$, join$, fatalStatus$).pipe(takeUntil(merge(shutdown$, fatalSocketShutdown$.pipe(observeOn(asapScheduler)))), finalize(() => {}));
3504
+ });
3505
+ })))
3506
+ ]
3507
+ });
3508
+ function trackMutation(dispatchAction, extract) {
3509
+ const { requestId } = dispatchAction;
3510
+ const resultPromise = firstValueFrom(merge(store.actions$.pipe(ofType(memoryRestEvents.mutationFinished), filter((action) => action.outcome.requestId === requestId), map((action) => action.outcome)), store.actions$.pipe(ofType(memoryAdapterEvents.stopped), map(() => ({
3511
+ requestId,
3512
+ sessionId: store.getState().sessionId,
3513
+ ok: false,
3514
+ error: /* @__PURE__ */ new Error("Memory store stopped before mutation completed")
3515
+ })))).pipe(take(1))).then((outcome) => {
3516
+ if (!outcome.ok) throw outcome.error;
3517
+ return extract(outcome);
3518
+ });
3519
+ store.dispatch(dispatchAction);
3520
+ return resultPromise;
3521
+ }
3522
+ return {
3523
+ start() {
3524
+ store.init();
3525
+ store.dispatch(memoryAdapterEvents.started());
3526
+ },
3527
+ stop() {
3528
+ store.dispatch(memoryAdapterEvents.stopped());
3529
+ store.stop();
3530
+ },
3531
+ setContext(context) {
3532
+ store.dispatch(memoryAdapterEvents.contextChanged({ context }));
3533
+ },
3534
+ refresh() {
3535
+ const { sessionId, context } = store.getState();
3536
+ if (!context) return Promise.resolve();
3537
+ const done = firstValueFrom(merge(store.actions$.pipe(ofType(memoryRestEvents.listSucceeded, memoryRestEvents.listFailed, memoryRestEvents.listUnavailable), filter((action) => action.sessionId === sessionId)), store.actions$.pipe(ofType(memoryAdapterEvents.stopped)), store.actions$.pipe(ofType(memoryAdapterEvents.contextChanged))).pipe(take(1))).then((action) => {
3538
+ if (memoryRestEvents.listFailed.match(action)) throw action.error;
3539
+ if (memoryAdapterEvents.stopped.match(action)) throw new Error("Memory store stopped before refresh completed");
3540
+ if (memoryAdapterEvents.contextChanged.match(action)) return;
3541
+ });
3542
+ store.dispatch(memoryRestEvents.listRequested({ sessionId }));
3543
+ return done;
3544
+ },
3545
+ addMemory(input) {
3546
+ return trackMutation(memoryAdapterEvents.addRequested({
3547
+ requestId: createMemoryRequestId(),
3548
+ input
3549
+ }), (outcome) => {
3550
+ if (outcome.memory == null) throw new Error("add resolved without a memory");
3551
+ return outcome.memory;
3552
+ });
3553
+ },
3554
+ updateMemory(id, changes) {
3555
+ return trackMutation(memoryAdapterEvents.updateRequested({
3556
+ requestId: createMemoryRequestId(),
3557
+ id,
3558
+ changes
3559
+ }), (outcome) => {
3560
+ if (outcome.memory == null) throw new Error("update resolved without a memory");
3561
+ return outcome.memory;
3562
+ });
3563
+ },
3564
+ removeMemory(id) {
3565
+ return trackMutation(memoryAdapterEvents.removeRequested({
3566
+ requestId: createMemoryRequestId(),
3567
+ id
3568
+ }), () => void 0);
3569
+ },
3570
+ getState() {
3571
+ return store.getState();
3572
+ },
3573
+ getServerState() {
3574
+ return initialMemoryState;
3575
+ },
3576
+ select: store.select.bind(store)
3577
+ };
3578
+ }
3579
+ const ɵmemoryAdapterEvents = memoryAdapterEvents;
3580
+ const ɵmemoryRestEvents = memoryRestEvents;
3581
+ const ɵmemoryDomainEvents = memoryDomainEvents;
3582
+ const ɵmemoryReducer = memoryReducer;
3583
+ const ɵmapMemoryMetadataEvent = mapMemoryMetadataEvent;
3584
+ const ɵselectMemories = selectMemories;
3585
+ const ɵselectMemoriesIsLoading = selectMemoriesIsLoading;
3586
+ const ɵselectMemoriesError = selectMemoriesError;
3587
+ const ɵselectMemoriesIsMutating = selectMemoriesIsMutating;
3588
+ const ɵselectMemoriesAvailable = selectMemoriesAvailable;
3589
+ const ɵselectMemoriesRealtimeStatus = selectMemoriesRealtimeStatus;
3590
+
3591
+ //#endregion
3592
+ //#region src/core/core.ts
3593
+ let CopilotKitCoreErrorCode = /* @__PURE__ */ function(CopilotKitCoreErrorCode) {
3594
+ CopilotKitCoreErrorCode["RUNTIME_INFO_FETCH_FAILED"] = "runtime_info_fetch_failed";
3595
+ CopilotKitCoreErrorCode["AGENT_CONNECT_FAILED"] = "agent_connect_failed";
3596
+ CopilotKitCoreErrorCode["AGENT_RUN_FAILED"] = "agent_run_failed";
3597
+ CopilotKitCoreErrorCode["AGENT_RUN_FAILED_EVENT"] = "agent_run_failed_event";
3598
+ CopilotKitCoreErrorCode["AGENT_RUN_ERROR_EVENT"] = "agent_run_error_event";
3599
+ CopilotKitCoreErrorCode["TOOL_ARGUMENT_PARSE_FAILED"] = "tool_argument_parse_failed";
3600
+ CopilotKitCoreErrorCode["TOOL_HANDLER_FAILED"] = "tool_handler_failed";
3601
+ CopilotKitCoreErrorCode["TOOL_NOT_FOUND"] = "tool_not_found";
3602
+ CopilotKitCoreErrorCode["AGENT_NOT_FOUND"] = "agent_not_found";
3603
+ /**
3604
+ * Emitted when an agent run fails because the thread is already locked
3605
+ * by another active run.
3606
+ *
3607
+ * @example
3608
+ * ```tsx
3609
+ * <CopilotKitProvider
3610
+ * onError={({ code, error, context }) => {
3611
+ * if (code === "agent_thread_locked") {
3612
+ * // Show "Agent is busy, retry?" UI
3613
+ * }
3614
+ * }}
3615
+ * />
3616
+ * ```
3617
+ */
3618
+ CopilotKitCoreErrorCode["AGENT_THREAD_LOCKED"] = "agent_thread_locked";
3619
+ CopilotKitCoreErrorCode["TRANSCRIPTION_FAILED"] = "transcription_failed";
3620
+ CopilotKitCoreErrorCode["TRANSCRIPTION_SERVICE_NOT_CONFIGURED"] = "transcription_service_not_configured";
3621
+ CopilotKitCoreErrorCode["TRANSCRIPTION_INVALID_AUDIO"] = "transcription_invalid_audio";
3622
+ CopilotKitCoreErrorCode["TRANSCRIPTION_RATE_LIMITED"] = "transcription_rate_limited";
3623
+ CopilotKitCoreErrorCode["TRANSCRIPTION_AUTH_FAILED"] = "transcription_auth_failed";
3624
+ CopilotKitCoreErrorCode["TRANSCRIPTION_NETWORK_ERROR"] = "transcription_network_error";
3625
+ CopilotKitCoreErrorCode["SUBSCRIBER_CALLBACK_FAILED"] = "subscriber_callback_failed";
3626
+ return CopilotKitCoreErrorCode;
3627
+ }({});
3628
+ /**
3629
+ * Runtime allowlist derived from {@link SUBSCRIBE_TO_AGENT_KEYS}. Hoisted
3630
+ * to module scope so the Set is allocated once, not per-subscription.
3631
+ */
3632
+ const ALLOWED_KEYS = new Set([
3633
+ "onMessagesChanged",
3634
+ "onStateChanged",
3635
+ "onRunInitialized",
3636
+ "onRunFinalized",
3637
+ "onRunFailed",
3638
+ "onRunErrorEvent"
3639
+ ]);
3640
+ let CopilotKitCoreRuntimeConnectionStatus = /* @__PURE__ */ function(CopilotKitCoreRuntimeConnectionStatus) {
3641
+ CopilotKitCoreRuntimeConnectionStatus["Disconnected"] = "disconnected";
3642
+ CopilotKitCoreRuntimeConnectionStatus["Connected"] = "connected";
3643
+ CopilotKitCoreRuntimeConnectionStatus["Connecting"] = "connecting";
3644
+ CopilotKitCoreRuntimeConnectionStatus["Error"] = "error";
3645
+ return CopilotKitCoreRuntimeConnectionStatus;
3646
+ }({});
3647
+ /**
3648
+ * Normalize a header map to the internal invariant: a `Record<string, string>`
3649
+ * with no `null`/`undefined` values. Entries whose value is `null`/`undefined`
3650
+ * are dropped (this is how a header is cleared). Shared by the constructor and
3651
+ * `setHeaders` so both write paths into `_headers` enforce the same invariant.
3652
+ */
3653
+ function normalizeHeaders(headers) {
3654
+ return Object.fromEntries(Object.entries(headers).filter((entry) => entry[1] != null));
3655
+ }
3656
+ var CopilotKitCore = class {
3657
+ _headers;
3658
+ _credentials;
3659
+ _properties;
3660
+ _defaultThrottleMs;
3661
+ _debug;
3662
+ subscribers = /* @__PURE__ */ new Set();
3663
+ agentRegistry;
3664
+ contextStore;
3665
+ suggestionEngine;
3666
+ runHandler;
3667
+ stateManager;
3668
+ threadStoreRegistry;
3669
+ /**
3670
+ * The single core-owned memory store, created lazily on first
3671
+ * `getMemoryStore()` and kept user-scoped for the lifetime of the core.
3672
+ * Its runtime context is wired by the core itself (see `syncMemoryContext`),
3673
+ * so callers never register or look it up by `agentId`.
3674
+ */
3675
+ _memoryStore;
3676
+ /**
3677
+ * Tracks the agent IDs from the most recent `onAgentsChanged` notification.
3678
+ * Used to gate thread-store auto-unregister so the FIRST empty-agents
3679
+ * notification (before published agents are merged in) does not rip out a
3680
+ * store that was registered prior to that initial notification.
3681
+ */
3682
+ previousAgentIds = /* @__PURE__ */ new Set();
3683
+ constructor({ runtimeUrl, runtimeTransport = "auto", headers = {}, credentials, properties = {}, agents__unsafe_dev_only = {}, tools = [], suggestionsConfig = [], debug }) {
3684
+ this._headers = normalizeHeaders(headers);
3685
+ this._credentials = credentials;
3686
+ this._properties = properties;
3687
+ this._debug = debug;
3688
+ this.agentRegistry = new AgentRegistry(this);
3689
+ this.contextStore = new ContextStore(this);
3690
+ this.suggestionEngine = new SuggestionEngine(this);
3691
+ this.runHandler = new RunHandler(this);
3692
+ this.stateManager = new StateManager(this);
3693
+ this.threadStoreRegistry = new ThreadStoreRegistry(this);
3694
+ this.agentRegistry.initialize(agents__unsafe_dev_only);
3695
+ this.runHandler.initialize(tools);
3696
+ this.suggestionEngine.initialize(suggestionsConfig);
3697
+ this.stateManager.initialize();
3698
+ this.agentRegistry.setRuntimeTransport(runtimeTransport);
3699
+ this.agentRegistry.setRuntimeUrl(runtimeUrl);
3700
+ this.previousAgentIds = new Set(Object.keys(agents__unsafe_dev_only));
3701
+ this.subscribe({
3702
+ onRuntimeConnectionStatusChanged: () => {
3703
+ if (this._memoryStore) this.syncMemoryContext();
3704
+ },
3705
+ onAgentsChanged: ({ agents }) => {
3706
+ Object.values(agents).forEach((agent) => {
3707
+ if (agent.agentId) this.stateManager.subscribeToAgent(agent);
3708
+ });
3709
+ const currentAgentIds = new Set(Object.keys(agents));
3710
+ for (const agentId of Object.keys(this.threadStoreRegistry.getAll())) if (this.previousAgentIds.has(agentId) && !currentAgentIds.has(agentId)) try {
3711
+ this.threadStoreRegistry.unregister(agentId);
3712
+ } catch (err) {
3713
+ console.error(`CopilotKitCore.onAgentsChanged: threadStoreRegistry.unregister failed for "${agentId}":`, err);
3714
+ }
3715
+ for (const agentId of this.previousAgentIds) if (!currentAgentIds.has(agentId)) try {
3716
+ this.stateManager.unsubscribeFromAgent(agentId);
3717
+ } catch (err) {
3718
+ console.error(`CopilotKitCore.onAgentsChanged: stateManager.unsubscribeFromAgent failed for "${agentId}":`, err);
3719
+ }
3720
+ this.previousAgentIds = currentAgentIds;
3721
+ }
3722
+ });
3723
+ }
3724
+ /**
3725
+ * Internal method used by delegate classes and subclasses to notify subscribers
3726
+ */
3727
+ async notifySubscribers(handler, errorMessage) {
3728
+ await Promise.all(Array.from(this.subscribers).map(async (subscriber) => {
3729
+ try {
3730
+ await handler(subscriber);
3731
+ } catch (error) {
3732
+ console.error(errorMessage, error);
3733
+ }
3734
+ }));
3735
+ }
3736
+ /**
3737
+ * Internal method used by delegate classes to emit errors
3738
+ */
3739
+ async emitError({ error, code, context = {} }) {
3740
+ await this.notifySubscribers((subscriber) => subscriber.onError?.({
3741
+ copilotkit: this,
3742
+ error,
3743
+ code,
3744
+ context
3745
+ }), "Subscriber onError error:");
3746
+ }
3747
+ /**
3748
+ * Log a message to the console and emit an error to subscribers.
3749
+ * Catches failures from `emitError` itself to prevent unhandled rejections.
3750
+ */
3751
+ logAndEmitError(message, params, logLevel = "error") {
3752
+ console[logLevel](message, params.error);
3753
+ this.emitError(params).catch((emitErr) => {
3754
+ console.error(message + " — emitError itself failed:", emitErr);
3755
+ });
3756
+ }
3757
+ /**
3758
+ * Snapshot accessors
3759
+ */
2723
3760
  get context() {
2724
3761
  return this.contextStore.context;
2725
3762
  }
@@ -2803,6 +3840,9 @@ var CopilotKitCore = class {
2803
3840
  get threadEndpoints() {
2804
3841
  return this.agentRegistry.threadEndpoints;
2805
3842
  }
3843
+ get suggestions() {
3844
+ return this.agentRegistry.suggestions;
3845
+ }
2806
3846
  get a2uiEnabled() {
2807
3847
  return this.agentRegistry.a2uiEnabled;
2808
3848
  }
@@ -2856,6 +3896,7 @@ var CopilotKitCore = class {
2856
3896
  */
2857
3897
  setHeaders(headers) {
2858
3898
  this._headers = normalizeHeaders(headers);
3899
+ if (this._memoryStore) this.syncMemoryContext();
2859
3900
  this.agentRegistry.applyHeadersToAgents(this.agentRegistry.agents);
2860
3901
  this.notifySubscribers((subscriber) => subscriber.onHeadersChanged?.({
2861
3902
  copilotkit: this,
@@ -2961,6 +4002,46 @@ var CopilotKitCore = class {
2961
4002
  return this.threadStoreRegistry.getAll();
2962
4003
  }
2963
4004
  /**
4005
+ * Returns the single core-owned, user-scoped memory store, creating and
4006
+ * starting it on first access. Unlike thread stores, memory is not scoped per
4007
+ * agent: there is exactly one store whose runtime context the core wires
4008
+ * itself (see `syncMemoryContext`), so consumers (e.g. a `useMemories`
4009
+ * binding) just read this store rather than registering one.
4010
+ */
4011
+ getMemoryStore() {
4012
+ return this.ensureMemoryStore();
4013
+ }
4014
+ /**
4015
+ * Lazily creates, starts, and context-syncs the core-owned memory store on
4016
+ * first access, then returns it. Subsequent calls return the existing store.
4017
+ * The store is constructed with a bound `globalThis.fetch` and immediately
4018
+ * has its runtime context synced from the current connection state.
4019
+ */
4020
+ ensureMemoryStore() {
4021
+ if (!this._memoryStore) {
4022
+ this._memoryStore = createMemoryStore({ fetch: globalThis.fetch.bind(globalThis) });
4023
+ this._memoryStore.start();
4024
+ this.syncMemoryContext();
4025
+ }
4026
+ return this._memoryStore;
4027
+ }
4028
+ /**
4029
+ * Pushes the current runtime wiring into the memory store. When the runtime
4030
+ * is connected and both the intelligence WebSocket URL and runtime URL are
4031
+ * available, the store receives a context (runtime URL, WebSocket URL, and a
4032
+ * copy of the current headers); otherwise its context is cleared. No-op when
4033
+ * the store has not been created yet.
4034
+ */
4035
+ syncMemoryContext() {
4036
+ if (!this._memoryStore) return;
4037
+ if (this.runtimeConnectionStatus === CopilotKitCoreRuntimeConnectionStatus.Connected && this.intelligence?.wsUrl && this.runtimeUrl) this._memoryStore.setContext({
4038
+ runtimeUrl: this.runtimeUrl,
4039
+ wsUrl: this.intelligence.wsUrl,
4040
+ headers: { ...this.headers }
4041
+ });
4042
+ else this._memoryStore.setContext(null);
4043
+ }
4044
+ /**
2964
4045
  * Suggestions management (delegated to SuggestionEngine)
2965
4046
  */
2966
4047
  addSuggestionsConfig(config) {
@@ -3123,510 +4204,253 @@ var CopilotKitCore = class {
3123
4204
  throttler.maybeExecute();
3124
4205
  };
3125
4206
  if (subscriber.onStateChanged) wrappedSubscriber.onStateChanged = (params) => {
3126
- latestStateParams = params;
3127
- throttler.maybeExecute();
3128
- };
3129
- const subscription = agent.subscribe(wrappedSubscriber);
3130
- return { unsubscribe: () => {
3131
- active = false;
3132
- throttler.cancel();
3133
- subscription.unsubscribe();
3134
- } };
3135
- }
3136
- /**
3137
- * Agent connectivity (delegated to RunHandler)
3138
- */
3139
- async connectAgent(params) {
3140
- return this.runHandler.connectAgent(params);
3141
- }
3142
- stopAgent(params) {
3143
- this.runHandler.abortCurrentRun();
3144
- params.agent.abortRun();
3145
- }
3146
- async runAgent(params) {
3147
- return this.runHandler.runAgent(params);
3148
- }
3149
- /**
3150
- * Programmatically execute a registered frontend tool without going through an LLM turn.
3151
- * The handler runs, render components show up in the UI, and both the tool call and
3152
- * result messages are added to `agent.messages`.
3153
- */
3154
- async runTool(params) {
3155
- return this.runHandler.runTool(params);
3156
- }
3157
- /**
3158
- * State management (delegated to StateManager)
3159
- */
3160
- getStateByRun(agentId, threadId, runId) {
3161
- return this.stateManager.getStateByRun(agentId, threadId, runId);
3162
- }
3163
- getRunIdForMessage(agentId, threadId, messageId) {
3164
- return this.stateManager.getRunIdForMessage(agentId, threadId, messageId);
3165
- }
3166
- getRunIdsForThread(agentId, threadId) {
3167
- return this.stateManager.getRunIdsForThread(agentId, threadId);
3168
- }
3169
- /**
3170
- * Internal method used by RunHandler to build frontend tools
3171
- */
3172
- buildFrontendTools(agentId) {
3173
- return this.runHandler.buildFrontendTools(agentId);
3174
- }
3175
- /**
3176
- * Called before each follow-up agent run (after tool execution).
3177
- *
3178
- * When a frontend tool handler calls framework state setters (e.g. React's
3179
- * setState), those updates are batched and deferred — they do not take effect
3180
- * until the framework's scheduler runs (React uses MessageChannel).
3181
- * useAgentContext registers context via useLayoutEffect, which runs
3182
- * synchronously after React commits that deferred batch.
3183
- *
3184
- * Without yielding here, the follow-up runAgent reads the context store
3185
- * synchronously while the deferred updates are still pending, producing stale
3186
- * context for the next agent turn.
3187
- *
3188
- * Override in framework-specific subclasses to yield to the framework
3189
- * scheduler before the follow-up run. The base implementation is a no-op
3190
- * because non-React environments have no deferred state to flush.
3191
- */
3192
- async waitForPendingFrameworkUpdates() {}
3193
- };
3194
-
3195
- //#endregion
3196
- //#region src/types.ts
3197
- /**
3198
- * Status of a tool call execution
3199
- */
3200
- let ToolCallStatus = /* @__PURE__ */ function(ToolCallStatus) {
3201
- ToolCallStatus["InProgress"] = "inProgress";
3202
- ToolCallStatus["Executing"] = "executing";
3203
- ToolCallStatus["Complete"] = "complete";
3204
- return ToolCallStatus;
3205
- }({});
3206
-
3207
- //#endregion
3208
- //#region src/utils/markdown.ts
3209
- function completePartialMarkdown(input) {
3210
- let s = input;
3211
- const fenceMatches = Array.from(s.matchAll(/^(\s*)(`{3,}|~{3,})/gm));
3212
- if (fenceMatches.length % 2 === 1) {
3213
- const [, indent, fence] = fenceMatches[0];
3214
- s += `\n${indent}${fence}`;
3215
- }
3216
- if (s.match(/\[([^\]]*)\]\(([^)]*)$/)) s += ")";
3217
- const openElements = [];
3218
- const chars = Array.from(s);
3219
- const codeBlockRanges = [];
3220
- const inlineCodeRanges = [];
3221
- let tempCodeFenceCount = 0;
3222
- let currentCodeBlockStart = -1;
3223
- for (let i = 0; i < chars.length; i++) if (i === 0 || chars[i - 1] === "\n") {
3224
- const lineMatch = s.substring(i).match(/^(\s*)(`{3,}|~{3,})/);
3225
- if (lineMatch) {
3226
- tempCodeFenceCount++;
3227
- if (tempCodeFenceCount % 2 === 1) currentCodeBlockStart = i;
3228
- else if (currentCodeBlockStart !== -1) {
3229
- codeBlockRanges.push({
3230
- start: currentCodeBlockStart,
3231
- end: i + lineMatch[0].length
3232
- });
3233
- currentCodeBlockStart = -1;
3234
- }
3235
- i += lineMatch[0].length - 1;
3236
- }
3237
- }
3238
- for (let i = 0; i < chars.length; i++) if (chars[i] === "`") {
3239
- let backslashCount = 0;
3240
- for (let j = i - 1; j >= 0 && chars[j] === "\\"; j--) backslashCount++;
3241
- if (backslashCount % 2 === 0) {
3242
- for (let j = i + 1; j < chars.length; j++) if (chars[j] === "`") {
3243
- let closingBackslashCount = 0;
3244
- for (let k = j - 1; k >= 0 && chars[k] === "\\"; k--) closingBackslashCount++;
3245
- if (closingBackslashCount % 2 === 0) {
3246
- inlineCodeRanges.push({
3247
- start: i,
3248
- end: j + 1
3249
- });
3250
- i = j;
3251
- break;
3252
- }
3253
- }
3254
- }
3255
- }
3256
- const isInCode = (pos) => {
3257
- return codeBlockRanges.some((range) => pos >= range.start && pos < range.end) || inlineCodeRanges.some((range) => pos >= range.start && pos < range.end);
3258
- };
3259
- for (let i = 0; i < chars.length; i++) {
3260
- const char = chars[i];
3261
- const nextChar = chars[i + 1];
3262
- const prevChar = chars[i - 1];
3263
- if (isInCode(i)) continue;
3264
- if (char === "[") {
3265
- let isCompleteLink = false;
3266
- let bracketDepth = 1;
3267
- let j = i + 1;
3268
- while (j < chars.length && bracketDepth > 0) {
3269
- if (chars[j] === "[" && !isInCode(j)) bracketDepth++;
3270
- if (chars[j] === "]" && !isInCode(j)) bracketDepth--;
3271
- j++;
3272
- }
3273
- if (bracketDepth === 0 && chars[j] === "(") {
3274
- let parenDepth = 1;
3275
- j++;
3276
- while (j < chars.length && parenDepth > 0) {
3277
- if (chars[j] === "(" && !isInCode(j)) parenDepth++;
3278
- if (chars[j] === ")" && !isInCode(j)) parenDepth--;
3279
- j++;
3280
- }
3281
- if (parenDepth === 0) {
3282
- isCompleteLink = true;
3283
- i = j - 1;
3284
- continue;
3285
- }
3286
- }
3287
- if (!isCompleteLink) {
3288
- const existingIndex = openElements.findIndex((el) => el.type === "bracket");
3289
- if (existingIndex !== -1) openElements.splice(existingIndex, 1);
3290
- else openElements.push({
3291
- type: "bracket",
3292
- marker: "[",
3293
- position: i
3294
- });
3295
- }
3296
- } else if (char === "*" && nextChar === "*") {
3297
- const existingIndex = openElements.findIndex((el) => el.type === "bold_star");
3298
- if (existingIndex !== -1) openElements.splice(existingIndex, 1);
3299
- else openElements.push({
3300
- type: "bold_star",
3301
- marker: "**",
3302
- position: i
3303
- });
3304
- i++;
3305
- } else if (char === "_" && nextChar === "_") {
3306
- const existingIndex = openElements.findIndex((el) => el.type === "bold_underscore");
3307
- if (existingIndex !== -1) openElements.splice(existingIndex, 1);
3308
- else openElements.push({
3309
- type: "bold_underscore",
3310
- marker: "__",
3311
- position: i
3312
- });
3313
- i++;
3314
- } else if (char === "~" && nextChar === "~") {
3315
- const existingIndex = openElements.findIndex((el) => el.type === "strike");
3316
- if (existingIndex !== -1) openElements.splice(existingIndex, 1);
3317
- else openElements.push({
3318
- type: "strike",
3319
- marker: "~~",
3320
- position: i
3321
- });
3322
- i++;
3323
- } else if (char === "*" && prevChar !== "*" && nextChar !== "*") {
3324
- const existingIndex = openElements.findIndex((el) => el.type === "italic_star");
3325
- if (existingIndex !== -1) openElements.splice(existingIndex, 1);
3326
- else openElements.push({
3327
- type: "italic_star",
3328
- marker: "*",
3329
- position: i
3330
- });
3331
- } else if (char === "_" && prevChar !== "_" && nextChar !== "_") {
3332
- const existingIndex = openElements.findIndex((el) => el.type === "italic_underscore");
3333
- if (existingIndex !== -1) openElements.splice(existingIndex, 1);
3334
- else openElements.push({
3335
- type: "italic_underscore",
3336
- marker: "_",
3337
- position: i
3338
- });
3339
- }
4207
+ latestStateParams = params;
4208
+ throttler.maybeExecute();
4209
+ };
4210
+ const subscription = agent.subscribe(wrappedSubscriber);
4211
+ return { unsubscribe: () => {
4212
+ active = false;
4213
+ throttler.cancel();
4214
+ subscription.unsubscribe();
4215
+ } };
3340
4216
  }
3341
- let backtickCount = 0;
3342
- for (let i = 0; i < chars.length; i++) if (chars[i] === "`" && !isInCode(i)) backtickCount++;
3343
- if (backtickCount % 2 === 1) s += "`";
3344
- openElements.sort((a, b) => b.position - a.position);
3345
- const closers = openElements.map((el) => {
3346
- switch (el.type) {
3347
- case "bracket": return "]";
3348
- case "bold_star": return "**";
3349
- case "bold_underscore": return "__";
3350
- case "strike": return "~~";
3351
- case "italic_star": return "*";
3352
- case "italic_underscore": return "_";
3353
- default: return "";
3354
- }
3355
- });
3356
- let result = s + closers.join("");
3357
- const finalFenceMatches = Array.from(result.matchAll(/^(\s*)(`{3,}|~{3,})/gm));
3358
- const hasUnclosedBacktick = (result.match(/`/g) || []).length % 2 === 1;
3359
- const hasUnclosedCodeFence = finalFenceMatches.length % 2 === 1;
3360
- let shouldCloseParens = !hasUnclosedBacktick && !hasUnclosedCodeFence;
3361
- if (shouldCloseParens) {
3362
- const lastOpenParen = result.lastIndexOf("(");
3363
- if (lastOpenParen !== -1) {
3364
- if ((result.substring(0, lastOpenParen).match(/`/g) || []).length % 2 === 1) shouldCloseParens = false;
3365
- }
4217
+ /**
4218
+ * Agent connectivity (delegated to RunHandler)
4219
+ */
4220
+ async connectAgent(params) {
4221
+ return this.runHandler.connectAgent(params);
3366
4222
  }
3367
- if (shouldCloseParens) {
3368
- const openParens = (result.match(/\(/g) || []).length;
3369
- const closeParens = (result.match(/\)/g) || []).length;
3370
- if (openParens > closeParens) result += ")".repeat(openParens - closeParens);
4223
+ stopAgent(params) {
4224
+ this.runHandler.abortCurrentRun();
4225
+ params.agent.abortRun();
3371
4226
  }
3372
- return result;
3373
- }
3374
-
3375
- //#endregion
3376
- //#region src/utils/micro-redux.ts
3377
- const INTERNAL_ACTION_TYPES = {
3378
- boot: "@@micro-redux/boot",
3379
- init: "@@micro-redux/init",
3380
- stop: "@@micro-redux/stop"
3381
- };
3382
- const INTERNAL_BOOT_ACTION = { type: INTERNAL_ACTION_TYPES.boot };
3383
- /**
3384
- * Builds a typed action creator from a type string and payload factory.
3385
- */
3386
- function createTypedActionCreator(type, factory) {
3387
- const creator = ((...args) => ({
3388
- ...factory(...args),
3389
- type
3390
- }));
3391
- creator.type = type;
3392
- creator.match = (action) => action.type === type;
3393
- return creator;
3394
- }
3395
- /**
3396
- * Declares a payload-based action config for `createActionGroup`.
3397
- *
3398
- * @example
3399
- * ```ts
3400
- * const actions = createActionGroup("User", {
3401
- * loaded: props<{ id: string }>(),
3402
- * });
3403
- * ```
3404
- */
3405
- function props() {
3406
- return { kind: "props" };
3407
- }
3408
- /**
3409
- * Declares a no-payload action config for `createActionGroup`.
3410
- *
3411
- * @example
3412
- * ```ts
3413
- * const actions = createActionGroup("User", {
3414
- * reset: empty(),
3415
- * });
3416
- * ```
3417
- */
3418
- function empty() {
3419
- return { kind: "empty" };
3420
- }
3421
- /**
3422
- * Creates a namespaced group of typed action creators.
3423
- *
3424
- * Action types are formatted as: `[Source] actionName`.
3425
- */
3426
- function createActionGroup(source, config) {
3427
- const group = {};
3428
- for (const eventName of Object.keys(config)) {
3429
- const eventConfig = config[eventName];
3430
- if (!eventConfig) continue;
3431
- const actionType = `[${source}] ${eventName}`;
3432
- if (eventConfig.kind === "props") {
3433
- group[eventName] = createTypedActionCreator(actionType, (payload) => ({ ...payload }));
3434
- continue;
3435
- }
3436
- group[eventName] = createTypedActionCreator(actionType, () => ({}));
4227
+ async runAgent(params) {
4228
+ return this.runHandler.runAgent(params);
3437
4229
  }
3438
- return group;
3439
- }
3440
- /**
3441
- * Registers one reducer handler for one or more action creators.
3442
- *
3443
- * @throws Error when called without at least one action creator and reducer.
3444
- */
3445
- function on(...args) {
3446
- if (args.length < 2) throw new Error("on requires at least one action creator and one reducer");
3447
- const reducer = args[args.length - 1];
3448
- return {
3449
- creators: args.slice(0, -1),
3450
- reducer
3451
- };
3452
- }
3453
- /**
3454
- * Creates a reducer from an initial state and `on(...)` handler entries.
3455
- *
3456
- * Unknown action types return the current state unchanged.
3457
- */
3458
- function createReducer(initialState, ...entries) {
3459
- const reducerMap = /* @__PURE__ */ new Map();
3460
- for (const entry of entries) for (const creator of entry.creators) {
3461
- const handlers = reducerMap.get(creator.type) ?? [];
3462
- handlers.push(entry.reducer);
3463
- reducerMap.set(creator.type, handlers);
4230
+ /**
4231
+ * Programmatically execute a registered frontend tool without going through an LLM turn.
4232
+ * The handler runs, render components show up in the UI, and both the tool call and
4233
+ * result messages are added to `agent.messages`.
4234
+ */
4235
+ async runTool(params) {
4236
+ return this.runHandler.runTool(params);
3464
4237
  }
3465
- return (state, action) => {
3466
- const currentState = state ?? initialState;
3467
- const handlers = reducerMap.get(action.type);
3468
- if (!handlers || handlers.length === 0) return currentState;
3469
- let nextState = currentState;
3470
- for (const handler of handlers) nextState = handler(nextState, action);
3471
- return nextState;
3472
- };
3473
- }
3474
- /**
3475
- * Creates a selector that caches and reuses the last computed result
3476
- * when all input references are unchanged.
3477
- */
3478
- function createSelector(...args) {
3479
- if (args.length === 1) {
3480
- const projector = args[0];
3481
- let hasCached = false;
3482
- let lastState;
3483
- let lastResult;
3484
- return (state) => {
3485
- if (hasCached && state === lastState) return lastResult;
3486
- lastState = state;
3487
- lastResult = projector(state);
3488
- hasCached = true;
3489
- return lastResult;
3490
- };
4238
+ /**
4239
+ * State management (delegated to StateManager)
4240
+ */
4241
+ getStateByRun(agentId, threadId, runId) {
4242
+ return this.stateManager.getStateByRun(agentId, threadId, runId);
3491
4243
  }
3492
- const projector = args[args.length - 1];
3493
- const selectors = args.slice(0, -1);
3494
- let hasCached = false;
3495
- let lastInputs = [];
3496
- let lastResult;
3497
- return (state) => {
3498
- const inputs = selectors.map((selector) => selector(state));
3499
- if (hasCached && inputs.length === lastInputs.length && inputs.every((value, index) => value === lastInputs[index])) return lastResult;
3500
- lastInputs = inputs;
3501
- lastResult = projector(...inputs);
3502
- hasCached = true;
3503
- return lastResult;
3504
- };
3505
- }
3506
- /**
3507
- * RxJS operator that maps state emissions through a selector and suppresses
3508
- * unchanged projected values via reference equality.
3509
- */
3510
- function select(selector) {
3511
- return (source$) => source$.pipe(map(selector), distinctUntilChanged());
3512
- }
3513
- /**
3514
- * RxJS operator that filters an action stream by action creators and narrows
3515
- * the output action type to the matched creator union.
3516
- *
3517
- * @throws Error when called without at least one action creator.
3518
- */
3519
- function ofType(...creators) {
3520
- if (creators.length === 0) throw new Error("ofType requires at least one action creator");
3521
- const actionTypes = new Set(creators.map((creator) => creator.type));
3522
- return (source$) => {
3523
- return source$.pipe(filter((action) => {
3524
- return actionTypes.has(action.type);
3525
- }));
3526
- };
3527
- }
3528
- /**
3529
- * Creates an effect descriptor consumed by `createStore`.
3530
- */
3531
- function createEffect(factory, options = {}) {
3532
- if (options.dispatch === false) return {
3533
- run: factory,
3534
- dispatch: false
3535
- };
3536
- return {
3537
- run: factory,
3538
- dispatch: true
3539
- };
3540
- }
4244
+ getRunIdForMessage(agentId, threadId, messageId) {
4245
+ return this.stateManager.getRunIdForMessage(agentId, threadId, messageId);
4246
+ }
4247
+ getRunIdsForThread(agentId, threadId) {
4248
+ return this.stateManager.getRunIdsForThread(agentId, threadId);
4249
+ }
4250
+ /**
4251
+ * Internal method used by RunHandler to build frontend tools
4252
+ */
4253
+ buildFrontendTools(agentId) {
4254
+ return this.runHandler.buildFrontendTools(agentId);
4255
+ }
4256
+ /**
4257
+ * Called before each follow-up agent run (after tool execution).
4258
+ *
4259
+ * When a frontend tool handler calls framework state setters (e.g. React's
4260
+ * setState), those updates are batched and deferred — they do not take effect
4261
+ * until the framework's scheduler runs (React uses MessageChannel).
4262
+ * useAgentContext registers context via useLayoutEffect, which runs
4263
+ * synchronously after React commits that deferred batch.
4264
+ *
4265
+ * Without yielding here, the follow-up runAgent reads the context store
4266
+ * synchronously while the deferred updates are still pending, producing stale
4267
+ * context for the next agent turn.
4268
+ *
4269
+ * Override in framework-specific subclasses to yield to the framework
4270
+ * scheduler before the follow-up run. The base implementation is a no-op
4271
+ * because non-React environments have no deferred state to flush.
4272
+ */
4273
+ async waitForPendingFrameworkUpdates() {}
4274
+ };
4275
+
4276
+ //#endregion
4277
+ //#region src/types.ts
3541
4278
  /**
3542
- * Creates a small observable store with reducer + effects.
3543
- *
3544
- * Behavior:
3545
- * - `init()` starts effects and dispatches `@@micro-redux/init`.
3546
- * - `stop()` dispatches `@@micro-redux/stop` and unsubscribes all effects.
3547
- * - Effect action observation is scheduled on `asapScheduler` to avoid
3548
- * synchronous re-entrancy in the effect loop.
3549
- * - Any effect error triggers fail-fast teardown and errors both `actions$`
3550
- * and `state$`.
4279
+ * Status of a tool call execution
3551
4280
  */
3552
- function createStore(options) {
3553
- const reducer = options.reducer;
3554
- const effects = options.effects ?? [];
3555
- let hasFatalError = false;
3556
- let isRunning = false;
3557
- let effectSubscriptions = new Subscription();
3558
- let currentState = reducer(void 0, INTERNAL_BOOT_ACTION);
3559
- const stateSubject = new BehaviorSubject(currentState);
3560
- const actionsSubject = new Subject();
3561
- const dispatchInternal = (action) => {
3562
- if (hasFatalError) throw new Error("Store is in a failed state due to an effect error");
3563
- currentState = reducer(currentState, action);
3564
- stateSubject.next(currentState);
3565
- actionsSubject.next(action);
3566
- };
3567
- const failFast = (error) => {
3568
- if (hasFatalError) return;
3569
- hasFatalError = true;
3570
- isRunning = false;
3571
- effectSubscriptions.unsubscribe();
3572
- effectSubscriptions = new Subscription();
3573
- actionsSubject.error(error);
3574
- stateSubject.error(error);
3575
- };
3576
- const startEffects = () => {
3577
- for (const effect of effects) {
3578
- const scheduledActions$ = actionsSubject.asObservable().pipe(observeOn(asapScheduler));
3579
- const state$ = stateSubject.asObservable();
3580
- if (effect.dispatch) {
3581
- const subscription = effect.run(scheduledActions$, state$).subscribe({
3582
- next: (effectAction) => {
3583
- if (hasFatalError) return;
3584
- dispatchInternal(effectAction);
3585
- },
3586
- error: (error) => {
3587
- failFast(error);
3588
- }
4281
+ let ToolCallStatus = /* @__PURE__ */ function(ToolCallStatus) {
4282
+ ToolCallStatus["InProgress"] = "inProgress";
4283
+ ToolCallStatus["Executing"] = "executing";
4284
+ ToolCallStatus["Complete"] = "complete";
4285
+ return ToolCallStatus;
4286
+ }({});
4287
+
4288
+ //#endregion
4289
+ //#region src/utils/markdown.ts
4290
+ function completePartialMarkdown(input) {
4291
+ let s = input;
4292
+ const fenceMatches = Array.from(s.matchAll(/^(\s*)(`{3,}|~{3,})/gm));
4293
+ if (fenceMatches.length % 2 === 1) {
4294
+ const [, indent, fence] = fenceMatches[0];
4295
+ s += `\n${indent}${fence}`;
4296
+ }
4297
+ if (s.match(/\[([^\]]*)\]\(([^)]*)$/)) s += ")";
4298
+ const openElements = [];
4299
+ const chars = Array.from(s);
4300
+ const codeBlockRanges = [];
4301
+ const inlineCodeRanges = [];
4302
+ let tempCodeFenceCount = 0;
4303
+ let currentCodeBlockStart = -1;
4304
+ for (let i = 0; i < chars.length; i++) if (i === 0 || chars[i - 1] === "\n") {
4305
+ const lineMatch = s.substring(i).match(/^(\s*)(`{3,}|~{3,})/);
4306
+ if (lineMatch) {
4307
+ tempCodeFenceCount++;
4308
+ if (tempCodeFenceCount % 2 === 1) currentCodeBlockStart = i;
4309
+ else if (currentCodeBlockStart !== -1) {
4310
+ codeBlockRanges.push({
4311
+ start: currentCodeBlockStart,
4312
+ end: i + lineMatch[0].length
3589
4313
  });
3590
- effectSubscriptions.add(subscription);
3591
- continue;
4314
+ currentCodeBlockStart = -1;
3592
4315
  }
3593
- const subscription = effect.run(scheduledActions$, state$).subscribe({ error: (error) => {
3594
- failFast(error);
3595
- } });
3596
- effectSubscriptions.add(subscription);
4316
+ i += lineMatch[0].length - 1;
3597
4317
  }
3598
- };
3599
- return {
3600
- dispatch(action) {
3601
- dispatchInternal(action);
3602
- },
3603
- getState() {
3604
- return currentState;
3605
- },
3606
- get state$() {
3607
- return stateSubject.asObservable();
3608
- },
3609
- get actions$() {
3610
- return actionsSubject.asObservable();
3611
- },
3612
- select(selector) {
3613
- return stateSubject.asObservable().pipe(select(selector));
3614
- },
3615
- init() {
3616
- if (hasFatalError || isRunning) return;
3617
- isRunning = true;
3618
- startEffects();
3619
- if (hasFatalError) return;
3620
- dispatchInternal({ type: INTERNAL_ACTION_TYPES.init });
3621
- },
3622
- stop() {
3623
- if (hasFatalError || !isRunning) return;
3624
- dispatchInternal({ type: INTERNAL_ACTION_TYPES.stop });
3625
- effectSubscriptions.unsubscribe();
3626
- effectSubscriptions = new Subscription();
3627
- isRunning = false;
4318
+ }
4319
+ for (let i = 0; i < chars.length; i++) if (chars[i] === "`") {
4320
+ let backslashCount = 0;
4321
+ for (let j = i - 1; j >= 0 && chars[j] === "\\"; j--) backslashCount++;
4322
+ if (backslashCount % 2 === 0) {
4323
+ for (let j = i + 1; j < chars.length; j++) if (chars[j] === "`") {
4324
+ let closingBackslashCount = 0;
4325
+ for (let k = j - 1; k >= 0 && chars[k] === "\\"; k--) closingBackslashCount++;
4326
+ if (closingBackslashCount % 2 === 0) {
4327
+ inlineCodeRanges.push({
4328
+ start: i,
4329
+ end: j + 1
4330
+ });
4331
+ i = j;
4332
+ break;
4333
+ }
4334
+ }
3628
4335
  }
4336
+ }
4337
+ const isInCode = (pos) => {
4338
+ return codeBlockRanges.some((range) => pos >= range.start && pos < range.end) || inlineCodeRanges.some((range) => pos >= range.start && pos < range.end);
3629
4339
  };
4340
+ for (let i = 0; i < chars.length; i++) {
4341
+ const char = chars[i];
4342
+ const nextChar = chars[i + 1];
4343
+ const prevChar = chars[i - 1];
4344
+ if (isInCode(i)) continue;
4345
+ if (char === "[") {
4346
+ let isCompleteLink = false;
4347
+ let bracketDepth = 1;
4348
+ let j = i + 1;
4349
+ while (j < chars.length && bracketDepth > 0) {
4350
+ if (chars[j] === "[" && !isInCode(j)) bracketDepth++;
4351
+ if (chars[j] === "]" && !isInCode(j)) bracketDepth--;
4352
+ j++;
4353
+ }
4354
+ if (bracketDepth === 0 && chars[j] === "(") {
4355
+ let parenDepth = 1;
4356
+ j++;
4357
+ while (j < chars.length && parenDepth > 0) {
4358
+ if (chars[j] === "(" && !isInCode(j)) parenDepth++;
4359
+ if (chars[j] === ")" && !isInCode(j)) parenDepth--;
4360
+ j++;
4361
+ }
4362
+ if (parenDepth === 0) {
4363
+ isCompleteLink = true;
4364
+ i = j - 1;
4365
+ continue;
4366
+ }
4367
+ }
4368
+ if (!isCompleteLink) {
4369
+ const existingIndex = openElements.findIndex((el) => el.type === "bracket");
4370
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
4371
+ else openElements.push({
4372
+ type: "bracket",
4373
+ marker: "[",
4374
+ position: i
4375
+ });
4376
+ }
4377
+ } else if (char === "*" && nextChar === "*") {
4378
+ const existingIndex = openElements.findIndex((el) => el.type === "bold_star");
4379
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
4380
+ else openElements.push({
4381
+ type: "bold_star",
4382
+ marker: "**",
4383
+ position: i
4384
+ });
4385
+ i++;
4386
+ } else if (char === "_" && nextChar === "_") {
4387
+ const existingIndex = openElements.findIndex((el) => el.type === "bold_underscore");
4388
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
4389
+ else openElements.push({
4390
+ type: "bold_underscore",
4391
+ marker: "__",
4392
+ position: i
4393
+ });
4394
+ i++;
4395
+ } else if (char === "~" && nextChar === "~") {
4396
+ const existingIndex = openElements.findIndex((el) => el.type === "strike");
4397
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
4398
+ else openElements.push({
4399
+ type: "strike",
4400
+ marker: "~~",
4401
+ position: i
4402
+ });
4403
+ i++;
4404
+ } else if (char === "*" && prevChar !== "*" && nextChar !== "*") {
4405
+ const existingIndex = openElements.findIndex((el) => el.type === "italic_star");
4406
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
4407
+ else openElements.push({
4408
+ type: "italic_star",
4409
+ marker: "*",
4410
+ position: i
4411
+ });
4412
+ } else if (char === "_" && prevChar !== "_" && nextChar !== "_") {
4413
+ const existingIndex = openElements.findIndex((el) => el.type === "italic_underscore");
4414
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
4415
+ else openElements.push({
4416
+ type: "italic_underscore",
4417
+ marker: "_",
4418
+ position: i
4419
+ });
4420
+ }
4421
+ }
4422
+ let backtickCount = 0;
4423
+ for (let i = 0; i < chars.length; i++) if (chars[i] === "`" && !isInCode(i)) backtickCount++;
4424
+ if (backtickCount % 2 === 1) s += "`";
4425
+ openElements.sort((a, b) => b.position - a.position);
4426
+ const closers = openElements.map((el) => {
4427
+ switch (el.type) {
4428
+ case "bracket": return "]";
4429
+ case "bold_star": return "**";
4430
+ case "bold_underscore": return "__";
4431
+ case "strike": return "~~";
4432
+ case "italic_star": return "*";
4433
+ case "italic_underscore": return "_";
4434
+ default: return "";
4435
+ }
4436
+ });
4437
+ let result = s + closers.join("");
4438
+ const finalFenceMatches = Array.from(result.matchAll(/^(\s*)(`{3,}|~{3,})/gm));
4439
+ const hasUnclosedBacktick = (result.match(/`/g) || []).length % 2 === 1;
4440
+ const hasUnclosedCodeFence = finalFenceMatches.length % 2 === 1;
4441
+ let shouldCloseParens = !hasUnclosedBacktick && !hasUnclosedCodeFence;
4442
+ if (shouldCloseParens) {
4443
+ const lastOpenParen = result.lastIndexOf("(");
4444
+ if (lastOpenParen !== -1) {
4445
+ if ((result.substring(0, lastOpenParen).match(/`/g) || []).length % 2 === 1) shouldCloseParens = false;
4446
+ }
4447
+ }
4448
+ if (shouldCloseParens) {
4449
+ const openParens = (result.match(/\(/g) || []).length;
4450
+ const closeParens = (result.match(/\)/g) || []).length;
4451
+ if (openParens > closeParens) result += ")".repeat(openParens - closeParens);
4452
+ }
4453
+ return result;
3630
4454
  }
3631
4455
 
3632
4456
  //#endregion
@@ -3641,6 +4465,7 @@ const initialThreadState = {
3641
4465
  isLoading: false,
3642
4466
  isFetchingNextPage: false,
3643
4467
  error: null,
4468
+ fetchMoreError: null,
3644
4469
  context: null,
3645
4470
  sessionId: 0,
3646
4471
  metadataCredentialsRequested: false,
@@ -3726,6 +4551,7 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3726
4551
  isLoading: Boolean(context),
3727
4552
  isFetchingNextPage: false,
3728
4553
  error: null,
4554
+ fetchMoreError: null,
3729
4555
  metadataCredentialsRequested: false,
3730
4556
  metadataJoinCode: null,
3731
4557
  nextCursor: null,
@@ -3737,6 +4563,7 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3737
4563
  isLoading: false,
3738
4564
  isFetchingNextPage: false,
3739
4565
  error: null,
4566
+ fetchMoreError: null,
3740
4567
  metadataCredentialsRequested: false,
3741
4568
  metadataJoinCode: null,
3742
4569
  nextCursor: null,
@@ -3747,7 +4574,8 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3747
4574
  return {
3748
4575
  ...state,
3749
4576
  isLoading: true,
3750
- error: null
4577
+ error: null,
4578
+ fetchMoreError: null
3751
4579
  };
3752
4580
  }), on(threadRestEvents.listSucceeded, (state, { sessionId, threads, joinCode, nextCursor }) => {
3753
4581
  if (sessionId !== state.sessionId) return state;
@@ -3757,6 +4585,7 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3757
4585
  threads: sortThreadsByRecency(threads),
3758
4586
  isLoading: false,
3759
4587
  error: null,
4588
+ fetchMoreError: null,
3760
4589
  metadataJoinCode: joinCode,
3761
4590
  metadataCredentialsRequested: joinCodeChanged ? false : state.metadataCredentialsRequested,
3762
4591
  nextCursor
@@ -3776,6 +4605,7 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3776
4605
  ...state,
3777
4606
  threads: merged,
3778
4607
  isFetchingNextPage: false,
4608
+ fetchMoreError: null,
3779
4609
  nextCursor
3780
4610
  };
3781
4611
  }), on(threadRestEvents.nextPageFailed, (state, { sessionId, error }) => {
@@ -3783,7 +4613,7 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3783
4613
  return {
3784
4614
  ...state,
3785
4615
  isFetchingNextPage: false,
3786
- error
4616
+ fetchMoreError: error
3787
4617
  };
3788
4618
  }), on(threadRestEvents.metadataCredentialsFailed, (state, { sessionId }) => {
3789
4619
  if (sessionId !== state.sessionId) return state;
@@ -3801,7 +4631,8 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3801
4631
  if (!state.nextCursor || state.isFetchingNextPage) return state;
3802
4632
  return {
3803
4633
  ...state,
3804
- isFetchingNextPage: true
4634
+ isFetchingNextPage: true,
4635
+ fetchMoreError: null
3805
4636
  };
3806
4637
  }), on(threadAdapterEvents.renameRequested, (state, { threadId, name }) => {
3807
4638
  const existing = state.threads.find((thread) => thread.id === threadId);
@@ -3930,6 +4761,7 @@ function createThreadSelectors() {
3930
4761
  threads: createSelector((state) => state.threads),
3931
4762
  isLoading: createSelector((state) => state.isLoading),
3932
4763
  error: createSelector((state) => state.error),
4764
+ fetchMoreError: createSelector((state) => state.fetchMoreError),
3933
4765
  hasNextPage: createSelector((state) => state.nextCursor != null),
3934
4766
  isFetchingNextPage: createSelector((state) => state.isFetchingNextPage),
3935
4767
  isMutating: createSelector((state) => state.inFlightMutationCount > 0)
@@ -3939,6 +4771,7 @@ const standaloneSelectors = createThreadSelectors();
3939
4771
  const selectThreads = standaloneSelectors.threads;
3940
4772
  const selectThreadsIsLoading = standaloneSelectors.isLoading;
3941
4773
  const selectThreadsError = standaloneSelectors.error;
4774
+ const selectFetchMoreError = standaloneSelectors.fetchMoreError;
3942
4775
  const selectHasNextPage = standaloneSelectors.hasNextPage;
3943
4776
  const selectIsFetchingNextPage = standaloneSelectors.isFetchingNextPage;
3944
4777
  const selectIsMutating = standaloneSelectors.isMutating;
@@ -4312,6 +5145,7 @@ const ɵcreateThreadSelectors = createThreadSelectors;
4312
5145
  const ɵselectThreads = selectThreads;
4313
5146
  const ɵselectThreadsIsLoading = selectThreadsIsLoading;
4314
5147
  const ɵselectThreadsError = selectThreadsError;
5148
+ const ɵselectFetchMoreError = selectFetchMoreError;
4315
5149
  const ɵselectHasNextPage = selectHasNextPage;
4316
5150
  const ɵselectIsFetchingNextPage = selectIsFetchingNextPage;
4317
5151
  const ɵselectIsMutating = selectIsMutating;
@@ -4369,5 +5203,5 @@ function ɵisCopilotKitFeature(name) {
4369
5203
  }
4370
5204
 
4371
5205
  //#endregion
4372
- export { AgentRegistry, AgentThreadLockedError, ContextStore, CopilotKitCore, CopilotKitCoreErrorCode, CopilotKitCoreRuntimeConnectionStatus, IntelligenceAgent, ProxiedCopilotRuntimeAgent, RunHandler, StateManager, SuggestionEngine, ToolCallStatus, completePartialMarkdown, createActionGroup, createEffect, createReducer, createSelector, createStore, empty, ensureObjectArgs, isRunCompletionAware, ofType, on, parseToolArguments, props, select, ɵCOPILOTKIT_FEATURES, ɵMAX_SOCKET_RETRIES, ɵcreateThreadSelectors, createThreadStore as ɵcreateThreadStore, ɵisCopilotKitFeature, ɵjoinPhoenixChannel$, ɵobservePhoenixEvent$, ɵobservePhoenixJoinOutcome$, ɵobservePhoenixSocketHealth$, ɵobservePhoenixSocketSignals$, ɵphoenixChannel$, ɵphoenixSocket$, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectIsMutating, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading, ɵthreadAdapterEvents };
5206
+ export { AgentRegistry, AgentThreadLockedError, ContextStore, CopilotKitCore, CopilotKitCoreErrorCode, CopilotKitCoreRuntimeConnectionStatus, IntelligenceAgent, MEMORY_ERROR_REGISTRY, MemoryError, ProxiedCopilotRuntimeAgent, RunHandler, StateManager, SuggestionEngine, ToolCallStatus, completePartialMarkdown, createActionGroup, createEffect, createReducer, createSelector, createStore, empty, ensureObjectArgs, isAbortError, isRunCompletionAware, ofType, on, parseToolArguments, props, select, ɵCOPILOTKIT_FEATURES, ɵMAX_SOCKET_RETRIES, createMemoryStore as ɵcreateMemoryStore, ɵcreateThreadSelectors, createThreadStore as ɵcreateThreadStore, ɵisCopilotKitFeature, isRetryableStatus as ɵisRetryableMemoryStatus, ɵjoinPhoenixChannel$, ɵmapMemoryMetadataEvent, ɵmemoryAdapterEvents, ɵmemoryDomainEvents, ɵmemoryReducer, ɵmemoryRestEvents, ɵobservePhoenixEvent$, ɵobservePhoenixJoinOutcome$, ɵobservePhoenixSocketHealth$, ɵobservePhoenixSocketSignals$, ɵphoenixChannel$, ɵphoenixSocket$, ɵselectFetchMoreError, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectIsMutating, ɵselectMemories, ɵselectMemoriesAvailable, ɵselectMemoriesError, ɵselectMemoriesIsLoading, ɵselectMemoriesIsMutating, ɵselectMemoriesRealtimeStatus, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading, ɵthreadAdapterEvents };
4373
5207
  //# sourceMappingURL=index.mjs.map