@copilotkit/core 1.61.2 → 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
@@ -1745,6 +1745,10 @@ var RunHandler = class {
1745
1745
  if (incomingThreadId && typeof proxied.clearReplayCursor === "function") proxied.clearReplayCursor(incomingThreadId);
1746
1746
  }
1747
1747
  this._internal.applyHeadersToAgent(agent);
1748
+ await this._internal.notifySubscribers((subscriber) => subscriber.onAgentRunStarted?.({
1749
+ copilotkit: this.core,
1750
+ agent
1751
+ }), "Subscriber onAgentRunStarted error:");
1748
1752
  const runAgentResult = await agent.connectAgent({
1749
1753
  forwardedProps: this._internal.properties,
1750
1754
  tools: this.buildFrontendTools(agent.agentId),
@@ -1788,6 +1792,10 @@ var RunHandler = class {
1788
1792
  controller.abort();
1789
1793
  originalAbortRun();
1790
1794
  };
1795
+ await this._internal.notifySubscribers((subscriber) => subscriber.onAgentRunStarted?.({
1796
+ copilotkit: this.core,
1797
+ agent
1798
+ }), "Subscriber onAgentRunStarted error:");
1791
1799
  }
1792
1800
  this._runDepth++;
1793
1801
  try {
@@ -3589,7 +3597,9 @@ const initialThreadState = {
3589
3597
  sessionId: 0,
3590
3598
  metadataCredentialsRequested: false,
3591
3599
  metadataJoinCode: null,
3592
- nextCursor: null
3600
+ nextCursor: null,
3601
+ inFlightMutationCount: 0,
3602
+ pendingDeletes: {}
3593
3603
  };
3594
3604
  const threadAdapterEvents = createActionGroup("Thread Adapter", {
3595
3605
  started: empty(),
@@ -3599,7 +3609,8 @@ const threadAdapterEvents = createActionGroup("Thread Adapter", {
3599
3609
  renameRequested: props(),
3600
3610
  archiveRequested: props(),
3601
3611
  unarchiveRequested: props(),
3602
- deleteRequested: props()
3612
+ deleteRequested: props(),
3613
+ newThreadStarted: empty()
3603
3614
  });
3604
3615
  const threadRestEvents = createActionGroup("Thread REST", {
3605
3616
  listRequested: props(),
@@ -3646,7 +3657,9 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3646
3657
  error: null,
3647
3658
  metadataCredentialsRequested: false,
3648
3659
  metadataJoinCode: null,
3649
- nextCursor: null
3660
+ nextCursor: null,
3661
+ inFlightMutationCount: 0,
3662
+ pendingDeletes: {}
3650
3663
  })), on(threadAdapterEvents.stopped, (state) => ({
3651
3664
  ...state,
3652
3665
  threads: [],
@@ -3655,7 +3668,9 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3655
3668
  error: null,
3656
3669
  metadataCredentialsRequested: false,
3657
3670
  metadataJoinCode: null,
3658
- nextCursor: null
3671
+ nextCursor: null,
3672
+ inFlightMutationCount: 0,
3673
+ pendingDeletes: {}
3659
3674
  })), on(threadRestEvents.listRequested, (state, { sessionId }) => {
3660
3675
  if (sessionId !== state.sessionId || !state.context) return state;
3661
3676
  return {
@@ -3697,12 +3712,9 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3697
3712
  isFetchingNextPage: false,
3698
3713
  error
3699
3714
  };
3700
- }), on(threadRestEvents.metadataCredentialsFailed, (state, { sessionId, error }) => {
3715
+ }), on(threadRestEvents.metadataCredentialsFailed, (state, { sessionId }) => {
3701
3716
  if (sessionId !== state.sessionId) return state;
3702
- return {
3703
- ...state,
3704
- error
3705
- };
3717
+ return state;
3706
3718
  }), on(threadRestEvents.metadataCredentialsRequested, (state, { sessionId }) => {
3707
3719
  if (sessionId !== state.sessionId) return state;
3708
3720
  return {
@@ -3715,10 +3727,105 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3715
3727
  ...state,
3716
3728
  isFetchingNextPage: true
3717
3729
  };
3718
- }), 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) => ({
3719
3797
  ...state,
3720
- error: outcome.ok ? state.error : outcome.error
3721
- })), 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 }) => {
3722
3829
  if (sessionId !== state.sessionId) return state;
3723
3830
  return {
3724
3831
  ...state,
@@ -3731,11 +3838,34 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3731
3838
  threads: state.threads.filter((thread) => thread.id !== threadId)
3732
3839
  };
3733
3840
  }));
