@copilotkit/core 1.61.1 → 1.62.0

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
@@ -151,12 +151,6 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
151
151
  activeChannel = null;
152
152
  canonicalRunId = null;
153
153
  sharedState;
154
- /**
155
- * Resolves when the active run's pipeline finalizes; `undefined` between runs.
156
- * Set/cleared inside {@link connectAgent}'s observable lifecycle. Declared
157
- * here so {@link RunCompletionAware} consumers can read it without a cast.
158
- */
159
- activeRunCompletionPromise;
160
154
  constructor(config, sharedState = { lastSeenEventIds: /* @__PURE__ */ new Map() }) {
161
155
  super();
162
156
  this.config = config;
@@ -202,7 +196,7 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
202
196
  const previousMessageIds = new Set(this.messages.map((m) => m.id));
203
197
  const subscribers = [
204
198
  { onRunFinishedEvent: (event) => {
205
- result = event.result;
199
+ if (event.outcome === "success") result = event.result;
206
200
  } },
207
201
  ...this.subscribers,
208
202
  subscriber ?? {}
@@ -210,10 +204,10 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
210
204
  await this.onInitialize(input, subscribers);
211
205
  self.activeRunDetach$ = new Subject();
212
206
  let resolveCompletion;
213
- this.activeRunCompletionPromise = new Promise((resolve) => {
207
+ self.activeRunCompletionPromise = new Promise((resolve) => {
214
208
  resolveCompletion = resolve;
215
209
  });
216
- const source$ = defer(() => this.connect(input)).pipe(transformChunks(this.debug), takeUntil(self.activeRunDetach$));
210
+ const source$ = defer(() => this.connect(input)).pipe(transformChunks(this.debugLogger), takeUntil(self.activeRunDetach$));
217
211
  const applied$ = this.apply(input, source$, subscribers);
218
212
  await lastValueFrom(this.processApplyEvents(input, applied$, subscribers).pipe(catchError((error) => {
219
213
  this.isRunning = false;
@@ -223,7 +217,7 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
223
217
  this.onFinalize(input, subscribers);
224
218
  resolveCompletion?.();
225
219
  resolveCompletion = void 0;
226
- this.activeRunCompletionPromise = void 0;
220
+ self.activeRunCompletionPromise = void 0;
227
221
  self.activeRunDetach$ = void 0;
228
222
  })), { defaultValue: void 0 });
229
223
  const newMessages = structuredClone_(this.messages).filter((m) => !previousMessageIds.has(m.id));
@@ -725,8 +719,9 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
725
719
  cloned.setState(this.state);
726
720
  cloned.setMessages(this.messages);
727
721
  if (this.delegate) {
728
- cloned.delegate = this.delegate.clone();
729
- cloned.syncDelegate(cloned.delegate);
722
+ const clonedDelegate = this.delegate.clone();
723
+ cloned.delegate = clonedDelegate;
724
+ cloned.syncDelegate(clonedDelegate);
730
725
  }
731
726
  return cloned;
732
727
  }
@@ -881,6 +876,17 @@ var AgentRegistry = class {
881
876
  _openGenerativeUIEnabled = false;
882
877
  _licenseStatus;
883
878
  _telemetryDisabled = false;
879
+ /**
880
+ * The headers each HttpAgent was constructed with, captured on the first
881
+ * `applyHeadersToAgent` call for that agent (which, for agents the registry
882
+ * owns, happens at registration before any core headers are applied). Core
883
+ * headers are merged ON TOP of this baseline so that headers configured
884
+ * directly on an agent (e.g. an `Authorization` for a self-hosted backend)
885
+ * survive registration instead of being silently replaced. The baseline is
886
+ * captured once and never re-captured, so a later direct mutation of
887
+ * `agent.headers` is not folded into it. See #5635.
888
+ */
889
+ agentOwnHeaders = /* @__PURE__ */ new WeakMap();
884
890
  constructor(core) {
885
891
  this.core = core;
886
892
  }
@@ -1053,10 +1059,19 @@ var AgentRegistry = class {
1053
1059
  console.warn(`Agent ${id} not found`);
1054
1060
  }
1055
1061
  /**
1056
- * Apply current headers to an agent
1062
+ * Apply current core headers to an agent, merged ON TOP of the agent's own
1063
+ * construction-time headers (the per-agent baseline in `agentOwnHeaders`).
1064
+ * Core wins on a key conflict. Non-`HttpAgent` agents are left untouched
1065
+ * because only `HttpAgent` carries a `headers` field. See #5635.
1057
1066
  */
1058
1067
  applyHeadersToAgent(agent) {
1059
- if (agent instanceof HttpAgent) agent.headers = { ...this.core.headers };
1068
+ if (agent instanceof HttpAgent) {
1069
+ if (!this.agentOwnHeaders.has(agent)) this.agentOwnHeaders.set(agent, { ...agent.headers });
1070
+ agent.headers = {
1071
+ ...this.agentOwnHeaders.get(agent),
1072
+ ...this.core.headers
1073
+ };
1074
+ }
1060
1075
  }
1061
1076
  /**
1062
1077
  * Apply current headers to all agents
@@ -1728,7 +1743,11 @@ var RunHandler = class {
1728
1743
  const proxied = agent;
1729
1744
  if (incomingThreadId && typeof proxied.clearReplayCursor === "function") proxied.clearReplayCursor(incomingThreadId);
1730
1745
  }
1731
- if (agent instanceof HttpAgent) agent.headers = { ...this._internal.headers };
1746
+ this._internal.applyHeadersToAgent(agent);
1747
+ await this._internal.notifySubscribers((subscriber) => subscriber.onAgentRunStarted?.({
1748
+ copilotkit: this.core,
1749
+ agent
1750
+ }), "Subscriber onAgentRunStarted error:");
1732
1751
  const runAgentResult = await agent.connectAgent({
1733
1752
  forwardedProps: this._internal.properties,
1734
1753
  tools: this.buildFrontendTools(agent.agentId),
@@ -1749,15 +1768,18 @@ var RunHandler = class {
1749
1768
  context
1750
1769
  });
1751
1770
  }
1752
- return { newMessages: [] };
1771
+ return {
1772
+ result: void 0,
1773
+ newMessages: []
1774
+ };
1753
1775
  }
1754
1776
  }
1755
1777
  /**
1756
1778
  * Run an agent
1757
1779
  */
1758
- async runAgent({ agent, forwardedProps }) {
1780
+ async runAgent({ agent, forwardedProps, resume }) {
1759
1781
  if (agent.agentId) this._internal.suggestionEngine.clearSuggestions(agent.agentId);
1760
- if (agent instanceof HttpAgent) agent.headers = { ...this._internal.headers };
1782
+ this._internal.applyHeadersToAgent(agent);
1761
1783
  if (agent.detachActiveRun) await agent.detachActiveRun();
1762
1784
  const isTopLevel = this._runDepth === 0;
1763
1785
  let originalAbortRun;
@@ -1769,6 +1791,10 @@ var RunHandler = class {
1769
1791
  controller.abort();
1770
1792
  originalAbortRun();
1771
1793
  };
1794
+ await this._internal.notifySubscribers((subscriber) => subscriber.onAgentRunStarted?.({
1795
+ copilotkit: this.core,
1796
+ agent
1797
+ }), "Subscriber onAgentRunStarted error:");
1772
1798
  }
1773
1799
  this._runDepth++;
1774
1800
  try {
@@ -1777,6 +1803,7 @@ var RunHandler = class {
1777
1803
  ...this._internal.properties,
1778
1804
  ...forwardedProps
1779
1805
  },
1806
+ ...resume !== void 0 ? { resume } : {},
1780
1807
  tools: this.buildFrontendTools(agent.agentId),
1781
1808
  context: this._internal.getContextForAgent(agent.agentId)
1782
1809
  }, this.createAgentErrorSubscriber(agent));
@@ -1793,7 +1820,10 @@ var RunHandler = class {
1793
1820
  code: CopilotKitCoreErrorCode.AGENT_RUN_FAILED,
1794
1821
  context
1795
1822
  });
1796
- return { newMessages: [] };
1823
+ return {
1824
+ result: void 0,
1825
+ newMessages: []
1826
+ };
1797
1827
  } finally {
1798
1828
  this._runDepth--;
1799
1829
  if (isTopLevel && originalAbortRun) agent.abortRun = originalAbortRun;
@@ -2065,18 +2095,19 @@ var RunHandler = class {
2065
2095
  throw error;
2066
2096
  }
2067
2097
  const toolCallId = randomUUID$1();
2098
+ const assistantToolCall = {
2099
+ id: toolCallId,
2100
+ type: "function",
2101
+ function: {
2102
+ name,
2103
+ arguments: JSON.stringify(parameters)
2104
+ }
2105
+ };
2068
2106
  const assistantMessage = {
2069
2107
  id: randomUUID$1(),
2070
2108
  role: "assistant",
2071
2109
  content: "",
2072
- toolCalls: [{
2073
- id: toolCallId,
2074
- type: "function",
2075
- function: {
2076
- name,
2077
- arguments: JSON.stringify(parameters)
2078
- }
2079
- }]
2110
+ toolCalls: [assistantToolCall]
2080
2111
  };
2081
2112
  agent.messages.push(assistantMessage);
2082
2113
  let handlerResult = {
@@ -2086,7 +2117,7 @@ var RunHandler = class {
2086
2117
  };
2087
2118
  if (tool.handler) handlerResult = await this.executeToolHandler({
2088
2119
  tool,
2089
- toolCall: assistantMessage.toolCalls[0],
2120
+ toolCall: assistantToolCall,
2090
2121
  agent,
2091
2122
  agentId: resolvedAgentId,
2092
2123
  handlerArgs: parameters,
@@ -2172,9 +2203,9 @@ const EMPTY_TOOL_SCHEMA = {
2172
2203
  */
2173
2204
  function createToolSchema(tool) {
2174
2205
  if (!tool.parameters) return { ...EMPTY_TOOL_SCHEMA };
2175
- const rawSchema = schemaToJsonSchema(tool.parameters, { zodToJsonSchema });
2206
+ const rawSchema = schemaToJsonSchema(tool.parameters, { zodToJsonSchema: (schema, options) => zodToJsonSchema(schema, options) });
2176
2207
  if (!rawSchema || typeof rawSchema !== "object") return { ...EMPTY_TOOL_SCHEMA };
2177
- const { $schema, ...schema } = rawSchema;
2208
+ const { $schema: _$schema, ...schema } = rawSchema;
2178
2209
  if (typeof schema.type !== "string") schema.type = "object";
2179
2210
  if (typeof schema.properties !== "object" || schema.properties === null) schema.properties = {};
2180
2211
  stripAdditionalProperties(schema);
@@ -2761,9 +2792,19 @@ var CopilotKitCore = class {
2761
2792
  * });
2762
2793
  * ```
2763
2794
  *
2764
- * The resulting header set is also re-applied to every registered
2765
- * `HttpAgent`-derived agent (replacing its `headers`) and `onHeadersChanged`
2766
- * subscribers are notified.
2795
+ * The resulting header set is also re-applied to every agent in the registry
2796
+ * and `onHeadersChanged` subscribers are notified. These headers are merged
2797
+ * ON TOP of the headers each `HttpAgent` was constructed with, so per-agent
2798
+ * headers (e.g. an `Authorization` for a self-hosted backend) are preserved;
2799
+ * on a key conflict the core-level value wins.
2800
+ *
2801
+ * Because the agent's construction-time headers form the merge baseline, this
2802
+ * method can override a per-agent header but cannot REMOVE one: clearing a
2803
+ * core key here only drops the core-level override, after which the agent's
2804
+ * own value (if any) re-surfaces. To change a header an agent was constructed
2805
+ * with, set it at the provider/core level instead of on the agent, or update
2806
+ * it on the agent directly. The clear-on-logout pattern above is for
2807
+ * core-level headers.
2767
2808
  */
2768
2809
  setHeaders(headers) {
2769
2810
  this._headers = normalizeHeaders(headers);
@@ -2822,6 +2863,23 @@ var CopilotKitCore = class {
2822
2863
  return this.agentRegistry.getAgent(id);
2823
2864
  }
2824
2865
  /**
2866
+ * Re-apply the current headers to a single agent (delegated to
2867
+ * AgentRegistry). Core headers are merged on top of the agent's own
2868
+ * construction-time headers rather than replacing them, so headers
2869
+ * configured directly on an `HttpAgent` (e.g. an `Authorization` for a
2870
+ * self-hosted backend) survive header updates instead of being silently
2871
+ * dropped (see #5635). On a key conflict the core-level value wins.
2872
+ *
2873
+ * The merge baseline is the agent's headers as captured the first time the
2874
+ * agent is applied (at registration), so the way to change headers
2875
+ * afterwards is `setHeaders` (which re-applies to every agent), not mutating
2876
+ * `agent.headers` directly — a direct mutation is overwritten on the next
2877
+ * re-apply.
2878
+ */
2879
+ applyHeadersToAgent(agent) {
2880
+ this.agentRegistry.applyHeadersToAgent(agent);
2881
+ }
2882
+ /**
2825
2883
  * Context management (delegated to ContextStore).
2826
2884
  * Pass `agentIds` to restrict the entry to runs of specific agents;
2827
2885
  * omit it for context every agent should receive.
@@ -3538,7 +3596,9 @@ const initialThreadState = {
3538
3596
  sessionId: 0,
3539
3597
  metadataCredentialsRequested: false,
3540
3598
  metadataJoinCode: null,
3541
- nextCursor: null
3599
+ nextCursor: null,
3600
+ inFlightMutationCount: 0,
3601
+ pendingDeletes: {}
3542
3602
  };
3543
3603
  const threadAdapterEvents = createActionGroup("Thread Adapter", {
3544
3604
  started: empty(),
@@ -3547,7 +3607,9 @@ const threadAdapterEvents = createActionGroup("Thread Adapter", {
3547
3607
  fetchNextPageRequested: empty(),
3548
3608
  renameRequested: props(),
3549
3609
  archiveRequested: props(),
3550
- deleteRequested: props()
3610
+ unarchiveRequested: props(),
3611
+ deleteRequested: props(),
3612
+ newThreadStarted: empty()
3551
3613
  });
3552
3614
  const threadRestEvents = createActionGroup("Thread REST", {
3553
3615
  listRequested: props(),
@@ -3594,7 +3656,9 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3594
3656
  error: null,
3595
3657
  metadataCredentialsRequested: false,
3596
3658
  metadataJoinCode: null,
3597
- nextCursor: null
3659
+ nextCursor: null,
3660
+ inFlightMutationCount: 0,
3661
+ pendingDeletes: {}
3598
3662
  })), on(threadAdapterEvents.stopped, (state) => ({
3599
3663
  ...state,
3600
3664
  threads: [],
@@ -3603,7 +3667,9 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3603
3667
  error: null,
3604
3668
  metadataCredentialsRequested: false,
3605
3669
  metadataJoinCode: null,
3606
- nextCursor: null
3670
+ nextCursor: null,
3671
+ inFlightMutationCount: 0,
3672
+ pendingDeletes: {}
3607
3673
  })), on(threadRestEvents.listRequested, (state, { sessionId }) => {
3608
3674
  if (sessionId !== state.sessionId || !state.context) return state;
3609
3675
  return {
@@ -3645,12 +3711,9 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3645
3711
  isFetchingNextPage: false,
3646
3712
  error
3647
3713
  };
3648
- }), on(threadRestEvents.metadataCredentialsFailed, (state, { sessionId, error }) => {
3714
+ }), on(threadRestEvents.metadataCredentialsFailed, (state, { sessionId }) => {
3649
3715
  if (sessionId !== state.sessionId) return state;
3650
- return {
3651
- ...state,
3652
- error
3653
- };
3716
+ return state;
3654
3717
  }), on(threadRestEvents.metadataCredentialsRequested, (state, { sessionId }) => {
3655
3718
  if (sessionId !== state.sessionId) return state;
3656
3719
  return {
@@ -3663,10 +3726,105 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3663
3726
  ...state,
3664
3727
  isFetchingNextPage: true
3665
3728
  };
3666
- }), on(threadRestEvents.mutationFinished, (state, { outcome }) => ({
3729
+ }), on(threadAdapterEvents.renameRequested, (state, { threadId, name }) => {
3730
+ const existing = state.threads.find((thread) => thread.id === threadId);
3731
+ const inFlightMutationCount = state.inFlightMutationCount + 1;
3732
+ if (!existing) return {
3733
+ ...state,
3734
+ inFlightMutationCount
3735
+ };
3736
+ return {
3737
+ ...state,
3738
+ threads: upsertThread(state.threads, {
3739
+ ...existing,
3740
+ name
3741
+ }),
3742
+ inFlightMutationCount
3743
+ };
3744
+ }), on(threadAdapterEvents.archiveRequested, (state, { threadId }) => {
3745
+ const existing = state.threads.find((thread) => thread.id === threadId);
3746
+ const inFlightMutationCount = state.inFlightMutationCount + 1;
3747
+ if (!existing) return {
3748
+ ...state,
3749
+ inFlightMutationCount
3750
+ };
3751
+ if (!state.context?.includeArchived) return {
3752
+ ...state,
3753
+ threads: state.threads.filter((thread) => thread.id !== threadId),
3754
+ inFlightMutationCount
3755
+ };
3756
+ return {
3757
+ ...state,
3758
+ threads: upsertThread(state.threads, {
3759
+ ...existing,
3760
+ archived: true
3761
+ }),
3762
+ inFlightMutationCount
3763
+ };
3764
+ }), on(threadAdapterEvents.unarchiveRequested, (state, { threadId }) => {
3765
+ const existing = state.threads.find((thread) => thread.id === threadId);
3766
+ const inFlightMutationCount = state.inFlightMutationCount + 1;
3767
+ if (!existing) return {
3768
+ ...state,
3769
+ inFlightMutationCount
3770
+ };
3771
+ return {
3772
+ ...state,
3773
+ threads: upsertThread(state.threads, {
3774
+ ...existing,
3775
+ archived: false
3776
+ }),
3777
+ inFlightMutationCount
3778
+ };
3779
+ }), on(threadAdapterEvents.deleteRequested, (state, { requestId, threadId }) => {
3780
+ const existing = state.threads.find((thread) => thread.id === threadId);
3781
+ const inFlightMutationCount = state.inFlightMutationCount + 1;
3782
+ if (!existing) return {
3783
+ ...state,
3784
+ inFlightMutationCount
3785
+ };
3786
+ return {
3787
+ ...state,
3788
+ threads: state.threads.filter((thread) => thread.id !== threadId),
3789
+ pendingDeletes: {
3790
+ ...state.pendingDeletes,
3791
+ [requestId]: existing
3792
+ },
3793
+ inFlightMutationCount
3794
+ };
3795
+ }), on(threadAdapterEvents.newThreadStarted, (state) => ({
3667
3796
  ...state,
3668
- error: outcome.ok ? state.error : outcome.error
3669
- })), on(threadDomainEvents.threadUpserted, (state, { sessionId, thread }) => {
3797
+ error: null
3798
+ })), on(threadRestEvents.mutationFinished, (state, { outcome }) => {
3799
+ if (outcome.sessionId !== state.sessionId) return state;
3800
+ const inFlightMutationCount = Math.max(0, state.inFlightMutationCount - 1);
3801
+ if (outcome.ok) {
3802
+ if (state.pendingDeletes[outcome.requestId] === void 0) return {
3803
+ ...state,
3804
+ inFlightMutationCount
3805
+ };
3806
+ const { [outcome.requestId]: _settled, ...rest } = state.pendingDeletes;
3807
+ return {
3808
+ ...state,
3809
+ inFlightMutationCount,
3810
+ pendingDeletes: rest
3811
+ };
3812
+ }
3813
+ const rolledBack = state.pendingDeletes[outcome.requestId];
3814
+ if (rolledBack === void 0) return {
3815
+ ...state,
3816
+ inFlightMutationCount,
3817
+ error: outcome.error
3818
+ };
3819
+ const { [outcome.requestId]: _restored, ...rest } = state.pendingDeletes;
3820
+ return {
3821
+ ...state,
3822
+ threads: upsertThread(state.threads, rolledBack),
3823
+ pendingDeletes: rest,
3824
+ inFlightMutationCount,
3825
+ error: outcome.error
3826
+ };
3827
+ }), on(threadDomainEvents.threadUpserted, (state, { sessionId, thread }) => {
3670
3828
  if (sessionId !== state.sessionId) return state;
3671
3829
  return {
3672
3830
  ...state,
@@ -3679,23 +3837,49 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3679
3837
  threads: state.threads.filter((thread) => thread.id !== threadId)
3680
3838
  };
3681
3839
  }));
3682
- const selectThreads = createSelector((state) => state.threads);
3683
- const selectThreadsIsLoading = createSelector((state) => state.isLoading);
3684
- const selectThreadsError = createSelector((state) => state.error);
3685
- const selectHasNextPage = createSelector((state) => state.nextCursor != null);
3686
- const selectIsFetchingNextPage = createSelector((state) => state.isFetchingNextPage);
3840
+ /**
3841
+ * Builds a fresh set of memoized thread selectors.
3842
+ *
3843
+ * Each `createSelector` closure owns a private one-entry cache. Sharing a
3844
+ * single module-level selector instance across multiple concurrent stores
3845
+ * (e.g. a `<CopilotThreadsDrawer>` plus an independent `useThreads`) makes every
3846
+ * cross-store emission a cache miss, defeating memoization and risking
3847
+ * emission instability for any future selector that allocates a new
3848
+ * object/array. Creating a per-store instance keeps each store's cache
3849
+ * isolated so concurrent stores never thrash one another.
3850
+ */
3851
+ function createThreadSelectors() {
3852
+ return {
3853
+ threads: createSelector((state) => state.threads),
3854
+ isLoading: createSelector((state) => state.isLoading),
3855
+ error: createSelector((state) => state.error),
3856
+ hasNextPage: createSelector((state) => state.nextCursor != null),
3857
+ isFetchingNextPage: createSelector((state) => state.isFetchingNextPage),
3858
+ isMutating: createSelector((state) => state.inFlightMutationCount > 0)
3859
+ };
3860
+ }
3861
+ const standaloneSelectors = createThreadSelectors();
3862
+ const selectThreads = standaloneSelectors.threads;
3863
+ const selectThreadsIsLoading = standaloneSelectors.isLoading;
3864
+ const selectThreadsError = standaloneSelectors.error;
3865
+ const selectHasNextPage = standaloneSelectors.hasNextPage;
3866
+ const selectIsFetchingNextPage = standaloneSelectors.isFetchingNextPage;
3867
+ const selectIsMutating = standaloneSelectors.isMutating;
3687
3868
  let threadRequestId = 0;
3688
3869
  function createThreadRequestId() {
3689
3870
  threadRequestId += 1;
3690
3871
  return `thread-request-${threadRequestId}`;
3691
3872
  }
3873
+ function threadFromFetch(input, init) {
3874
+ return fromFetch(input, init);
3875
+ }
3692
3876
  function createThreadFetchObservable(environment, context, sessionId) {
3693
3877
  return defer(() => {
3694
3878
  const params = { agentId: context.agentId };
3695
3879
  if (context.includeArchived) params.includeArchived = "true";
3696
3880
  if (context.limit != null) params.limit = String(context.limit);
3697
3881
  const qs = new URLSearchParams(params);
3698
- return fromFetch(`${context.runtimeUrl}/threads?${qs.toString()}`, {
3882
+ return threadFromFetch(`${context.runtimeUrl}/threads?${qs.toString()}`, {
3699
3883
  selector: (response) => {
3700
3884
  if (!response.ok) throw new Error(`Failed to fetch threads: ${response.status}`);
3701
3885
  return response.json();
@@ -3723,7 +3907,7 @@ function createThreadFetchObservable(environment, context, sessionId) {
3723
3907
  }
3724
3908
  function createThreadMetadataCredentialsObservable(environment, context, sessionId) {
3725
3909
  return defer(() => {
3726
- return fromFetch(`${context.runtimeUrl}${THREAD_SUBSCRIBE_PATH}`, {
3910
+ return threadFromFetch(`${context.runtimeUrl}${THREAD_SUBSCRIBE_PATH}`, {
3727
3911
  selector: async (response) => {
3728
3912
  if (!response.ok) throw new Error(`Failed to fetch thread metadata credentials: ${response.status}`);
3729
3913
  return response.json();
@@ -3756,7 +3940,7 @@ function createThreadMetadataCredentialsObservable(environment, context, session
3756
3940
  }
3757
3941
  function createThreadMutationObservable(environment, context, request) {
3758
3942
  return defer(() => {
3759
- return fromFetch(`${context.runtimeUrl}${request.path}`, {
3943
+ return threadFromFetch(`${context.runtimeUrl}${request.path}`, {
3760
3944
  selector: async (response) => {
3761
3945
  if (!response.ok) throw new Error(`Request failed: ${response.status}`);
3762
3946
  return null;
@@ -3770,10 +3954,12 @@ function createThreadMutationObservable(environment, context, request) {
3770
3954
  body: JSON.stringify(request.body)
3771
3955
  }).pipe(map(() => threadRestEvents.mutationFinished({ outcome: {
3772
3956
  requestId: request.requestId,
3957
+ sessionId: request.sessionId,
3773
3958
  ok: true
3774
3959
  } })), catchError((error) => {
3775
3960
  return of(threadRestEvents.mutationFinished({ outcome: {
3776
3961
  requestId: request.requestId,
3962
+ sessionId: request.sessionId,
3777
3963
  ok: false,
3778
3964
  error: error instanceof Error ? error : new Error(String(error))
3779
3965
  } }));
@@ -3781,6 +3967,7 @@ function createThreadMutationObservable(environment, context, request) {
3781
3967
  });
3782
3968
  }
3783
3969
  function createThreadStore(environment) {
3970
+ const selectors = createThreadSelectors();
3784
3971
  const store = createStore({
3785
3972
  reducer: threadReducer,
3786
3973
  effects: [
@@ -3847,6 +4034,10 @@ function createThreadStore(environment) {
3847
4034
  thread: action.payload.thread
3848
4035
  });
3849
4036
  }))),
4037
+ createEffect((actions$, state$) => actions$.pipe(ofType(threadSocketEvents.joinFailed, threadSocketEvents.joinTimedOut, threadSocketEvents.errored, threadRestEvents.metadataCredentialsFailed), withLatestFrom(state$), filter(([action, state]) => action.sessionId === state.sessionId), tap(([action]) => {
4038
+ const reason = threadSocketEvents.joinTimedOut.match(action) ? "channel join timed out" : threadSocketEvents.joinFailed.match(action) ? "channel join was rejected" : threadRestEvents.metadataCredentialsFailed.match(action) ? "metadata credentials fetch failed" : "socket errored";
4039
+ console.warn(`[threads] realtime ${reason}; the thread list may be stale until reconnect`);
4040
+ })), { dispatch: false }),
3850
4041
  createEffect((actions$, state$) => actions$.pipe(ofType(threadAdapterEvents.fetchNextPageRequested), withLatestFrom(state$), filter(([, state]) => Boolean(state.context) && Boolean(state.nextCursor)), switchMap$1(([, state]) => {
3851
4042
  const context = state.context;
3852
4043
  const params = {
@@ -3855,7 +4046,7 @@ function createThreadStore(environment) {
3855
4046
  };
3856
4047
  if (context.includeArchived) params.includeArchived = "true";
3857
4048
  if (context.limit != null) params.limit = String(context.limit);
3858
- return fromFetch(`${context.runtimeUrl}/threads?${new URLSearchParams(params).toString()}`, {
4049
+ return threadFromFetch(`${context.runtimeUrl}/threads?${new URLSearchParams(params).toString()}`, {
3859
4050
  selector: (response) => {
3860
4051
  if (!response.ok) throw new Error(`Failed to fetch next page: ${response.status}`);
3861
4052
  return response.json();
@@ -3877,12 +4068,14 @@ function createThreadStore(environment) {
3877
4068
  error: error instanceof Error ? error : new Error(String(error))
3878
4069
  }))), takeUntil(actions$.pipe(ofType(threadAdapterEvents.contextChanged, threadAdapterEvents.stopped))));
3879
4070
  }))),
3880
- createEffect((actions$, state$) => actions$.pipe(ofType(threadAdapterEvents.renameRequested, threadAdapterEvents.archiveRequested, threadAdapterEvents.deleteRequested), withLatestFrom(state$), mergeMap(([action, state]) => {
4071
+ createEffect((actions$, state$) => actions$.pipe(ofType(threadAdapterEvents.renameRequested, threadAdapterEvents.archiveRequested, threadAdapterEvents.unarchiveRequested, threadAdapterEvents.deleteRequested), withLatestFrom(state$), mergeMap(([action, state]) => {
4072
+ const sessionId = state.sessionId;
3881
4073
  const context = state.context;
3882
4074
  if (!context?.runtimeUrl) {
3883
4075
  const requestId = action.requestId;
3884
4076
  return of(threadRestEvents.mutationFinished({ outcome: {
3885
4077
  requestId,
4078
+ sessionId,
3886
4079
  ok: false,
3887
4080
  error: /* @__PURE__ */ new Error("Runtime URL is not configured")
3888
4081
  } }));
@@ -3890,6 +4083,7 @@ function createThreadStore(environment) {
3890
4083
  const commonBody = { agentId: context.agentId };
3891
4084
  if (threadAdapterEvents.renameRequested.match(action)) return createThreadMutationObservable(environment, context, {
3892
4085
  requestId: action.requestId,
4086
+ sessionId,
3893
4087
  method: "PATCH",
3894
4088
  path: `/threads/${encodeURIComponent(action.threadId)}`,
3895
4089
  body: {
@@ -3899,12 +4093,24 @@ function createThreadStore(environment) {
3899
4093
  });
3900
4094
  if (threadAdapterEvents.archiveRequested.match(action)) return createThreadMutationObservable(environment, context, {
3901
4095
  requestId: action.requestId,
4096
+ sessionId,
3902
4097
  method: "POST",
3903
4098
  path: `/threads/${encodeURIComponent(action.threadId)}/archive`,
3904
4099
  body: commonBody
3905
4100
  });
4101
+ if (threadAdapterEvents.unarchiveRequested.match(action)) return createThreadMutationObservable(environment, context, {
4102
+ requestId: action.requestId,
4103
+ sessionId,
4104
+ method: "PATCH",
4105
+ path: `/threads/${encodeURIComponent(action.threadId)}`,
4106
+ body: {
4107
+ ...commonBody,
4108
+ archived: false
4109
+ }
4110
+ });
3906
4111
  return createThreadMutationObservable(environment, context, {
3907
4112
  requestId: action.requestId,
4113
+ sessionId,
3908
4114
  method: "DELETE",
3909
4115
  path: `/threads/${encodeURIComponent(action.threadId)}`,
3910
4116
  body: commonBody
@@ -3915,6 +4121,7 @@ function createThreadStore(environment) {
3915
4121
  function trackMutation(dispatchAction) {
3916
4122
  const resultPromise = firstValueFrom(merge(store.actions$.pipe(ofType(threadRestEvents.mutationFinished), filter((action) => action.outcome.requestId === dispatchAction.requestId), map((action) => action.outcome)), store.actions$.pipe(ofType(threadAdapterEvents.stopped), map(() => ({
3917
4123
  requestId: dispatchAction.requestId,
4124
+ sessionId: store.getState().sessionId,
3918
4125
  ok: false,
3919
4126
  error: /* @__PURE__ */ new Error("Thread store stopped before mutation completed")
3920
4127
  })))).pipe(take(1))).then((outcome) => {
@@ -3924,13 +4131,23 @@ function createThreadStore(environment) {
3924
4131
  store.dispatch(dispatchAction);
3925
4132
  return resultPromise;
3926
4133
  }
4134
+ let errorSubscription = null;
4135
+ const subscribeErrors = () => {
4136
+ if (!environment.onError || errorSubscription) return;
4137
+ errorSubscription = store.actions$.pipe(ofType(threadRestEvents.mutationFinished), filter((action) => !action.outcome.ok && action.outcome.sessionId === store.getState().sessionId)).subscribe((action) => {
4138
+ if (!action.outcome.ok) environment.onError?.(action.outcome.error);
4139
+ });
4140
+ };
3927
4141
  return {
3928
4142
  start() {
3929
4143
  store.init();
4144
+ subscribeErrors();
3930
4145
  store.dispatch(threadAdapterEvents.started());
3931
4146
  },
3932
4147
  stop() {
3933
4148
  store.dispatch(threadAdapterEvents.stopped());
4149
+ errorSubscription?.unsubscribe();
4150
+ errorSubscription = null;
3934
4151
  store.stop();
3935
4152
  },
3936
4153
  setContext(context) {
@@ -3941,6 +4158,14 @@ function createThreadStore(environment) {
3941
4158
  if (!context) return;
3942
4159
  store.dispatch(threadRestEvents.listRequested({ sessionId }));
3943
4160
  },
4161
+ refetchThreads() {
4162
+ const { sessionId, context } = store.getState();
4163
+ if (!context) return;
4164
+ store.dispatch(threadRestEvents.listRequested({ sessionId }));
4165
+ },
4166
+ startNewThread() {
4167
+ store.dispatch(threadAdapterEvents.newThreadStarted());
4168
+ },
3944
4169
  fetchNextPage() {
3945
4170
  store.dispatch(threadAdapterEvents.fetchNextPageRequested());
3946
4171
  },
@@ -3957,6 +4182,12 @@ function createThreadStore(environment) {
3957
4182
  threadId
3958
4183
  }));
3959
4184
  },
4185
+ unarchiveThread(threadId) {
4186
+ return trackMutation(threadAdapterEvents.unarchiveRequested({
4187
+ requestId: createThreadRequestId(),
4188
+ threadId
4189
+ }));
4190
+ },
3960
4191
  deleteThread(threadId) {
3961
4192
  return trackMutation(threadAdapterEvents.deleteRequested({
3962
4193
  requestId: createThreadRequestId(),
@@ -3966,15 +4197,21 @@ function createThreadStore(environment) {
3966
4197
  getState() {
3967
4198
  return store.getState();
3968
4199
  },
3969
- select: store.select.bind(store)
4200
+ getServerState() {
4201
+ return initialThreadState;
4202
+ },
4203
+ select: store.select.bind(store),
4204
+ selectors
3970
4205
  };
3971
4206
  }
3972
4207
  const ɵthreadAdapterEvents = threadAdapterEvents;
4208
+ const ɵcreateThreadSelectors = createThreadSelectors;
3973
4209
  const ɵselectThreads = selectThreads;
3974
4210
  const ɵselectThreadsIsLoading = selectThreadsIsLoading;
3975
4211
  const ɵselectThreadsError = selectThreadsError;
3976
4212
  const ɵselectHasNextPage = selectHasNextPage;
3977
4213
  const ɵselectIsFetchingNextPage = selectIsFetchingNextPage;
4214
+ const ɵselectIsMutating = selectIsMutating;
3978
4215
  /**
3979
4216
  * Number of consecutive WebSocket connection failures after which the
3980
4217
  * threads channel tears itself down rather than retrying indefinitely.
@@ -3984,5 +4221,50 @@ const ɵselectIsFetchingNextPage = selectIsFetchingNextPage;
3984
4221
  const ɵMAX_SOCKET_RETRIES = MAX_SOCKET_RETRIES;
3985
4222
 
3986
4223
  //#endregion
3987
- 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, ɵMAX_SOCKET_RETRIES, createThreadStore as ɵcreateThreadStore, ɵjoinPhoenixChannel$, ɵobservePhoenixEvent$, ɵobservePhoenixJoinOutcome$, ɵobservePhoenixSocketHealth$, ɵobservePhoenixSocketSignals$, ɵphoenixChannel$, ɵphoenixSocket$, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading, ɵthreadAdapterEvents };
4224
+ //#region src/features.ts
4225
+ /**
4226
+ * Central registry of CopilotKit feature names.
4227
+ *
4228
+ * Feature names are the strings passed to a license context's `checkFeature`
4229
+ * (see `@copilotkit/shared`'s `LicenseContextValue`). Historically the only
4230
+ * features referenced by literal were the chat surfaces (`chat`, `sidebar`,
4231
+ * `popup`); this module makes the set explicit and typed so new surfaces —
4232
+ * such as the threads drawer — register a real, discoverable feature name
4233
+ * rather than an ad-hoc string scattered across components.
4234
+ *
4235
+ * `checkFeature` itself still accepts any `string` for backward compatibility;
4236
+ * this registry simply documents and types the names CopilotKit recognises.
4237
+ */
4238
+ /**
4239
+ * The set of feature names CopilotKit recognises.
4240
+ *
4241
+ * Frozen so the array cannot be mutated at runtime. Add new surfaces here when
4242
+ * they begin gating behaviour on `checkFeature`.
4243
+ */
4244
+ const ɵCOPILOTKIT_FEATURES = Object.freeze([
4245
+ "chat",
4246
+ "sidebar",
4247
+ "popup",
4248
+ "threads"
4249
+ ]);
4250
+ const FEATURE_SET = new Set(ɵCOPILOTKIT_FEATURES);
4251
+ /**
4252
+ * Type guard: returns `true` when `name` is a recognised CopilotKit feature.
4253
+ *
4254
+ * @param name - Candidate feature name.
4255
+ * @returns Whether `name` is a registered feature.
4256
+ *
4257
+ * @example
4258
+ * ```ts
4259
+ * if (ɵisCopilotKitFeature("threads")) {
4260
+ * // narrowed to ɵCopilotKitFeature
4261
+ * }
4262
+ * ```
4263
+ */
4264
+ function ɵisCopilotKitFeature(name) {
4265
+ return FEATURE_SET.has(name);
4266
+ }
4267
+
4268
+ //#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 };
3988
4270
  //# sourceMappingURL=index.mjs.map