@copilotkit/core 1.77.0 → 1.77.1

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.umd.js CHANGED
@@ -194,10 +194,17 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
194
194
  *
195
195
  * The socket is constructed and connected on subscription, and disconnected on
196
196
  * teardown. Each subscription creates an isolated socket instance.
197
+ *
198
+ * Heartbeats default to every 15s: Phoenix's own 30s default matches common
199
+ * reverse-proxy WebSocket idle timeouts (e.g. Azure Application Gateway), which
200
+ * would drop the socket during a quiet run. Callers may override it.
197
201
  */
198
202
  function ɵphoenixSocket$(options) {
199
203
  return (0, rxjs.defer)(() => {
200
- const socket = new phoenix.Socket(options.url, options.options);
204
+ const socket = new phoenix.Socket(options.url, {
205
+ heartbeatIntervalMs: 15e3,
206
+ ...options.options
207
+ });
201
208
  const signals$ = ɵcreatePhoenixSocketSignals$(socket).pipe((0, rxjs_operators.shareReplay)({
202
209
  bufferSize: 1,
203
210
  refCount: true
@@ -323,6 +330,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
323
330
  const CONNECT_STREAM_IDLE_REPLAY_FALLBACK_MS = 100;
324
331
  const CONNECT_IDLE_WARNING_MS = 3e4;
325
332
  const MAX_UNOPENED_CREDENTIAL_REFRESHES = 2;
333
+ function isMcpResourceRead(input) {
334
+ const forwardedProps = input.forwardedProps;
335
+ const request = forwardedProps === null || forwardedProps === void 0 ? void 0 : forwardedProps.__proxiedMCPRequest;
336
+ return (forwardedProps === null || forwardedProps === void 0 ? void 0 : forwardedProps.__copilotkitMcpResourceReadOnly) === true && typeof request === "object" && request !== null && request.method === "resources/read";
337
+ }
326
338
  var AgentThreadLockedError = class extends Error {
327
339
  constructor(threadId) {
328
340
  super(threadId ? `Thread ${threadId} is locked` : "Thread is locked");
@@ -430,10 +442,22 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
430
442
  * server-pushed AG-UI events to the Observable subscriber.
431
443
  */
432
444
  run(input) {
433
- this.threadId = input.threadId;
434
- this.canonicalRunId = input.runId;
445
+ if (!isMcpResourceRead(input)) {
446
+ this.threadId = input.threadId;
447
+ this.canonicalRunId = input.runId;
448
+ }
435
449
  return (0, rxjs.defer)(() => this.requestJoinCredentials$("run", input)).pipe((0, rxjs.switchMap)((credentials) => {
436
450
  if (credentials === null) return (0, rxjs.throwError)(() => /* @__PURE__ */ new Error("REST run request returned no credentials"));
451
+ if ("kind" in credentials) return (0, rxjs.of)({
452
+ type: _ag_ui_client.EventType.RUN_STARTED,
453
+ threadId: credentials.threadId,
454
+ runId: credentials.runId
455
+ }, {
456
+ type: _ag_ui_client.EventType.RUN_FINISHED,
457
+ threadId: credentials.threadId,
458
+ runId: credentials.runId,
459
+ result: credentials.result
460
+ });
437
461
  const canonicalInput = this.applyCanonicalRunIdentity(input, credentials, { fallbackToInputRunId: true });
438
462
  return this.observeThread$(canonicalInput, credentials, {
439
463
  completeOnRunError: false,
@@ -527,7 +551,18 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
527
551
  const text = await response.text().catch(() => "");
528
552
  throw new Error(text || response.statusText || String(response.status));
529
553
  }
530
- return this.normalizeJoinCredentials(await response.json(), input);
554
+ const payload = await response.json();
555
+ if (mode === "run" && isMcpResourceRead(input) && typeof payload === "object" && payload !== null && payload.kind === "mcp-resource-read") {
556
+ const resource = payload;
557
+ if (typeof resource.threadId !== "string" || !resource.threadId || typeof resource.runId !== "string" || !resource.runId) throw new Error("invalid MCP resource response identity");
558
+ return {
559
+ kind: "mcp-resource-read",
560
+ threadId: resource.threadId,
561
+ runId: resource.runId,
562
+ result: resource.result
563
+ };
564
+ }
565
+ return this.normalizeJoinCredentials(payload, input);
531
566
  } catch (error) {
532
567
  if (error instanceof AgentThreadLockedError) throw error;
533
568
  throw new Error(`REST ${mode} request failed: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
@@ -1268,6 +1303,22 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
1268
1303
  }
1269
1304
  return cloned;
1270
1305
  }
1306
+ /** Run presentation-only MCP reads on a separate client agent so their
1307
+ * lifecycle cannot replace an in-flight approval's detach/completion state. */
1308
+ async ɵrunMcpResourceRead(parameters) {
1309
+ var _parameters$forwarded;
1310
+ const request = (_parameters$forwarded = parameters.forwardedProps) === null || _parameters$forwarded === void 0 ? void 0 : _parameters$forwarded.__proxiedMCPRequest;
1311
+ if (typeof request !== "object" || request === null || request.method !== "resources/read") throw new Error("Only MCP resources/read can use the read-only path");
1312
+ await this.ensureRuntimeConfiguration();
1313
+ const resourceParameters = {
1314
+ ...parameters,
1315
+ forwardedProps: {
1316
+ ...parameters.forwardedProps,
1317
+ __copilotkitMcpResourceReadOnly: true
1318
+ }
1319
+ };
1320
+ return this.runtimeMode === _copilotkit_shared.RUNTIME_MODE_INTELLIGENCE ? this.clone().runAgent(resourceParameters) : this.runAgent(resourceParameters);
1321
+ }
1271
1322
  /**
1272
1323
  * Drop the delegate's cached `lastSeenEventId` for this thread so
1273
1324
  * the next connect requests a full historical replay from the
@@ -3571,7 +3622,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3571
3622
  this.processAgentResult({
3572
3623
  runAgentResult,
3573
3624
  agent,
3574
- toolExecutionMode: "human-in-the-loop",
3625
+ toolExecutionMode: "restore",
3575
3626
  signal: controller.signal
3576
3627
  }).catch(async (error) => {
3577
3628
  await this._internal.emitError({
@@ -3709,11 +3760,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3709
3760
  const threadId = agent.threadId;
3710
3761
  const executing = (_this$_executingToolC = this._executingToolCalls.get(agent)) !== null && _this$_executingToolC !== void 0 ? _this$_executingToolC : /* @__PURE__ */ new Set();
3711
3762
  this._executingToolCalls.set(agent, executing);
3712
- if (toolExecutionMode === "human-in-the-loop" && agent.messages.some((message) => {
3763
+ if (toolExecutionMode === "restore" && agent.messages.some((message) => {
3713
3764
  var _message$toolCalls;
3714
3765
  return message.role === "assistant" && ((_message$toolCalls = message.toolCalls) === null || _message$toolCalls === void 0 ? void 0 : _message$toolCalls.some((call) => executing.has(JSON.stringify([threadId, call.id])) && !agent.messages.some((result) => result.role === "tool" && result.toolCallId === call.id && !this.isFrontendPlaceholderResult(result))));
3715
3766
  })) return runAgentResult;
3716
- const messagesToProcess = toolExecutionMode === "human-in-the-loop" ? [...agent.messages] : newMessages;
3767
+ const messagesToProcess = toolExecutionMode === "restore" ? [...agent.messages] : newMessages;
3717
3768
  for (const message of messagesToProcess) if (message.role === "assistant") for (const toolCall of message.toolCalls || []) {
3718
3769
  var _agent$messages$find;
3719
3770
  const tool = this.getTool({
@@ -3730,7 +3781,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3730
3781
  return wildcardTool;
3731
3782
  };
3732
3783
  const executableTool = tool !== null && tool !== void 0 ? tool : getWildcardTool();
3733
- if (toolExecutionMode === "human-in-the-loop" && ((executableTool === null || executableTool === void 0 ? void 0 : executableTool.type) !== "human-in-the-loop" || !executableTool.handler)) continue;
3784
+ if (toolExecutionMode === "restore" && (!(executableTool === null || executableTool === void 0 ? void 0 : executableTool.handler) || executableTool.type !== "human-in-the-loop" && executableTool.reconnectBehavior !== "resume-pending")) continue;
3734
3785
  const executionKey = JSON.stringify([threadId, toolCall.id]);
3735
3786
  if ((signal === null || signal === void 0 ? void 0 : signal.aborted) || agent.threadId !== threadId || executing.has(executionKey)) continue;
3736
3787
  const existingResultIndex = newMessages.findIndex((m) => m.role === "tool" && m.toolCallId === toolCall.id);
@@ -3762,14 +3813,14 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3762
3813
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) abortInteraction();
3763
3814
  }
3764
3815
  const executionSignal = (_interactionControlle = interactionController === null || interactionController === void 0 ? void 0 : interactionController.signal) !== null && _interactionControlle !== void 0 ? _interactionControlle : signal;
3765
- const discardOnAbort = toolExecutionMode === "human-in-the-loop" || !!interactionController;
3816
+ const discardOnAbort = toolExecutionMode === "restore" || !!interactionController;
3766
3817
  try {
3767
3818
  if (tool) {
3768
- if (await this.executeSpecificTool(tool, toolCall, message, agent, agentId, executionSignal, discardOnAbort)) needsFollowUp = true;
3819
+ if (await this.executeSpecificTool(tool, toolCall, message, agent, agentId, executionSignal, discardOnAbort, toolExecutionMode === "restore")) needsFollowUp = true;
3769
3820
  } else {
3770
3821
  const fallbackTool = getWildcardTool();
3771
3822
  if (fallbackTool) {
3772
- if (await this.executeWildcardTool(fallbackTool, toolCall, message, agent, agentId, executionSignal, discardOnAbort)) needsFollowUp = true;
3823
+ if (await this.executeWildcardTool(fallbackTool, toolCall, message, agent, agentId, executionSignal, discardOnAbort, toolExecutionMode === "restore")) needsFollowUp = true;
3773
3824
  }
3774
3825
  }
3775
3826
  } finally {
@@ -3806,7 +3857,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3806
3857
  async executeToolHandler({ tool, toolCall, agent, agentId, handlerArgs, toolType, messageId, signal = (() => {
3807
3858
  var _this$_runAbortContro3;
3808
3859
  return (_this$_runAbortContro3 = this._runAbortController) === null || _this$_runAbortContro3 === void 0 ? void 0 : _this$_runAbortContro3.signal;
3809
- })(), discardOnAbort = false }) {
3860
+ })(), discardOnAbort = false, isReplay = false }) {
3810
3861
  let toolCallResult = "";
3811
3862
  let toolCallText = "";
3812
3863
  let errorMessage;
@@ -3845,7 +3896,8 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3845
3896
  const result = await tool.handler(parsedArgs, {
3846
3897
  toolCall,
3847
3898
  agent,
3848
- signal
3899
+ signal,
3900
+ ...isReplay ? { isReplay } : {}
3849
3901
  });
3850
3902
  ({content: toolCallResult, text: toolCallText} = toToolResultContent(result));
3851
3903
  } catch (error) {
@@ -3886,7 +3938,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3886
3938
  /**
3887
3939
  * Execute a specific tool
3888
3940
  */
3889
- async executeSpecificTool(tool, toolCall, message, agent, agentId, signal, discardOnAbort = false) {
3941
+ async executeSpecificTool(tool, toolCall, message, agent, agentId, signal, discardOnAbort = false, isReplay = false) {
3890
3942
  const threadId = agent.threadId;
3891
3943
  if ((tool === null || tool === void 0 ? void 0 : tool.agentId) && tool.agentId !== agent.agentId) return false;
3892
3944
  let handlerResult = {
@@ -3904,7 +3956,8 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3904
3956
  toolType: "specific",
3905
3957
  messageId: message.id,
3906
3958
  signal,
3907
- discardOnAbort
3959
+ discardOnAbort,
3960
+ isReplay
3908
3961
  });
3909
3962
  {
3910
3963
  var _agent$messages$inser;
@@ -3929,7 +3982,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3929
3982
  * specific tools, so this method keeps its own arg-wrapping logic rather than
3930
3983
  * delegating to `executeToolHandler`.
3931
3984
  */
3932
- async executeWildcardTool(wildcardTool, toolCall, message, agent, agentId, signal, discardOnAbort = false) {
3985
+ async executeWildcardTool(wildcardTool, toolCall, message, agent, agentId, signal, discardOnAbort = false, isReplay = false) {
3933
3986
  const threadId = agent.threadId;
3934
3987
  if ((wildcardTool === null || wildcardTool === void 0 ? void 0 : wildcardTool.agentId) && wildcardTool.agentId !== agent.agentId) return false;
3935
3988
  let toolCallResult = "";
@@ -3973,7 +4026,8 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3973
4026
  const result = await wildcardTool.handler(wildcardArgs, {
3974
4027
  toolCall,
3975
4028
  agent,
3976
- signal
4029
+ signal,
4030
+ ...isReplay ? { isReplay } : {}
3977
4031
  });
3978
4032
  ({content: toolCallResult, text: toolCallText} = toToolResultContent(result));
3979
4033
  } catch (error) {
@@ -4813,9 +4867,12 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4813
4867
  session.retry = void 0;
4814
4868
  this.connect(session);
4815
4869
  }
4816
- }
4870
+ },
4871
+ linkedThreads: /* @__PURE__ */ new Set(),
4872
+ agentWatches: /* @__PURE__ */ new Map()
4817
4873
  };
4818
4874
  this.session = session;
4875
+ session.runWatch = this.core.subscribe({ onAgentRunStarted: ({ agent }) => this.watchAgent(session, agent) });
4819
4876
  if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
4820
4877
  window.addEventListener("offline", session.onOffline);
4821
4878
  window.addEventListener("online", session.onOnline);
@@ -4844,6 +4901,29 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4844
4901
  if (session === null || session === void 0 ? void 0 : session.ready) (_session$collector = session.collector) === null || _session$collector === void 0 || _session$collector.emit(name, value);
4845
4902
  else if (session === null || session === void 0 ? void 0 : session.started) this.addDropped(session, 1, false);
4846
4903
  }
4904
+ watchAgent(session, agent) {
4905
+ if (this.session !== session || session.agentWatches.has(agent)) return;
4906
+ session.agentWatches.set(agent, agent.subscribe({ onRunStartedEvent: ({ event }) => this.linkThread(session, event) }));
4907
+ }
4908
+ /** Sends one `thread.linked` per Thread. A link that cannot be sent waits for the next run. */
4909
+ linkThread(session, { threadId }) {
4910
+ const connection = session.connection;
4911
+ if (!session.ready || !connection || !threadId || session.linkedThreads.has(threadId)) return;
4912
+ session.linkedThreads.add(threadId);
4913
+ const event = {
4914
+ type: "CUSTOM",
4915
+ name: "thread.linked",
4916
+ timestamp: Date.now(),
4917
+ value: { threadId }
4918
+ };
4919
+ try {
4920
+ const { beforeSend } = session.config;
4921
+ const kept = beforeSend === void 0 ? event : beforeSend(event);
4922
+ if (kept !== null) this.send(session, connection, kept);
4923
+ } catch (_unused) {
4924
+ this.report(session, "INVALID_EVENT");
4925
+ }
4926
+ }
4847
4927
  report(session, code) {
4848
4928
  try {
4849
4929
  var _session$config$onErr, _session$config;
@@ -4851,16 +4931,19 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4851
4931
  code,
4852
4932
  message: `Trajectory capture: ${code}.`
4853
4933
  });
4854
- } catch (_unused) {}
4934
+ } catch (_unused2) {}
4855
4935
  }
4856
4936
  current(session, connection) {
4857
4937
  return this.session === session && session.connection === connection;
4858
4938
  }
4859
4939
  end(session, result) {
4860
- var _session$collector2;
4940
+ var _session$runWatch, _session$collector2;
4861
4941
  if (this.session !== session) return;
4862
4942
  this.session = void 0;
4863
4943
  clearTimeout(session.retry);
4944
+ (_session$runWatch = session.runWatch) === null || _session$runWatch === void 0 || _session$runWatch.unsubscribe();
4945
+ for (const watch of session.agentWatches.values()) watch.unsubscribe();
4946
+ session.agentWatches.clear();
4864
4947
  (_session$collector2 = session.collector) === null || _session$collector2 === void 0 || _session$collector2.stop();
4865
4948
  session.ready = false;
4866
4949
  if (session.queue.length > 0) this.report(session, "EVENTS_DROPPED");
@@ -4908,6 +4991,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4908
4991
  });
4909
4992
  session.ready = false;
4910
4993
  this.discardQueue(session);
4994
+ session.linkedThreads.clear();
4911
4995
  for (const pending of connection.pending) pending.finish("PERSISTENCE_UNKNOWN");
4912
4996
  if (!this.current(session, connection)) return;
4913
4997
  this.cleanup(session, connection);
@@ -5023,11 +5107,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
5023
5107
  trajectoryId: session.trajectoryId
5024
5108
  });
5025
5109
  this.onChange();
5026
- } catch (_unused2) {
5110
+ } catch (_unused3) {
5027
5111
  this.fail(session, connection, "CAPTURE_FAILED", false);
5028
5112
  }
5029
5113
  }).receive("error", (joinError) => this.fail(session, connection, errorCode(joinError, "JOIN_FAILED"))).receive("timeout", () => this.fail(session, connection, "CONNECTION_TIMEOUT"));
5030
- } catch (_unused3) {
5114
+ } catch (_unused4) {
5031
5115
  this.fail(session, connection, "CONNECTION_FAILED");
5032
5116
  }
5033
5117
  }
@@ -5157,7 +5241,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
5157
5241
  ].includes(reason);
5158
5242
  pending.finish(rollback ? reason.toUpperCase() : "PERSISTENCE_UNKNOWN", rollback);
5159
5243
  }).receive("timeout", () => pending.finish("PERSISTENCE_UNKNOWN"));
5160
- } catch (_unused4) {
5244
+ } catch (_unused5) {
5161
5245
  pending.finish("PERSISTENCE_UNKNOWN");
5162
5246
  }
5163
5247
  }