@copilotkit/core 1.75.1 → 1.76.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
@@ -1,11 +1,28 @@
1
1
  import { Throttler } from "@tanstack/pacer";
2
- import { AGUIConnectNotImplementedError, AbstractAgent, EventType, HttpAgent, buildResumeArray, isInterruptExpired, randomUUID, runHttpRequest, structuredClone_, transformChunks, transformHttpEventStream } from "@ag-ui/client";
3
- import { RUNTIME_MODE_INTELLIGENCE, RUNTIME_MODE_SSE, logger, parseInspectorMetadataV1, partialJSONParse, phoenixExponentialBackoff, randomUUID as randomUUID$1, resolveDebugConfig, schemaToJsonSchema } from "@copilotkit/shared";
2
+ import { AGUIConnectNotImplementedError, AbstractAgent, CompatibilityBoundary, EventType, HttpAgent, buildResumeArray, isInterruptExpired, randomUUID, runHttpRequest, structuredClone_, transformChunks, transformHttpEventStream } from "@ag-ui/client";
3
+ import { RUNTIME_MODE_INTELLIGENCE, RUNTIME_MODE_SSE, logger, parseInspectorMetadataV1, partialJSONParse, phoenixExponentialBackoff, randomUUID as randomUUID$1, resolveDebugConfig, schemaToJsonSchema, stripIntelligenceRoutingFields } from "@copilotkit/shared";
4
4
  import { BehaviorSubject, EMPTY, NEVER, Notification, Observable, Subject, Subscription, asapScheduler, combineLatest, concat, defer, dematerialize, firstValueFrom, from, lastValueFrom, merge, observeOn, of, switchMap, throwError } from "rxjs";
5
- import { catchError, concatWith, delay, distinctUntilChanged, endWith, filter, finalize, ignoreElements, map, mergeMap, observeOn as observeOn$1, scan, share, shareReplay, startWith, switchMap as switchMap$1, take, takeUntil, tap, timeout, withLatestFrom } from "rxjs/operators";
5
+ import { catchError, concatMap, concatWith, delay, distinctUntilChanged, endWith, filter, finalize, ignoreElements, map, mergeMap, observeOn as observeOn$1, scan, share, shareReplay, startWith, switchMap as switchMap$1, take, takeUntil, tap, timeout, withLatestFrom } from "rxjs/operators";
6
6
  import { Socket } from "phoenix";
7
7
  import { zodToJsonSchema } from "zod-to-json-schema";
8
8
 
9
+ //#region src/utils/http-agent.ts
10
+ /**
11
+ * Whether an agent is an `HttpAgent`, including one built from another copy of
12
+ * `@ag-ui/client` (an app that pins its own 0.x version next to CopilotKit's
13
+ * 1.0, for example). `instanceof` is false across package copies, which made
14
+ * CopilotKit silently skip headers and abort wiring for those agents. The
15
+ * fallback checks the public fields CopilotKit uses, which every published
16
+ * `HttpAgent` has.
17
+ */
18
+ function ɵisHttpAgent(agent) {
19
+ if (agent instanceof HttpAgent) return true;
20
+ if (typeof agent !== "object" || agent === null) return false;
21
+ const candidate = agent;
22
+ return typeof candidate.url === "string" && typeof candidate.headers === "object" && candidate.headers !== null && candidate.abortController instanceof AbortController && typeof candidate.runAgent === "function";
23
+ }
24
+
25
+ //#endregion
9
26
  //#region src/utils/connect-replay.ts
10
27
  /**
11
28
  * Runs an agent's `connect()` stream through the AbstractAgent apply pipeline
@@ -22,18 +39,27 @@ import { zodToJsonSchema } from "zod-to-json-schema";
22
39
  * Cannot send event type 'RUN_STARTED': The run has already errored with
23
40
  * 'RUN_ERROR'. No further events can be sent.
24
41
  *
25
- * `transformChunks` is still applied — message reassembly is needed either way.
42
+ * The AG-UI 1.0 compatibility boundary still runs first, so stored 0.x history
43
+ * is translated (THINKING_* events, `binary` parts, legacy nulls) exactly as in
44
+ * the base pipeline. `enforceEvents` is omitted too: a replay is stored
45
+ * history, and one event that the 1.0 schema rejects (the Intelligence gateway
46
+ * replays RUN_STARTED without `runId`, for example) must not stop the whole
47
+ * thread from hydrating. That keeps the leniency this path had before 1.0.
26
48
  *
27
49
  * Connection-local replay hooks track the phase before applying events. An
28
50
  * explicitly live RUN_ERROR clears busy without closing the connection;
29
51
  * historical errors remain data. Connections without hooks retain the existing
30
- * completion behavior. Subscriber, detach, and result contracts are preserved.
52
+ * completion behavior.
53
+ *
54
+ * Apart from the above, this mirrors the base `AbstractAgent.connectAgent`
55
+ * implementation, so callers keep the same subscriber notifications, detach
56
+ * semantics, and `{ result, newMessages }` return shape.
31
57
  *
32
58
  * TODO: Remove this in favour of the base implementation once AG-UI's
33
59
  * AbstractAgent supports opting out of `verifyEvents` for transports whose
34
60
  * connection life-cycle isn't a single run AND preserves the connection-local
35
61
  * replay lifecycle and running-state behavior below. Skipping verification alone
36
- * is insufficient. As of `@ag-ui/client@0.0.57`
62
+ * is insufficient. As of `@ag-ui/client@1.0.1`
37
63
  * `connectAgent(parameters?, subscriber?)` takes no such option.
38
64
  *
39
65
  * @param agent - The agent whose `connect()` stream should be consumed.
@@ -42,6 +68,9 @@ import { zodToJsonSchema } from "zod-to-json-schema";
42
68
  */