3734
- const selectThreads = createSelector((state) => state.threads);
3735
- const selectThreadsIsLoading = createSelector((state) => state.isLoading);
3736
- const selectThreadsError = createSelector((state) => state.error);
3737
- const selectHasNextPage = createSelector((state) => state.nextCursor != null);
3738
- 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;
3739
3869
  let threadRequestId = 0;
3740
3870
  function createThreadRequestId() {
3741
3871
  threadRequestId += 1;
@@ -3825,10 +3955,12 @@ function createThreadMutationObservable(environment, context, request) {
3825
3955
  body: JSON.stringify(request.body)
3826
3956
  }).pipe((0, rxjs_operators.map)(() => threadRestEvents.mutationFinished({ outcome: {
3827
3957
  requestId: request.requestId,
3958
+ sessionId: request.sessionId,
3828
3959
  ok: true
3829
3960
  } })), (0, rxjs_operators.catchError)((error) => {
3830
3961
  return (0, rxjs.of)(threadRestEvents.mutationFinished({ outcome: {
3831
3962
  requestId: request.requestId,
3963
+ sessionId: request.sessionId,
3832
3964
  ok: false,
3833
3965
  error: error instanceof Error ? error : new Error(String(error))
3834
3966
  } }));
@@ -3836,6 +3968,7 @@ function createThreadMutationObservable(environment, context, request) {
3836
3968
  });
3837
3969
  }
3838
3970
  function createThreadStore(environment) {
3971
+ const selectors = createThreadSelectors();
3839
3972
  const store = createStore({
3840
3973
  reducer: threadReducer,
3841
3974
  effects: [
@@ -3902,6 +4035,10 @@ function createThreadStore(environment) {
3902
4035
  thread: action.payload.thread
3903
4036
  });
3904
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 }),
3905
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]) => {
3906
4043
  const context = state.context;
3907
4044
  const params = {
@@ -3933,11 +4070,13 @@ function createThreadStore(environment) {
3933
4070
  }))), (0, rxjs_operators.takeUntil)(actions$.pipe(ofType(threadAdapterEvents.contextChanged, threadAdapterEvents.stopped))));
3934
4071
  }))),
3935
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;
3936
4074
  const context = state.context;
