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