@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.cjs +101 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +43 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +101 -18
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +107 -23
- package/dist/index.umd.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -196,10 +196,17 @@ function ɵcreatePhoenixJoinOutcome$(channel) {
|
|
|
196
196
|
*
|
|
197
197
|
* The socket is constructed and connected on subscription, and disconnected on
|
|
198
198
|
* teardown. Each subscription creates an isolated socket instance.
|
|
199
|
+
*
|
|
200
|
+
* Heartbeats default to every 15s: Phoenix's own 30s default matches common
|
|
201
|
+
* reverse-proxy WebSocket idle timeouts (e.g. Azure Application Gateway), which
|
|
202
|
+
* would drop the socket during a quiet run. Callers may override it.
|
|
199
203
|
*/
|
|
200
204
|
function ɵphoenixSocket$(options) {
|
|
201
205
|
return (0, rxjs.defer)(() => {
|
|
202
|
-
const socket = new phoenix.Socket(options.url,
|
|
206
|
+
const socket = new phoenix.Socket(options.url, {
|
|
207
|
+
heartbeatIntervalMs: 15e3,
|
|
208
|
+
...options.options
|
|
209
|
+
});
|
|
203
210
|
const signals$ = ɵcreatePhoenixSocketSignals$(socket).pipe((0, rxjs_operators.shareReplay)({
|
|
204
211
|
bufferSize: 1,
|
|
205
212
|
refCount: true
|
|
@@ -283,6 +290,11 @@ const STOP_RUN_EVENT = "stop_run";
|
|
|
283
290
|
const CONNECT_STREAM_IDLE_REPLAY_FALLBACK_MS = 100;
|
|
284
291
|
const CONNECT_IDLE_WARNING_MS = 3e4;
|
|
285
292
|
const MAX_UNOPENED_CREDENTIAL_REFRESHES = 2;
|
|
293
|
+
function isMcpResourceRead(input) {
|
|
294
|
+
const forwardedProps = input.forwardedProps;
|
|
295
|
+
const request = forwardedProps?.__proxiedMCPRequest;
|
|
296
|
+
return forwardedProps?.__copilotkitMcpResourceReadOnly === true && typeof request === "object" && request !== null && request.method === "resources/read";
|
|
297
|
+
}
|
|
286
298
|
var AgentThreadLockedError = class extends Error {
|
|
287
299
|
constructor(threadId) {
|
|
288
300
|
super(threadId ? `Thread ${threadId} is locked` : "Thread is locked");
|
|
@@ -390,10 +402,22 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
|
|
|
390
402
|
* server-pushed AG-UI events to the Observable subscriber.
|
|
391
403
|
*/
|
|
392
404
|
run(input) {
|
|
393
|
-
|
|
394
|
-
|
|
405
|
+
if (!isMcpResourceRead(input)) {
|
|
406
|
+
this.threadId = input.threadId;
|
|
407
|
+
this.canonicalRunId = input.runId;
|
|
408
|
+
}
|
|
395
409
|
return (0, rxjs.defer)(() => this.requestJoinCredentials$("run", input)).pipe((0, rxjs.switchMap)((credentials) => {
|
|
396
410
|
if (credentials === null) return (0, rxjs.throwError)(() => /* @__PURE__ */ new Error("REST run request returned no credentials"));
|
|
411
|
+
if ("kind" in credentials) return (0, rxjs.of)({
|
|
412
|
+
type: _ag_ui_client.EventType.RUN_STARTED,
|
|
413
|
+
threadId: credentials.threadId,
|
|
414
|
+
runId: credentials.runId
|
|
415
|
+
}, {
|
|
416
|
+
type: _ag_ui_client.EventType.RUN_FINISHED,
|
|
417
|
+
threadId: credentials.threadId,
|
|
418
|
+
runId: credentials.runId,
|
|
419
|
+
result: credentials.result
|
|
420
|
+
});
|
|
397
421
|
const canonicalInput = this.applyCanonicalRunIdentity(input, credentials, { fallbackToInputRunId: true });
|
|
398
422
|
return this.observeThread$(canonicalInput, credentials, {
|
|
399
423
|
completeOnRunError: false,
|
|
@@ -485,7 +509,18 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
|
|
|
485
509
|
const text = await response.text().catch(() => "");
|
|
486
510
|
throw new Error(text || response.statusText || String(response.status));
|
|
487
511
|
}
|
|
488
|
-
|
|
512
|
+
const payload = await response.json();
|
|
513
|
+
if (mode === "run" && isMcpResourceRead(input) && typeof payload === "object" && payload !== null && payload.kind === "mcp-resource-read") {
|
|
514
|
+
const resource = payload;
|
|
515
|
+
if (typeof resource.threadId !== "string" || !resource.threadId || typeof resource.runId !== "string" || !resource.runId) throw new Error("invalid MCP resource response identity");
|
|
516
|
+
return {
|
|
517
|
+
kind: "mcp-resource-read",
|
|
518
|
+
threadId: resource.threadId,
|
|
519
|
+
runId: resource.runId,
|
|
520
|
+
result: resource.result
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
return this.normalizeJoinCredentials(payload, input);
|
|
489
524
|
} catch (error) {
|
|
490
525
|
if (error instanceof AgentThreadLockedError) throw error;
|
|
491
526
|
throw new Error(`REST ${mode} request failed: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
@@ -1268,6 +1303,21 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends _ag_ui
|
|
|
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
|
+
const request = parameters.forwardedProps?.__proxiedMCPRequest;
|
|
1310
|
+
if (typeof request !== "object" || request === null || request.method !== "resources/read") throw new Error("Only MCP resources/read can use the read-only path");
|
|
1311
|
+
await this.ensureRuntimeConfiguration();
|
|
1312
|
+
const resourceParameters = {
|
|
1313
|
+
...parameters,
|
|
1314
|
+
forwardedProps: {
|
|
1315
|
+
...parameters.forwardedProps,
|
|
1316
|
+
__copilotkitMcpResourceReadOnly: true
|
|
1317
|
+
}
|
|
1318
|
+
};
|
|
1319
|
+
return this.runtimeMode === _copilotkit_shared.RUNTIME_MODE_INTELLIGENCE ? this.clone().runAgent(resourceParameters) : this.runAgent(resourceParameters);
|
|
1320
|
+
}
|
|
1271
1321
|
/**
|
|
1272
1322
|
* Drop the delegate's cached `lastSeenEventId` for this thread so
|
|
1273
1323
|
* the next connect requests a full historical replay from the
|
|
@@ -3489,7 +3539,7 @@ var RunHandler = class {
|
|
|
3489
3539
|
this.processAgentResult({
|
|
3490
3540
|
runAgentResult,
|
|
3491
3541
|
agent,
|
|
3492
|
-
toolExecutionMode: "
|
|
3542
|
+
toolExecutionMode: "restore",
|
|
3493
3543
|
signal: controller.signal
|
|
3494
3544
|
}).catch(async (error) => {
|
|
3495
3545
|
await this._internal.emitError({
|
|
@@ -3620,8 +3670,8 @@ var RunHandler = class {
|
|
|
3620
3670
|
const threadId = agent.threadId;
|
|
3621
3671
|
const executing = this._executingToolCalls.get(agent) ?? /* @__PURE__ */ new Set();
|
|
3622
3672
|
this._executingToolCalls.set(agent, executing);
|
|
3623
|
-
if (toolExecutionMode === "
|
|
3624
|
-
const messagesToProcess = toolExecutionMode === "
|
|
3673
|
+
if (toolExecutionMode === "restore" && agent.messages.some((message) => message.role === "assistant" && 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))))) return runAgentResult;
|
|
3674
|
+
const messagesToProcess = toolExecutionMode === "restore" ? [...agent.messages] : newMessages;
|
|
3625
3675
|
for (const message of messagesToProcess) if (message.role === "assistant") for (const toolCall of message.toolCalls || []) {
|
|
3626
3676
|
const tool = this.getTool({
|
|
3627
3677
|
toolName: toolCall.function.name,
|
|
@@ -3637,7 +3687,7 @@ var RunHandler = class {
|
|
|
3637
3687
|
return wildcardTool;
|
|
3638
3688
|
};
|
|
3639
3689
|
const executableTool = tool ?? getWildcardTool();
|
|
3640
|
-
if (toolExecutionMode === "
|
|
3690
|
+
if (toolExecutionMode === "restore" && (!executableTool?.handler || executableTool.type !== "human-in-the-loop" && executableTool.reconnectBehavior !== "resume-pending")) continue;
|
|
3641
3691
|
const executionKey = JSON.stringify([threadId, toolCall.id]);
|
|
3642
3692
|
if (signal?.aborted || agent.threadId !== threadId || executing.has(executionKey)) continue;
|
|
3643
3693
|
const existingResultIndex = newMessages.findIndex((m) => m.role === "tool" && m.toolCallId === toolCall.id);
|
|
@@ -3668,14 +3718,14 @@ var RunHandler = class {
|
|
|
3668
3718
|
if (signal?.aborted) abortInteraction();
|
|
3669
3719
|
}
|
|
3670
3720
|
const executionSignal = interactionController?.signal ?? signal;
|
|
3671
|
-
const discardOnAbort = toolExecutionMode === "
|
|
3721
|
+
const discardOnAbort = toolExecutionMode === "restore" || !!interactionController;
|
|
3672
3722
|
try {
|
|
3673
3723
|
if (tool) {
|
|
3674
|
-
if (await this.executeSpecificTool(tool, toolCall, message, agent, agentId, executionSignal, discardOnAbort)) needsFollowUp = true;
|
|
3724
|
+
if (await this.executeSpecificTool(tool, toolCall, message, agent, agentId, executionSignal, discardOnAbort, toolExecutionMode === "restore")) needsFollowUp = true;
|
|
3675
3725
|
} else {
|
|
3676
3726
|
const fallbackTool = getWildcardTool();
|
|
3677
3727
|
if (fallbackTool) {
|
|
3678
|
-
if (await this.executeWildcardTool(fallbackTool, toolCall, message, agent, agentId, executionSignal, discardOnAbort)) needsFollowUp = true;
|
|
3728
|
+
if (await this.executeWildcardTool(fallbackTool, toolCall, message, agent, agentId, executionSignal, discardOnAbort, toolExecutionMode === "restore")) needsFollowUp = true;
|
|
3679
3729
|
}
|
|
3680
3730
|
}
|
|
3681
3731
|
} finally {
|
|
@@ -3709,7 +3759,7 @@ var RunHandler = class {
|
|
|
3709
3759
|
* Handles arg parsing, subscriber notifications, handler invocation, result stringification,
|
|
3710
3760
|
* and error handling.
|
|
3711
3761
|
*/
|
|
3712
|
-
async executeToolHandler({ tool, toolCall, agent, agentId, handlerArgs, toolType, messageId, signal = this._runAbortController?.signal, discardOnAbort = false }) {
|
|
3762
|
+
async executeToolHandler({ tool, toolCall, agent, agentId, handlerArgs, toolType, messageId, signal = this._runAbortController?.signal, discardOnAbort = false, isReplay = false }) {
|
|
3713
3763
|
let toolCallResult = "";
|
|
3714
3764
|
let toolCallText = "";
|
|
3715
3765
|
let errorMessage;
|
|
@@ -3745,7 +3795,8 @@ var RunHandler = class {
|
|
|
3745
3795
|
const result = await tool.handler(parsedArgs, {
|
|
3746
3796
|
toolCall,
|
|
3747
3797
|
agent,
|
|
3748
|
-
signal
|
|
3798
|
+
signal,
|
|
3799
|
+
...isReplay ? { isReplay } : {}
|
|
3749
3800
|
});
|
|
3750
3801
|
({content: toolCallResult, text: toolCallText} = toToolResultContent(result));
|
|
3751
3802
|
} catch (error) {
|
|
@@ -3783,7 +3834,7 @@ var RunHandler = class {
|
|
|
3783
3834
|
/**
|
|
3784
3835
|
* Execute a specific tool
|
|
3785
3836
|
*/
|
|
3786
|
-
async executeSpecificTool(tool, toolCall, message, agent, agentId, signal, discardOnAbort = false) {
|
|
3837
|
+
async executeSpecificTool(tool, toolCall, message, agent, agentId, signal, discardOnAbort = false, isReplay = false) {
|
|
3787
3838
|
const threadId = agent.threadId;
|
|
3788
3839
|
if (tool?.agentId && tool.agentId !== agent.agentId) return false;
|
|
3789
3840
|
let handlerResult = {
|
|
@@ -3801,7 +3852,8 @@ var RunHandler = class {
|
|
|
3801
3852
|
toolType: "specific",
|
|
3802
3853
|
messageId: message.id,
|
|
3803
3854
|
signal,
|
|
3804
|
-
discardOnAbort
|
|
3855
|
+
discardOnAbort,
|
|
3856
|
+
isReplay
|
|
3805
3857
|
});
|
|
3806
3858
|
{
|
|
3807
3859
|
const messageIndex = agent.messages.findIndex((m) => m.id === message.id);
|
|
@@ -3825,7 +3877,7 @@ var RunHandler = class {
|
|
|
3825
3877
|
* specific tools, so this method keeps its own arg-wrapping logic rather than
|
|
3826
3878
|
* delegating to `executeToolHandler`.
|
|
3827
3879
|
*/
|
|
3828
|
-
async executeWildcardTool(wildcardTool, toolCall, message, agent, agentId, signal, discardOnAbort = false) {
|
|
3880
|
+
async executeWildcardTool(wildcardTool, toolCall, message, agent, agentId, signal, discardOnAbort = false, isReplay = false) {
|
|
3829
3881
|
const threadId = agent.threadId;
|
|
3830
3882
|
if (wildcardTool?.agentId && wildcardTool.agentId !== agent.agentId) return false;
|
|
3831
3883
|
let toolCallResult = "";
|
|
@@ -3866,7 +3918,8 @@ var RunHandler = class {
|
|
|
3866
3918
|
const result = await wildcardTool.handler(wildcardArgs, {
|
|
3867
3919
|
toolCall,
|
|
3868
3920
|
agent,
|
|
3869
|
-
signal
|
|
3921
|
+
signal,
|
|
3922
|
+
...isReplay ? { isReplay } : {}
|
|
3870
3923
|
});
|
|
3871
3924
|
({content: toolCallResult, text: toolCallText} = toToolResultContent(result));
|
|
3872
3925
|
} catch (error) {
|
|
@@ -4649,9 +4702,12 @@ var TrajectoryConnection = class {
|
|
|
4649
4702
|
session.retry = void 0;
|
|
4650
4703
|
this.connect(session);
|
|
4651
4704
|
}
|
|
4652
|
-
}
|
|
4705
|
+
},
|
|
4706
|
+
linkedThreads: /* @__PURE__ */ new Set(),
|
|
4707
|
+
agentWatches: /* @__PURE__ */ new Map()
|
|
4653
4708
|
};
|
|
4654
4709
|
this.session = session;
|
|
4710
|
+
session.runWatch = this.core.subscribe({ onAgentRunStarted: ({ agent }) => this.watchAgent(session, agent) });
|
|
4655
4711
|
if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
|
|
4656
4712
|
window.addEventListener("offline", session.onOffline);
|
|
4657
4713
|
window.addEventListener("online", session.onOnline);
|
|
@@ -4679,6 +4735,29 @@ var TrajectoryConnection = class {
|
|
|
4679
4735
|
if (session?.ready) session.collector?.emit(name, value);
|
|
4680
4736
|
else if (session?.started) this.addDropped(session, 1, false);
|
|
4681
4737
|
}
|
|
4738
|
+
watchAgent(session, agent) {
|
|
4739
|
+
if (this.session !== session || session.agentWatches.has(agent)) return;
|
|
4740
|
+
session.agentWatches.set(agent, agent.subscribe({ onRunStartedEvent: ({ event }) => this.linkThread(session, event) }));
|
|
4741
|
+
}
|
|
4742
|
+
/** Sends one `thread.linked` per Thread. A link that cannot be sent waits for the next run. */
|
|
4743
|
+
linkThread(session, { threadId }) {
|
|
4744
|
+
const connection = session.connection;
|
|
4745
|
+
if (!session.ready || !connection || !threadId || session.linkedThreads.has(threadId)) return;
|
|
4746
|
+
session.linkedThreads.add(threadId);
|
|
4747
|
+
const event = {
|
|
4748
|
+
type: "CUSTOM",
|
|
4749
|
+
name: "thread.linked",
|
|
4750
|
+
timestamp: Date.now(),
|
|
4751
|
+
value: { threadId }
|
|
4752
|
+
};
|
|
4753
|
+
try {
|
|
4754
|
+
const { beforeSend } = session.config;
|
|
4755
|
+
const kept = beforeSend === void 0 ? event : beforeSend(event);
|
|
4756
|
+
if (kept !== null) this.send(session, connection, kept);
|
|
4757
|
+
} catch {
|
|
4758
|
+
this.report(session, "INVALID_EVENT");
|
|
4759
|
+
}
|
|
4760
|
+
}
|
|
4682
4761
|
report(session, code) {
|
|
4683
4762
|
try {
|
|
4684
4763
|
session.config.onError?.({
|
|
@@ -4694,6 +4773,9 @@ var TrajectoryConnection = class {
|
|
|
4694
4773
|
if (this.session !== session) return;
|
|
4695
4774
|
this.session = void 0;
|
|
4696
4775
|
clearTimeout(session.retry);
|
|
4776
|
+
session.runWatch?.unsubscribe();
|
|
4777
|
+
for (const watch of session.agentWatches.values()) watch.unsubscribe();
|
|
4778
|
+
session.agentWatches.clear();
|
|
4697
4779
|
session.collector?.stop();
|
|
4698
4780
|
session.ready = false;
|
|
4699
4781
|
if (session.queue.length > 0) this.report(session, "EVENTS_DROPPED");
|
|
@@ -4740,6 +4822,7 @@ var TrajectoryConnection = class {
|
|
|
4740
4822
|
});
|
|
4741
4823
|
session.ready = false;
|
|
4742
4824
|
this.discardQueue(session);
|
|
4825
|
+
session.linkedThreads.clear();
|
|
4743
4826
|
for (const pending of connection.pending) pending.finish("PERSISTENCE_UNKNOWN");
|
|
4744
4827
|
if (!this.current(session, connection)) return;
|
|
4745
4828
|
this.cleanup(session, connection);
|