3937
4075
  if (!context?.runtimeUrl) {
3938
4076
  const requestId = action.requestId;
3939
4077
  return (0, rxjs.of)(threadRestEvents.mutationFinished({ outcome: {
3940
4078
  requestId,
4079
+ sessionId,
3941
4080
  ok: false,
3942
4081
  error: /* @__PURE__ */ new Error("Runtime URL is not configured")
3943
4082
  } }));
@@ -3945,6 +4084,7 @@ function createThreadStore(environment) {
3945
4084
  const commonBody = { agentId: context.agentId };
3946
4085
  if (threadAdapterEvents.renameRequested.match(action)) return createThreadMutationObservable(environment, context, {
3947
4086
  requestId: action.requestId,
4087
+ sessionId,
3948
4088
  method: "PATCH",
3949
4089
  path: `/threads/${encodeURIComponent(action.threadId)}`,
3950
4090
  body: {
@@ -3954,12 +4094,14 @@ function createThreadStore(environment) {
3954
4094
  });
3955
4095
  if (threadAdapterEvents.archiveRequested.match(action)) return createThreadMutationObservable(environment, context, {
3956
4096
  requestId: action.requestId,
4097
+ sessionId,
3957
4098
  method: "POST",
3958
4099
  path: `/threads/${encodeURIComponent(action.threadId)}/archive`,
3959
4100
  body: commonBody
3960
4101
  });
3961
4102
  if (threadAdapterEvents.unarchiveRequested.match(action)) return createThreadMutationObservable(environment, context, {
3962
4103
  requestId: action.requestId,
4104
+ sessionId,
3963
4105
  method: "PATCH",
3964
4106
  path: `/threads/${encodeURIComponent(action.threadId)}`,
3965
4107
  body: {
@@ -3969,6 +4111,7 @@ function createThreadStore(environment) {
3969
4111
  });
3970
4112
  return createThreadMutationObservable(environment, context, {
3971
4113
  requestId: action.requestId,
4114
+ sessionId,
3972
4115
  method: "DELETE",
3973
4116
  path: `/threads/${encodeURIComponent(action.threadId)}`,
3974
4117
  body: commonBody
@@ -3979,6 +4122,7 @@ function createThreadStore(environment) {
3979
4122
  function trackMutation(dispatchAction) {
3980
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)(() => ({
3981
4124
  requestId: dispatchAction.requestId,
4125
+ sessionId: store.getState().sessionId,
3982
4126
  ok: false,
3983
4127
  error: /* @__PURE__ */ new Error("Thread store stopped before mutation completed")
3984
4128
  })))).pipe((0, rxjs_operators.take)(1))).then((outcome) => {
@@ -3988,13 +4132,23 @@ function createThreadStore(environment) {
3988
4132
  store.dispatch(dispatchAction);
3989
4133
  return resultPromise;
3990
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
+ };
3991
4142
  return {
3992
4143
  start() {
3993
4144
  store.init();
4145
+ subscribeErrors();
3994
4146
  store.dispatch(threadAdapterEvents.started());
3995
4147
  },
3996
4148
  stop() {
3997
4149
  store.dispatch(threadAdapterEvents.stopped());
4150
+ errorSubscription?.unsubscribe();
4151
+ errorSubscription = null;
3998
4152
  store.stop();
3999
4153
  },
4000
4154
  setContext(context) {
@@ -4005,6 +4159,14 @@ function createThreadStore(environment) {
4005
4159
  if (!context) return;
4006
4160
  store.dispatch(threadRestEvents.listRequested({ sessionId }));
4007
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
+ },
4008
4170
  fetchNextPage() {
4009
4171
  store.dispatch(threadAdapterEvents.fetchNextPageRequested());
4010
4172
  },
@@ -4036,15 +4198,21 @@ function createThreadStore(environment) {
4036
4198
  getState() {
4037
4199
  return store.getState();
4038
4200
  },
4039
- select: store.select.bind(store)
4201
+ getServerState() {
4202
+ return initialThreadState;
4203
+ },
4204
+ select: store.select.bind(store),
4205
+ selectors
4040
4206
  };
4041
4207
  }
4042
4208
  const ɵthreadAdapterEvents = threadAdapterEvents;
4209
+ const ɵcreateThreadSelectors = createThreadSelectors;
4043
4210
  const ɵselectThreads = selectThreads;
4044
4211
  const ɵselectThreadsIsLoading = selectThreadsIsLoading;
4045
4212
  const ɵselectThreadsError = selectThreadsError;
4046
4213
  const ɵselectHasNextPage = selectHasNextPage;
4047
4214
  const ɵselectIsFetchingNextPage = selectIsFetchingNextPage;
4215
+ const ɵselectIsMutating = selectIsMutating;
4048
4216
  /**
4049
4217
  * Number of consecutive WebSocket connection failures after which the
4050
4218
  * threads channel tears itself down rather than retrying indefinitely.
@@ -4053,6 +4221,51 @@ const ɵselectIsFetchingNextPage = selectIsFetchingNextPage;
4053
4221
  */
4054
4222
  const ɵMAX_SOCKET_RETRIES = MAX_SOCKET_RETRIES;
4055
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
+
4056
4269
  //#endregion
4057
4270
  exports.AgentRegistry = AgentRegistry;
4058
4271
  exports.AgentThreadLockedError = AgentThreadLockedError;
@@ -4080,8 +4293,11 @@ exports.on = on;
4080
4293
  exports.parseToolArguments = parseToolArguments;
4081
4294
  exports.props = props;
4082
4295
  exports.select = select;
4296
+ exports.ɵCOPILOTKIT_FEATURES = ɵCOPILOTKIT_FEATURES;
4083
4297
  exports.ɵMAX_SOCKET_RETRIES = ɵMAX_SOCKET_RETRIES;
4298
+ exports.ɵcreateThreadSelectors = ɵcreateThreadSelectors;
4084
4299
  exports.ɵcreateThreadStore = createThreadStore;
4300
+ exports.ɵisCopilotKitFeature = ɵisCopilotKitFeature;
4085
4301
  exports.ɵjoinPhoenixChannel$ = ɵjoinPhoenixChannel$;
4086
4302
  exports.ɵobservePhoenixEvent$ = ɵobservePhoenixEvent$;
4087
4303
  exports.ɵobservePhoenixJoinOutcome$ = ɵobservePhoenixJoinOutcome$;
@@ -4091,6 +4307,7 @@ exports.ɵphoenixChannel$ = ɵphoenixChannel$;
4091
4307
  exports.ɵphoenixSocket$ = ɵphoenixSocket$;
4092
4308
  exports.ɵselectHasNextPage = ɵselectHasNextPage;
4093
4309
  exports.ɵselectIsFetchingNextPage = ɵselectIsFetchingNextPage;
4310
+ exports.ɵselectIsMutating = ɵselectIsMutating;
4094
4311
  exports.ɵselectThreads = ɵselectThreads;
4095
4312
  exports.ɵselectThreadsError = ɵselectThreadsError;
4096
4313
  exports.ɵselectThreadsIsLoading = ɵselectThreadsIsLoading;