43
69
  async function ɵconnectWithoutEventVerification(agent, parameters, subscriber, connect) {
44
70
  const self = agent;
71
+ let finalizeRun = () => {
72
+ agent.isRunning = false;
73
+ };
45
74
  try {
46
75
  agent.isRunning = true;
47
76
  agent.agentId = agent.agentId ?? randomUUID();
@@ -65,12 +94,35 @@ async function ɵconnectWithoutEventVerification(agent, parameters, subscriber,
65
94
  ...agent.subscribers,
66
95
  subscriber ?? {}
67
96
  ];
68
- await self.onInitialize(input, subscribers);
69
- self.activeRunDetach$ = new Subject();
97
+ const activeRunDetach$ = new Subject();
98
+ self.activeRunDetach$ = activeRunDetach$;
70
99
  let resolveCompletion;
71
100
  self.activeRunCompletionPromise = new Promise((resolve) => {
72
101
  resolveCompletion = resolve;
73
102
  });
103
+ let detached = false;
104
+ let finalized = false;
105
+ const detachSubscription = activeRunDetach$.subscribe(() => {
106
+ detached = true;
107
+ });
108
+ finalizeRun = () => {
109
+ if (finalized) return;
110
+ finalized = true;
111
+ detachSubscription.unsubscribe();
112
+ if (self.activeRunDetach$ === activeRunDetach$) {
113
+ agent.isRunning = false;
114
+ self.activeRunCompletionPromise = void 0;
115
+ self.activeRunDetach$ = void 0;
116
+ self.onFinalize(input, subscribers);
117
+ }
118
+ resolveCompletion?.();
119
+ resolveCompletion = void 0;
120
+ };
121
+ await self.onInitialize(input, subscribers);
122
+ if (detached) return {
123
+ result: void 0,
124
+ newMessages: []
125
+ };
74
126
  const lifecycle = {
75
127
  onReplayStarted: () => {
76
128
  isReplaying = true;
@@ -80,29 +132,23 @@ async function ɵconnectWithoutEventVerification(agent, parameters, subscriber,
80
132
  isReplaying = false;
81
133
  }
82
134
  };
83
- const source$ = defer(() => connect ? connect(input, lifecycle) : self.connect(input)).pipe(tap((event) => {
135
+ const source$ = defer(() => new CompatibilityBoundary().run(input, { run: () => connect ? connect(input, lifecycle) : self.connect(input) })).pipe(tap((event) => {
84
136
  if (!isReplaying && event.type === EventType.RUN_ERROR) liveErrors.add(event);
85
- }), transformChunks(self.debugLogger), takeUntil(self.activeRunDetach$));
137
+ }), transformChunks(self.debugLogger), takeUntil(activeRunDetach$));
86
138
  const applied$ = self.apply(input, source$, subscribers);
87
139
  await lastValueFrom(self.processApplyEvents(input, applied$, subscribers).pipe(catchError((error) => {
140
+ if (self.activeRunDetach$ !== activeRunDetach$) return EMPTY;
88
141
  agent.isRunning = false;
89
142
  if (error instanceof AGUIConnectNotImplementedError) return EMPTY;
90
143
  return self.onError(input, error, subscribers);
91
- }), finalize(() => {
92
- agent.isRunning = false;
93
- self.onFinalize(input, subscribers);
94
- resolveCompletion?.();
95
- resolveCompletion = void 0;
96
- self.activeRunCompletionPromise = void 0;
97
- self.activeRunDetach$ = void 0;
98
- })), { defaultValue: void 0 });
144
+ }), finalize(() => finalizeRun())), { defaultValue: void 0 });
99
145
  const newMessages = structuredClone_(agent.messages).filter((m) => !previousMessageIds.has(m.id));
100
146
  return {
101
147
  result,
102
148
  newMessages
103
149
  };
104
150
  } finally {
105
- agent.isRunning = false;
151
+ finalizeRun();
106
152
  }
