@copilotkit/core 1.62.1 → 1.62.2-canary.1784333495

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,8 +1,8 @@
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, concat, defer, dematerialize, firstValueFrom, from, lastValueFrom, merge, of, switchMap, throwError } from "rxjs";
5
- import { catchError, 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
8
  import { fromFetch } from "rxjs/fetch";
@@ -130,6 +130,7 @@ const CLIENT_AG_UI_EVENT = "ag_ui_event";
130
130
  const REPLAY_COMPLETE_EVENT = "replay_complete";
131
131
  const STREAM_IDLE_EVENT = "stream_idle";
132
132
  const STOP_RUN_EVENT = "stop_run";
133
+ const CONNECT_STREAM_IDLE_REPLAY_FALLBACK_MS = 100;
133
134
  var AgentThreadLockedError = class extends Error {
134
135
  constructor(threadId) {
135
136
  super(threadId ? `Thread ${threadId} is locked` : "Thread is locked");
@@ -279,12 +280,14 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
279
280
  connect(input) {
280
281
  this.threadId = input.threadId;
281
282
  this.canonicalRunId = null;
282
- return defer(() => this.requestJoinCredentials$("connect", input)).pipe(switchMap((credentials) => {
283
+ const replayCursor = this.getReconnectCursor(input);
284
+ return defer(() => this.requestJoinCredentials$("connect", input, replayCursor)).pipe(switchMap((credentials) => {
283
285
  if (credentials === null) return EMPTY;
284
286
  const canonicalInput = this.applyCanonicalRunIdentity(input, credentials, { fallbackToInputRunId: false });
285
287
  return this.observeThread$(canonicalInput, credentials, {
286
288
  completeOnRunError: false,
287
- streamMode: "connect"
289
+ streamMode: "connect",
290
+ replayCursor
288
291
  });
289
292
  }));
290
293
  }
@@ -307,7 +310,7 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
307
310
  cleanup() {
308
311
  this.cleanupOwned(this.activeChannel, this.socket);
309
312
  }
310
- requestJoinCredentials$(mode, input) {
313
+ requestJoinCredentials$(mode, input, replayCursor) {
311
314
  return defer(async () => {
312
315
  try {
313
316
  const response = await fetch(this.buildRuntimeUrl(mode), {
@@ -324,7 +327,7 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
324
327
  context: input.context,
325
328
  state: input.state,
326
329
  forwardedProps: input.forwardedProps,
327
- ...mode === "connect" ? { lastSeenEventId: this.getReconnectCursor(input) } : {}
330
+ ...mode === "connect" ? { lastSeenEventId: replayCursor === void 0 ? this.getReconnectCursor(input) : replayCursor } : {}
328
331
  }),
329
332
  ...this.config.credentials ? { credentials: this.config.credentials } : {}
330
333
  });
@@ -360,10 +363,11 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
360
363
  observeThread$(input, credentials, options) {
361
364
  return this.observeThreadSession$(input, credentials, options).pipe(catchError((error) => {
362
365
  if (!this.isSocketReconnectExhaustedError(error)) return throwError(() => error);
363
- return this.requestJoinCredentials$("connect", input).pipe(switchMap((refreshedCredentials) => refreshedCredentials === null ? EMPTY : this.observeThread$(this.applyCanonicalRunIdentity(input, refreshedCredentials, { fallbackToInputRunId: options.streamMode === "run" }), refreshedCredentials, {
366
+ const replayCursor = this.getReconnectCursor(input);
367
+ return this.requestJoinCredentials$("connect", input, replayCursor).pipe(switchMap((refreshedCredentials) => refreshedCredentials === null ? EMPTY : this.observeThread$(this.applyCanonicalRunIdentity(input, refreshedCredentials, { fallbackToInputRunId: options.streamMode === "run" }), refreshedCredentials, {
364
368
  ...options,
365
369
  channelMode: "connect",
366
- replayCursor: this.getReconnectCursor(input)
370
+ replayCursor
367
371
  })));
368
372
  }));
369
373
  }
@@ -400,15 +404,23 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
400
404
  bufferSize: 1,
401
405
  refCount: true
402
406
  }));
403
- const threadEvents$ = this.observeThreadEvents$(input.threadId, channel$, options).pipe(share());
404
- const replayComplete$ = this.observeControlEvent$(input.threadId, channel$, REPLAY_COMPLETE_EVENT).pipe(ignoreElements(), share());
407
+ const reconnectCursor = this.readDurableEventId(options.replayCursor ?? this.getReconnectCursor(input));
408
+ let latestObservedReplayCursor = null;
409
+ const threadEvents$ = this.observeThreadEvents$(input.threadId, channel$, options).pipe(tap((payload) => {
410
+ latestObservedReplayCursor = this.readEventId(payload) ?? latestObservedReplayCursor;
411
+ }), share());
412
+ const replayComplete$ = this.observeControlEvent$(input.threadId, channel$, REPLAY_COMPLETE_EVENT).pipe(shareReplay({
413
+ bufferSize: 1,
414
+ refCount: true
415
+ }));
405
416
  const streamIdle$ = this.observeControlEvent$(input.threadId, channel$, STREAM_IDLE_EVENT).pipe(shareReplay({
406
417
  bufferSize: 1,
407
418
  refCount: true
408
419
  }));
409
- const streamIdleCompletion$ = options.streamMode === "connect" ? streamIdle$.pipe(take(1)) : EMPTY;
410
- const terminal$ = merge(threadEvents$.pipe(ignoreElements(), endWith(null), take(1)), streamIdleCompletion$);
411
- return merge(this.joinThreadChannel$(channel$), this.observeSocketHealth$(socket$).pipe(takeUntil(terminal$)), threadEvents$.pipe(takeUntil(streamIdleCompletion$)), replayComplete$.pipe(takeUntil(terminal$)), streamIdleCompletion$.pipe(ignoreElements())).pipe(finalize(() => this.cleanupOwned(ownChannel, ownSocket)));
420
+ const streamIdleCompletion$ = options.streamMode === "connect" ? merge(combineLatest([replayComplete$.pipe(take(1)), streamIdle$.pipe(take(1))]), streamIdle$.pipe(take(1), filter((payload) => this.canFallbackCompleteConnect(payload, reconnectCursor, latestObservedReplayCursor)), delay(CONNECT_STREAM_IDLE_REPLAY_FALLBACK_MS))).pipe(take(1)) : EMPTY;
421
+ const threadCompleted$ = threadEvents$.pipe(ignoreElements(), endWith(null), take(1));
422
+ const terminal$ = merge(threadCompleted$, streamIdleCompletion$);
423
+ return merge(this.joinThreadChannel$(channel$), this.observeSocketHealth$(socket$).pipe(takeUntil(terminal$)), threadEvents$.pipe(takeUntil(streamIdleCompletion$)), replayComplete$.pipe(ignoreElements(), takeUntil(terminal$)), streamIdleCompletion$.pipe(ignoreElements(), takeUntil(threadCompleted$))).pipe(finalize(() => this.cleanupOwned(ownChannel, ownSocket)));
412
424
  });
413
425
  }
414
426
  joinThreadChannel$(channel$) {
@@ -474,23 +486,36 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
474
486
  updateLastSeenEventId(threadId, payload) {
475
487
  const eventId = this.readEventId(payload);
476
488
  if (!eventId) return;
477
- this.sharedState.lastSeenEventIds.set(threadId, eventId);
489
+ this.advanceLastSeenEventId(threadId, eventId);
478
490
  }
479
491
  updateLastSeenEventIdFromControl(threadId, payload) {
480
492
  const eventId = this.readControlEventId(payload);
481
493
  if (!eventId) return;
494
+ this.advanceLastSeenEventId(threadId, eventId);
495
+ }
496
+ advanceLastSeenEventId(threadId, eventId) {
482
497
  this.sharedState.lastSeenEventIds.set(threadId, eventId);
483
498
  }
484
499
  readEventId(payload) {
485
500
  const metadata = payload.metadata;
486
501
  if (!metadata || typeof metadata !== "object") return null;
487
- const runnerEventId = metadata.cpki_event_id;
488
- return typeof runnerEventId === "string" ? runnerEventId : null;
502
+ const eventMetadata = metadata;
503
+ return this.readDurableEventId(eventMetadata.cpki_event_id);
489
504
  }
490
505
  readControlEventId(payload) {
491
506
  if (!payload || typeof payload !== "object") return null;
492
- const latestEventId = payload.latestEventId;
493
- return typeof latestEventId === "string" ? latestEventId : null;
507
+ const controlPayload = payload;
508
+ const latestEventId = controlPayload.latestEventId ?? controlPayload.latest_event_id;
509
+ return this.readDurableEventId(latestEventId);
510
+ }
511
+ readDurableEventId(eventId) {
512
+ if (typeof eventId !== "string" || eventId.trim() === "") return null;
513
+ return eventId.startsWith("cpki_ingested") ? null : eventId;
514
+ }
515
+ canFallbackCompleteConnect(streamIdlePayload, reconnectCursor, latestObservedReplayCursor) {
516
+ const idleCursor = this.readControlEventId(streamIdlePayload);
517
+ if (!idleCursor) return true;
518
+ return idleCursor === reconnectCursor || idleCursor === latestObservedReplayCursor;
494
519
  }
495
520
  applyCanonicalRunIdentity(input, credentials, options) {
496
521
  this.threadId = credentials.threadId;
@@ -1659,7 +1684,9 @@ var RunHandler = class {
1659
1684
  * downstream churn into duplicate `cpki_event_id` rows in the
1660
1685
  * inspector and intermittent "Message not found" toasts.
1661
1686
  */
1662
- _lastConnectedThreadId = null;
1687
+ _lastConnectedThreadIdsByAgent = /* @__PURE__ */ new Map();
1688
+ _anonymousAgentIds = /* @__PURE__ */ new WeakMap();
1689
+ _nextAnonymousAgentId = 0;
1663
1690
  constructor(core) {
1664
1691
  this.core = core;
1665
1692
  }
@@ -1679,6 +1706,20 @@ var RunHandler = class {
1679
1706
  return this.core;
1680
1707
  }
1681
1708
  /**
1709
+ * Return a stable restore-tracking key for the logical agent being
1710
+ * connected. Named agents share their last-thread marker across proxy
1711
+ * instances; anonymous agents fall back to object identity.
1712
+ */
1713
+ getConnectRestoreKey(agent) {
1714
+ if (agent.agentId) return `agent:${agent.agentId}`;
1715
+ const existing = this._anonymousAgentIds.get(agent);
1716
+ if (existing) return existing;
1717
+ const anonymousId = `anonymous:${this._nextAnonymousAgentId}`;
1718
+ this._nextAnonymousAgentId += 1;
1719
+ this._anonymousAgentIds.set(agent, anonymousId);
1720
+ return anonymousId;
1721
+ }
1722
+ /**
1682
1723
  * Get all tools as a readonly array
1683
1724
  */
1684
1725
  get tools() {
@@ -1734,14 +1775,18 @@ var RunHandler = class {
1734
1775
  async connectAgent({ agent }) {
1735
1776
  try {
1736
1777
  const incomingThreadId = agent.threadId ?? null;
1737
- const isFreshRestore = incomingThreadId !== this._lastConnectedThreadId;
1738
- this._lastConnectedThreadId = incomingThreadId;
1778
+ const restoreKey = this.getConnectRestoreKey(agent);
1779
+ const isFreshRestore = incomingThreadId !== (this._lastConnectedThreadIdsByAgent.get(restoreKey) ?? null);
1780
+ this._lastConnectedThreadIdsByAgent.set(restoreKey, incomingThreadId);
1739
1781
  await agent.detachActiveRun();
1740
1782
  if (isFreshRestore) {
1741
1783
  agent.setMessages([]);
1742
1784
  agent.setState({});
1743
- const proxied = agent;
1744
- if (incomingThreadId && typeof proxied.clearReplayCursor === "function") proxied.clearReplayCursor(incomingThreadId);
1785
+ const cursorAware = agent;
1786
+ if (incomingThreadId) {
1787
+ if (typeof cursorAware.clearReplayCursor === "function") cursorAware.clearReplayCursor(incomingThreadId);
1788
+ if (typeof cursorAware.clearReconnectCursor === "function") cursorAware.clearReconnectCursor(incomingThreadId);
1789
+ }
1745
1790
  }
1746
1791
  this._internal.applyHeadersToAgent(agent);
1747
1792
  await this._internal.notifySubscribers((subscriber) => subscriber.onAgentRunStarted?.({
@@ -1755,7 +1800,8 @@ var RunHandler = class {
1755
1800
  }, this.createAgentErrorSubscriber(agent));
1756
1801
  return this.processAgentResult({
1757
1802
  runAgentResult,
1758
- agent
1803
+ agent,
1804
+ executeFrontendTools: false
1759
1805
  });
1760
1806
  } catch (error) {
1761
1807
  const connectError = error instanceof Error ? error : new Error(String(error));
@@ -1777,7 +1823,7 @@ var RunHandler = class {
1777
1823
  /**
1778
1824
  * Run an agent
1779
1825
  */
1780
- async runAgent({ agent, forwardedProps, resume }) {
1826
+ async runAgent({ agent, forwardedProps, resume, runId }) {
1781
1827
  if (agent.agentId) this._internal.suggestionEngine.clearSuggestions(agent.agentId);
1782
1828
  this._internal.applyHeadersToAgent(agent);
1783
1829
  if (agent.detachActiveRun) await agent.detachActiveRun();
@@ -1804,6 +1850,7 @@ var RunHandler = class {
1804
1850
  ...forwardedProps
1805
1851
  },
1806
1852
  ...resume !== void 0 ? { resume } : {},
1853
+ ...runId !== void 0 ? { runId } : {},
1807
1854
  tools: this.buildFrontendTools(agent.agentId),
1808
1855
  context: this._internal.getContextForAgent(agent.agentId)
1809
1856
  }, this.createAgentErrorSubscriber(agent));
@@ -1832,25 +1879,27 @@ var RunHandler = class {
1832
1879
  /**
1833
1880
  * Process agent result and execute tools
1834
1881
  */
1835
- async processAgentResult({ runAgentResult, agent }) {
1882
+ async processAgentResult({ runAgentResult, agent, executeFrontendTools = true }) {
1836
1883
  const { newMessages } = runAgentResult;
1837
1884
  const agentId = agent.agentId;
1838
1885
  let needsFollowUp = false;
1839
- for (const message of newMessages) if (message.role === "assistant") {
1840
- for (const toolCall of message.toolCalls || []) if (newMessages.findIndex((m) => m.role === "tool" && m.toolCallId === toolCall.id) === -1) {
1841
- const tool = this.getTool({
1842
- toolName: toolCall.function.name,
1843
- agentId: agent.agentId
1844
- });
1845
- if (tool) {
1846
- if (await this.executeSpecificTool(tool, toolCall, message, agent, agentId)) needsFollowUp = true;
1847
- } else {
1848
- const wildcardTool = this.getTool({
1849
- toolName: "*",
1886
+ if (executeFrontendTools) {
1887
+ for (const message of newMessages) if (message.role === "assistant") {
1888
+ for (const toolCall of message.toolCalls || []) if (newMessages.findIndex((m) => m.role === "tool" && m.toolCallId === toolCall.id) === -1) {
1889
+ const tool = this.getTool({
1890
+ toolName: toolCall.function.name,
1850
1891
  agentId: agent.agentId
1851
1892
  });
1852
- if (wildcardTool) {
1853
- if (await this.executeWildcardTool(wildcardTool, toolCall, message, agent, agentId)) needsFollowUp = true;
1893
+ if (tool) {
1894
+ if (await this.executeSpecificTool(tool, toolCall, message, agent, agentId)) needsFollowUp = true;
1895
+ } else {
1896
+ const wildcardTool = this.getTool({
1897
+ toolName: "*",
1898
+ agentId: agent.agentId
1899
+ });
1900
+ if (wildcardTool) {
1901
+ if (await this.executeWildcardTool(wildcardTool, toolCall, message, agent, agentId)) needsFollowUp = true;
1902
+ }
1854
1903
  }
1855
1904
  }
1856
1905
  }
@@ -2460,6 +2509,124 @@ var StateManager = class {
2460
2509
  }
2461
2510
  };
2462
2511
 
2512
+ //#endregion
2513
+ //#region src/core/subagent-registry.ts
2514
+ /**
2515
+ * Tracks subagent lifecycle events (SUBAGENT_STARTED / FINISHED / ERROR) per
2516
+ * owning agent so consumers can resolve a message's `subagentId` to a name,
2517
+ * description, and running status.
2518
+ *
2519
+ * Modeled on {@link SuggestionEngine}: an in-memory `Record` keyed by (owning)
2520
+ * agentId, mutated from streamed AG-UI events, made reactive by calling
2521
+ * `notifySubscribers(onSubagentsChanged)` after every change. It owns its own
2522
+ * per-agent `agent.subscribe` (like {@link StateManager}) so it is fully
2523
+ * self-contained and adds no coupling to the state/message tracking path.
2524
+ *
2525
+ * No-op for runs without subagents (a normal LangGraph run never emits
2526
+ * SUBAGENT_* events), so the registry simply stays empty.
2527
+ */
2528
+ var SubagentRegistry = class {
2529
+ _subagents = {};
2530
+ agentSubscriptions = /* @__PURE__ */ new Map();
2531
+ constructor(core) {
2532
+ this.core = core;
2533
+ }
2534
+ /**
2535
+ * Subscribe to an agent's subagent lifecycle events. Idempotent per agentId:
2536
+ * an existing subscription for the same agent is revoked and replaced.
2537
+ */
2538
+ subscribeToAgent(agent) {
2539
+ if (!agent.agentId) return;
2540
+ const agentId = agent.agentId;
2541
+ const existingUnsubscribe = this.agentSubscriptions.get(agentId);
2542
+ if (existingUnsubscribe) {
2543
+ existingUnsubscribe();
2544
+ this.agentSubscriptions.delete(agentId);
2545
+ }
2546
+ let revoked = false;
2547
+ const { unsubscribe } = agent.subscribe({
2548
+ onSubagentStartedEvent: ({ event }) => {
2549
+ if (revoked) return;
2550
+ this.handleStarted(agentId, event);
2551
+ },
2552
+ onSubagentFinishedEvent: ({ event }) => {
2553
+ if (revoked) return;
2554
+ this.handleFinished(agentId, event);
2555
+ },
2556
+ onSubagentErrorEvent: ({ event }) => {
2557
+ if (revoked) return;
2558
+ this.handleError(agentId, event);
2559
+ }
2560
+ });
2561
+ this.agentSubscriptions.set(agentId, () => {
2562
+ revoked = true;
2563
+ unsubscribe();
2564
+ });
2565
+ }
2566
+ /**
2567
+ * Unsubscribe an agent's subscription (symmetric with StateManager cleanup).
2568
+ */
2569
+ unsubscribeFromAgent(agentId) {
2570
+ const unsubscribe = this.agentSubscriptions.get(agentId);
2571
+ if (unsubscribe) {
2572
+ unsubscribe();
2573
+ this.agentSubscriptions.delete(agentId);
2574
+ }
2575
+ }
2576
+ /**
2577
+ * Get the subagents observed for an owning agent, keyed by subagentId.
2578
+ * Returns a fresh shallow copy each call so consumers (and React state) see
2579
+ * a new reference when anything changed.
2580
+ */
2581
+ getSubagents(agentId) {
2582
+ return { ...this._subagents[agentId] };
2583
+ }
2584
+ handleStarted(agentId, event) {
2585
+ const bucket = this._subagents[agentId] ??= {};
2586
+ bucket[event.subagentId] = {
2587
+ subagentId: event.subagentId,
2588
+ name: event.name,
2589
+ description: event.description,
2590
+ parentSubagentId: event.parentSubagentId,
2591
+ status: "running"
2592
+ };
2593
+ this.notifySubagentsChanged(agentId);
2594
+ }
2595
+ handleFinished(agentId, event) {
2596
+ const bucket = this._subagents[agentId];
2597
+ const existing = bucket?.[event.subagentId];
2598
+ if (!bucket || !existing) return;
2599
+ bucket[event.subagentId] = {
2600
+ ...existing,
2601
+ status: "finished"
2602
+ };
2603
+ this.notifySubagentsChanged(agentId);
2604
+ }
2605
+ handleError(agentId, event) {
2606
+ const bucket = this._subagents[agentId] ??= {};
2607
+ const existing = bucket[event.subagentId];
2608
+ bucket[event.subagentId] = existing ? {
2609
+ ...existing,
2610
+ status: "error",
2611
+ error: event.message
2612
+ } : {
2613
+ subagentId: event.subagentId,
2614
+ name: event.subagentId,
2615
+ status: "error",
2616
+ error: event.message
2617
+ };
2618
+ this.notifySubagentsChanged(agentId);
2619
+ }
2620
+ async notifySubagentsChanged(agentId) {
2621
+ const subagents = this.getSubagents(agentId);
2622
+ await this.core.notifySubscribers((subscriber) => subscriber.onSubagentsChanged?.({
2623
+ copilotkit: this.core,
2624
+ agentId,
2625
+ subagents
2626
+ }), "Subscriber onSubagentsChanged error:");
2627
+ }
2628
+ };
2629
+
2463
2630
  //#endregion
2464
2631
  //#region src/core/thread-store-registry.ts
2465
2632
  var ThreadStoreRegistry = class {
@@ -2516,211 +2683,1205 @@ var ThreadStoreRegistry = class {
2516
2683
  };
2517
2684
 
2518
2685
  //#endregion
2519
- //#region src/core/core.ts
2520
- let CopilotKitCoreErrorCode = /* @__PURE__ */ function(CopilotKitCoreErrorCode) {
2521
- CopilotKitCoreErrorCode["RUNTIME_INFO_FETCH_FAILED"] = "runtime_info_fetch_failed";
2522
- CopilotKitCoreErrorCode["AGENT_CONNECT_FAILED"] = "agent_connect_failed";
2523
- CopilotKitCoreErrorCode["AGENT_RUN_FAILED"] = "agent_run_failed";
2524
- CopilotKitCoreErrorCode["AGENT_RUN_FAILED_EVENT"] = "agent_run_failed_event";
2525
- CopilotKitCoreErrorCode["AGENT_RUN_ERROR_EVENT"] = "agent_run_error_event";
2526
- CopilotKitCoreErrorCode["TOOL_ARGUMENT_PARSE_FAILED"] = "tool_argument_parse_failed";
2527
- CopilotKitCoreErrorCode["TOOL_HANDLER_FAILED"] = "tool_handler_failed";
2528
- CopilotKitCoreErrorCode["TOOL_NOT_FOUND"] = "tool_not_found";
2529
- CopilotKitCoreErrorCode["AGENT_NOT_FOUND"] = "agent_not_found";
2530
- /**
2531
- * Emitted when an agent run fails because the thread is already locked
2532
- * by another active run.
2533
- *
2534
- * @example
2535
- * ```tsx
2536
- * <CopilotKitProvider
2537
- * onError={({ code, error, context }) => {
2538
- * if (code === "agent_thread_locked") {
2539
- * // Show "Agent is busy, retry?" UI
2540
- * }
2541
- * }}
2542
- * />
2543
- * ```
2544
- */
2545
- CopilotKitCoreErrorCode["AGENT_THREAD_LOCKED"] = "agent_thread_locked";
2546
- CopilotKitCoreErrorCode["TRANSCRIPTION_FAILED"] = "transcription_failed";
2547
- CopilotKitCoreErrorCode["TRANSCRIPTION_SERVICE_NOT_CONFIGURED"] = "transcription_service_not_configured";
2548
- CopilotKitCoreErrorCode["TRANSCRIPTION_INVALID_AUDIO"] = "transcription_invalid_audio";
2549
- CopilotKitCoreErrorCode["TRANSCRIPTION_RATE_LIMITED"] = "transcription_rate_limited";
2550
- CopilotKitCoreErrorCode["TRANSCRIPTION_AUTH_FAILED"] = "transcription_auth_failed";
2551
- CopilotKitCoreErrorCode["TRANSCRIPTION_NETWORK_ERROR"] = "transcription_network_error";
2552
- CopilotKitCoreErrorCode["SUBSCRIBER_CALLBACK_FAILED"] = "subscriber_callback_failed";
2553
- return CopilotKitCoreErrorCode;
2554
- }({});
2686
+ //#region src/utils/micro-redux.ts
2687
+ const INTERNAL_ACTION_TYPES = {
2688
+ boot: "@@micro-redux/boot",
2689
+ init: "@@micro-redux/init",
2690
+ stop: "@@micro-redux/stop"
2691
+ };
2692
+ const INTERNAL_BOOT_ACTION = { type: INTERNAL_ACTION_TYPES.boot };
2555
2693
  /**
2556
- * Runtime allowlist derived from {@link SUBSCRIBE_TO_AGENT_KEYS}. Hoisted
2557
- * to module scope so the Set is allocated once, not per-subscription.
2694
+ * Builds a typed action creator from a type string and payload factory.
2558
2695
  */
2559
- const ALLOWED_KEYS = new Set([
2560
- "onMessagesChanged",
2561
- "onStateChanged",
2562
- "onRunInitialized",
2563
- "onRunFinalized",
2564
- "onRunFailed",
2565
- "onRunErrorEvent"
2566
- ]);
2567
- let CopilotKitCoreRuntimeConnectionStatus = /* @__PURE__ */ function(CopilotKitCoreRuntimeConnectionStatus) {
2568
- CopilotKitCoreRuntimeConnectionStatus["Disconnected"] = "disconnected";
2569
- CopilotKitCoreRuntimeConnectionStatus["Connected"] = "connected";
2570
- CopilotKitCoreRuntimeConnectionStatus["Connecting"] = "connecting";
2571
- CopilotKitCoreRuntimeConnectionStatus["Error"] = "error";
2572
- return CopilotKitCoreRuntimeConnectionStatus;
2573
- }({});
2696
+ function createTypedActionCreator(type, factory) {
2697
+ const creator = ((...args) => ({
2698
+ ...factory(...args),
2699
+ type
2700
+ }));
2701
+ creator.type = type;
2702
+ creator.match = (action) => action.type === type;
2703
+ return creator;
2704
+ }
2574
2705
  /**
2575
- * Normalize a header map to the internal invariant: a `Record<string, string>`
2576
- * with no `null`/`undefined` values. Entries whose value is `null`/`undefined`
2577
- * are dropped (this is how a header is cleared). Shared by the constructor and
2578
- * `setHeaders` so both write paths into `_headers` enforce the same invariant.
2706
+ * Declares a payload-based action config for `createActionGroup`.
2707
+ *
2708
+ * @example
2709
+ * ```ts
2710
+ * const actions = createActionGroup("User", {
2711
+ * loaded: props<{ id: string }>(),
2712
+ * });
2713
+ * ```
2579
2714
  */
2580
- function normalizeHeaders(headers) {
2581
- return Object.fromEntries(Object.entries(headers).filter((entry) => entry[1] != null));
2715
+ function props() {
2716
+ return { kind: "props" };
2582
2717
  }
2583
- var CopilotKitCore = class {
2584
- _headers;
2585
- _credentials;
2586
- _properties;
2587
- _defaultThrottleMs;
2588
- _debug;
2589
- subscribers = /* @__PURE__ */ new Set();
2590
- agentRegistry;
2591
- contextStore;
2592
- suggestionEngine;
2593
- runHandler;
2594
- stateManager;
2595
- threadStoreRegistry;
2596
- /**
2597
- * Tracks the agent IDs from the most recent `onAgentsChanged` notification.
2598
- * Used to gate thread-store auto-unregister so the FIRST empty-agents
2599
- * notification (before published agents are merged in) does not rip out a
2600
- * store that was registered prior to that initial notification.
2601
- */
2602
- previousAgentIds = /* @__PURE__ */ new Set();
2603
- constructor({ runtimeUrl, runtimeTransport = "auto", headers = {}, credentials, properties = {}, agents__unsafe_dev_only = {}, tools = [], suggestionsConfig = [], debug }) {
2604
- this._headers = normalizeHeaders(headers);
2605
- this._credentials = credentials;
2606
- this._properties = properties;
2607
- this._debug = debug;
2608
- this.agentRegistry = new AgentRegistry(this);
2609
- this.contextStore = new ContextStore(this);
2610
- this.suggestionEngine = new SuggestionEngine(this);
2611
- this.runHandler = new RunHandler(this);
2612
- this.stateManager = new StateManager(this);
2613
- this.threadStoreRegistry = new ThreadStoreRegistry(this);
2614
- this.agentRegistry.initialize(agents__unsafe_dev_only);
2615
- this.runHandler.initialize(tools);
2616
- this.suggestionEngine.initialize(suggestionsConfig);
2617
- this.stateManager.initialize();
2618
- this.agentRegistry.setRuntimeTransport(runtimeTransport);
2619
- this.agentRegistry.setRuntimeUrl(runtimeUrl);
2620
- this.previousAgentIds = new Set(Object.keys(agents__unsafe_dev_only));
2621
- this.subscribe({ onAgentsChanged: ({ agents }) => {
2622
- Object.values(agents).forEach((agent) => {
2623
- if (agent.agentId) this.stateManager.subscribeToAgent(agent);
2624
- });
2625
- const currentAgentIds = new Set(Object.keys(agents));
2626
- for (const agentId of Object.keys(this.threadStoreRegistry.getAll())) if (this.previousAgentIds.has(agentId) && !currentAgentIds.has(agentId)) try {
2627
- this.threadStoreRegistry.unregister(agentId);
2628
- } catch (err) {
2629
- console.error(`CopilotKitCore.onAgentsChanged: threadStoreRegistry.unregister failed for "${agentId}":`, err);
2630
- }
2631
- for (const agentId of this.previousAgentIds) if (!currentAgentIds.has(agentId)) try {
2632
- this.stateManager.unsubscribeFromAgent(agentId);
2633
- } catch (err) {
2634
- console.error(`CopilotKitCore.onAgentsChanged: stateManager.unsubscribeFromAgent failed for "${agentId}":`, err);
2635
- }
2636
- this.previousAgentIds = currentAgentIds;
2637
- } });
2638
- }
2639
- /**
2640
- * Internal method used by delegate classes and subclasses to notify subscribers
2641
- */
2642
- async notifySubscribers(handler, errorMessage) {
2643
- await Promise.all(Array.from(this.subscribers).map(async (subscriber) => {
2644
- try {
2645
- await handler(subscriber);
2646
- } catch (error) {
2647
- console.error(errorMessage, error);
2648
- }
2649
- }));
2718
+ /**
2719
+ * Declares a no-payload action config for `createActionGroup`.
2720
+ *
2721
+ * @example
2722
+ * ```ts
2723
+ * const actions = createActionGroup("User", {
2724
+ * reset: empty(),
2725
+ * });
2726
+ * ```
2727
+ */
2728
+ function empty() {
2729
+ return { kind: "empty" };
2730
+ }
2731
+ /**
2732
+ * Creates a namespaced group of typed action creators.
2733
+ *
2734
+ * Action types are formatted as: `[Source] actionName`.
2735
+ */
2736
+ function createActionGroup(source, config) {
2737
+ const group = {};
2738
+ for (const eventName of Object.keys(config)) {
2739
+ const eventConfig = config[eventName];
2740
+ if (!eventConfig) continue;
2741
+ const actionType = `[${source}] ${eventName}`;
2742
+ if (eventConfig.kind === "props") {
2743
+ group[eventName] = createTypedActionCreator(actionType, (payload) => ({ ...payload }));
2744
+ continue;
2745
+ }
2746
+ group[eventName] = createTypedActionCreator(actionType, () => ({}));
2650
2747
  }
2651
- /**
2652
- * Internal method used by delegate classes to emit errors
2653
- */
2654
- async emitError({ error, code, context = {} }) {
2655
- await this.notifySubscribers((subscriber) => subscriber.onError?.({
2656
- copilotkit: this,
2657
- error,
2658
- code,
2659
- context
2660
- }), "Subscriber onError error:");
2661
- }
2662
- /**
2663
- * Log a message to the console and emit an error to subscribers.
2664
- * Catches failures from `emitError` itself to prevent unhandled rejections.
2665
- */
2666
- logAndEmitError(message, params, logLevel = "error") {
2667
- console[logLevel](message, params.error);
2668
- this.emitError(params).catch((emitErr) => {
2669
- console.error(message + " — emitError itself failed:", emitErr);
2670
- });
2671
- }
2672
- /**
2673
- * Snapshot accessors
2674
- */
2675
- get context() {
2676
- return this.contextStore.context;
2677
- }
2678
- get agents() {
2679
- return this.agentRegistry.agents;
2680
- }
2681
- get tools() {
2682
- return this.runHandler.tools;
2683
- }
2684
- get runtimeUrl() {
2685
- return this.agentRegistry.runtimeUrl;
2686
- }
2687
- setRuntimeUrl(runtimeUrl) {
2688
- this.agentRegistry.setRuntimeUrl(runtimeUrl);
2689
- }
2690
- get runtimeTransport() {
2691
- return this.agentRegistry.runtimeTransport;
2692
- }
2693
- setRuntimeTransport(runtimeTransport) {
2694
- this.agentRegistry.setRuntimeTransport(runtimeTransport);
2695
- }
2696
- get runtimeVersion() {
2697
- return this.agentRegistry.runtimeVersion;
2698
- }
2699
- get headers() {
2700
- return this._headers;
2748
+ return group;
2749
+ }
2750
+ /**
2751
+ * Registers one reducer handler for one or more action creators.
2752
+ *
2753
+ * @throws Error when called without at least one action creator and reducer.
2754
+ */
2755
+ function on(...args) {
2756
+ if (args.length < 2) throw new Error("on requires at least one action creator and one reducer");
2757
+ const reducer = args[args.length - 1];
2758
+ return {
2759
+ creators: args.slice(0, -1),
2760
+ reducer
2761
+ };
2762
+ }
2763
+ /**
2764
+ * Creates a reducer from an initial state and `on(...)` handler entries.
2765
+ *
2766
+ * Unknown action types return the current state unchanged.
2767
+ */
2768
+ function createReducer(initialState, ...entries) {
2769
+ const reducerMap = /* @__PURE__ */ new Map();
2770
+ for (const entry of entries) for (const creator of entry.creators) {
2771
+ const handlers = reducerMap.get(creator.type) ?? [];
2772
+ handlers.push(entry.reducer);
2773
+ reducerMap.set(creator.type, handlers);
2701
2774
  }
2702
- get credentials() {
2703
- return this._credentials;
2775
+ return (state, action) => {
2776
+ const currentState = state ?? initialState;
2777
+ const handlers = reducerMap.get(action.type);
2778
+ if (!handlers || handlers.length === 0) return currentState;
2779
+ let nextState = currentState;
2780
+ for (const handler of handlers) nextState = handler(nextState, action);
2781
+ return nextState;
2782
+ };
2783
+ }
2784
+ /**
2785
+ * Creates a selector that caches and reuses the last computed result
2786
+ * when all input references are unchanged.
2787
+ */
2788
+ function createSelector(...args) {
2789
+ if (args.length === 1) {
2790
+ const projector = args[0];
2791
+ let hasCached = false;
2792
+ let lastState;
2793
+ let lastResult;
2794
+ return (state) => {
2795
+ if (hasCached && state === lastState) return lastResult;
2796
+ lastState = state;
2797
+ lastResult = projector(state);
2798
+ hasCached = true;
2799
+ return lastResult;
2800
+ };
2704
2801
  }
2705
- get properties() {
2706
- return this._properties;
2802
+ const projector = args[args.length - 1];
2803
+ const selectors = args.slice(0, -1);
2804
+ let hasCached = false;
2805
+ let lastInputs = [];
2806
+ let lastResult;
2807
+ return (state) => {
2808
+ const inputs = selectors.map((selector) => selector(state));
2809
+ if (hasCached && inputs.length === lastInputs.length && inputs.every((value, index) => value === lastInputs[index])) return lastResult;
2810
+ lastInputs = inputs;
2811
+ lastResult = projector(...inputs);
2812
+ hasCached = true;
2813
+ return lastResult;
2814
+ };
2815
+ }
2816
+ /**
2817
+ * RxJS operator that maps state emissions through a selector and suppresses
2818
+ * unchanged projected values via reference equality.
2819
+ */
2820
+ function select(selector) {
2821
+ return (source$) => source$.pipe(map(selector), distinctUntilChanged());
2822
+ }
2823
+ /**
2824
+ * RxJS operator that filters an action stream by action creators and narrows
2825
+ * the output action type to the matched creator union.
2826
+ *
2827
+ * @throws Error when called without at least one action creator.
2828
+ */
2829
+ function ofType(...creators) {
2830
+ if (creators.length === 0) throw new Error("ofType requires at least one action creator");
2831
+ const actionTypes = new Set(creators.map((creator) => creator.type));
2832
+ return (source$) => {
2833
+ return source$.pipe(filter((action) => {
2834
+ return actionTypes.has(action.type);
2835
+ }));
2836
+ };
2837
+ }
2838
+ /**
2839
+ * Creates an effect descriptor consumed by `createStore`.
2840
+ */
2841
+ function createEffect(factory, options = {}) {
2842
+ if (options.dispatch === false) return {
2843
+ run: factory,
2844
+ dispatch: false
2845
+ };
2846
+ return {
2847
+ run: factory,
2848
+ dispatch: true
2849
+ };
2850
+ }
2851
+ /**
2852
+ * Creates a small observable store with reducer + effects.
2853
+ *
2854
+ * Behavior:
2855
+ * - `init()` starts effects and dispatches `@@micro-redux/init`.
2856
+ * - `stop()` dispatches `@@micro-redux/stop` and unsubscribes all effects.
2857
+ * - Effect action observation is scheduled on `asapScheduler` to avoid
2858
+ * synchronous re-entrancy in the effect loop.
2859
+ * - Any effect error triggers fail-fast teardown and errors both `actions$`
2860
+ * and `state$`.
2861
+ */
2862
+ function createStore(options) {
2863
+ const reducer = options.reducer;
2864
+ const effects = options.effects ?? [];
2865
+ let hasFatalError = false;
2866
+ let isRunning = false;
2867
+ let effectSubscriptions = new Subscription();
2868
+ let currentState = reducer(void 0, INTERNAL_BOOT_ACTION);
2869
+ const stateSubject = new BehaviorSubject(currentState);
2870
+ const actionsSubject = new Subject();
2871
+ const dispatchInternal = (action) => {
2872
+ if (hasFatalError) throw new Error("Store is in a failed state due to an effect error");
2873
+ currentState = reducer(currentState, action);
2874
+ stateSubject.next(currentState);
2875
+ actionsSubject.next(action);
2876
+ };
2877
+ const failFast = (error) => {
2878
+ if (hasFatalError) return;
2879
+ hasFatalError = true;
2880
+ isRunning = false;
2881
+ effectSubscriptions.unsubscribe();
2882
+ effectSubscriptions = new Subscription();
2883
+ actionsSubject.error(error);
2884
+ stateSubject.error(error);
2885
+ };
2886
+ const startEffects = () => {
2887
+ for (const effect of effects) {
2888
+ const scheduledActions$ = actionsSubject.asObservable().pipe(observeOn$1(asapScheduler));
2889
+ const state$ = stateSubject.asObservable();
2890
+ if (effect.dispatch) {
2891
+ const subscription = effect.run(scheduledActions$, state$).subscribe({
2892
+ next: (effectAction) => {
2893
+ if (hasFatalError) return;
2894
+ dispatchInternal(effectAction);
2895
+ },
2896
+ error: (error) => {
2897
+ failFast(error);
2898
+ }
2899
+ });
2900
+ effectSubscriptions.add(subscription);
2901
+ continue;
2902
+ }
2903
+ const subscription = effect.run(scheduledActions$, state$).subscribe({ error: (error) => {
2904
+ failFast(error);
2905
+ } });
2906
+ effectSubscriptions.add(subscription);
2907
+ }
2908
+ };
2909
+ return {
2910
+ dispatch(action) {
2911
+ dispatchInternal(action);
2912
+ },
2913
+ getState() {
2914
+ return currentState;
2915
+ },
2916
+ get state$() {
2917
+ return stateSubject.asObservable();
2918
+ },
2919
+ get actions$() {
2920
+ return actionsSubject.asObservable();
2921
+ },
2922
+ select(selector) {
2923
+ return stateSubject.asObservable().pipe(select(selector));
2924
+ },
2925
+ init() {
2926
+ if (hasFatalError || isRunning) return;
2927
+ isRunning = true;
2928
+ startEffects();
2929
+ if (hasFatalError) return;
2930
+ dispatchInternal({ type: INTERNAL_ACTION_TYPES.init });
2931
+ },
2932
+ stop() {
2933
+ if (hasFatalError || !isRunning) return;
2934
+ dispatchInternal({ type: INTERNAL_ACTION_TYPES.stop });
2935
+ effectSubscriptions.unsubscribe();
2936
+ effectSubscriptions = new Subscription();
2937
+ isRunning = false;
2938
+ }
2939
+ };
2940
+ }
2941
+
2942
+ //#endregion
2943
+ //#region src/memory-errors.ts
2944
+ /**
2945
+ * The memory error registry: the single source of truth for memory error
2946
+ * metadata. Keyed by `code` so a code maps to exactly one category/retryable
2947
+ * default.
2948
+ *
2949
+ * Categories/retryable rationale:
2950
+ * - List/credentials/mutation failures are `dependency` (the runtime/platform
2951
+ * the store calls failed). A 5xx is transient (`retryable: true`); a 4xx is
2952
+ * caller-caused, so the helpers below downgrade those to non-retryable.
2953
+ * - Timeouts are `dependency` and `retryable: true` (the request may simply be
2954
+ * slow).
2955
+ */
2956
+ const MEMORY_ERROR_REGISTRY = Object.freeze({
2957
+ MEMORY_LIST_FAILED: {
2958
+ code: "MEMORY_LIST_FAILED",
2959
+ category: "dependency",
2960
+ retryable: true,
2961
+ message: "Failed to fetch memories",
2962
+ docsPath: "docs/errors/memory.md#memory_list_failed"
2963
+ },
2964
+ MEMORY_CREDENTIALS_FAILED: {
2965
+ code: "MEMORY_CREDENTIALS_FAILED",
2966
+ category: "dependency",
2967
+ retryable: true,
2968
+ message: "Failed to fetch memory subscribe credentials",
2969
+ docsPath: "docs/errors/memory.md#memory_credentials_failed"
2970
+ },
2971
+ MEMORY_MUTATION_FAILED: {
2972
+ code: "MEMORY_MUTATION_FAILED",
2973
+ category: "dependency",
2974
+ retryable: true,
2975
+ message: "Memory mutation request failed",
2976
+ docsPath: "docs/errors/memory.md#memory_mutation_failed"
2977
+ },
2978
+ MEMORY_REQUEST_TIMEOUT: {
2979
+ code: "MEMORY_REQUEST_TIMEOUT",
2980
+ category: "dependency",
2981
+ retryable: true,
2982
+ message: "Request timed out",
2983
+ docsPath: "docs/errors/memory.md#memory_request_timeout"
2707
2984
  }
2985
+ });
2986
+ /**
2987
+ * An `Error` carrying a stable memory error `code`, `category`, and `retryable`
2988
+ * flag so consumers and observability can branch on it without string-matching
2989
+ * the message.
2990
+ *
2991
+ * It is a real `Error` subclass: `instanceof Error` is true, so the public
2992
+ * `MemoryState.error: Error | null` contract (and the `error: Error | null`
2993
+ * surfaced by `useMemories` / `injectMemories`) holds unchanged. Existing
2994
+ * consumers that only read `.message` keep working; the richer fields are
2995
+ * additive.
2996
+ */
2997
+ var MemoryError = class MemoryError extends Error {
2998
+ /** Stable, machine-readable error identifier. */
2999
+ code;
3000
+ /** Coarse error category for grouping/observability. */
3001
+ category;
3002
+ /** Whether retrying the operation could plausibly succeed. */
3003
+ retryable;
2708
3004
  /**
2709
- * Default throttle interval (ms) used by `subscribeToAgentWithOptions()`
2710
- * when the caller does not specify an explicit `throttleMs`.
2711
- * `undefined` means no default is configured; `0` means no throttling.
3005
+ * @param code - Registry code identifying this error.
3006
+ * @param options - Optional overrides:
3007
+ * - `message`: human-readable message (defaults to the registry message;
3008
+ * callers pass a status-bearing string to preserve prior wording).
3009
+ * - `retryable`: overrides the registry default (e.g. a 4xx list failure is
3010
+ * not retryable even though the code's default is).
3011
+ * - `cause`: the underlying error, preserved for debugging.
2712
3012
  */
2713
- get defaultThrottleMs() {
2714
- return this._defaultThrottleMs;
3013
+ constructor(code, options) {
3014
+ const entry = MEMORY_ERROR_REGISTRY[code];
3015
+ super(options?.message ?? entry.message, { cause: options?.cause });
3016
+ this.name = "MemoryError";
3017
+ this.code = code;
3018
+ this.category = entry.category;
3019
+ this.retryable = options?.retryable ?? entry.retryable;
3020
+ Object.setPrototypeOf(this, MemoryError.prototype);
2715
3021
  }
2716
- /**
2717
- * Set the default throttle interval (ms) for `subscribeToAgentWithOptions()`.
2718
- *
2719
- * Accepts a non-negative finite number or `undefined` (to clear the
2720
- * default). Invalid values (NaN, Infinity, negative) are logged as
2721
- * errors and ignored — the previous valid value is preserved.
2722
- */
2723
- setDefaultThrottleMs(value) {
3022
+ };
3023
+ /**
3024
+ * Narrows an HTTP status to whether the failure is worth retrying: a 5xx (or a
3025
+ * missing/0 status) is a transient dependency failure and retryable; a 4xx is
3026
+ * caller-caused and not. Used so a `MEMORY_LIST_FAILED`/`MEMORY_MUTATION_FAILED`
3027
+ * carries an accurate `retryable` flag derived from the response status.
3028
+ */
3029
+ function isRetryableStatus(status) {
3030
+ return !(Number.isInteger(status) && status >= 400 && status <= 499);
3031
+ }
3032
+
3033
+ //#endregion
3034
+ //#region src/memory.ts
3035
+ const MEMORIES_PATH = "/memories";
3036
+ const MEMORIES_SUBSCRIBE_PATH = "/memories/subscribe";
3037
+ const REQUEST_TIMEOUT_MS$1 = 15e3;
3038
+ /** Consecutive socket errors tolerated before the realtime stream gives up. */
3039
+ const MAX_SOCKET_RETRIES$1 = 5;
3040
+ /**
3041
+ * HTTP status codes that indicate memory routes are not configured (non-fatal).
3042
+ * 404/501 mean the route is absent/unimplemented; 422 is the runtime's
3043
+ * MISSING_INTELLIGENCE signal (intelligence not configured on the deployment —
3044
+ * see packages/runtime/.../handlers/intelligence/memories.ts). All three map to
3045
+ * the graceful "not available" locked-teaser path, not a hard error.
3046
+ */
3047
+ const ROUTE_UNAVAILABLE_STATUSES = new Set([
3048
+ 404,
3049
+ 422,
3050
+ 501
3051
+ ]);
3052
+ /**
3053
+ * Thrown when a memory route returns a 404/422/501 — treated as "not
3054
+ * configured" (422 is the runtime's MISSING_INTELLIGENCE signal).
3055
+ */
3056
+ var MemoryRouteUnavailableError = class extends Error {};
3057
+ const initialMemoryState = Object.freeze({
3058
+ memories: Object.freeze([]),
3059
+ isLoading: false,
3060
+ inFlightMutationCount: 0,
3061
+ error: null,
3062
+ context: null,
3063
+ sessionId: 0,
3064
+ available: true,
3065
+ realtimeStatus: "connecting"
3066
+ });
3067
+ const memoryAdapterEvents = createActionGroup("Memory Adapter", {
3068
+ started: empty(),
3069
+ stopped: empty(),
3070
+ contextChanged: props(),
3071
+ addRequested: props(),
3072
+ updateRequested: props(),
3073
+ removeRequested: props()
3074
+ });
3075
+ const memoryRestEvents = createActionGroup("Memory REST", {
3076
+ listRequested: props(),
3077
+ listSucceeded: props(),
3078
+ listFailed: props(),
3079
+ listUnavailable: props(),
3080
+ mutationFinished: props(),
3081
+ credentialsRequested: props(),
3082
+ credentialsSucceeded: props(),
3083
+ credentialsFailed: props(),
3084
+ credentialsUnavailable: props()
3085
+ });
3086
+ const memoryDomainEvents = createActionGroup("Memory Domain", {
3087
+ memoryUpserted: props(),
3088
+ memoryInvalidated: props(),
3089
+ realtimeConnecting: props(),
3090
+ realtimeConnected: props(),
3091
+ realtimeUnavailable: props()
3092
+ });
3093
+ /** Projects a wire memory to the public {@link Memory} shape (drops tenant ids). */
3094
+ function toMemory(memory) {
3095
+ return {
3096
+ id: memory.id,
3097
+ kind: memory.kind,
3098
+ scope: memory.scope,
3099
+ content: memory.content,
3100
+ sourceThreadIds: memory.sourceThreadIds,
3101
+ invalidatedAt: memory.invalidatedAt
3102
+ };
3103
+ }
3104
+ /**
3105
+ * Maps a realtime `memory_metadata` event to the domain action that applies it:
3106
+ * `created`/`updated` upsert the projected memory, `invalidated` removes it by
3107
+ * id. Carries the current `sessionId` so the reducer's session guard can drop
3108
+ * deltas left over from a previous context.
3109
+ */
3110
+ function mapMemoryMetadataEvent(event, sessionId) {
3111
+ if (event.operation === "invalidated") return memoryDomainEvents.memoryInvalidated({
3112
+ sessionId,
3113
+ memoryId: event.invalidated.id
3114
+ });
3115
+ return memoryDomainEvents.memoryUpserted({
3116
+ sessionId,
3117
+ memory: toMemory(event.memory)
3118
+ });
3119
+ }
3120
+ /**
3121
+ * Inserts or replaces a memory by id. A new memory is prepended (newest first,
3122
+ * matching the REST list's `created_at DESC` ordering without needing a date
3123
+ * field on the public projection); an existing memory is replaced in place.
3124
+ */
3125
+ function upsertMemory(memories, memory) {
3126
+ const existingIndex = memories.findIndex((item) => item.id === memory.id);
3127
+ if (existingIndex === -1) return [memory, ...memories];
3128
+ const next = [...memories];
3129
+ next[existingIndex] = memory;
3130
+ return next;
3131
+ }
3132
+ const memoryReducer = createReducer(initialMemoryState, on(memoryAdapterEvents.contextChanged, (state, { context }) => ({
3133
+ ...state,
3134
+ context,
3135
+ sessionId: state.sessionId + 1,
3136
+ memories: [],
3137
+ isLoading: Boolean(context),
3138
+ inFlightMutationCount: 0,
3139
+ error: null,
3140
+ available: true,
3141
+ realtimeStatus: "connecting"
3142
+ })), on(memoryAdapterEvents.stopped, (state) => ({
3143
+ ...state,
3144
+ memories: [],
3145
+ isLoading: false,
3146
+ inFlightMutationCount: 0,
3147
+ error: null,
3148
+ available: true,
3149
+ realtimeStatus: "connecting"
3150
+ })), on(memoryRestEvents.listRequested, (state, { sessionId }) => {
3151
+ if (sessionId !== state.sessionId || !state.context) return state;
3152
+ return {
3153
+ ...state,
3154
+ isLoading: true,
3155
+ error: null,
3156
+ available: true
3157
+ };
3158
+ }), on(memoryRestEvents.listFailed, (state, { sessionId, error }) => {
3159
+ if (sessionId !== state.sessionId) return state;
3160
+ return {
3161
+ ...state,
3162
+ isLoading: false,
3163
+ error
3164
+ };
3165
+ }), on(memoryRestEvents.listUnavailable, (state, { sessionId }) => {
3166
+ if (sessionId !== state.sessionId) return state;
3167
+ return {
3168
+ ...state,
3169
+ memories: [],
3170
+ isLoading: false,
3171
+ error: null,
3172
+ available: false
3173
+ };
3174
+ }), on(memoryAdapterEvents.addRequested, memoryAdapterEvents.updateRequested, memoryAdapterEvents.removeRequested, (state) => ({
3175
+ ...state,
3176
+ inFlightMutationCount: state.inFlightMutationCount + 1
3177
+ })), on(memoryRestEvents.mutationFinished, (state, { outcome }) => {
3178
+ if (outcome.sessionId !== state.sessionId) return state;
3179
+ return {
3180
+ ...state,
3181
+ inFlightMutationCount: Math.max(0, state.inFlightMutationCount - 1),
3182
+ error: outcome.ok ? null : outcome.error
3183
+ };
3184
+ }), on(memoryRestEvents.listSucceeded, (state, { sessionId, memories }) => {
3185
+ if (sessionId !== state.sessionId) return state;
3186
+ return {
3187
+ ...state,
3188
+ memories,
3189
+ isLoading: false,
3190
+ error: null,
3191
+ available: true
3192
+ };
3193
+ }), on(memoryDomainEvents.memoryUpserted, (state, { sessionId, memory }) => {
3194
+ if (sessionId !== state.sessionId) return state;
3195
+ return {
3196
+ ...state,
3197
+ memories: upsertMemory(state.memories, memory)
3198
+ };
3199
+ }), on(memoryDomainEvents.memoryInvalidated, (state, { sessionId, memoryId }) => {
3200
+ if (sessionId !== state.sessionId) return state;
3201
+ return {
3202
+ ...state,
3203
+ memories: state.memories.filter((memory) => memory.id !== memoryId)
3204
+ };
3205
+ }), on(memoryDomainEvents.realtimeConnecting, (state, { sessionId }) => {
3206
+ if (sessionId !== state.sessionId) return state;
3207
+ return {
3208
+ ...state,
3209
+ realtimeStatus: "connecting"
3210
+ };
3211
+ }), on(memoryDomainEvents.realtimeConnected, (state, { sessionId }) => {
3212
+ if (sessionId !== state.sessionId) return state;
3213
+ return {
3214
+ ...state,
3215
+ realtimeStatus: "connected"
3216
+ };
3217
+ }), on(memoryDomainEvents.realtimeUnavailable, (state, { sessionId }) => {
3218
+ if (sessionId !== state.sessionId) return state;
3219
+ return {
3220
+ ...state,
3221
+ realtimeStatus: "unavailable"
3222
+ };
3223
+ }));
3224
+ const selectMemories = createSelector((state) => state.memories);
3225
+ const selectMemoriesIsLoading = createSelector((state) => state.isLoading);
3226
+ const selectMemoriesError = createSelector((state) => state.error);
3227
+ /**
3228
+ * Reports whether at least one mutation is in flight. Derived from
3229
+ * `inFlightMutationCount` so concurrent mutations stay correct: the boolean
3230
+ * only clears once every in-flight mutation has settled.
3231
+ */
3232
+ const selectMemoriesIsMutating = createSelector((state) => state.inFlightMutationCount > 0);
3233
+ /**
3234
+ * Reports whether the memory routes are available. Becomes `false` after a
3235
+ * 404, 422 (the runtime's MISSING_INTELLIGENCE signal), or 501 response during
3236
+ * an auto-activation read; defaults to `true`.
3237
+ */
3238
+ const selectMemoriesAvailable = createSelector((state) => state.available);
3239
+ /**
3240
+ * Reports the realtime-connection health (see {@link MemoryRealtimeStatus}).
3241
+ * Distinct from `available`/`error`: it reflects ONLY the live socket/channel,
3242
+ * so the UI can suppress a "live" indicator once realtime permanently dies even
3243
+ * while the REST list route stays healthy. Defaults to `"connecting"`.
3244
+ */
3245
+ const selectMemoriesRealtimeStatus = createSelector((state) => state.realtimeStatus);
3246
+ const MEMORY_METADATA_EVENT = "memory_metadata";
3247
+ function memoryFromFetch(input, init) {
3248
+ return fromFetch(input, init);
3249
+ }
3250
+ /**
3251
+ * Fetches the memory snapshot and maps it to a success/failure action. Keeps
3252
+ * only user-scoped memories (v1 surfaces user scope only; the realtime stream
3253
+ * is user-scoped too), so project-scoped rows visible to the caller are
3254
+ * dropped from the store.
3255
+ */
3256
+ function createMemoryFetchObservable(environment, context, sessionId) {
3257
+ return defer(() => {
3258
+ const qs = context.includeInvalidated ? "?includeInvalidated=true" : "";
3259
+ return memoryFromFetch(`${context.runtimeUrl}${MEMORIES_PATH}${qs}`, {
3260
+ selector: (response) => {
3261
+ if (!response.ok) {
3262
+ if (ROUTE_UNAVAILABLE_STATUSES.has(response.status)) throw new MemoryRouteUnavailableError(String(response.status));
3263
+ throw new MemoryError("MEMORY_LIST_FAILED", {
3264
+ message: `Failed to fetch memories: ${response.status}`,
3265
+ retryable: isRetryableStatus(response.status)
3266
+ });
3267
+ }
3268
+ return response.json();
3269
+ },
3270
+ fetch: environment.fetch,
3271
+ method: "GET",
3272
+ headers: { ...context.headers }
3273
+ }).pipe(timeout({
3274
+ first: REQUEST_TIMEOUT_MS$1,
3275
+ with: () => {
3276
+ throw new MemoryError("MEMORY_REQUEST_TIMEOUT");
3277
+ }
3278
+ }), map((data) => memoryRestEvents.listSucceeded({
3279
+ sessionId,
3280
+ memories: data.memories.filter((memory) => memory.scope === "user")
3281
+ })), catchError((error) => {
3282
+ if (error instanceof MemoryRouteUnavailableError) return of(memoryRestEvents.listUnavailable({ sessionId }));
3283
+ return of(memoryRestEvents.listFailed({
3284
+ sessionId,
3285
+ error: error instanceof Error ? error : new Error(String(error))
3286
+ }));
3287
+ }));
3288
+ });
3289
+ }
3290
+ /**
3291
+ * Fetches join credentials from the `/memories/subscribe` endpoint and maps
3292
+ * the response to a success/failure action. Requires both `joinToken` and
3293
+ * `joinCode` to be non-empty strings; throws otherwise so the `catchError`
3294
+ * path emits `credentialsFailed`.
3295
+ */
3296
+ function createMemoryCredentialsFetchObservable(environment, context, sessionId) {
3297
+ return defer(() => memoryFromFetch(`${context.runtimeUrl}${MEMORIES_SUBSCRIBE_PATH}`, {
3298
+ selector: async (response) => {
3299
+ if (!response.ok) {
3300
+ if (ROUTE_UNAVAILABLE_STATUSES.has(response.status)) throw new MemoryRouteUnavailableError(String(response.status));
3301
+ throw new MemoryError("MEMORY_CREDENTIALS_FAILED", {
3302
+ message: `Failed to fetch memory subscribe credentials: ${response.status}`,
3303
+ retryable: isRetryableStatus(response.status)
3304
+ });
3305
+ }
3306
+ return response.json();
3307
+ },
3308
+ fetch: environment.fetch,
3309
+ method: "POST",
3310
+ headers: {
3311
+ ...context.headers,
3312
+ "Content-Type": "application/json"
3313
+ },
3314
+ body: JSON.stringify({})
3315
+ }).pipe(timeout({
3316
+ first: REQUEST_TIMEOUT_MS$1,
3317
+ with: () => {
3318
+ throw new MemoryError("MEMORY_REQUEST_TIMEOUT");
3319
+ }
3320
+ }), map((data) => {
3321
+ if (typeof data.joinToken !== "string" || data.joinToken.length === 0) throw new Error("missing joinToken");
3322
+ if (typeof data.joinCode !== "string" || data.joinCode.length === 0) throw new Error("missing joinCode");
3323
+ return memoryRestEvents.credentialsSucceeded({
3324
+ sessionId,
3325
+ joinToken: data.joinToken,
3326
+ joinCode: data.joinCode
3327
+ });
3328
+ }), catchError((error) => {
3329
+ if (error instanceof MemoryRouteUnavailableError) return of(memoryRestEvents.credentialsUnavailable({ sessionId }));
3330
+ return of(memoryRestEvents.credentialsFailed({
3331
+ sessionId,
3332
+ error: error instanceof Error ? error : new Error(String(error))
3333
+ }));
3334
+ })));
3335
+ }
3336
+ /** Projects a REST mutation response to the public {@link Memory} shape. */
3337
+ function responseToMemory(data) {
3338
+ return {
3339
+ id: data.id,
3340
+ kind: data.kind,
3341
+ scope: data.scope,
3342
+ content: data.content,
3343
+ sourceThreadIds: data.sourceThreadIds,
3344
+ invalidatedAt: data.invalidatedAt
3345
+ };
3346
+ }
3347
+ const MEMORY_KINDS = new Set([
3348
+ "topical",
3349
+ "episodic",
3350
+ "operational"
3351
+ ]);
3352
+ const MEMORY_SCOPES = new Set(["user", "project"]);
3353
+ /**
3354
+ * Validates the required fields of a create/supersede REST response before it is
3355
+ * projected to a {@link Memory}. Returns an `Error` describing the first
3356
+ * missing/invalid field, or `null` when the body is well-formed. Without this a
3357
+ * malformed 200 body yields a Memory with undefined fields that gets upserted.
3358
+ */
3359
+ function validateMutationResponse(data) {
3360
+ if (!data || typeof data !== "object") return /* @__PURE__ */ new Error("memory mutation response missing/invalid body");
3361
+ if (typeof data.id !== "string" || data.id.length === 0) return /* @__PURE__ */ new Error("memory mutation response missing/invalid id");
3362
+ if (typeof data.kind !== "string" || !MEMORY_KINDS.has(data.kind)) return /* @__PURE__ */ new Error("memory mutation response missing/invalid kind");
3363
+ if (typeof data.scope !== "string" || !MEMORY_SCOPES.has(data.scope)) return /* @__PURE__ */ new Error("memory mutation response missing/invalid scope");
3364
+ if (typeof data.content !== "string") return /* @__PURE__ */ new Error("memory mutation response missing/invalid content");
3365
+ if (!Array.isArray(data.sourceThreadIds) || !data.sourceThreadIds.every((id) => typeof id === "string")) return /* @__PURE__ */ new Error("memory mutation response missing/invalid sourceThreadIds");
3366
+ if (data.invalidatedAt !== null && typeof data.invalidatedAt !== "string") return /* @__PURE__ */ new Error("memory mutation response missing/invalid invalidatedAt");
3367
+ return null;
3368
+ }
3369
+ /**
3370
+ * Request body for create/supersede. `scope` is forwarded only when the caller
3371
+ * supplies it: the platform owns the default (`scope` defaults to `"user"` in
3372
+ * the `/api/memories` schema), so the store does not second-guess it here —
3373
+ * one default, at the contract owner. `sourceThreadIds` defaults to `[]`.
3374
+ */
3375
+ function toMutationBody(input) {
3376
+ return {
3377
+ content: input.content,
3378
+ kind: input.kind,
3379
+ ...input.scope !== void 0 ? { scope: input.scope } : {},
3380
+ sourceThreadIds: input.sourceThreadIds ?? []
3381
+ };
3382
+ }
3383
+ /**
3384
+ * Builds the actions a successful mutation dispatches (server-authoritative):
3385
+ * the REST response is applied to local state immediately, and the realtime
3386
+ * event reconciles idempotently (upsert-by-id). `mutationFinished` resolves the
3387
+ * caller's promise.
3388
+ */
3389
+ function buildMutationSuccessActions(request, data) {
3390
+ const { requestId, sessionId } = request;
3391
+ if (request.kind === "remove") return [memoryDomainEvents.memoryInvalidated({
3392
+ sessionId,
3393
+ memoryId: request.id
3394
+ }), memoryRestEvents.mutationFinished({ outcome: {
3395
+ requestId,
3396
+ sessionId,
3397
+ ok: true,
3398
+ memory: null
3399
+ } })];
3400
+ const validationError = validateMutationResponse(data);
3401
+ if (validationError) return [memoryRestEvents.mutationFinished({ outcome: {
3402
+ requestId,
3403
+ sessionId,
3404
+ ok: false,
3405
+ error: validationError
3406
+ } })];
3407
+ const memory = responseToMemory(data);
3408
+ if (request.kind === "update") {
3409
+ const retiredId = data.retiredId;
3410
+ if (typeof retiredId !== "string" || retiredId.length === 0) return [memoryRestEvents.mutationFinished({ outcome: {
3411
+ requestId,
3412
+ sessionId,
3413
+ ok: false,
3414
+ error: /* @__PURE__ */ new Error("supersede response missing retiredId")
3415
+ } })];
3416
+ return [
3417
+ memoryDomainEvents.memoryInvalidated({
3418
+ sessionId,
3419
+ memoryId: retiredId
3420
+ }),
3421
+ memoryDomainEvents.memoryUpserted({
3422
+ sessionId,
3423
+ memory
3424
+ }),
3425
+ memoryRestEvents.mutationFinished({ outcome: {
3426
+ requestId,
3427
+ sessionId,
3428
+ ok: true,
3429
+ memory
3430
+ } })
3431
+ ];
3432
+ }
3433
+ return [memoryDomainEvents.memoryUpserted({
3434
+ sessionId,
3435
+ memory
3436
+ }), memoryRestEvents.mutationFinished({ outcome: {
3437
+ requestId,
3438
+ sessionId,
3439
+ ok: true,
3440
+ memory
3441
+ } })];
3442
+ }
3443
+ /** Performs a create/supersede/retire HTTP call and emits its resulting actions. */
3444
+ function createMemoryMutationObservable(environment, context, request) {
3445
+ const method = request.kind === "add" ? "POST" : request.kind === "update" ? "PATCH" : "DELETE";
3446
+ const path = request.kind === "add" ? MEMORIES_PATH : `${MEMORIES_PATH}/${encodeURIComponent(request.id)}`;
3447
+ return defer(() => memoryFromFetch(`${context.runtimeUrl}${path}`, {
3448
+ selector: async (response) => {
3449
+ if (!response.ok) throw new MemoryError("MEMORY_MUTATION_FAILED", {
3450
+ message: `Request failed: ${response.status}`,
3451
+ retryable: isRetryableStatus(response.status)
3452
+ });
3453
+ return request.kind === "remove" ? null : await response.json();
3454
+ },
3455
+ fetch: environment.fetch,
3456
+ method,
3457
+ headers: {
3458
+ ...context.headers,
3459
+ "Content-Type": "application/json"
3460
+ },
3461
+ body: request.kind === "remove" ? void 0 : JSON.stringify(request.body)
3462
+ }).pipe(timeout({
3463
+ first: REQUEST_TIMEOUT_MS$1,
3464
+ with: () => {
3465
+ throw new MemoryError("MEMORY_REQUEST_TIMEOUT");
3466
+ }
3467
+ }), mergeMap((data) => of(...buildMutationSuccessActions(request, data))), catchError((error) => of(memoryRestEvents.mutationFinished({ outcome: {
3468
+ requestId: request.requestId,
3469
+ sessionId: request.sessionId,
3470
+ ok: false,
3471
+ error: error instanceof Error ? error : new Error(String(error))
3472
+ } })))));
3473
+ }
3474
+ /**
3475
+ * Creates the framework-agnostic memory store: a REST snapshot on `setContext`,
3476
+ * server-authoritative add/update/remove mutations, and realtime
3477
+ * `memory_metadata` deltas off the injected `user_meta` event source — all
3478
+ * reduced into observable state.
3479
+ */
3480
+ function createMemoryStore(environment) {
3481
+ let memoryRequestId = 0;
3482
+ const createMemoryRequestId = () => {
3483
+ memoryRequestId += 1;
3484
+ return `memory-request-${memoryRequestId}`;
3485
+ };
3486
+ const bootstrapEffect = createEffect((actions$, state$) => actions$.pipe(ofType(memoryAdapterEvents.contextChanged), withLatestFrom(state$), filter(([, state]) => Boolean(state.context)), map(([, state]) => memoryRestEvents.listRequested({ sessionId: state.sessionId }))));
3487
+ const credentialsBootstrapEffect = createEffect((actions$, state$) => actions$.pipe(ofType(memoryAdapterEvents.contextChanged), withLatestFrom(state$), filter(([, state]) => Boolean(state.context)), map(([, state]) => memoryRestEvents.credentialsRequested({ sessionId: state.sessionId }))));
3488
+ 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) => ({
3489
+ action,
3490
+ context
3491
+ })), takeUntil(actions$.pipe(ofType(memoryAdapterEvents.contextChanged, memoryAdapterEvents.stopped))), switchMap$1(({ action: currentAction, context }) => createMemoryCredentialsFetchObservable(environment, context, currentAction.sessionId))))));
3492
+ const store = createStore({
3493
+ reducer: memoryReducer,
3494
+ effects: [
3495
+ bootstrapEffect,
3496
+ credentialsBootstrapEffect,
3497
+ 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) => ({
3498
+ action,
3499
+ context
3500
+ })), takeUntil(actions$.pipe(ofType(memoryAdapterEvents.contextChanged, memoryAdapterEvents.stopped))), switchMap$1(({ action: currentAction, context }) => createMemoryFetchObservable(environment, context, currentAction.sessionId)))))),
3501
+ credentialsFetchEffect,
3502
+ createEffect((actions$, state$) => actions$.pipe(ofType(memoryAdapterEvents.addRequested, memoryAdapterEvents.updateRequested, memoryAdapterEvents.removeRequested), withLatestFrom(state$), mergeMap(([action, state]) => {
3503
+ const context = state.context;
3504
+ const sessionId = state.sessionId;
3505
+ if (!context?.runtimeUrl) return of(memoryRestEvents.mutationFinished({ outcome: {
3506
+ requestId: action.requestId,
3507
+ sessionId,
3508
+ ok: false,
3509
+ error: /* @__PURE__ */ new Error("Runtime URL is not configured")
3510
+ } }));
3511
+ if (memoryAdapterEvents.addRequested.match(action)) return createMemoryMutationObservable(environment, context, {
3512
+ requestId: action.requestId,
3513
+ sessionId,
3514
+ kind: "add",
3515
+ body: toMutationBody(action.input)
3516
+ });
3517
+ if (memoryAdapterEvents.updateRequested.match(action)) return createMemoryMutationObservable(environment, context, {
3518
+ requestId: action.requestId,
3519
+ sessionId,
3520
+ kind: "update",
3521
+ id: action.id,
3522
+ body: toMutationBody(action.changes)
3523
+ });
3524
+ return createMemoryMutationObservable(environment, context, {
3525
+ requestId: action.requestId,
3526
+ sessionId,
3527
+ kind: "remove",
3528
+ id: action.id
3529
+ });
3530
+ }))),
3531
+ createEffect((actions$, state$) => actions$.pipe(ofType(memoryRestEvents.credentialsSucceeded), withLatestFrom(state$), filter(([action, state]) => {
3532
+ return action.sessionId === state.sessionId && Boolean(state.context?.wsUrl);
3533
+ }), switchMap$1(([action, state]) => {
3534
+ const context = state.context;
3535
+ const { joinToken, joinCode } = action;
3536
+ const sessionId = action.sessionId;
3537
+ const shutdown$ = actions$.pipe(ofType(memoryAdapterEvents.contextChanged, memoryAdapterEvents.stopped));
3538
+ return defer(() => {
3539
+ const socket$ = ɵphoenixSocket$({
3540
+ url: context.wsUrl,
3541
+ options: {
3542
+ params: { join_token: joinToken },
3543
+ reconnectAfterMs: phoenixExponentialBackoff(100, 1e4),
3544
+ rejoinAfterMs: phoenixExponentialBackoff(1e3, 3e4)
3545
+ }
3546
+ }).pipe(shareReplay({
3547
+ bufferSize: 1,
3548
+ refCount: true
3549
+ }));
3550
+ const channel$ = ɵphoenixChannel$({
3551
+ socket$,
3552
+ topic: `user_meta:memories:${joinCode}`
3553
+ }).pipe(shareReplay({
3554
+ bufferSize: 1,
3555
+ refCount: true
3556
+ }));
3557
+ const fatalSocketShutdown$ = ɵobservePhoenixSocketHealth$(ɵobservePhoenixSocketSignals$(socket$).pipe(share()), MAX_SOCKET_RETRIES$1).pipe(catchError(() => {
3558
+ console.warn(`[memory] WebSocket failed after ${MAX_SOCKET_RETRIES$1} attempts, giving up`);
3559
+ return of(void 0);
3560
+ }), share());
3561
+ const fatalStatus$ = fatalSocketShutdown$.pipe(map(() => memoryDomainEvents.realtimeUnavailable({ sessionId })));
3562
+ const metadata$ = channel$.pipe(switchMap$1(({ channel }) => ɵobservePhoenixEvent$(channel, MEMORY_METADATA_EVENT)), map((event) => mapMemoryMetadataEvent(event, action.sessionId)));
3563
+ const join$ = ɵjoinPhoenixChannel$(channel$).pipe(concatWith(of(memoryDomainEvents.realtimeConnected({ sessionId }))), catchError((error) => {
3564
+ console.warn(`[memory] failed to join user_meta:memories:${joinCode}`, error);
3565
+ return of(memoryDomainEvents.realtimeUnavailable({ sessionId }));
3566
+ }));
3567
+ return merge(of(memoryDomainEvents.realtimeConnecting({ sessionId })), metadata$, join$, fatalStatus$).pipe(takeUntil(merge(shutdown$, fatalSocketShutdown$.pipe(observeOn(asapScheduler)))), finalize(() => {}));
3568
+ });
3569
+ })))
3570
+ ]
3571
+ });
3572
+ function trackMutation(dispatchAction, extract) {
3573
+ const { requestId } = dispatchAction;
3574
+ 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(() => ({
3575
+ requestId,
3576
+ sessionId: store.getState().sessionId,
3577
+ ok: false,
3578
+ error: /* @__PURE__ */ new Error("Memory store stopped before mutation completed")
3579
+ })))).pipe(take(1))).then((outcome) => {
3580
+ if (!outcome.ok) throw outcome.error;
3581
+ return extract(outcome);
3582
+ });
3583
+ store.dispatch(dispatchAction);
3584
+ return resultPromise;
3585
+ }
3586
+ return {
3587
+ start() {
3588
+ store.init();
3589
+ store.dispatch(memoryAdapterEvents.started());
3590
+ },
3591
+ stop() {
3592
+ store.dispatch(memoryAdapterEvents.stopped());
3593
+ store.stop();
3594
+ },
3595
+ setContext(context) {
3596
+ store.dispatch(memoryAdapterEvents.contextChanged({ context }));
3597
+ },
3598
+ refresh() {
3599
+ const { sessionId, context } = store.getState();
3600
+ if (!context) return Promise.resolve();
3601
+ 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) => {
3602
+ if (memoryRestEvents.listFailed.match(action)) throw action.error;
3603
+ if (memoryAdapterEvents.stopped.match(action)) throw new Error("Memory store stopped before refresh completed");
3604
+ if (memoryAdapterEvents.contextChanged.match(action)) return;
3605
+ });
3606
+ store.dispatch(memoryRestEvents.listRequested({ sessionId }));
3607
+ return done;
3608
+ },
3609
+ addMemory(input) {
3610
+ return trackMutation(memoryAdapterEvents.addRequested({
3611
+ requestId: createMemoryRequestId(),
3612
+ input
3613
+ }), (outcome) => {
3614
+ if (outcome.memory == null) throw new Error("add resolved without a memory");
3615
+ return outcome.memory;
3616
+ });
3617
+ },
3618
+ updateMemory(id, changes) {
3619
+ return trackMutation(memoryAdapterEvents.updateRequested({
3620
+ requestId: createMemoryRequestId(),
3621
+ id,
3622
+ changes
3623
+ }), (outcome) => {
3624
+ if (outcome.memory == null) throw new Error("update resolved without a memory");
3625
+ return outcome.memory;
3626
+ });
3627
+ },
3628
+ removeMemory(id) {
3629
+ return trackMutation(memoryAdapterEvents.removeRequested({
3630
+ requestId: createMemoryRequestId(),
3631
+ id
3632
+ }), () => void 0);
3633
+ },
3634
+ getState() {
3635
+ return store.getState();
3636
+ },
3637
+ getServerState() {
3638
+ return initialMemoryState;
3639
+ },
3640
+ select: store.select.bind(store)
3641
+ };
3642
+ }
3643
+ const ɵmemoryAdapterEvents = memoryAdapterEvents;
3644
+ const ɵmemoryRestEvents = memoryRestEvents;
3645
+ const ɵmemoryDomainEvents = memoryDomainEvents;
3646
+ const ɵmemoryReducer = memoryReducer;
3647
+ const ɵmapMemoryMetadataEvent = mapMemoryMetadataEvent;
3648
+ const ɵselectMemories = selectMemories;
3649
+ const ɵselectMemoriesIsLoading = selectMemoriesIsLoading;
3650
+ const ɵselectMemoriesError = selectMemoriesError;
3651
+ const ɵselectMemoriesIsMutating = selectMemoriesIsMutating;
3652
+ const ɵselectMemoriesAvailable = selectMemoriesAvailable;
3653
+ const ɵselectMemoriesRealtimeStatus = selectMemoriesRealtimeStatus;
3654
+
3655
+ //#endregion
3656
+ //#region src/core/core.ts
3657
+ let CopilotKitCoreErrorCode = /* @__PURE__ */ function(CopilotKitCoreErrorCode) {
3658
+ CopilotKitCoreErrorCode["RUNTIME_INFO_FETCH_FAILED"] = "runtime_info_fetch_failed";
3659
+ CopilotKitCoreErrorCode["AGENT_CONNECT_FAILED"] = "agent_connect_failed";
3660
+ CopilotKitCoreErrorCode["AGENT_RUN_FAILED"] = "agent_run_failed";
3661
+ CopilotKitCoreErrorCode["AGENT_RUN_FAILED_EVENT"] = "agent_run_failed_event";
3662
+ CopilotKitCoreErrorCode["AGENT_RUN_ERROR_EVENT"] = "agent_run_error_event";
3663
+ CopilotKitCoreErrorCode["TOOL_ARGUMENT_PARSE_FAILED"] = "tool_argument_parse_failed";
3664
+ CopilotKitCoreErrorCode["TOOL_HANDLER_FAILED"] = "tool_handler_failed";
3665
+ CopilotKitCoreErrorCode["TOOL_NOT_FOUND"] = "tool_not_found";
3666
+ CopilotKitCoreErrorCode["AGENT_NOT_FOUND"] = "agent_not_found";
3667
+ /**
3668
+ * Emitted when an agent run fails because the thread is already locked
3669
+ * by another active run.
3670
+ *
3671
+ * @example
3672
+ * ```tsx
3673
+ * <CopilotKitProvider
3674
+ * onError={({ code, error, context }) => {
3675
+ * if (code === "agent_thread_locked") {
3676
+ * // Show "Agent is busy, retry?" UI
3677
+ * }
3678
+ * }}
3679
+ * />
3680
+ * ```
3681
+ */
3682
+ CopilotKitCoreErrorCode["AGENT_THREAD_LOCKED"] = "agent_thread_locked";
3683
+ CopilotKitCoreErrorCode["TRANSCRIPTION_FAILED"] = "transcription_failed";
3684
+ CopilotKitCoreErrorCode["TRANSCRIPTION_SERVICE_NOT_CONFIGURED"] = "transcription_service_not_configured";
3685
+ CopilotKitCoreErrorCode["TRANSCRIPTION_INVALID_AUDIO"] = "transcription_invalid_audio";
3686
+ CopilotKitCoreErrorCode["TRANSCRIPTION_RATE_LIMITED"] = "transcription_rate_limited";
3687
+ CopilotKitCoreErrorCode["TRANSCRIPTION_AUTH_FAILED"] = "transcription_auth_failed";
3688
+ CopilotKitCoreErrorCode["TRANSCRIPTION_NETWORK_ERROR"] = "transcription_network_error";
3689
+ CopilotKitCoreErrorCode["SUBSCRIBER_CALLBACK_FAILED"] = "subscriber_callback_failed";
3690
+ return CopilotKitCoreErrorCode;
3691
+ }({});
3692
+ /**
3693
+ * Runtime allowlist derived from {@link SUBSCRIBE_TO_AGENT_KEYS}. Hoisted
3694
+ * to module scope so the Set is allocated once, not per-subscription.
3695
+ */
3696
+ const ALLOWED_KEYS = new Set([
3697
+ "onMessagesChanged",
3698
+ "onStateChanged",
3699
+ "onRunInitialized",
3700
+ "onRunFinalized",
3701
+ "onRunFailed",
3702
+ "onRunErrorEvent"
3703
+ ]);
3704
+ let CopilotKitCoreRuntimeConnectionStatus = /* @__PURE__ */ function(CopilotKitCoreRuntimeConnectionStatus) {
3705
+ CopilotKitCoreRuntimeConnectionStatus["Disconnected"] = "disconnected";
3706
+ CopilotKitCoreRuntimeConnectionStatus["Connected"] = "connected";
3707
+ CopilotKitCoreRuntimeConnectionStatus["Connecting"] = "connecting";
3708
+ CopilotKitCoreRuntimeConnectionStatus["Error"] = "error";
3709
+ return CopilotKitCoreRuntimeConnectionStatus;
3710
+ }({});
3711
+ /**
3712
+ * Normalize a header map to the internal invariant: a `Record<string, string>`
3713
+ * with no `null`/`undefined` values. Entries whose value is `null`/`undefined`
3714
+ * are dropped (this is how a header is cleared). Shared by the constructor and
3715
+ * `setHeaders` so both write paths into `_headers` enforce the same invariant.
3716
+ */
3717
+ function normalizeHeaders(headers) {
3718
+ return Object.fromEntries(Object.entries(headers).filter((entry) => entry[1] != null));
3719
+ }
3720
+ var CopilotKitCore = class {
3721
+ _headers;
3722
+ _credentials;
3723
+ _properties;
3724
+ _defaultThrottleMs;
3725
+ _debug;
3726
+ subscribers = /* @__PURE__ */ new Set();
3727
+ agentRegistry;
3728
+ contextStore;
3729
+ suggestionEngine;
3730
+ runHandler;
3731
+ stateManager;
3732
+ subagentRegistry;
3733
+ threadStoreRegistry;
3734
+ /**
3735
+ * The single core-owned memory store, created lazily on first
3736
+ * `getMemoryStore()` and kept user-scoped for the lifetime of the core.
3737
+ * Its runtime context is wired by the core itself (see `syncMemoryContext`),
3738
+ * so callers never register or look it up by `agentId`.
3739
+ */
3740
+ _memoryStore;
3741
+ /**
3742
+ * Tracks the agent IDs from the most recent `onAgentsChanged` notification.
3743
+ * Used to gate thread-store auto-unregister so the FIRST empty-agents
3744
+ * notification (before published agents are merged in) does not rip out a
3745
+ * store that was registered prior to that initial notification.
3746
+ */
3747
+ previousAgentIds = /* @__PURE__ */ new Set();
3748
+ constructor({ runtimeUrl, runtimeTransport = "auto", headers = {}, credentials, properties = {}, agents__unsafe_dev_only = {}, tools = [], suggestionsConfig = [], debug }) {
3749
+ this._headers = normalizeHeaders(headers);
3750
+ this._credentials = credentials;
3751
+ this._properties = properties;
3752
+ this._debug = debug;
3753
+ this.agentRegistry = new AgentRegistry(this);
3754
+ this.contextStore = new ContextStore(this);
3755
+ this.suggestionEngine = new SuggestionEngine(this);
3756
+ this.runHandler = new RunHandler(this);
3757
+ this.stateManager = new StateManager(this);
3758
+ this.subagentRegistry = new SubagentRegistry(this);
3759
+ this.threadStoreRegistry = new ThreadStoreRegistry(this);
3760
+ this.agentRegistry.initialize(agents__unsafe_dev_only);
3761
+ this.runHandler.initialize(tools);
3762
+ this.suggestionEngine.initialize(suggestionsConfig);
3763
+ this.stateManager.initialize();
3764
+ this.agentRegistry.setRuntimeTransport(runtimeTransport);
3765
+ this.agentRegistry.setRuntimeUrl(runtimeUrl);
3766
+ this.previousAgentIds = new Set(Object.keys(agents__unsafe_dev_only));
3767
+ this.subscribe({
3768
+ onRuntimeConnectionStatusChanged: () => {
3769
+ if (this._memoryStore) this.syncMemoryContext();
3770
+ },
3771
+ onAgentsChanged: ({ agents }) => {
3772
+ Object.values(agents).forEach((agent) => {
3773
+ if (agent.agentId) {
3774
+ this.stateManager.subscribeToAgent(agent);
3775
+ this.subagentRegistry.subscribeToAgent(agent);
3776
+ }
3777
+ });
3778
+ const currentAgentIds = new Set(Object.keys(agents));
3779
+ for (const agentId of Object.keys(this.threadStoreRegistry.getAll())) if (this.previousAgentIds.has(agentId) && !currentAgentIds.has(agentId)) try {
3780
+ this.threadStoreRegistry.unregister(agentId);
3781
+ } catch (err) {
3782
+ console.error(`CopilotKitCore.onAgentsChanged: threadStoreRegistry.unregister failed for "${agentId}":`, err);
3783
+ }
3784
+ for (const agentId of this.previousAgentIds) if (!currentAgentIds.has(agentId)) {
3785
+ try {
3786
+ this.stateManager.unsubscribeFromAgent(agentId);
3787
+ } catch (err) {
3788
+ console.error(`CopilotKitCore.onAgentsChanged: stateManager.unsubscribeFromAgent failed for "${agentId}":`, err);
3789
+ }
3790
+ try {
3791
+ this.subagentRegistry.unsubscribeFromAgent(agentId);
3792
+ } catch (err) {
3793
+ console.error(`CopilotKitCore.onAgentsChanged: subagentRegistry.unsubscribeFromAgent failed for "${agentId}":`, err);
3794
+ }
3795
+ }
3796
+ this.previousAgentIds = currentAgentIds;
3797
+ }
3798
+ });
3799
+ }
3800
+ /**
3801
+ * Internal method used by delegate classes and subclasses to notify subscribers
3802
+ */
3803
+ async notifySubscribers(handler, errorMessage) {
3804
+ await Promise.all(Array.from(this.subscribers).map(async (subscriber) => {
3805
+ try {
3806
+ await handler(subscriber);
3807
+ } catch (error) {
3808
+ console.error(errorMessage, error);
3809
+ }
3810
+ }));
3811
+ }
3812
+ /**
3813
+ * Internal method used by delegate classes to emit errors
3814
+ */
3815
+ async emitError({ error, code, context = {} }) {
3816
+ await this.notifySubscribers((subscriber) => subscriber.onError?.({
3817
+ copilotkit: this,
3818
+ error,
3819
+ code,
3820
+ context
3821
+ }), "Subscriber onError error:");
3822
+ }
3823
+ /**
3824
+ * Log a message to the console and emit an error to subscribers.
3825
+ * Catches failures from `emitError` itself to prevent unhandled rejections.
3826
+ */
3827
+ logAndEmitError(message, params, logLevel = "error") {
3828
+ console[logLevel](message, params.error);
3829
+ this.emitError(params).catch((emitErr) => {
3830
+ console.error(message + " — emitError itself failed:", emitErr);
3831
+ });
3832
+ }
3833
+ /**
3834
+ * Snapshot accessors
3835
+ */
3836
+ get context() {
3837
+ return this.contextStore.context;
3838
+ }
3839
+ get agents() {
3840
+ return this.agentRegistry.agents;
3841
+ }
3842
+ get tools() {
3843
+ return this.runHandler.tools;
3844
+ }
3845
+ get runtimeUrl() {
3846
+ return this.agentRegistry.runtimeUrl;
3847
+ }
3848
+ setRuntimeUrl(runtimeUrl) {
3849
+ this.agentRegistry.setRuntimeUrl(runtimeUrl);
3850
+ }
3851
+ get runtimeTransport() {
3852
+ return this.agentRegistry.runtimeTransport;
3853
+ }
3854
+ setRuntimeTransport(runtimeTransport) {
3855
+ this.agentRegistry.setRuntimeTransport(runtimeTransport);
3856
+ }
3857
+ get runtimeVersion() {
3858
+ return this.agentRegistry.runtimeVersion;
3859
+ }
3860
+ get headers() {
3861
+ return this._headers;
3862
+ }
3863
+ get credentials() {
3864
+ return this._credentials;
3865
+ }
3866
+ get properties() {
3867
+ return this._properties;
3868
+ }
3869
+ /**
3870
+ * Default throttle interval (ms) used by `subscribeToAgentWithOptions()`
3871
+ * when the caller does not specify an explicit `throttleMs`.
3872
+ * `undefined` means no default is configured; `0` means no throttling.
3873
+ */
3874
+ get defaultThrottleMs() {
3875
+ return this._defaultThrottleMs;
3876
+ }
3877
+ /**
3878
+ * Set the default throttle interval (ms) for `subscribeToAgentWithOptions()`.
3879
+ *
3880
+ * Accepts a non-negative finite number or `undefined` (to clear the
3881
+ * default). Invalid values (NaN, Infinity, negative) are logged as
3882
+ * errors and ignored — the previous valid value is preserved.
3883
+ */
3884
+ setDefaultThrottleMs(value) {
2724
3885
  if (value !== void 0 && (!Number.isFinite(value) || value < 0)) {
2725
3886
  this.logAndEmitError(`CopilotKitCore.setDefaultThrottleMs: value must be a non-negative finite number or undefined, got ${value}. Keeping current value (${this._defaultThrottleMs}).`, {
2726
3887
  error: /* @__PURE__ */ new Error(`setDefaultThrottleMs: invalid value (${value}), keeping current value (${this._defaultThrottleMs})`),
@@ -2808,6 +3969,7 @@ var CopilotKitCore = class {
2808
3969
  */
2809
3970
  setHeaders(headers) {
2810
3971
  this._headers = normalizeHeaders(headers);
3972
+ if (this._memoryStore) this.syncMemoryContext();
2811
3973
  this.agentRegistry.applyHeadersToAgents(this.agentRegistry.agents);
2812
3974
  this.notifySubscribers((subscriber) => subscriber.onHeadersChanged?.({
2813
3975
  copilotkit: this,
@@ -2906,11 +4068,51 @@ var CopilotKitCore = class {
2906
4068
  unregisterThreadStore(agentId) {
2907
4069
  this.threadStoreRegistry.unregister(agentId);
2908
4070
  }
2909
- getThreadStore(agentId) {
2910
- return this.threadStoreRegistry.get(agentId);
4071
+ getThreadStore(agentId) {
4072
+ return this.threadStoreRegistry.get(agentId);
4073
+ }
4074
+ getThreadStores() {
4075
+ return this.threadStoreRegistry.getAll();
4076
+ }
4077
+ /**
4078
+ * Returns the single core-owned, user-scoped memory store, creating and
4079
+ * starting it on first access. Unlike thread stores, memory is not scoped per
4080
+ * agent: there is exactly one store whose runtime context the core wires
4081
+ * itself (see `syncMemoryContext`), so consumers (e.g. a `useMemories`
4082
+ * binding) just read this store rather than registering one.
4083
+ */
4084
+ getMemoryStore() {
4085
+ return this.ensureMemoryStore();
4086
+ }
4087
+ /**
4088
+ * Lazily creates, starts, and context-syncs the core-owned memory store on
4089
+ * first access, then returns it. Subsequent calls return the existing store.
4090
+ * The store is constructed with a bound `globalThis.fetch` and immediately
4091
+ * has its runtime context synced from the current connection state.
4092
+ */
4093
+ ensureMemoryStore() {
4094
+ if (!this._memoryStore) {
4095
+ this._memoryStore = createMemoryStore({ fetch: globalThis.fetch.bind(globalThis) });
4096
+ this._memoryStore.start();
4097
+ this.syncMemoryContext();
4098
+ }
4099
+ return this._memoryStore;
2911
4100
  }
2912
- getThreadStores() {
2913
- return this.threadStoreRegistry.getAll();
4101
+ /**
4102
+ * Pushes the current runtime wiring into the memory store. When the runtime
4103
+ * is connected and both the intelligence WebSocket URL and runtime URL are
4104
+ * available, the store receives a context (runtime URL, WebSocket URL, and a
4105
+ * copy of the current headers); otherwise its context is cleared. No-op when
4106
+ * the store has not been created yet.
4107
+ */
4108
+ syncMemoryContext() {
4109
+ if (!this._memoryStore) return;
4110
+ if (this.runtimeConnectionStatus === CopilotKitCoreRuntimeConnectionStatus.Connected && this.intelligence?.wsUrl && this.runtimeUrl) this._memoryStore.setContext({
4111
+ runtimeUrl: this.runtimeUrl,
4112
+ wsUrl: this.intelligence.wsUrl,
4113
+ headers: { ...this.headers }
4114
+ });
4115
+ else this._memoryStore.setContext(null);
2914
4116
  }
2915
4117
  /**
2916
4118
  * Suggestions management (delegated to SuggestionEngine)
@@ -2931,6 +4133,16 @@ var CopilotKitCore = class {
2931
4133
  return this.suggestionEngine.getSuggestions(agentId);
2932
4134
  }
2933
4135
  /**
4136
+ * Subagent registry (delegated to SubagentRegistry).
4137
+ *
4138
+ * Returns the subagents observed for an owning agent during its run(s),
4139
+ * keyed by `subagentId`, each carrying `name` / `description` / `status`.
4140
+ * Empty for runs that never emit SUBAGENT_* events.
4141
+ */
4142
+ getSubagents(agentId) {
4143
+ return this.subagentRegistry.getSubagents(agentId);
4144
+ }
4145
+ /**
2934
4146
  * Tool management (delegated to RunHandler)
2935
4147
  */
2936
4148
  addTool(tool) {
@@ -3074,516 +4286,260 @@ var CopilotKitCore = class {
3074
4286
  latestMessagesParams = params;
3075
4287
  throttler.maybeExecute();
3076
4288
  };
3077
- if (subscriber.onStateChanged) wrappedSubscriber.onStateChanged = (params) => {
3078
- latestStateParams = params;
3079
- throttler.maybeExecute();
3080
- };
3081
- const subscription = agent.subscribe(wrappedSubscriber);
3082
- return { unsubscribe: () => {
3083
- active = false;
3084
- throttler.cancel();
3085
- subscription.unsubscribe();
3086
- } };
3087
- }
3088
- /**
3089
- * Agent connectivity (delegated to RunHandler)
3090
- */
3091
- async connectAgent(params) {
3092
- return this.runHandler.connectAgent(params);
3093
- }
3094
- stopAgent(params) {
3095
- this.runHandler.abortCurrentRun();
3096
- params.agent.abortRun();
3097
- }
3098
- async runAgent(params) {
3099
- return this.runHandler.runAgent(params);
3100
- }
3101
- /**
3102
- * Programmatically execute a registered frontend tool without going through an LLM turn.
3103
- * The handler runs, render components show up in the UI, and both the tool call and
3104
- * result messages are added to `agent.messages`.
3105
- */
3106
- async runTool(params) {
3107
- return this.runHandler.runTool(params);
3108
- }
3109
- /**
3110
- * State management (delegated to StateManager)
3111
- */
3112
- getStateByRun(agentId, threadId, runId) {
3113
- return this.stateManager.getStateByRun(agentId, threadId, runId);
3114
- }
3115
- getRunIdForMessage(agentId, threadId, messageId) {
3116
- return this.stateManager.getRunIdForMessage(agentId, threadId, messageId);
3117
- }
3118
- getRunIdsForThread(agentId, threadId) {
3119
- return this.stateManager.getRunIdsForThread(agentId, threadId);
3120
- }
3121
- /**
3122
- * Internal method used by RunHandler to build frontend tools
3123
- */
3124
- buildFrontendTools(agentId) {
3125
- return this.runHandler.buildFrontendTools(agentId);
3126
- }
3127
- /**
3128
- * Called before each follow-up agent run (after tool execution).
3129
- *
3130
- * When a frontend tool handler calls framework state setters (e.g. React's
3131
- * setState), those updates are batched and deferred — they do not take effect
3132
- * until the framework's scheduler runs (React uses MessageChannel).
3133
- * useAgentContext registers context via useLayoutEffect, which runs
3134
- * synchronously after React commits that deferred batch.
3135
- *
3136
- * Without yielding here, the follow-up runAgent reads the context store
3137
- * synchronously while the deferred updates are still pending, producing stale
3138
- * context for the next agent turn.
3139
- *
3140
- * Override in framework-specific subclasses to yield to the framework
3141
- * scheduler before the follow-up run. The base implementation is a no-op
3142
- * because non-React environments have no deferred state to flush.
3143
- */
3144
- async waitForPendingFrameworkUpdates() {}
3145
- };
3146
-
3147
- //#endregion
3148
- //#region src/types.ts
3149
- /**
3150
- * Status of a tool call execution
3151
- */
3152
- let ToolCallStatus = /* @__PURE__ */ function(ToolCallStatus) {
3153
- ToolCallStatus["InProgress"] = "inProgress";
3154
- ToolCallStatus["Executing"] = "executing";
3155
- ToolCallStatus["Complete"] = "complete";
3156
- return ToolCallStatus;
3157
- }({});
3158
-
3159
- //#endregion
3160
- //#region src/utils/markdown.ts
3161
- function completePartialMarkdown(input) {
3162
- let s = input;
3163
- const fenceMatches = Array.from(s.matchAll(/^(\s*)(`{3,}|~{3,})/gm));
3164
- if (fenceMatches.length % 2 === 1) {
3165
- const [, indent, fence] = fenceMatches[0];
3166
- s += `\n${indent}${fence}`;
3167
- }
3168
- if (s.match(/\[([^\]]*)\]\(([^)]*)$/)) s += ")";
3169
- const openElements = [];
3170
- const chars = Array.from(s);
3171
- const codeBlockRanges = [];
3172
- const inlineCodeRanges = [];
3173
- let tempCodeFenceCount = 0;
3174
- let currentCodeBlockStart = -1;
3175
- for (let i = 0; i < chars.length; i++) if (i === 0 || chars[i - 1] === "\n") {
3176
- const lineMatch = s.substring(i).match(/^(\s*)(`{3,}|~{3,})/);
3177
- if (lineMatch) {
3178
- tempCodeFenceCount++;
3179
- if (tempCodeFenceCount % 2 === 1) currentCodeBlockStart = i;
3180
- else if (currentCodeBlockStart !== -1) {
3181
- codeBlockRanges.push({
3182
- start: currentCodeBlockStart,
3183
- end: i + lineMatch[0].length
3184
- });
3185
- currentCodeBlockStart = -1;
3186
- }
3187
- i += lineMatch[0].length - 1;
3188
- }
3189
- }
3190
- for (let i = 0; i < chars.length; i++) if (chars[i] === "`") {
3191
- let backslashCount = 0;
3192
- for (let j = i - 1; j >= 0 && chars[j] === "\\"; j--) backslashCount++;
3193
- if (backslashCount % 2 === 0) {
3194
- for (let j = i + 1; j < chars.length; j++) if (chars[j] === "`") {
3195
- let closingBackslashCount = 0;
3196
- for (let k = j - 1; k >= 0 && chars[k] === "\\"; k--) closingBackslashCount++;
3197
- if (closingBackslashCount % 2 === 0) {
3198
- inlineCodeRanges.push({
3199
- start: i,
3200
- end: j + 1
3201
- });
3202
- i = j;
3203
- break;
3204
- }
3205
- }
3206
- }
3207
- }
3208
- const isInCode = (pos) => {
3209
- return codeBlockRanges.some((range) => pos >= range.start && pos < range.end) || inlineCodeRanges.some((range) => pos >= range.start && pos < range.end);
3210
- };
3211
- for (let i = 0; i < chars.length; i++) {
3212
- const char = chars[i];
3213
- const nextChar = chars[i + 1];
3214
- const prevChar = chars[i - 1];
3215
- if (isInCode(i)) continue;
3216
- if (char === "[") {
3217
- let isCompleteLink = false;
3218
- let bracketDepth = 1;
3219
- let j = i + 1;
3220
- while (j < chars.length && bracketDepth > 0) {
3221
- if (chars[j] === "[" && !isInCode(j)) bracketDepth++;
3222
- if (chars[j] === "]" && !isInCode(j)) bracketDepth--;
3223
- j++;
3224
- }
3225
- if (bracketDepth === 0 && chars[j] === "(") {
3226
- let parenDepth = 1;
3227
- j++;
3228
- while (j < chars.length && parenDepth > 0) {
3229
- if (chars[j] === "(" && !isInCode(j)) parenDepth++;
3230
- if (chars[j] === ")" && !isInCode(j)) parenDepth--;
3231
- j++;
3232
- }
3233
- if (parenDepth === 0) {
3234
- isCompleteLink = true;
3235
- i = j - 1;
3236
- continue;
3237
- }
3238
- }
3239
- if (!isCompleteLink) {
3240
- const existingIndex = openElements.findIndex((el) => el.type === "bracket");
3241
- if (existingIndex !== -1) openElements.splice(existingIndex, 1);
3242
- else openElements.push({
3243
- type: "bracket",
3244
- marker: "[",
3245
- position: i
3246
- });
3247
- }
3248
- } else if (char === "*" && nextChar === "*") {
3249
- const existingIndex = openElements.findIndex((el) => el.type === "bold_star");
3250
- if (existingIndex !== -1) openElements.splice(existingIndex, 1);
3251
- else openElements.push({
3252
- type: "bold_star",
3253
- marker: "**",
3254
- position: i
3255
- });
3256
- i++;
3257
- } else if (char === "_" && nextChar === "_") {
3258
- const existingIndex = openElements.findIndex((el) => el.type === "bold_underscore");
3259
- if (existingIndex !== -1) openElements.splice(existingIndex, 1);
3260
- else openElements.push({
3261
- type: "bold_underscore",
3262
- marker: "__",
3263
- position: i
3264
- });
3265
- i++;
3266
- } else if (char === "~" && nextChar === "~") {
3267
- const existingIndex = openElements.findIndex((el) => el.type === "strike");
3268
- if (existingIndex !== -1) openElements.splice(existingIndex, 1);
3269
- else openElements.push({
3270
- type: "strike",
3271
- marker: "~~",
3272
- position: i
3273
- });
3274
- i++;
3275
- } else if (char === "*" && prevChar !== "*" && nextChar !== "*") {
3276
- const existingIndex = openElements.findIndex((el) => el.type === "italic_star");
3277
- if (existingIndex !== -1) openElements.splice(existingIndex, 1);
3278
- else openElements.push({
3279
- type: "italic_star",
3280
- marker: "*",
3281
- position: i
3282
- });
3283
- } else if (char === "_" && prevChar !== "_" && nextChar !== "_") {
3284
- const existingIndex = openElements.findIndex((el) => el.type === "italic_underscore");
3285
- if (existingIndex !== -1) openElements.splice(existingIndex, 1);
3286
- else openElements.push({
3287
- type: "italic_underscore",
3288
- marker: "_",
3289
- position: i
3290
- });
3291
- }
3292
- }
3293
- let backtickCount = 0;
3294
- for (let i = 0; i < chars.length; i++) if (chars[i] === "`" && !isInCode(i)) backtickCount++;
3295
- if (backtickCount % 2 === 1) s += "`";
3296
- openElements.sort((a, b) => b.position - a.position);
3297
- const closers = openElements.map((el) => {
3298
- switch (el.type) {
3299
- case "bracket": return "]";
3300
- case "bold_star": return "**";
3301
- case "bold_underscore": return "__";
3302
- case "strike": return "~~";
3303
- case "italic_star": return "*";
3304
- case "italic_underscore": return "_";
3305
- default: return "";
3306
- }
3307
- });
3308
- let result = s + closers.join("");
3309
- const finalFenceMatches = Array.from(result.matchAll(/^(\s*)(`{3,}|~{3,})/gm));
3310
- const hasUnclosedBacktick = (result.match(/`/g) || []).length % 2 === 1;
3311
- const hasUnclosedCodeFence = finalFenceMatches.length % 2 === 1;
3312
- let shouldCloseParens = !hasUnclosedBacktick && !hasUnclosedCodeFence;
3313
- if (shouldCloseParens) {
3314
- const lastOpenParen = result.lastIndexOf("(");
3315
- if (lastOpenParen !== -1) {
3316
- if ((result.substring(0, lastOpenParen).match(/`/g) || []).length % 2 === 1) shouldCloseParens = false;
3317
- }
4289
+ if (subscriber.onStateChanged) wrappedSubscriber.onStateChanged = (params) => {
4290
+ latestStateParams = params;
4291
+ throttler.maybeExecute();
4292
+ };
4293
+ const subscription = agent.subscribe(wrappedSubscriber);
4294
+ return { unsubscribe: () => {
4295
+ active = false;
4296
+ throttler.cancel();
4297
+ subscription.unsubscribe();
4298
+ } };
3318
4299
  }
3319
- if (shouldCloseParens) {
3320
- const openParens = (result.match(/\(/g) || []).length;
3321
- const closeParens = (result.match(/\)/g) || []).length;
3322
- if (openParens > closeParens) result += ")".repeat(openParens - closeParens);
4300
+ /**
4301
+ * Agent connectivity (delegated to RunHandler)
4302
+ */
4303
+ async connectAgent(params) {
4304
+ return this.runHandler.connectAgent(params);
3323
4305
  }
3324
- return result;
3325
- }
3326
-
3327
- //#endregion
3328
- //#region src/utils/micro-redux.ts
3329
- const INTERNAL_ACTION_TYPES = {
3330
- boot: "@@micro-redux/boot",
3331
- init: "@@micro-redux/init",
3332
- stop: "@@micro-redux/stop"
3333
- };
3334
- const INTERNAL_BOOT_ACTION = { type: INTERNAL_ACTION_TYPES.boot };
3335
- /**
3336
- * Builds a typed action creator from a type string and payload factory.
3337
- */
3338
- function createTypedActionCreator(type, factory) {
3339
- const creator = ((...args) => ({
3340
- ...factory(...args),
3341
- type
3342
- }));
3343
- creator.type = type;
3344
- creator.match = (action) => action.type === type;
3345
- return creator;
3346
- }
3347
- /**
3348
- * Declares a payload-based action config for `createActionGroup`.
3349
- *
3350
- * @example
3351
- * ```ts
3352
- * const actions = createActionGroup("User", {
3353
- * loaded: props<{ id: string }>(),
3354
- * });
3355
- * ```
3356
- */
3357
- function props() {
3358
- return { kind: "props" };
3359
- }
3360
- /**
3361
- * Declares a no-payload action config for `createActionGroup`.
3362
- *
3363
- * @example
3364
- * ```ts
3365
- * const actions = createActionGroup("User", {
3366
- * reset: empty(),
3367
- * });
3368
- * ```
3369
- */
3370
- function empty() {
3371
- return { kind: "empty" };
3372
- }
3373
- /**
3374
- * Creates a namespaced group of typed action creators.
3375
- *
3376
- * Action types are formatted as: `[Source] actionName`.
3377
- */
3378
- function createActionGroup(source, config) {
3379
- const group = {};
3380
- for (const eventName of Object.keys(config)) {
3381
- const eventConfig = config[eventName];
3382
- if (!eventConfig) continue;
3383
- const actionType = `[${source}] ${eventName}`;
3384
- if (eventConfig.kind === "props") {
3385
- group[eventName] = createTypedActionCreator(actionType, (payload) => ({ ...payload }));
3386
- continue;
3387
- }
3388
- group[eventName] = createTypedActionCreator(actionType, () => ({}));
4306
+ stopAgent(params) {
4307
+ this.runHandler.abortCurrentRun();
4308
+ params.agent.abortRun();
3389
4309
  }
3390
- return group;
3391
- }
3392
- /**
3393
- * Registers one reducer handler for one or more action creators.
3394
- *
3395
- * @throws Error when called without at least one action creator and reducer.
3396
- */
3397
- function on(...args) {
3398
- if (args.length < 2) throw new Error("on requires at least one action creator and one reducer");
3399
- const reducer = args[args.length - 1];
3400
- return {
3401
- creators: args.slice(0, -1),
3402
- reducer
3403
- };
3404
- }
3405
- /**
3406
- * Creates a reducer from an initial state and `on(...)` handler entries.
3407
- *
3408
- * Unknown action types return the current state unchanged.
3409
- */
3410
- function createReducer(initialState, ...entries) {
3411
- const reducerMap = /* @__PURE__ */ new Map();
3412
- for (const entry of entries) for (const creator of entry.creators) {
3413
- const handlers = reducerMap.get(creator.type) ?? [];
3414
- handlers.push(entry.reducer);
3415
- reducerMap.set(creator.type, handlers);
4310
+ async runAgent(params) {
4311
+ return this.runHandler.runAgent(params);
3416
4312
  }
3417
- return (state, action) => {
3418
- const currentState = state ?? initialState;
3419
- const handlers = reducerMap.get(action.type);
3420
- if (!handlers || handlers.length === 0) return currentState;
3421
- let nextState = currentState;
3422
- for (const handler of handlers) nextState = handler(nextState, action);
3423
- return nextState;
3424
- };
3425
- }
3426
- /**
3427
- * Creates a selector that caches and reuses the last computed result
3428
- * when all input references are unchanged.
3429
- */
3430
- function createSelector(...args) {
3431
- if (args.length === 1) {
3432
- const projector = args[0];
3433
- let hasCached = false;
3434
- let lastState;
3435
- let lastResult;
3436
- return (state) => {
3437
- if (hasCached && state === lastState) return lastResult;
3438
- lastState = state;
3439
- lastResult = projector(state);
3440
- hasCached = true;
3441
- return lastResult;
3442
- };
4313
+ /**
4314
+ * Programmatically execute a registered frontend tool without going through an LLM turn.
4315
+ * The handler runs, render components show up in the UI, and both the tool call and
4316
+ * result messages are added to `agent.messages`.
4317
+ */
4318
+ async runTool(params) {
4319
+ return this.runHandler.runTool(params);
3443
4320
  }
3444
- const projector = args[args.length - 1];
3445
- const selectors = args.slice(0, -1);
3446
- let hasCached = false;
3447
- let lastInputs = [];
3448
- let lastResult;
3449
- return (state) => {
3450
- const inputs = selectors.map((selector) => selector(state));
3451
- if (hasCached && inputs.length === lastInputs.length && inputs.every((value, index) => value === lastInputs[index])) return lastResult;
3452
- lastInputs = inputs;
3453
- lastResult = projector(...inputs);
3454
- hasCached = true;
3455
- return lastResult;
3456
- };
3457
- }
3458
- /**
3459
- * RxJS operator that maps state emissions through a selector and suppresses
3460
- * unchanged projected values via reference equality.
3461
- */
3462
- function select(selector) {
3463
- return (source$) => source$.pipe(map(selector), distinctUntilChanged());
3464
- }
3465
- /**
3466
- * RxJS operator that filters an action stream by action creators and narrows
3467
- * the output action type to the matched creator union.
3468
- *
3469
- * @throws Error when called without at least one action creator.
3470
- */
3471
- function ofType(...creators) {
3472
- if (creators.length === 0) throw new Error("ofType requires at least one action creator");
3473
- const actionTypes = new Set(creators.map((creator) => creator.type));
3474
- return (source$) => {
3475
- return source$.pipe(filter((action) => {
3476
- return actionTypes.has(action.type);
3477
- }));
3478
- };
3479
- }
3480
- /**
3481
- * Creates an effect descriptor consumed by `createStore`.
3482
- */
3483
- function createEffect(factory, options = {}) {
3484
- if (options.dispatch === false) return {
3485
- run: factory,
3486
- dispatch: false
3487
- };
3488
- return {
3489
- run: factory,
3490
- dispatch: true
3491
- };
3492
- }
3493
- /**
3494
- * Creates a small observable store with reducer + effects.
3495
- *
3496
- * Behavior:
3497
- * - `init()` starts effects and dispatches `@@micro-redux/init`.
3498
- * - `stop()` dispatches `@@micro-redux/stop` and unsubscribes all effects.
3499
- * - Effect action observation is scheduled on `asapScheduler` to avoid
3500
- * synchronous re-entrancy in the effect loop.
3501
- * - Any effect error triggers fail-fast teardown and errors both `actions$`
3502
- * and `state$`.
3503
- */
3504
- function createStore(options) {
3505
- const reducer = options.reducer;
3506
- const effects = options.effects ?? [];
3507
- let hasFatalError = false;
3508
- let isRunning = false;
3509
- let effectSubscriptions = new Subscription();
3510
- let currentState = reducer(void 0, INTERNAL_BOOT_ACTION);
3511
- const stateSubject = new BehaviorSubject(currentState);
3512
- const actionsSubject = new Subject();
3513
- const dispatchInternal = (action) => {
3514
- if (hasFatalError) throw new Error("Store is in a failed state due to an effect error");
3515
- currentState = reducer(currentState, action);
3516
- stateSubject.next(currentState);
3517
- actionsSubject.next(action);
3518
- };
3519
- const failFast = (error) => {
3520
- if (hasFatalError) return;
3521
- hasFatalError = true;
3522
- isRunning = false;
3523
- effectSubscriptions.unsubscribe();
3524
- effectSubscriptions = new Subscription();
3525
- actionsSubject.error(error);
3526
- stateSubject.error(error);
3527
- };
3528
- const startEffects = () => {
3529
- for (const effect of effects) {
3530
- const scheduledActions$ = actionsSubject.asObservable().pipe(observeOn(asapScheduler));
3531
- const state$ = stateSubject.asObservable();
3532
- if (effect.dispatch) {
3533
- const subscription = effect.run(scheduledActions$, state$).subscribe({
3534
- next: (effectAction) => {
3535
- if (hasFatalError) return;
3536
- dispatchInternal(effectAction);
3537
- },
3538
- error: (error) => {
3539
- failFast(error);
3540
- }
4321
+ /**
4322
+ * State management (delegated to StateManager)
4323
+ */
4324
+ getStateByRun(agentId, threadId, runId) {
4325
+ return this.stateManager.getStateByRun(agentId, threadId, runId);
4326
+ }
4327
+ getRunIdForMessage(agentId, threadId, messageId) {
4328
+ return this.stateManager.getRunIdForMessage(agentId, threadId, messageId);
4329
+ }
4330
+ getRunIdsForThread(agentId, threadId) {
4331
+ return this.stateManager.getRunIdsForThread(agentId, threadId);
4332
+ }
4333
+ /**
4334
+ * Internal method used by RunHandler to build frontend tools
4335
+ */
4336
+ buildFrontendTools(agentId) {
4337
+ return this.runHandler.buildFrontendTools(agentId);
4338
+ }
4339
+ /**
4340
+ * Called before each follow-up agent run (after tool execution).
4341
+ *
4342
+ * When a frontend tool handler calls framework state setters (e.g. React's
4343
+ * setState), those updates are batched and deferred — they do not take effect
4344
+ * until the framework's scheduler runs (React uses MessageChannel).
4345
+ * useAgentContext registers context via useLayoutEffect, which runs
4346
+ * synchronously after React commits that deferred batch.
4347
+ *
4348
+ * Without yielding here, the follow-up runAgent reads the context store
4349
+ * synchronously while the deferred updates are still pending, producing stale
4350
+ * context for the next agent turn.
4351
+ *
4352
+ * Override in framework-specific subclasses to yield to the framework
4353
+ * scheduler before the follow-up run. The base implementation is a no-op
4354
+ * because non-React environments have no deferred state to flush.
4355
+ */
4356
+ async waitForPendingFrameworkUpdates() {}
4357
+ };
4358
+
4359
+ //#endregion
4360
+ //#region src/types.ts
4361
+ /**
4362
+ * Status of a tool call execution
4363
+ */
4364
+ let ToolCallStatus = /* @__PURE__ */ function(ToolCallStatus) {
4365
+ ToolCallStatus["InProgress"] = "inProgress";
4366
+ ToolCallStatus["Executing"] = "executing";
4367
+ ToolCallStatus["Complete"] = "complete";
4368
+ return ToolCallStatus;
4369
+ }({});
4370
+
4371
+ //#endregion
4372
+ //#region src/utils/markdown.ts
4373
+ function completePartialMarkdown(input) {
4374
+ let s = input;
4375
+ const fenceMatches = Array.from(s.matchAll(/^(\s*)(`{3,}|~{3,})/gm));
4376
+ if (fenceMatches.length % 2 === 1) {
4377
+ const [, indent, fence] = fenceMatches[0];
4378
+ s += `\n${indent}${fence}`;
4379
+ }
4380
+ if (s.match(/\[([^\]]*)\]\(([^)]*)$/)) s += ")";
4381
+ const openElements = [];
4382
+ const chars = Array.from(s);
4383
+ const codeBlockRanges = [];
4384
+ const inlineCodeRanges = [];
4385
+ let tempCodeFenceCount = 0;
4386
+ let currentCodeBlockStart = -1;
4387
+ for (let i = 0; i < chars.length; i++) if (i === 0 || chars[i - 1] === "\n") {
4388
+ const lineMatch = s.substring(i).match(/^(\s*)(`{3,}|~{3,})/);
4389
+ if (lineMatch) {
4390
+ tempCodeFenceCount++;
4391
+ if (tempCodeFenceCount % 2 === 1) currentCodeBlockStart = i;
4392
+ else if (currentCodeBlockStart !== -1) {
4393
+ codeBlockRanges.push({
4394
+ start: currentCodeBlockStart,
4395
+ end: i + lineMatch[0].length
3541
4396
  });
3542
- effectSubscriptions.add(subscription);
3543
- continue;
4397
+ currentCodeBlockStart = -1;
3544
4398
  }
3545
- const subscription = effect.run(scheduledActions$, state$).subscribe({ error: (error) => {
3546
- failFast(error);
3547
- } });
3548
- effectSubscriptions.add(subscription);
4399
+ i += lineMatch[0].length - 1;
3549
4400
  }
3550
- };
3551
- return {
3552
- dispatch(action) {
3553
- dispatchInternal(action);
3554
- },
3555
- getState() {
3556
- return currentState;
3557
- },
3558
- get state$() {
3559
- return stateSubject.asObservable();
3560
- },
3561
- get actions$() {
3562
- return actionsSubject.asObservable();
3563
- },
3564
- select(selector) {
3565
- return stateSubject.asObservable().pipe(select(selector));
3566
- },
3567
- init() {
3568
- if (hasFatalError || isRunning) return;
3569
- isRunning = true;
3570
- startEffects();
3571
- if (hasFatalError) return;
3572
- dispatchInternal({ type: INTERNAL_ACTION_TYPES.init });
3573
- },
3574
- stop() {
3575
- if (hasFatalError || !isRunning) return;
3576
- dispatchInternal({ type: INTERNAL_ACTION_TYPES.stop });
3577
- effectSubscriptions.unsubscribe();
3578
- effectSubscriptions = new Subscription();
3579
- isRunning = false;
4401
+ }
4402
+ for (let i = 0; i < chars.length; i++) if (chars[i] === "`") {
4403
+ let backslashCount = 0;
4404
+ for (let j = i - 1; j >= 0 && chars[j] === "\\"; j--) backslashCount++;
4405
+ if (backslashCount % 2 === 0) {
4406
+ for (let j = i + 1; j < chars.length; j++) if (chars[j] === "`") {
4407
+ let closingBackslashCount = 0;
4408
+ for (let k = j - 1; k >= 0 && chars[k] === "\\"; k--) closingBackslashCount++;
4409
+ if (closingBackslashCount % 2 === 0) {
4410
+ inlineCodeRanges.push({
4411
+ start: i,
4412
+ end: j + 1
4413
+ });
4414
+ i = j;
4415
+ break;
4416
+ }
4417
+ }
3580
4418
  }
4419
+ }
4420
+ const isInCode = (pos) => {
4421
+ return codeBlockRanges.some((range) => pos >= range.start && pos < range.end) || inlineCodeRanges.some((range) => pos >= range.start && pos < range.end);
3581
4422
  };
4423
+ for (let i = 0; i < chars.length; i++) {
4424
+ const char = chars[i];
4425
+ const nextChar = chars[i + 1];
4426
+ const prevChar = chars[i - 1];
4427
+ if (isInCode(i)) continue;
4428
+ if (char === "[") {
4429
+ let isCompleteLink = false;
4430
+ let bracketDepth = 1;
4431
+ let j = i + 1;
4432
+ while (j < chars.length && bracketDepth > 0) {
4433
+ if (chars[j] === "[" && !isInCode(j)) bracketDepth++;
4434
+ if (chars[j] === "]" && !isInCode(j)) bracketDepth--;
4435
+ j++;
4436
+ }
4437
+ if (bracketDepth === 0 && chars[j] === "(") {
4438
+ let parenDepth = 1;
4439
+ j++;
4440
+ while (j < chars.length && parenDepth > 0) {
4441
+ if (chars[j] === "(" && !isInCode(j)) parenDepth++;
4442
+ if (chars[j] === ")" && !isInCode(j)) parenDepth--;
4443
+ j++;
4444
+ }
4445
+ if (parenDepth === 0) {
4446
+ isCompleteLink = true;
4447
+ i = j - 1;
4448
+ continue;
4449
+ }
4450
+ }
4451
+ if (!isCompleteLink) {
4452
+ const existingIndex = openElements.findIndex((el) => el.type === "bracket");
4453
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
4454
+ else openElements.push({
4455
+ type: "bracket",
4456
+ marker: "[",
4457
+ position: i
4458
+ });
4459
+ }
4460
+ } else if (char === "*" && nextChar === "*") {
4461
+ const existingIndex = openElements.findIndex((el) => el.type === "bold_star");
4462
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
4463
+ else openElements.push({
4464
+ type: "bold_star",
4465
+ marker: "**",
4466
+ position: i
4467
+ });
4468
+ i++;
4469
+ } else if (char === "_" && nextChar === "_") {
4470
+ const existingIndex = openElements.findIndex((el) => el.type === "bold_underscore");
4471
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
4472
+ else openElements.push({
4473
+ type: "bold_underscore",
4474
+ marker: "__",
4475
+ position: i
4476
+ });
4477
+ i++;
4478
+ } else if (char === "~" && nextChar === "~") {
4479
+ const existingIndex = openElements.findIndex((el) => el.type === "strike");
4480
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
4481
+ else openElements.push({
4482
+ type: "strike",
4483
+ marker: "~~",
4484
+ position: i
4485
+ });
4486
+ i++;
4487
+ } else if (char === "*" && prevChar !== "*" && nextChar !== "*") {
4488
+ const existingIndex = openElements.findIndex((el) => el.type === "italic_star");
4489
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
4490
+ else openElements.push({
4491
+ type: "italic_star",
4492
+ marker: "*",
4493
+ position: i
4494
+ });
4495
+ } else if (char === "_" && prevChar !== "_" && nextChar !== "_") {
4496
+ const existingIndex = openElements.findIndex((el) => el.type === "italic_underscore");
4497
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
4498
+ else openElements.push({
4499
+ type: "italic_underscore",
4500
+ marker: "_",
4501
+ position: i
4502
+ });
4503
+ }
4504
+ }
4505
+ let backtickCount = 0;
4506
+ for (let i = 0; i < chars.length; i++) if (chars[i] === "`" && !isInCode(i)) backtickCount++;
4507
+ if (backtickCount % 2 === 1) s += "`";
4508
+ openElements.sort((a, b) => b.position - a.position);
4509
+ const closers = openElements.map((el) => {
4510
+ switch (el.type) {
4511
+ case "bracket": return "]";
4512
+ case "bold_star": return "**";
4513
+ case "bold_underscore": return "__";
4514
+ case "strike": return "~~";
4515
+ case "italic_star": return "*";
4516
+ case "italic_underscore": return "_";
4517
+ default: return "";
4518
+ }
4519
+ });
4520
+ let result = s + closers.join("");
4521
+ const finalFenceMatches = Array.from(result.matchAll(/^(\s*)(`{3,}|~{3,})/gm));
4522
+ const hasUnclosedBacktick = (result.match(/`/g) || []).length % 2 === 1;
4523
+ const hasUnclosedCodeFence = finalFenceMatches.length % 2 === 1;
4524
+ let shouldCloseParens = !hasUnclosedBacktick && !hasUnclosedCodeFence;
4525
+ if (shouldCloseParens) {
4526
+ const lastOpenParen = result.lastIndexOf("(");
4527
+ if (lastOpenParen !== -1) {
4528
+ if ((result.substring(0, lastOpenParen).match(/`/g) || []).length % 2 === 1) shouldCloseParens = false;
4529
+ }
4530
+ }
4531
+ if (shouldCloseParens) {
4532
+ const openParens = (result.match(/\(/g) || []).length;
4533
+ const closeParens = (result.match(/\)/g) || []).length;
4534
+ if (openParens > closeParens) result += ")".repeat(openParens - closeParens);
4535
+ }
4536
+ return result;
3582
4537
  }
3583
4538
 
3584
4539
  //#endregion
3585
4540
  //#region src/threads.ts
3586
4541
  const THREADS_CHANNEL_EVENT = "thread_metadata";
4542
+ const THREAD_RUN_ACTIVITY_CHANNEL_EVENT = "thread_run_activity";
3587
4543
  const THREAD_SUBSCRIBE_PATH = "/threads/subscribe";
3588
4544
  const MAX_SOCKET_RETRIES = 5;
3589
4545
  const REQUEST_TIMEOUT_MS = 15e3;
@@ -3592,6 +4548,7 @@ const initialThreadState = {
3592
4548
  isLoading: false,
3593
4549
  isFetchingNextPage: false,
3594
4550
  error: null,
4551
+ fetchMoreError: null,
3595
4552
  context: null,
3596
4553
  sessionId: 0,
3597
4554
  metadataCredentialsRequested: false,
@@ -3627,7 +4584,8 @@ const threadSocketEvents = createActionGroup("Thread Socket", {
3627
4584
  errored: props(),
3628
4585
  joinFailed: props(),
3629
4586
  joinTimedOut: props(),
3630
- metadataReceived: props()
4587
+ metadataReceived: props(),
4588
+ runActivityReceived: props()
3631
4589
  });
3632
4590
  const threadDomainEvents = createActionGroup("Thread Domain", {
3633
4591
  threadUpserted: props(),
@@ -3646,6 +4604,28 @@ function upsertThread(threads, thread) {
3646
4604
  next[existingIndex] = thread;
3647
4605
  return sortThreadsByRecency(next);
3648
4606
  }
4607
+ /**
4608
+ * Returns a non-empty string payload field or undefined for absent fields.
4609
+ */
4610
+ function optionalString(value) {
4611
+ return typeof value === "string" && value.length > 0 ? value : void 0;
4612
+ }
4613
+ /**
4614
+ * Converts gateway run-activity payloads into the internal TypeScript shape.
4615
+ */
4616
+ function normalizeThreadRunActivityNotification(payload) {
4617
+ const threadId = optionalString(payload.threadId ?? payload.thread_id);
4618
+ const eventType = optionalString(payload.eventType ?? payload.event_type);
4619
+ if (!threadId || !eventType) return null;
4620
+ return {
4621
+ type: "thread_run_activity",
4622
+ threadId,
4623
+ agentId: optionalString(payload.agentId ?? payload.agent_id),
4624
+ runId: optionalString(payload.runId ?? payload.run_id),
4625
+ eventType,
4626
+ latestEventId: optionalString(payload.latestEventId ?? payload.latest_event_id)
4627
+ };
4628
+ }
3649
4629
  const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.contextChanged, (state, { context }) => ({
3650
4630
  ...state,
3651
4631
  context,
@@ -3654,6 +4634,7 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3654
4634
  isLoading: Boolean(context),
3655
4635
  isFetchingNextPage: false,
3656
4636
  error: null,
4637
+ fetchMoreError: null,
3657
4638
  metadataCredentialsRequested: false,
3658
4639
  metadataJoinCode: null,
3659
4640
  nextCursor: null,
@@ -3665,6 +4646,7 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3665
4646
  isLoading: false,
3666
4647
  isFetchingNextPage: false,
3667
4648
  error: null,
4649
+ fetchMoreError: null,
3668
4650
  metadataCredentialsRequested: false,
3669
4651
  metadataJoinCode: null,
3670
4652
  nextCursor: null,
@@ -3675,16 +4657,20 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3675
4657
  return {
3676
4658
  ...state,
3677
4659
  isLoading: true,
3678
- error: null
4660
+ error: null,
4661
+ fetchMoreError: null
3679
4662
  };
3680
4663
  }), on(threadRestEvents.listSucceeded, (state, { sessionId, threads, joinCode, nextCursor }) => {
3681
4664
  if (sessionId !== state.sessionId) return state;
4665
+ const joinCodeChanged = joinCode !== state.metadataJoinCode;
3682
4666
  return {
3683
4667
  ...state,
3684
4668
  threads: sortThreadsByRecency(threads),
3685
4669
  isLoading: false,
3686
4670
  error: null,
4671
+ fetchMoreError: null,
3687
4672
  metadataJoinCode: joinCode,
4673
+ metadataCredentialsRequested: joinCodeChanged ? false : state.metadataCredentialsRequested,
3688
4674
  nextCursor
3689
4675
  };
3690
4676
  }), on(threadRestEvents.listFailed, (state, { sessionId, error }) => {
@@ -3702,6 +4688,7 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3702
4688
  ...state,
3703
4689
  threads: merged,
3704
4690
  isFetchingNextPage: false,
4691
+ fetchMoreError: null,
3705
4692
  nextCursor
3706
4693
  };
3707
4694
  }), on(threadRestEvents.nextPageFailed, (state, { sessionId, error }) => {
@@ -3709,11 +4696,14 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3709
4696
  return {
3710
4697
  ...state,
3711
4698
  isFetchingNextPage: false,
3712
- error
4699
+ fetchMoreError: error
3713
4700
  };
3714
4701
  }), on(threadRestEvents.metadataCredentialsFailed, (state, { sessionId }) => {
3715
4702
  if (sessionId !== state.sessionId) return state;
3716
- return state;
4703
+ return {
4704
+ ...state,
4705
+ metadataCredentialsRequested: false
4706
+ };
3717
4707
  }), on(threadRestEvents.metadataCredentialsRequested, (state, { sessionId }) => {
3718
4708
  if (sessionId !== state.sessionId) return state;
3719
4709
  return {
@@ -3724,7 +4714,8 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3724
4714
  if (!state.nextCursor || state.isFetchingNextPage) return state;
3725
4715
  return {
3726
4716
  ...state,
3727
- isFetchingNextPage: true
4717
+ isFetchingNextPage: true,
4718
+ fetchMoreError: null
3728
4719
  };
3729
4720
  }), on(threadAdapterEvents.renameRequested, (state, { threadId, name }) => {
3730
4721
  const existing = state.threads.find((thread) => thread.id === threadId);
@@ -3853,6 +4844,7 @@ function createThreadSelectors() {
3853
4844
  threads: createSelector((state) => state.threads),
3854
4845
  isLoading: createSelector((state) => state.isLoading),
3855
4846
  error: createSelector((state) => state.error),
4847
+ fetchMoreError: createSelector((state) => state.fetchMoreError),
3856
4848
  hasNextPage: createSelector((state) => state.nextCursor != null),
3857
4849
  isFetchingNextPage: createSelector((state) => state.isFetchingNextPage),
3858
4850
  isMutating: createSelector((state) => state.inFlightMutationCount > 0)
@@ -3862,6 +4854,7 @@ const standaloneSelectors = createThreadSelectors();
3862
4854
  const selectThreads = standaloneSelectors.threads;
3863
4855
  const selectThreadsIsLoading = standaloneSelectors.isLoading;
3864
4856
  const selectThreadsError = standaloneSelectors.error;
4857
+ const selectFetchMoreError = standaloneSelectors.fetchMoreError;
3865
4858
  const selectHasNextPage = standaloneSelectors.hasNextPage;
3866
4859
  const selectIsFetchingNextPage = standaloneSelectors.isFetchingNextPage;
3867
4860
  const selectIsMutating = standaloneSelectors.isMutating;
@@ -3871,7 +4864,27 @@ function createThreadRequestId() {
3871
4864
  return `thread-request-${threadRequestId}`;
3872
4865
  }
3873
4866
  function threadFromFetch(input, init) {
3874
- return fromFetch(input, init);
4867
+ return new Observable((subscriber) => {
4868
+ const { fetch: fetchImpl, selector, signal, ...requestInit } = init;
4869
+ const controller = new AbortController();
4870
+ const abortRequest = () => controller.abort();
4871
+ if (signal?.aborted) abortRequest();
4872
+ else signal?.addEventListener("abort", abortRequest, { once: true });
4873
+ fetchImpl(input, {
4874
+ ...requestInit,
4875
+ signal: controller.signal
4876
+ }).then((response) => selector(response)).then((value) => {
4877
+ if (subscriber.closed) return;
4878
+ subscriber.next(value);
4879
+ subscriber.complete();
4880
+ }).catch((error) => {
4881
+ if (!subscriber.closed) subscriber.error(error);
4882
+ });
4883
+ return () => {
4884
+ signal?.removeEventListener("abort", abortRequest);
4885
+ abortRequest();
4886
+ };
4887
+ });
3875
4888
  }
3876
4889
  function createThreadFetchObservable(environment, context, sessionId) {
3877
4890
  return defer(() => {
@@ -4017,6 +5030,9 @@ function createThreadStore(environment) {
4017
5030
  return merge(socketSignals$.pipe(map((signal) => signal.type === "open" ? threadSocketEvents.opened({ sessionId: action.sessionId }) : threadSocketEvents.errored({ sessionId: action.sessionId }))), channel$.pipe(switchMap$1(({ channel }) => ɵobservePhoenixEvent$(channel, THREADS_CHANNEL_EVENT)), map((payload) => threadSocketEvents.metadataReceived({
4018
5031
  sessionId: action.sessionId,
4019
5032
  payload
5033
+ }))), channel$.pipe(switchMap$1(({ channel }) => ɵobservePhoenixEvent$(channel, THREAD_RUN_ACTIVITY_CHANNEL_EVENT)), map((payload) => normalizeThreadRunActivityNotification(payload)), filter((notification) => notification !== null), map((notification) => threadSocketEvents.runActivityReceived({
5034
+ sessionId: action.sessionId,
5035
+ notification
4020
5036
  }))), ɵobservePhoenixJoinOutcome$(channel$).pipe(filter((outcome) => outcome.type !== "joined"), map((outcome) => outcome.type === "timeout" ? threadSocketEvents.joinTimedOut({ sessionId: action.sessionId }) : threadSocketEvents.joinFailed({ sessionId: action.sessionId })))).pipe(takeUntil(merge(shutdown$, fatalSocketShutdown$)));
4021
5037
  });
4022
5038
  }))),
@@ -4194,6 +5210,9 @@ function createThreadStore(environment) {
4194
5210
  threadId
4195
5211
  }));
4196
5212
  },
5213
+ subscribeToRunActivity(callback) {
5214
+ return store.actions$.pipe(ofType(threadSocketEvents.runActivityReceived), filter((action) => action.sessionId === store.getState().sessionId), map((action) => action.notification)).subscribe(callback);
5215
+ },
4197
5216
  getState() {
4198
5217
  return store.getState();
4199
5218
  },
@@ -4209,6 +5228,7 @@ const ɵcreateThreadSelectors = createThreadSelectors;
4209
5228
  const ɵselectThreads = selectThreads;
4210
5229
  const ɵselectThreadsIsLoading = selectThreadsIsLoading;
4211
5230
  const ɵselectThreadsError = selectThreadsError;
5231
+ const ɵselectFetchMoreError = selectFetchMoreError;
4212
5232
  const ɵselectHasNextPage = selectHasNextPage;
4213
5233
  const ɵselectIsFetchingNextPage = selectIsFetchingNextPage;
4214
5234
  const ɵselectIsMutating = selectIsMutating;
@@ -4266,5 +5286,5 @@ function ɵisCopilotKitFeature(name) {
4266
5286
  }
4267
5287
 
4268
5288
  //#endregion
4269
- 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 };
5289
+ export { AgentRegistry, AgentThreadLockedError, ContextStore, CopilotKitCore, CopilotKitCoreErrorCode, CopilotKitCoreRuntimeConnectionStatus, IntelligenceAgent, MEMORY_ERROR_REGISTRY, MemoryError, ProxiedCopilotRuntimeAgent, RunHandler, StateManager, SubagentRegistry, SuggestionEngine, ToolCallStatus, completePartialMarkdown, createActionGroup, createEffect, createReducer, createSelector, createStore, empty, ensureObjectArgs, 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 };
4270
5290
  //# sourceMappingURL=index.mjs.map