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