107
153
  }
108
154
 
@@ -585,7 +631,7 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
585
631
  observeThreadEvents$(threadId, channel$, options) {
586
632
  return channel$.pipe(switchMap$1(({ channel }) => this.observeChannelEvent$(channel, CLIENT_AG_UI_EVENT)), tap((payload) => {
587
633
  this.updateLastSeenEventId(threadId, payload);
588
- }), mergeMap((payload) => this.createThreadNotifications(payload, {
634
+ }), map((payload) => options.streamMode === "run" ? stripIntelligenceRoutingFields(payload) : payload), mergeMap((payload) => this.createThreadNotifications(payload, {
589
635
  completeOnRunError: options.completeOnRunError,
590
636
  completeOnRunFinished: options.streamMode === "run",
591
637
  errorOnRunError: options.streamMode === "run"
@@ -839,6 +885,7 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
839
885
  _capabilities;
840
886
  delegate;
841
887
  runtimeInfoPromise;
888
+ connectionAttempt;
842
889
  _messageFilter;
843
890
  /**
844
891
  * The HTTP `run` this agent started and has not yet seen finish, as the
@@ -901,13 +948,12 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
901
948
  this.runtimeMode = runtimeMode;
902
949
  this.intelligence = intelligence;
903
950
  if (unchanged) return;
951
+ this.connectionAttempt = void 0;
904
952
  this.runtimeInfoPromise = void 0;
905
953
  const staleDelegate = this.delegate;
906
954
  this.delegate = void 0;
907
- if (staleDelegate) {
908
- staleDelegate.abortRun();
909
- this.detachActiveRun();
910
- }
955
+ if (staleDelegate) staleDelegate.abortRun();
956
+ this.detachActiveRun();
911
957
  }
912
958
  /**
913
959
  * The agent id used for outbound runtime requests — `runtimeAgentId` when
@@ -998,8 +1044,11 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
998
1044
  return this._capabilities ?? {};
999
1045
  }
1000
1046
  async detachActiveRun() {
1001
- if (this.delegate) await this.delegate.detachActiveRun();
1002
- await super.detachActiveRun();
1047
+ this.connectionAttempt = void 0;
1048
+ this.isRunning = false;
1049
+ const delegateDetach = this.delegate?.detachActiveRun();
1050
+ const proxyDetach = super.detachActiveRun();
1051
+ await Promise.all([delegateDetach, proxyDetach]);
1003
1052
  }
1004
1053
  abortRun() {
1005
1054
  if (this.delegate) {
@@ -1053,41 +1102,74 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
1053
1102
  });
1054
1103
  }
1055
1104
  async connectAgent(parameters, subscriber) {
1105
+ const detached = this.detachActiveRun();
1106
+ const connection = {};
1107
+ const threadId = this.threadId;
1108
+ this.connectionAttempt = connection;
1109
+ const isCurrentConnection = () => this.connectionAttempt === connection && this.threadId === threadId;
1110
+ await detached;
1111
+ if (!isCurrentConnection()) return {
1112
+ result: void 0,
1113
+ newMessages: []
1114
+ };
1115
+ await this.ensureRuntimeConfiguration();
1116
+ if (!isCurrentConnection()) return {
1117
+ result: void 0,
1118
+ newMessages: []
1119
+ };
1056
1120
  if (this.runtimeMode !== RUNTIME_MODE_INTELLIGENCE) return ɵconnectWithoutEventVerification(this, parameters, subscriber, (input, lifecycle) => this.connect(input, lifecycle));
1057
- if (this.delegate) await this.delegate.detachActiveRun();
1058
1121
  await this.resolveDelegate();
1122
+ if (!isCurrentConnection()) return {
1123
+ result: void 0,
1124
+ newMessages: []
1125
+ };
1059
1126
  const delegate = this.delegate;
1060
1127
  const bridgeSub = delegate.subscribe({
1061
1128
  onMessagesChanged: () => {
1129
+ if (!isCurrentConnection()) return;
1062
1130
  this.setMessages([...delegate.messages]);
1063
1131
  },
1064
1132
  onStateChanged: () => {
1133
+ if (!isCurrentConnection()) return;
1065
1134
  this.setState({ ...delegate.state });
1066
1135
  },
1067
1136
  onRunInitialized: () => {
1137
+ if (!isCurrentConnection()) return;
1068
1138
  this.isRunning = true;
1069
1139
  },
1070
1140
  onRunFinalized: () => {
1141
+ if (!isCurrentConnection()) return;
1142
+ this.pendingInterrupts = structuredClone_(delegate.pendingInterrupts);
1071
1143
  this.isRunning = false;
1072
1144
  },
1073
1145
  onRunFailed: () => {
1146
+ if (!isCurrentConnection()) return;
1074
1147
  this.isRunning = false;
1075
1148
  },
1076
1149
  onRunErrorEvent: () => {
1150
+ if (!isCurrentConnection()) return;
1077
1151
  this.isRunning = delegate.isRunning;
1078
1152
  },
1079
1153
  onRunStartedEvent: () => {
1154
+ if (!isCurrentConnection()) return;
1080
1155
  this.isRunning = delegate.isRunning;
1081
1156
  }
1082
1157
  });
1083
- const forwardedSubs = this.subscribers.map((s) => delegate.subscribe(s));
1158
+ const forwardedSubs = [...this.subscribers, ...subscriber ? [subscriber] : []].map((subscriber) => delegate.subscribe(new Proxy(subscriber, { get(target, property, receiver) {
1159
+ const callback = Reflect.get(target, property, receiver);
1160
+ if (typeof callback !== "function") return callback;
1161
+ return (...args) => isCurrentConnection() ? Reflect.apply(callback, target, args) : void 0;
1162
+ } })));
1084
1163
  try {
1085
- const result = await delegate.connectAgent(parameters, subscriber);
1086
- this.setMessages([...delegate.messages]);
1087
- this.setState({ ...delegate.state });
1164
+ const result = await delegate.connectAgent(parameters);
1165
+ if (isCurrentConnection()) {
1166
+ this.setMessages([...delegate.messages]);
1167
+ this.setState({ ...delegate.state });
1168
+ this.pendingInterrupts = structuredClone_(delegate.pendingInterrupts);
1169
+ }
1088
1170
  return result;
1089
1171
  } finally {
1090
- this.isRunning = false;
1172
+ if (isCurrentConnection()) this.isRunning = false;
1091
1173
  bridgeSub.unsubscribe();
1092
1174
  for (const sub of forwardedSubs) sub.unsubscribe();
1093
1175
  }
@@ -1109,14 +1191,31 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
1109
1191
  const input = this.#applyMessageFilter(unfiltered);
1110
1192
  this.activeRun = void 0;
1111
1193
  const routedId = this.routedAgentId();
1194
+ let httpEvents;
1112
1195
  if (this.transport === "single") {
1113
1196
  if (!this.singleEndpointUrl) throw new Error("Single endpoint transport requires a runtimeUrl");
1114
1197
  const requestInit = this.createSingleRouteRequestInit(input, "agent/connect", { agentId: routedId });
1115
- return withAbortErrorHandling(transformHttpEventStream(runHttpRequest(() => this.fetch(this.singleEndpointUrl, requestInit))));
1198
+ httpEvents = runHttpRequest(() => this.fetch(this.singleEndpointUrl, requestInit));
1199
+ } else {
1200
+ const connectUrl = `${this.runtimeUrl}/agent/${routedId}/connect`;
1201
+ const connectRequestInit = this.requestInit(input);
1202
+ httpEvents = runHttpRequest(() => this.fetch(connectUrl, connectRequestInit));
1116
1203
  }
1117
- const connectUrl = `${this.runtimeUrl}/agent/${routedId}/connect`;
1118
- const connectRequestInit = this.requestInit(input);
1119
- return withAbortErrorHandling(transformHttpEventStream(runHttpRequest(() => this.fetch(connectUrl, connectRequestInit))));
1204
+ return withAbortErrorHandling(defer(() => {
1205
+ let replayStarted = false;
1206
+ const emptyHistory = [{
1207
+ type: EventType.MESSAGES_SNAPSHOT,
1208
+ messages: []
1209
+ }, {
1210
+ type: EventType.STATE_SNAPSHOT,
1211
+ snapshot: {}
1212
+ }];
1213
+ return transformHttpEventStream(httpEvents).pipe(concatMap((event) => {
1214
+ if (replayStarted) return of(event);
1215
+ replayStarted = true;
1216
+ return from([...emptyHistory, event]);
1217
+ }), concatWith(defer(() => replayStarted ? EMPTY : from(emptyHistory))));
1218
+ }));
1120
1219
  }
1121
1220
  #runViaDelegate(input) {
1122
1221
  return defer(() => from(this.resolveDelegate())).pipe(switchMap$1((delegate) => withAbortErrorHandling(delegate.run(input))));
@@ -1159,6 +1258,7 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
1159
1258
  cloned.threadId = this.threadId;
1160
1259
  cloned.setState(this.state);
1161
1260
  cloned.setMessages(this.messages);
1261
+ cloned.pendingInterrupts = structuredClone_(this.pendingInterrupts);
1162
1262
  if (this.delegate) {
1163
1263
  const clonedDelegate = this.delegate.clone();
1164
1264
  cloned.delegate = clonedDelegate;
@@ -1196,6 +1296,7 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
1196
1296
  if (this.runtimeMode === RUNTIME_MODE_INTELLIGENCE || this.runtimeMode !== "pending" && this.transport !== "auto") return;
1197
1297
  if (!this.runtimeUrl) throw new Error("Runtime URL is not set");
1198
1298
  const runtimeInfoPromise = this.runtimeInfoPromise ?? this.fetchRuntimeInfo().then((runtimeInfo) => {
1299
+ if (this.runtimeInfoPromise !== runtimeInfoPromise) return;
1199
1300
  this.runtimeMode = runtimeInfo.mode ?? RUNTIME_MODE_SSE;
1200
1301
  this.intelligence = runtimeInfo.intelligence;
1201
1302
  });
@@ -1300,6 +1401,7 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
1300
1401
  delegate.debugLogger = this.debugLogger;
1301
1402
  delegate.setMessages(this.messages);
1302
1403
  delegate.setState(this.state);
1404
+ delegate.pendingInterrupts = structuredClone_(this.pendingInterrupts);
1303
1405
  if (hasHeaders(delegate)) delegate.headers = { ...this.headers };
1304
1406
  if (hasCredentials(delegate)) delegate.credentials = this.credentials;
1305
1407
  }
@@ -1735,7 +1837,7 @@ var AgentRegistry = class {
1735
1837
  * because only `HttpAgent` carries a `headers` field. See #5635.
1736
1838
  */
1737
1839
  applyHeadersToAgent(agent) {
1738
- if (agent instanceof HttpAgent) {
1840
+ if (ɵisHttpAgent(agent)) {
1739
1841
  if (!this.agentOwnHeaders.has(agent)) this.agentOwnHeaders.set(agent, { ...agent.headers });
1740
1842
  agent.headers = {
1741
1843
  ...this.agentOwnHeaders.get(agent),
@@ -3086,6 +3188,7 @@ var RunHandler = class {
3086
3188
  * inspector and intermittent "Message not found" toasts.
3087
3189
  */
3088
3190
  _lastConnectedThreadIdsByAgent = /* @__PURE__ */ new Map();
3191
+ _lastActiveThreadIdsByAgent = /* @__PURE__ */ new Map();
3089
3192
  _anonymousAgentIds = /* @__PURE__ */ new WeakMap();
3090
3193
  _nextAnonymousAgentId = 0;
3091
3194
  /** Prevent reconnects from starting an interaction already executing locally. */
@@ -3263,7 +3366,8 @@ var RunHandler = class {
3263
3366
  * Connect an agent (establish initial connection)
3264
3367
  */
3265
3368
  async connectAgent({ agent }) {
3266
- this._connectionGenerations.set(agent, {});
3369
+ const connection = {};
3370
+ this._connectionGenerations.set(agent, connection);
3267
3371
  const incomingThreadId = agent.threadId ?? null;
3268
3372
  const previousReplay = this._interactionAbortControllers.get(agent);
3269
3373
  if (previousReplay && previousReplay.threadId !== incomingThreadId) {
@@ -3273,12 +3377,19 @@ var RunHandler = class {
3273
3377
  }
3274
3378
  try {
3275
3379
  const restoreKey = this.getConnectRestoreKey(agent);
3276
- const isFreshRestore = incomingThreadId !== (this._lastConnectedThreadIdsByAgent.get(restoreKey) ?? null);
3277
- this._lastConnectedThreadIdsByAgent.set(restoreKey, incomingThreadId);
3380
+ const previousThreadId = this._lastActiveThreadIdsByAgent.get(restoreKey);
3381
+ const isFreshRestore = incomingThreadId !== (previousThreadId ?? this._lastConnectedThreadIdsByAgent.get(restoreKey) ?? null);
3278
3382
  await agent.detachActiveRun();
3383
+ if (this._connectionGenerations.get(agent) !== connection || (agent.threadId ?? null) !== incomingThreadId) return {
3384
+ result: void 0,
3385
+ newMessages: []
3386
+ };
3387
+ this._lastConnectedThreadIdsByAgent.set(restoreKey, incomingThreadId);
3388
+ this._lastActiveThreadIdsByAgent.set(restoreKey, incomingThreadId);
3279
3389
  if (isFreshRestore) {
3280
3390
  agent.setMessages([]);
3281
3391
  agent.setState({});
3392
+ if (previousThreadId !== void 0 && previousThreadId !== incomingThreadId) agent.pendingInterrupts = [];
3282
3393
  const cursorAware = agent;
3283
3394
  if (incomingThreadId) {
3284
3395
  if (typeof cursorAware.clearReplayCursor === "function") cursorAware.clearReplayCursor(incomingThreadId);
@@ -3290,12 +3401,16 @@ var RunHandler = class {
3290
3401
  copilotkit: this.core,
3291
3402
  agent
3292
3403
  }), "Subscriber onAgentRunStarted error:");
3404
+ if (this._connectionGenerations.get(agent) !== connection || (agent.threadId ?? null) !== incomingThreadId) return {
3405
+ result: void 0,
3406
+ newMessages: []
3407
+ };
3293
3408
  const runAgentResult = await agent.connectAgent({
3294
3409
  forwardedProps: this._internal.properties,
3295
3410
  tools: this.buildFrontendTools(agent.agentId),
3296
3411
  context: this._internal.getContextForAgent(agent.agentId)
3297
3412
  }, this.createAgentErrorSubscriber(agent));
3298
- if ((agent.threadId ?? null) !== incomingThreadId) return runAgentResult;
3413
+ if (this._connectionGenerations.get(agent) !== connection || (agent.threadId ?? null) !== incomingThreadId) return runAgentResult;
3299
3414
  const controller = new AbortController();
3300
3415
  const replay = this._interactionAbortControllers.get(agent) ?? {
3301
3416
  threadId: incomingThreadId,
@@ -3352,6 +3467,12 @@ var RunHandler = class {
3352
3467
  continuationHandoff?.cancel();
3353
3468
  throw error;
3354
3469
  }
3470
+ const resumedRunId = this._internal.stateManager.getContinuationRunId(agent, {
3471
+ threadId: agent.threadId,
3472
+ resume,
3473
+ forwardedProps
3474
+ });
3475
+ const logicalHandoff = continuationHandoff ?? (resumedRunId ? this._internal.stateManager.markNextRunAsContinuation(agent, resumedRunId) : void 0);
3355
3476
  const isTopLevel = this._runDepth === 0;
3356
3477
  let originalAbortRun;
3357
3478
  if (isTopLevel) {
@@ -3369,18 +3490,19 @@ var RunHandler = class {
3369
3490
  }
3370
3491
  this._runDepth++;
3371
3492
  try {
3372
- let logicalRunId = runId;
3493
+ let logicalRunId = resumedRunId ?? runId;
3373
3494
  const agentSubscriber = this.createAgentErrorSubscriber(agent);
3374
3495
  let started = false;
3375
3496
  const onRunStartedEvent = agentSubscriber.onRunStartedEvent;
3376
3497
  const onRunInitialized = agentSubscriber.onRunInitialized;
3377
3498
  agentSubscriber.onRunInitialized = async (params) => {
3378
- continuationHandoff?.bind(params.input);
3499
+ this._lastActiveThreadIdsByAgent.set(this.getConnectRestoreKey(agent), params.input.threadId);
3500
+ logicalHandoff?.bind(params.input);
3379
3501
  return onRunInitialized?.(params);
3380
3502
  };
3381
3503
  agentSubscriber.onRunStartedEvent = async (params) => {
3382
3504
  started = true;
3383
- if (!continuationHandoff) logicalRunId = params.input.runId;
3505
+ if (!logicalHandoff) logicalRunId = params.input.runId;
3384
3506
  return onRunStartedEvent?.(params);
3385
3507
  };
3386
3508
  const pinRunIdOnWire = runId !== void 0 && !continuationHandoff;
@@ -3395,14 +3517,14 @@ var RunHandler = class {
3395
3517
  context: this._internal.getContextForAgent(agent.agentId)
3396
3518
  };
3397
3519
  const runAgentResult = await agent.runAgent(agentRunInput, agentSubscriber);
3398
- if (!started) continuationHandoff?.cancel();
3520
+ if (!started) logicalHandoff?.cancel();
3399
3521
  return await this.processAgentResult({
3400
3522
  runAgentResult,
3401
3523
  agent,
3402
3524
  runId: logicalRunId
3403
3525
  });
3404
3526
  } catch (error) {
3405
- continuationHandoff?.cancel();
3527
+ logicalHandoff?.cancel();
3406
3528
  const runError = error instanceof Error ? error : new Error(String(error));
3407
3529
  const context = {};
3408
3530
  if (agent.agentId) context.agentId = agent.agentId;
@@ -3944,6 +4066,15 @@ var StateManager = class {
3944
4066
  activeRun = /* @__PURE__ */ new Map();
3945
4067
  agentSubscriptions = /* @__PURE__ */ new Map();
3946
4068
  pendingContinuations = /* @__PURE__ */ new WeakMap();
4069
+ interruptedRuns = /* @__PURE__ */ new WeakMap();
4070
+ /** Find the interrupted logical run without consuming its pending answers. */
4071
+ getContinuationRunId(agent, input) {
4072
+ const pending = this.interruptedRuns.get(agent)?.get(input.threadId);
4073
+ if (!pending) return void 0;
4074
+ if (pending.interruptIds === null) return input.resume === void 0 && isContinuation(input) ? pending.runId : void 0;
4075
+ const submitted = new Set(input.resume?.map((entry) => entry.interruptId));
4076
+ return submitted.size > 0 && submitted.size === input.resume?.length && submitted.size === pending.interruptIds.length && pending.interruptIds.every((id) => submitted.has(id)) ? pending.runId : void 0;
4077
+ }
3947
4078
  constructor(core) {
3948
4079
  this.core = core;
3949
4080
  }
@@ -3987,6 +4118,7 @@ var StateManager = class {
3987
4118
  }
3988
4119
  let revoked = false;
3989
4120
  let subRunId;
4121
+ let legacyInterrupted = false;
3990
4122
  let runFinished = false;
3991
4123
  const pendingResults = /* @__PURE__ */ new WeakMap();
3992
4124
  const reconcilePendingResults = (historyMessages, input) => {
@@ -4052,21 +4184,43 @@ var StateManager = class {
4052
4184
  if (revoked) return;
4053
4185
  const internalContinuation = [...this.pendingContinuations.get(agent) ?? []].find((pending) => pending.expectedInput === input);
4054
4186
  internalContinuation?.cancel();
4187
+ const startedInput = event.input ?? {
4188
+ ...input,
4189
+ threadId: event.threadId
4190
+ };
4191
+ const resumedRunId = this.getContinuationRunId(agent, startedInput);
4192
+ this.interruptedRuns.get(agent)?.delete(startedInput.threadId);
4193
+ legacyInterrupted = false;
4055
4194
  if (internalContinuation) subRunId = internalContinuation.expectedRunId ?? event.runId ?? input.runId;
4195
+ else if (resumedRunId) subRunId = resumedRunId;
4056
4196
  else if (runFinished && input.runId === subRunId && !isContinuation(input) && (event.runId == null || event.runId === subRunId)) subRunId = randomUUID();
4057
4197
  else subRunId = event.runId || input.runId;
4058
4198
  runFinished = false;
4059
4199
  this.handleRunStarted(agent, effectiveInput(input), state);
4060
4200
  },
4061
- onRunFinishedEvent: ({ input, state, messages }) => {
4201
+ onRunFinishedEvent: ({ event, input, state, messages }) => {
4062
4202
  if (revoked) return;
4063
4203
  runFinished = true;
4064
4204
  const effective = effectiveInput(input);
4205
+ let interrupted = this.interruptedRuns.get(agent);
4206
+ if (event.outcome?.type === "interrupt" || legacyInterrupted) {
4207
+ if (!interrupted) {
4208
+ interrupted = /* @__PURE__ */ new Map();
4209
+ this.interruptedRuns.set(agent, interrupted);
4210
+ }
4211
+ interrupted.set(event.threadId, {
4212
+ runId: effective.runId,
4213
+ interruptIds: event.outcome?.type === "interrupt" ? event.outcome.interrupts.map((interrupt) => interrupt.id) : null
4214
+ });
4215
+ } else interrupted?.delete(event.threadId);
4065
4216
  const mutation = reconcilePendingResults(messages, input);
4066
4217
  clearPendingResults(input);
4067
4218
  this.handleRunFinished(agent, effective, state);
4068
4219
  return mutation;
4069
4220
  },
4221
+ onCustomEvent: ({ event }) => {
4222
+ if (!revoked && event.name === "on_interrupt") legacyInterrupted = true;
4223
+ },
4070
4224
  onRunErrorEvent: ({ input, state, messages }) => {
4071
4225
  if (revoked) return;
4072
4226
  runFinished = true;
@@ -4123,6 +4277,7 @@ var StateManager = class {
4123
4277
  unsubscribe: () => {
4124
4278
  revoked = true;
4125
4279
  this.pendingContinuations.delete(agent);
4280
+ this.interruptedRuns.delete(agent);
4126
4281
  unsubscribe();
4127
4282
  }
4128
4283
  });
@@ -4283,6 +4438,8 @@ var StateManager = class {
4283
4438
  * Clear all state for an agent
4284
4439
  */
4285
4440
  clearAgentState(agentId) {
4441
+ const agent = this.agentSubscriptions.get(agentId)?.agent;
4442
+ if (agent) this.interruptedRuns.delete(agent);
4286
4443
  this.stateByRun.delete(agentId);
4287
4444
  this.messageToRun.delete(agentId);
4288
4445
  this.rawEventByMessage.delete(agentId);
@@ -4291,6 +4448,8 @@ var StateManager = class {
4291
4448
  * Clear all state for a thread
4292
4449
  */
4293
4450
  clearThreadState(agentId, threadId) {
4451
+ const agent = this.agentSubscriptions.get(agentId)?.agent;
4452
+ if (agent) this.interruptedRuns.get(agent)?.delete(threadId);
4294
4453
  this.stateByRun.get(agentId)?.delete(threadId);
4295
4454
  this.messageToRun.get(agentId)?.delete(threadId);
4296
4455
  this.rawEventByMessage.get(agentId)?.delete(threadId);
@@ -7355,5 +7514,5 @@ function onInspectorViewThreadResult(handler) {
7355
7514
  }
7356
7515
 
7357
7516
  //#endregion
7358
- export { AgentRegistry, AgentThreadLockedError, ContextStore, CopilotKitCore, CopilotKitCoreErrorCode, CopilotKitCoreRuntimeConnectionStatus, INSPECTOR_THREAD_BRIDGE_PLUGIN_ID, IntelligenceAgent, MEMORY_ERROR_REGISTRY, MemoryError, ProxiedCopilotRuntimeAgent, RunHandler, StateManager, SuggestionEngine, ToolCallStatus, WebMCPRegistry, completePartialMarkdown, createActionGroup, createEffect, createInspectorThreadRequestId, createReducer, createSelector, createStore, emitInspectorActiveThread, emitInspectorStopViewing, emitInspectorViewThread, emitInspectorViewThreadResult, empty, ensureObjectArgs, getWebMCPModelContext, isAbortError, isInspectorThreadBridgeEnabled, isRunCompletionAware, ofType, on, onInspectorActiveThread, onInspectorStopViewing, onInspectorViewThread, onInspectorViewThreadResult, parseToolArguments, props, select, ɵCOPILOTKIT_FEATURES, ɵInterruptState, ɵMAX_SOCKET_RETRIES, ɵRUNTIME_PROBE_TIMEOUT_MS, ɵTHREAD_REQUEST_TIMEOUT_MS, createMemoryStore as ɵcreateMemoryStore, ɵcreateThreadSelectors, createThreadStore as ɵcreateThreadStore, ɵisCopilotKitFeature, isRetryableStatus as ɵisRetryableMemoryStatus, ɵjoinPhoenixChannel$, ɵmapMemoryMetadataEvent, ɵmemoryAdapterEvents, ɵmemoryDomainEvents, ɵmemoryReducer, ɵmemoryRestEvents, ɵobservePhoenixEvent$, ɵobservePhoenixJoinOutcome$, ɵobservePhoenixSocketHealth$, ɵobservePhoenixSocketSignals$, ɵphoenixChannel$, ɵphoenixSocket$, ɵselectFetchMoreError, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectIsMutating, ɵselectMemories, ɵselectMemoriesAvailable, ɵselectMemoriesError, ɵselectMemoriesIsLoading, ɵselectMemoriesIsMutating, ɵselectMemoriesRealtimeStatus, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading, ɵthreadAdapterEvents };
7517
+ export { AgentRegistry, AgentThreadLockedError, ContextStore, CopilotKitCore, CopilotKitCoreErrorCode, CopilotKitCoreRuntimeConnectionStatus, INSPECTOR_THREAD_BRIDGE_PLUGIN_ID, IntelligenceAgent, MEMORY_ERROR_REGISTRY, MemoryError, ProxiedCopilotRuntimeAgent, RunHandler, StateManager, SuggestionEngine, ToolCallStatus, WebMCPRegistry, completePartialMarkdown, createActionGroup, createEffect, createInspectorThreadRequestId, createReducer, createSelector, createStore, emitInspectorActiveThread, emitInspectorStopViewing, emitInspectorViewThread, emitInspectorViewThreadResult, empty, ensureObjectArgs, getWebMCPModelContext, isAbortError, isInspectorThreadBridgeEnabled, isRunCompletionAware, ofType, on, onInspectorActiveThread, onInspectorStopViewing, onInspectorViewThread, onInspectorViewThreadResult, parseToolArguments, props, select, ɵCOPILOTKIT_FEATURES, ɵInterruptState, ɵMAX_SOCKET_RETRIES, ɵRUNTIME_PROBE_TIMEOUT_MS, ɵTHREAD_REQUEST_TIMEOUT_MS, createMemoryStore as ɵcreateMemoryStore, ɵcreateThreadSelectors, createThreadStore as ɵcreateThreadStore, ɵisCopilotKitFeature, ɵisHttpAgent, isRetryableStatus as ɵisRetryableMemoryStatus, ɵjoinPhoenixChannel$, ɵmapMemoryMetadataEvent, ɵmemoryAdapterEvents, ɵmemoryDomainEvents, ɵmemoryReducer, ɵmemoryRestEvents, ɵobservePhoenixEvent$, ɵobservePhoenixJoinOutcome$, ɵobservePhoenixSocketHealth$, ɵobservePhoenixSocketSignals$, ɵphoenixChannel$, ɵphoenixSocket$, ɵselectFetchMoreError, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectIsMutating, ɵselectMemories, ɵselectMemoriesAvailable, ɵselectMemoriesError, ɵselectMemoriesIsLoading, ɵselectMemoriesIsMutating, ɵselectMemoriesRealtimeStatus, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading, ɵthreadAdapterEvents };
7359
7518
  //# sourceMappingURL=index.mjs.map