@copilotkit/core 1.76.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 +981 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +135 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +135 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +981 -31
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1013 -35
- package/dist/index.umd.js.map +1 -1
- package/package.json +5 -4
package/dist/index.cjs
CHANGED
|
@@ -6,6 +6,7 @@ let rxjs = require("rxjs");
|
|
|
6
6
|
let rxjs_operators = require("rxjs/operators");
|
|
7
7
|
let phoenix = require("phoenix");
|
|
8
8
|
let zod_to_json_schema = require("zod-to-json-schema");
|
|
9
|
+
let _copilotkit_learning = require("@copilotkit/learning");
|
|
9
10
|
|
|
10
11
|
//#region src/utils/http-agent.ts
|
|
11
12
|
/**
|
|
@@ -195,10 +196,17 @@ function ɵcreatePhoenixJoinOutcome$(channel) {
|
|
|
195
196
|
*
|
|
196
197
|
* The socket is constructed and connected on subscription, and disconnected on
|
|
197
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.
|
|
198
203
|
*/
|
|
199
204
|
function ɵphoenixSocket$(options) {
|
|
200
205
|
return (0, rxjs.defer)(() => {
|
|
201
|
-
const socket = new phoenix.Socket(options.url,
|
|
206
|
+
const socket = new phoenix.Socket(options.url, {
|
|
207
|
+
heartbeatIntervalMs: 15e3,
|
|
208
|
+
...options.options
|
|
209
|
+
});
|
|
202
210
|
const signals$ = ɵcreatePhoenixSocketSignals$(socket).pipe((0, rxjs_operators.shareReplay)({
|
|
203
211
|
bufferSize: 1,
|
|
204
212
|
refCount: true
|
|
@@ -282,6 +290,11 @@ const STOP_RUN_EVENT = "stop_run";
|
|
|
282
290
|
const CONNECT_STREAM_IDLE_REPLAY_FALLBACK_MS = 100;
|
|
283
291
|
const CONNECT_IDLE_WARNING_MS = 3e4;
|
|
284
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
|
+
}
|
|
285
298
|
var AgentThreadLockedError = class extends Error {
|
|
286
299
|
constructor(threadId) {
|
|
287
300
|
super(threadId ? `Thread ${threadId} is locked` : "Thread is locked");
|
|
@@ -389,10 +402,22 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
|
|
|
389
402
|
* server-pushed AG-UI events to the Observable subscriber.
|
|
390
403
|
*/
|
|
391
404
|
run(input) {
|
|
392
|
-
|
|
393
|
-
|
|
405
|
+
if (!isMcpResourceRead(input)) {
|
|
406
|
+
this.threadId = input.threadId;
|
|
407
|
+
this.canonicalRunId = input.runId;
|
|
408
|
+
}
|
|
394
409
|
return (0, rxjs.defer)(() => this.requestJoinCredentials$("run", input)).pipe((0, rxjs.switchMap)((credentials) => {
|
|
395
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
|
+
});
|
|
396
421
|
const canonicalInput = this.applyCanonicalRunIdentity(input, credentials, { fallbackToInputRunId: true });
|
|
397
422
|
return this.observeThread$(canonicalInput, credentials, {
|
|
398
423
|
completeOnRunError: false,
|
|
@@ -484,7 +509,18 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
|
|
|
484
509
|
const text = await response.text().catch(() => "");
|
|
485
510
|
throw new Error(text || response.statusText || String(response.status));
|
|
486
511
|
}
|
|
487
|
-
|
|
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);
|
|
488
524
|
} catch (error) {
|
|
489
525
|
if (error instanceof AgentThreadLockedError) throw error;
|
|
490
526
|
throw new Error(`REST ${mode} request failed: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
@@ -1267,6 +1303,21 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends _ag_ui
|
|
|
1267
1303
|
}
|
|
1268
1304
|
return cloned;
|
|
1269
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
|
+
}
|
|
1270
1321
|
/**
|
|
1271
1322
|
* Drop the delegate's cached `lastSeenEventId` for this thread so
|
|
1272
1323
|
* the next connect requests a full historical replay from the
|
|
@@ -1907,6 +1958,10 @@ var AgentRegistry = class {
|
|
|
1907
1958
|
this.applyMessageFilterToAgent(agent);
|
|
1908
1959
|
});
|
|
1909
1960
|
}
|
|
1961
|
+
/** Preserve the exact configured URL when posting to a single endpoint. */
|
|
1962
|
+
get runtimeEndpointUrl() {
|
|
1963
|
+
return this._runtimeEndpointUrl;
|
|
1964
|
+
}
|
|
1910
1965
|
createRuntimeFetch() {
|
|
1911
1966
|
if (!this.runtimeFetch) this.runtimeFetch = (async (input, init) => {
|
|
1912
1967
|
const meta = () => runtimeRequestMeta(init);
|
|
@@ -2931,6 +2986,68 @@ function normalizeToolResultContent(content) {
|
|
|
2931
2986
|
function isForwardedToClientPlaceholder(content) {
|
|
2932
2987
|
return normalizeToolResultContent(content) === FORWARDED_TO_CLIENT;
|
|
2933
2988
|
}
|
|
2989
|
+
const MEDIA_PART_TYPES = new Set([
|
|
2990
|
+
"image",
|
|
2991
|
+
"audio",
|
|
2992
|
+
"video",
|
|
2993
|
+
"document"
|
|
2994
|
+
]);
|
|
2995
|
+
function isOptionalString(value) {
|
|
2996
|
+
return value === void 0 || typeof value === "string";
|
|
2997
|
+
}
|
|
2998
|
+
function isPartSource(value) {
|
|
2999
|
+
if (!value || typeof value !== "object") return false;
|
|
3000
|
+
const source = value;
|
|
3001
|
+
if (typeof source.value !== "string") return false;
|
|
3002
|
+
switch (source.type) {
|
|
3003
|
+
case "data": return typeof source.mimeType === "string";
|
|
3004
|
+
case "url": return isOptionalString(source.mimeType);
|
|
3005
|
+
case "file": return isOptionalString(source.provider) && isOptionalString(source.mimeType);
|
|
3006
|
+
default: return false;
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
/**
|
|
3010
|
+
* Mirrors `ContentPartSchema` from `@ag-ui/core/schemas`. The runtime parses
|
|
3011
|
+
* the request with that schema, and one invalid part there fails the whole run.
|
|
3012
|
+
* Core does not import the schema because the UMD build has no global for it.
|
|
3013
|
+
* A test checks this function against the schema.
|
|
3014
|
+
*/
|
|
3015
|
+
function isContentPart(value) {
|
|
3016
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
3017
|
+
const part = value;
|
|
3018
|
+
if (!isOptionalString(part.id) || part.metadata === null) return false;
|
|
3019
|
+
if (part.type === "text") return typeof part.text === "string";
|
|
3020
|
+
return typeof part.type === "string" && MEDIA_PART_TYPES.has(part.type) && isPartSource(part.source);
|
|
3021
|
+
}
|
|
3022
|
+
/**
|
|
3023
|
+
* Converts what a frontend tool handler returned into tool message content.
|
|
3024
|
+
* A non-empty array of AG-UI content parts passes through unchanged, so an
|
|
3025
|
+
* image reaches the model as media. Everything else becomes a string, as before.
|
|
3026
|
+
*
|
|
3027
|
+
* `text` is the string form, for surfaces typed as `result: string`
|
|
3028
|
+
* (`runTool`, `onToolExecutionEnd`). For content parts it is the JSON text they
|
|
3029
|
+
* received before. It is computed here, so a part that cannot be serialized
|
|
3030
|
+
* throws inside the caller's handler error boundary.
|
|
3031
|
+
*/
|
|
3032
|
+
function toToolResultContent(result) {
|
|
3033
|
+
if (result === void 0 || result === null) return {
|
|
3034
|
+
content: "",
|
|
3035
|
+
text: ""
|
|
3036
|
+
};
|
|
3037
|
+
if (typeof result === "string") return {
|
|
3038
|
+
content: result,
|
|
3039
|
+
text: result
|
|
3040
|
+
};
|
|
3041
|
+
const text = JSON.stringify(result);
|
|
3042
|
+
if (Array.isArray(result) && result.length > 0 && result.every(isContentPart)) return {
|
|
3043
|
+
content: result,
|
|
3044
|
+
text
|
|
3045
|
+
};
|
|
3046
|
+
return {
|
|
3047
|
+
content: text,
|
|
3048
|
+
text
|
|
3049
|
+
};
|
|
3050
|
+
}
|
|
2934
3051
|
|
|
2935
3052
|
//#endregion
|
|
2936
3053
|
//#region src/core/tool-schema.ts
|
|
@@ -3422,7 +3539,7 @@ var RunHandler = class {
|
|
|
3422
3539
|
this.processAgentResult({
|
|
3423
3540
|
runAgentResult,
|
|
3424
3541
|
agent,
|
|
3425
|
-
toolExecutionMode: "
|
|
3542
|
+
toolExecutionMode: "restore",
|
|
3426
3543
|
signal: controller.signal
|
|
3427
3544
|
}).catch(async (error) => {
|
|
3428
3545
|
await this._internal.emitError({
|
|
@@ -3553,8 +3670,8 @@ var RunHandler = class {
|
|
|
3553
3670
|
const threadId = agent.threadId;
|
|
3554
3671
|
const executing = this._executingToolCalls.get(agent) ?? /* @__PURE__ */ new Set();
|
|
3555
3672
|
this._executingToolCalls.set(agent, executing);
|
|
3556
|
-
if (toolExecutionMode === "
|
|
3557
|
-
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;
|
|
3558
3675
|
for (const message of messagesToProcess) if (message.role === "assistant") for (const toolCall of message.toolCalls || []) {
|
|
3559
3676
|
const tool = this.getTool({
|
|
3560
3677
|
toolName: toolCall.function.name,
|
|
@@ -3570,7 +3687,7 @@ var RunHandler = class {
|
|
|
3570
3687
|
return wildcardTool;
|
|
3571
3688
|
};
|
|
3572
3689
|
const executableTool = tool ?? getWildcardTool();
|
|
3573
|
-
if (toolExecutionMode === "
|
|
3690
|
+
if (toolExecutionMode === "restore" && (!executableTool?.handler || executableTool.type !== "human-in-the-loop" && executableTool.reconnectBehavior !== "resume-pending")) continue;
|
|
3574
3691
|
const executionKey = JSON.stringify([threadId, toolCall.id]);
|
|
3575
3692
|
if (signal?.aborted || agent.threadId !== threadId || executing.has(executionKey)) continue;
|
|
3576
3693
|
const existingResultIndex = newMessages.findIndex((m) => m.role === "tool" && m.toolCallId === toolCall.id);
|
|
@@ -3601,14 +3718,14 @@ var RunHandler = class {
|
|
|
3601
3718
|
if (signal?.aborted) abortInteraction();
|
|
3602
3719
|
}
|
|
3603
3720
|
const executionSignal = interactionController?.signal ?? signal;
|
|
3604
|
-
const discardOnAbort = toolExecutionMode === "
|
|
3721
|
+
const discardOnAbort = toolExecutionMode === "restore" || !!interactionController;
|
|
3605
3722
|
try {
|
|
3606
3723
|
if (tool) {
|
|
3607
|
-
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;
|
|
3608
3725
|
} else {
|
|
3609
3726
|
const fallbackTool = getWildcardTool();
|
|
3610
3727
|
if (fallbackTool) {
|
|
3611
|
-
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;
|
|
3612
3729
|
}
|
|
3613
3730
|
}
|
|
3614
3731
|
} finally {
|
|
@@ -3642,8 +3759,9 @@ var RunHandler = class {
|
|
|
3642
3759
|
* Handles arg parsing, subscriber notifications, handler invocation, result stringification,
|
|
3643
3760
|
* and error handling.
|
|
3644
3761
|
*/
|
|
3645
|
-
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 }) {
|
|
3646
3763
|
let toolCallResult = "";
|
|
3764
|
+
let toolCallText = "";
|
|
3647
3765
|
let errorMessage;
|
|
3648
3766
|
let isArgumentError = false;
|
|
3649
3767
|
let parsedArgs;
|
|
@@ -3677,11 +3795,10 @@ var RunHandler = class {
|
|
|
3677
3795
|
const result = await tool.handler(parsedArgs, {
|
|
3678
3796
|
toolCall,
|
|
3679
3797
|
agent,
|
|
3680
|
-
signal
|
|
3798
|
+
signal,
|
|
3799
|
+
...isReplay ? { isReplay } : {}
|
|
3681
3800
|
});
|
|
3682
|
-
|
|
3683
|
-
else if (typeof result === "string") toolCallResult = result;
|
|
3684
|
-
else toolCallResult = JSON.stringify(result);
|
|
3801
|
+
({content: toolCallResult, text: toolCallText} = toToolResultContent(result));
|
|
3685
3802
|
} catch (error) {
|
|
3686
3803
|
const handlerError = error instanceof Error ? error : new Error(String(error));
|
|
3687
3804
|
errorMessage = handlerError.message;
|
|
@@ -3698,17 +3815,18 @@ var RunHandler = class {
|
|
|
3698
3815
|
}
|
|
3699
3816
|
});
|
|
3700
3817
|
}
|
|
3701
|
-
if (errorMessage) toolCallResult = `Error: ${errorMessage}`;
|
|
3818
|
+
if (errorMessage) toolCallResult = toolCallText = `Error: ${errorMessage}`;
|
|
3702
3819
|
await this._internal.notifySubscribers((subscriber) => subscriber.onToolExecutionEnd?.({
|
|
3703
3820
|
copilotkit: this.core,
|
|
3704
3821
|
toolCallId: toolCall.id,
|
|
3705
3822
|
agentId,
|
|
3706
3823
|
toolName: toolCall.function.name,
|
|
3707
|
-
result: errorMessage ? "" :
|
|
3824
|
+
result: errorMessage ? "" : toolCallText,
|
|
3708
3825
|
error: errorMessage
|
|
3709
3826
|
}), "Subscriber onToolExecutionEnd error:");
|
|
3710
3827
|
return {
|
|
3711
|
-
|
|
3828
|
+
content: toolCallResult,
|
|
3829
|
+
result: toolCallText,
|
|
3712
3830
|
error: errorMessage,
|
|
3713
3831
|
isArgumentError
|
|
3714
3832
|
};
|
|
@@ -3716,10 +3834,11 @@ var RunHandler = class {
|
|
|
3716
3834
|
/**
|
|
3717
3835
|
* Execute a specific tool
|
|
3718
3836
|
*/
|
|
3719
|
-
async executeSpecificTool(tool, toolCall, message, agent, agentId, signal, discardOnAbort = false) {
|
|
3837
|
+
async executeSpecificTool(tool, toolCall, message, agent, agentId, signal, discardOnAbort = false, isReplay = false) {
|
|
3720
3838
|
const threadId = agent.threadId;
|
|
3721
3839
|
if (tool?.agentId && tool.agentId !== agent.agentId) return false;
|
|
3722
3840
|
let handlerResult = {
|
|
3841
|
+
content: "",
|
|
3723
3842
|
result: "",
|
|
3724
3843
|
error: void 0,
|
|
3725
3844
|
isArgumentError: false
|
|
@@ -3733,7 +3852,8 @@ var RunHandler = class {
|
|
|
3733
3852
|
toolType: "specific",
|
|
3734
3853
|
messageId: message.id,
|
|
3735
3854
|
signal,
|
|
3736
|
-
discardOnAbort
|
|
3855
|
+
discardOnAbort,
|
|
3856
|
+
isReplay
|
|
3737
3857
|
});
|
|
3738
3858
|
{
|
|
3739
3859
|
const messageIndex = agent.messages.findIndex((m) => m.id === message.id);
|
|
@@ -3744,7 +3864,7 @@ var RunHandler = class {
|
|
|
3744
3864
|
id: (0, _copilotkit_shared.randomUUID)(),
|
|
3745
3865
|
role: "tool",
|
|
3746
3866
|
toolCallId: toolCall.id,
|
|
3747
|
-
content: handlerResult.
|
|
3867
|
+
content: handlerResult.content
|
|
3748
3868
|
};
|
|
3749
3869
|
agent.messages.splice(insertAt, 0, toolMessage);
|
|
3750
3870
|
if (!handlerResult.error && tool?.followUp !== false) return true;
|
|
@@ -3757,10 +3877,11 @@ var RunHandler = class {
|
|
|
3757
3877
|
* specific tools, so this method keeps its own arg-wrapping logic rather than
|
|
3758
3878
|
* delegating to `executeToolHandler`.
|
|
3759
3879
|
*/
|
|
3760
|
-
async executeWildcardTool(wildcardTool, toolCall, message, agent, agentId, signal, discardOnAbort = false) {
|
|
3880
|
+
async executeWildcardTool(wildcardTool, toolCall, message, agent, agentId, signal, discardOnAbort = false, isReplay = false) {
|
|
3761
3881
|
const threadId = agent.threadId;
|
|
3762
3882
|
if (wildcardTool?.agentId && wildcardTool.agentId !== agent.agentId) return false;
|
|
3763
3883
|
let toolCallResult = "";
|
|
3884
|
+
let toolCallText = "";
|
|
3764
3885
|
let errorMessage;
|
|
3765
3886
|
if (wildcardTool?.handler) {
|
|
3766
3887
|
let parsedArgs;
|
|
@@ -3797,11 +3918,10 @@ var RunHandler = class {
|
|
|
3797
3918
|
const result = await wildcardTool.handler(wildcardArgs, {
|
|
3798
3919
|
toolCall,
|
|
3799
3920
|
agent,
|
|
3800
|
-
signal
|
|
3921
|
+
signal,
|
|
3922
|
+
...isReplay ? { isReplay } : {}
|
|
3801
3923
|
});
|
|
3802
|
-
|
|
3803
|
-
else if (typeof result === "string") toolCallResult = result;
|
|
3804
|
-
else toolCallResult = JSON.stringify(result);
|
|
3924
|
+
({content: toolCallResult, text: toolCallText} = toToolResultContent(result));
|
|
3805
3925
|
} catch (error) {
|
|
3806
3926
|
const handlerError = error instanceof Error ? error : new Error(String(error));
|
|
3807
3927
|
errorMessage = handlerError.message;
|
|
@@ -3818,13 +3938,13 @@ var RunHandler = class {
|
|
|
3818
3938
|
}
|
|
3819
3939
|
});
|
|
3820
3940
|
}
|
|
3821
|
-
if (errorMessage) toolCallResult = `Error: ${errorMessage}`;
|
|
3941
|
+
if (errorMessage) toolCallResult = toolCallText = `Error: ${errorMessage}`;
|
|
3822
3942
|
await this._internal.notifySubscribers((subscriber) => subscriber.onToolExecutionEnd?.({
|
|
3823
3943
|
copilotkit: this.core,
|
|
3824
3944
|
toolCallId: toolCall.id,
|
|
3825
3945
|
agentId,
|
|
3826
3946
|
toolName: toolCall.function.name,
|
|
3827
|
-
result: errorMessage ? "" :
|
|
3947
|
+
result: errorMessage ? "" : toolCallText,
|
|
3828
3948
|
error: errorMessage
|
|
3829
3949
|
}), "Subscriber onToolExecutionEnd error:");
|
|
3830
3950
|
}
|
|
@@ -3895,6 +4015,7 @@ var RunHandler = class {
|
|
|
3895
4015
|
};
|
|
3896
4016
|
agent.messages.push(assistantMessage);
|
|
3897
4017
|
let handlerResult = {
|
|
4018
|
+
content: "",
|
|
3898
4019
|
result: "",
|
|
3899
4020
|
error: void 0,
|
|
3900
4021
|
isArgumentError: false
|
|
@@ -3911,7 +4032,7 @@ var RunHandler = class {
|
|
|
3911
4032
|
id: (0, _copilotkit_shared.randomUUID)(),
|
|
3912
4033
|
role: "tool",
|
|
3913
4034
|
toolCallId,
|
|
3914
|
-
content: handlerResult.
|
|
4035
|
+
content: handlerResult.content
|
|
3915
4036
|
};
|
|
3916
4037
|
const assistantIndex = agent.messages.findIndex((m) => m.id === assistantMessage.id);
|
|
3917
4038
|
if (assistantIndex !== -1) agent.messages.splice(assistantIndex + 1, 0, toolResultMessage);
|
|
@@ -4512,6 +4633,750 @@ var ThreadStoreRegistry = class {
|
|
|
4512
4633
|
}
|
|
4513
4634
|
};
|
|
4514
4635
|
|
|
4636
|
+
//#endregion
|
|
4637
|
+
//#region src/core/trajectory-connection.ts
|
|
4638
|
+
const TIMEOUT_MS = 1e4;
|
|
4639
|
+
const BATCH_INTERVAL_MS = 2e3;
|
|
4640
|
+
const MAX_BATCH_EVENTS = 50;
|
|
4641
|
+
const MAX_BATCH_BYTES = 64 * 1024;
|
|
4642
|
+
const MAX_EVENT_BYTES = 16 * 1024;
|
|
4643
|
+
const byteLength = (value) => new TextEncoder().encode(JSON.stringify(value)).byteLength;
|
|
4644
|
+
const reconnectDelay = (0, _copilotkit_shared.phoenixExponentialBackoff)(1e3, 1e4);
|
|
4645
|
+
function isObject(value) {
|
|
4646
|
+
return typeof value === "object" && value !== null;
|
|
4647
|
+
}
|
|
4648
|
+
function isNonNegativeInteger(value) {
|
|
4649
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
4650
|
+
}
|
|
4651
|
+
function isBatchAcknowledgement(value, eventCount) {
|
|
4652
|
+
return isObject(value) && isNonNegativeInteger(value.accepted) && isNonNegativeInteger(value.rejected) && value.accepted + value.rejected === eventCount && (value.highestSeq === null || isNonNegativeInteger(value.highestSeq)) && (value.accepted === 0 || value.highestSeq !== null);
|
|
4653
|
+
}
|
|
4654
|
+
function isGrant(value) {
|
|
4655
|
+
return isObject(value) && typeof value.joinToken === "string" && value.joinToken.length > 0 && isObject(value.realtime) && typeof value.realtime.clientUrl === "string" && value.realtime.clientUrl.length > 0 && typeof value.realtime.topic === "string" && value.realtime.topic.length > 0;
|
|
4656
|
+
}
|
|
4657
|
+
function errorCode(value, fallback) {
|
|
4658
|
+
return isObject(value) && typeof value.code === "string" && /^[A-Z][A-Z0-9_]{0,63}$/.test(value.code) ? value.code : fallback;
|
|
4659
|
+
}
|
|
4660
|
+
/** Owns bounded, connected-only capture batches. Failed batches are never resent. */
|
|
4661
|
+
var TrajectoryConnection = class {
|
|
4662
|
+
session;
|
|
4663
|
+
nextSeq = Math.floor(Date.now()) * 1e3;
|
|
4664
|
+
constructor(core, onChange) {
|
|
4665
|
+
this.core = core;
|
|
4666
|
+
this.onChange = onChange;
|
|
4667
|
+
}
|
|
4668
|
+
get trajectoryId() {
|
|
4669
|
+
return this.session?.ready ? this.session.trajectoryId : null;
|
|
4670
|
+
}
|
|
4671
|
+
start(trajectoryId, config) {
|
|
4672
|
+
if (this.session && (trajectoryId === void 0 || trajectoryId === this.session.trajectoryId)) return this.session.promise;
|
|
4673
|
+
this.stop();
|
|
4674
|
+
const id = trajectoryId ?? crypto.randomUUID();
|
|
4675
|
+
let resolveStart;
|
|
4676
|
+
const promise = new Promise((resolve) => {
|
|
4677
|
+
resolveStart = resolve;
|
|
4678
|
+
});
|
|
4679
|
+
const session = {
|
|
4680
|
+
trajectoryId: id,
|
|
4681
|
+
config,
|
|
4682
|
+
promise,
|
|
4683
|
+
resolve: resolveStart,
|
|
4684
|
+
retries: 0,
|
|
4685
|
+
queue: [],
|
|
4686
|
+
dropped: 0,
|
|
4687
|
+
ready: false,
|
|
4688
|
+
started: false,
|
|
4689
|
+
offline: typeof navigator !== "undefined" && navigator.onLine === false,
|
|
4690
|
+
onOffline: () => {
|
|
4691
|
+
if (this.session !== session) return;
|
|
4692
|
+
session.offline = true;
|
|
4693
|
+
if (session.connection) this.fail(session, session.connection, "CONNECTION_LOST");
|
|
4694
|
+
clearTimeout(session.retry);
|
|
4695
|
+
session.retry = void 0;
|
|
4696
|
+
},
|
|
4697
|
+
onOnline: () => {
|
|
4698
|
+
if (this.session !== session) return;
|
|
4699
|
+
session.offline = false;
|
|
4700
|
+
if (!session.connection) {
|
|
4701
|
+
clearTimeout(session.retry);
|
|
4702
|
+
session.retry = void 0;
|
|
4703
|
+
this.connect(session);
|
|
4704
|
+
}
|
|
4705
|
+
},
|
|
4706
|
+
linkedThreads: /* @__PURE__ */ new Set(),
|
|
4707
|
+
agentWatches: /* @__PURE__ */ new Map()
|
|
4708
|
+
};
|
|
4709
|
+
this.session = session;
|
|
4710
|
+
session.runWatch = this.core.subscribe({ onAgentRunStarted: ({ agent }) => this.watchAgent(session, agent) });
|
|
4711
|
+
if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
|
|
4712
|
+
window.addEventListener("offline", session.onOffline);
|
|
4713
|
+
window.addEventListener("online", session.onOnline);
|
|
4714
|
+
}
|
|
4715
|
+
if (session.offline) {
|
|
4716
|
+
this.report(session, "CONNECTION_LOST");
|
|
4717
|
+
this.end(session, {
|
|
4718
|
+
status: "error",
|
|
4719
|
+
code: "CONNECTION_LOST"
|
|
4720
|
+
});
|
|
4721
|
+
} else this.connect(session);
|
|
4722
|
+
return promise;
|
|
4723
|
+
}
|
|
4724
|
+
stop() {
|
|
4725
|
+
const session = this.session;
|
|
4726
|
+
if (!session) return;
|
|
4727
|
+
if (session.ready && session.connection) this.flush(session, session.connection);
|
|
4728
|
+
this.end(session, {
|
|
4729
|
+
status: "error",
|
|
4730
|
+
code: "CANCELLED"
|
|
4731
|
+
});
|
|
4732
|
+
}
|
|
4733
|
+
emit(name, value) {
|
|
4734
|
+
const session = this.session;
|
|
4735
|
+
if (session?.ready) session.collector?.emit(name, value);
|
|
4736
|
+
else if (session?.started) this.addDropped(session, 1, false);
|
|
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
|
+
}
|
|
4761
|
+
report(session, code) {
|
|
4762
|
+
try {
|
|
4763
|
+
session.config.onError?.({
|
|
4764
|
+
code,
|
|
4765
|
+
message: `Trajectory capture: ${code}.`
|
|
4766
|
+
});
|
|
4767
|
+
} catch {}
|
|
4768
|
+
}
|
|
4769
|
+
current(session, connection) {
|
|
4770
|
+
return this.session === session && session.connection === connection;
|
|
4771
|
+
}
|
|
4772
|
+
end(session, result) {
|
|
4773
|
+
if (this.session !== session) return;
|
|
4774
|
+
this.session = void 0;
|
|
4775
|
+
clearTimeout(session.retry);
|
|
4776
|
+
session.runWatch?.unsubscribe();
|
|
4777
|
+
for (const watch of session.agentWatches.values()) watch.unsubscribe();
|
|
4778
|
+
session.agentWatches.clear();
|
|
4779
|
+
session.collector?.stop();
|
|
4780
|
+
session.ready = false;
|
|
4781
|
+
if (session.queue.length > 0) this.report(session, "EVENTS_DROPPED");
|
|
4782
|
+
this.discardQueue(session);
|
|
4783
|
+
if (session.connection) {
|
|
4784
|
+
for (const pending of session.connection.pending) {
|
|
4785
|
+
pending.finish("PERSISTENCE_UNKNOWN");
|
|
4786
|
+
this.report(session, "PERSISTENCE_UNKNOWN");
|
|
4787
|
+
}
|
|
4788
|
+
this.cleanup(session, session.connection);
|
|
4789
|
+
}
|
|
4790
|
+
if (typeof window !== "undefined" && typeof window.removeEventListener === "function") {
|
|
4791
|
+
window.removeEventListener("offline", session.onOffline);
|
|
4792
|
+
window.removeEventListener("online", session.onOnline);
|
|
4793
|
+
}
|
|
4794
|
+
session.resolve(result);
|
|
4795
|
+
this.onChange();
|
|
4796
|
+
}
|
|
4797
|
+
cleanup(session, connection) {
|
|
4798
|
+
session.connection = void 0;
|
|
4799
|
+
clearTimeout(connection.timer);
|
|
4800
|
+
connection.abort.abort();
|
|
4801
|
+
for (const pending of connection.pending) clearTimeout(pending.timer);
|
|
4802
|
+
connection.pending.clear();
|
|
4803
|
+
const { socket, channel } = connection;
|
|
4804
|
+
try {
|
|
4805
|
+
if (channel) {
|
|
4806
|
+
for (const [event, ref] of connection.channelRefs) channel.off(event, ref);
|
|
4807
|
+
channel.leave();
|
|
4808
|
+
}
|
|
4809
|
+
} finally {
|
|
4810
|
+
if (socket) {
|
|
4811
|
+
socket.off(connection.socketRefs);
|
|
4812
|
+
socket.disconnect();
|
|
4813
|
+
}
|
|
4814
|
+
}
|
|
4815
|
+
}
|
|
4816
|
+
fail(session, connection, code, retryable = true) {
|
|
4817
|
+
if (!this.current(session, connection)) return;
|
|
4818
|
+
session.collector?.stop();
|
|
4819
|
+
session.collector = void 0;
|
|
4820
|
+
if (session.ready && session.started) session.promise = new Promise((resolve) => {
|
|
4821
|
+
session.resolve = resolve;
|
|
4822
|
+
});
|
|
4823
|
+
session.ready = false;
|
|
4824
|
+
this.discardQueue(session);
|
|
4825
|
+
session.linkedThreads.clear();
|
|
4826
|
+
for (const pending of connection.pending) pending.finish("PERSISTENCE_UNKNOWN");
|
|
4827
|
+
if (!this.current(session, connection)) return;
|
|
4828
|
+
this.cleanup(session, connection);
|
|
4829
|
+
this.onChange();
|
|
4830
|
+
this.report(session, code);
|
|
4831
|
+
if (this.session !== session) return;
|
|
4832
|
+
if (!session.started || !retryable) {
|
|
4833
|
+
this.end(session, {
|
|
4834
|
+
status: "error",
|
|
4835
|
+
code
|
|
4836
|
+
});
|
|
4837
|
+
return;
|
|
4838
|
+
}
|
|
4839
|
+
if (session.offline || this.session !== session) return;
|
|
4840
|
+
session.retry = setTimeout(() => {
|
|
4841
|
+
session.retry = void 0;
|
|
4842
|
+
if (this.session === session && !session.offline) this.connect(session);
|
|
4843
|
+
}, reconnectDelay(++session.retries));
|
|
4844
|
+
}
|
|
4845
|
+
async connect(session) {
|
|
4846
|
+
if (this.session !== session || session.offline || session.connection) return;
|
|
4847
|
+
const connection = {
|
|
4848
|
+
abort: new AbortController(),
|
|
4849
|
+
socketRefs: [],
|
|
4850
|
+
channelRefs: [],
|
|
4851
|
+
pending: /* @__PURE__ */ new Set()
|
|
4852
|
+
};
|
|
4853
|
+
session.connection = connection;
|
|
4854
|
+
connection.timer = setTimeout(() => this.fail(session, connection, "CONNECTION_TIMEOUT"), TIMEOUT_MS);
|
|
4855
|
+
try {
|
|
4856
|
+
const runtimeUrl = this.core.runtimeUrl;
|
|
4857
|
+
if (!runtimeUrl) {
|
|
4858
|
+
this.fail(session, connection, "RUNTIME_REQUIRED", false);
|
|
4859
|
+
return;
|
|
4860
|
+
}
|
|
4861
|
+
let transport = this.core.runtimeTransport;
|
|
4862
|
+
if (transport === "auto") {
|
|
4863
|
+
transport = await this.detectedTransport(connection.abort.signal);
|
|
4864
|
+
if (!this.current(session, connection)) return;
|
|
4865
|
+
}
|
|
4866
|
+
const rest = transport === "rest";
|
|
4867
|
+
const response = await fetch(rest ? `${runtimeUrl}/trajectory/${encodeURIComponent(session.trajectoryId)}/connect` : this.core.ɵruntimeEndpointUrl ?? runtimeUrl, {
|
|
4868
|
+
method: "POST",
|
|
4869
|
+
redirect: "error",
|
|
4870
|
+
signal: connection.abort.signal,
|
|
4871
|
+
headers: {
|
|
4872
|
+
...this.core.headers,
|
|
4873
|
+
"Content-Type": "application/json"
|
|
4874
|
+
},
|
|
4875
|
+
credentials: this.core.credentials,
|
|
4876
|
+
body: JSON.stringify(rest ? {} : {
|
|
4877
|
+
method: "trajectory/connect",
|
|
4878
|
+
params: { trajectoryId: session.trajectoryId },
|
|
4879
|
+
body: {}
|
|
4880
|
+
})
|
|
4881
|
+
});
|
|
4882
|
+
if (!this.current(session, connection)) return;
|
|
4883
|
+
const payload = await response.json().catch(() => void 0);
|
|
4884
|
+
if (!this.current(session, connection)) return;
|
|
4885
|
+
if (!response.ok) {
|
|
4886
|
+
this.fail(session, connection, errorCode(payload, "CONNECTION_FAILED"), response.status >= 500 || response.status === 408 || response.status === 429);
|
|
4887
|
+
return;
|
|
4888
|
+
}
|
|
4889
|
+
if (!isGrant(payload)) {
|
|
4890
|
+
this.fail(session, connection, "INVALID_GRANT", false);
|
|
4891
|
+
return;
|
|
4892
|
+
}
|
|
4893
|
+
const socket = new phoenix.Socket(payload.realtime.clientUrl, {
|
|
4894
|
+
params: { join_token: payload.joinToken },
|
|
4895
|
+
timeout: TIMEOUT_MS
|
|
4896
|
+
});
|
|
4897
|
+
connection.socket = socket;
|
|
4898
|
+
const lost = () => this.fail(session, connection, "CONNECTION_LOST");
|
|
4899
|
+
connection.socketRefs.push(socket.onError(lost), socket.onClose(lost));
|
|
4900
|
+
const channel = socket.channel(payload.realtime.topic, {});
|
|
4901
|
+
connection.channel = channel;
|
|
4902
|
+
connection.channelRefs.push(["phx_error", channel.onError(lost)], ["phx_close", channel.onClose(lost)]);
|
|
4903
|
+
socket.connect();
|
|
4904
|
+
if (!this.current(session, connection)) return;
|
|
4905
|
+
channel.join(TIMEOUT_MS).receive("ok", () => {
|
|
4906
|
+
if (!this.current(session, connection)) return;
|
|
4907
|
+
try {
|
|
4908
|
+
clearTimeout(connection.timer);
|
|
4909
|
+
session.ready = true;
|
|
4910
|
+
session.retries = 0;
|
|
4911
|
+
session.collector = (0, _copilotkit_learning.createTrajectoryCollector)({
|
|
4912
|
+
...session.config,
|
|
4913
|
+
ignoreUrls: [
|
|
4914
|
+
...session.config.ignoreUrls ?? [],
|
|
4915
|
+
...this.core.runtimeUrl ? [this.core.runtimeUrl] : [],
|
|
4916
|
+
...this.core.ɵruntimeEndpointUrl ? [this.core.ɵruntimeEndpointUrl] : [],
|
|
4917
|
+
"https://telemetry.copilotkit.ai/",
|
|
4918
|
+
"https://cdn.copilotkit.ai/"
|
|
4919
|
+
],
|
|
4920
|
+
onError: (error) => {
|
|
4921
|
+
if (error.code === "CAPTURE_FAILED") this.fail(session, connection, error.code, false);
|
|
4922
|
+
else {
|
|
4923
|
+
if (error.code === "INVALID_EVENT" || error.code === "EVENT_TOO_LARGE") this.addDropped(session, 1);
|
|
4924
|
+
session.config.onError?.(error);
|
|
4925
|
+
}
|
|
4926
|
+
},
|
|
4927
|
+
send: (event) => this.send(session, connection, event)
|
|
4928
|
+
});
|
|
4929
|
+
session.collector.start();
|
|
4930
|
+
if (!this.current(session, connection)) return;
|
|
4931
|
+
if (session.dropped > 0) this.scheduleFlush(session, connection);
|
|
4932
|
+
session.started = true;
|
|
4933
|
+
session.resolve({
|
|
4934
|
+
status: "started",
|
|
4935
|
+
trajectoryId: session.trajectoryId
|
|
4936
|
+
});
|
|
4937
|
+
this.onChange();
|
|
4938
|
+
} catch {
|
|
4939
|
+
this.fail(session, connection, "CAPTURE_FAILED", false);
|
|
4940
|
+
}
|
|
4941
|
+
}).receive("error", (joinError) => this.fail(session, connection, errorCode(joinError, "JOIN_FAILED"))).receive("timeout", () => this.fail(session, connection, "CONNECTION_TIMEOUT"));
|
|
4942
|
+
} catch {
|
|
4943
|
+
this.fail(session, connection, "CONNECTION_FAILED");
|
|
4944
|
+
}
|
|
4945
|
+
}
|
|
4946
|
+
/** Resolves once auto-detection picks a transport, or with "auto" when the attempt is aborted. */
|
|
4947
|
+
detectedTransport(signal) {
|
|
4948
|
+
return new Promise((resolve) => {
|
|
4949
|
+
const finish = () => {
|
|
4950
|
+
subscription.unsubscribe();
|
|
4951
|
+
signal.removeEventListener("abort", finish);
|
|
4952
|
+
resolve(this.core.runtimeTransport);
|
|
4953
|
+
};
|
|
4954
|
+
const subscription = this.core.subscribe({ onRuntimeConnectionStatusChanged: () => {
|
|
4955
|
+
if (this.core.runtimeTransport !== "auto") finish();
|
|
4956
|
+
} });
|
|
4957
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
4958
|
+
});
|
|
4959
|
+
}
|
|
4960
|
+
addDropped(session, count, schedule = true) {
|
|
4961
|
+
session.dropped = Math.min(Number.MAX_SAFE_INTEGER, session.dropped + count);
|
|
4962
|
+
if (schedule && count > 0 && session.ready && session.connection) this.scheduleFlush(session, session.connection);
|
|
4963
|
+
}
|
|
4964
|
+
scheduleFlush(session, connection) {
|
|
4965
|
+
if (session.flushTimer !== void 0 || !this.current(session, connection)) return;
|
|
4966
|
+
session.flushTimer = setTimeout(() => {
|
|
4967
|
+
session.flushTimer = void 0;
|
|
4968
|
+
this.flush(session, connection);
|
|
4969
|
+
}, BATCH_INTERVAL_MS);
|
|
4970
|
+
}
|
|
4971
|
+
discardQueue(session) {
|
|
4972
|
+
clearTimeout(session.flushTimer);
|
|
4973
|
+
session.flushTimer = void 0;
|
|
4974
|
+
this.addDropped(session, session.queue.length, false);
|
|
4975
|
+
session.queue = [];
|
|
4976
|
+
}
|
|
4977
|
+
connected(connection) {
|
|
4978
|
+
return connection.socket?.isConnected() === true && connection.channel?.state === "joined";
|
|
4979
|
+
}
|
|
4980
|
+
send(session, connection, event) {
|
|
4981
|
+
if (!this.current(session, connection) || !session.ready) return;
|
|
4982
|
+
if (!this.connected(connection)) {
|
|
4983
|
+
this.addDropped(session, 1);
|
|
4984
|
+
this.fail(session, connection, "CONNECTION_LOST");
|
|
4985
|
+
return;
|
|
4986
|
+
}
|
|
4987
|
+
if (!Number.isSafeInteger(this.nextSeq)) {
|
|
4988
|
+
this.addDropped(session, 1);
|
|
4989
|
+
this.report(session, "SEQUENCE_EXHAUSTED");
|
|
4990
|
+
return;
|
|
4991
|
+
}
|
|
4992
|
+
const original = event.value;
|
|
4993
|
+
const fields = isObject(original) && !Array.isArray(original) && !Object.hasOwn(original, "seq") ? original : { data: original };
|
|
4994
|
+
const wire = {
|
|
4995
|
+
...event,
|
|
4996
|
+
value: {
|
|
4997
|
+
...fields,
|
|
4998
|
+
seq: this.nextSeq++
|
|
4999
|
+
}
|
|
5000
|
+
};
|
|
5001
|
+
if (byteLength(wire) > MAX_EVENT_BYTES) {
|
|
5002
|
+
this.addDropped(session, 1);
|
|
5003
|
+
this.report(session, "EVENT_TOO_LARGE");
|
|
5004
|
+
return;
|
|
5005
|
+
}
|
|
5006
|
+
const fits = () => session.queue.length < MAX_BATCH_EVENTS && byteLength({
|
|
5007
|
+
events: [...session.queue, wire],
|
|
5008
|
+
dropped: Number.MAX_SAFE_INTEGER
|
|
5009
|
+
}) <= MAX_BATCH_BYTES;
|
|
5010
|
+
if (!fits()) this.flush(session, connection);
|
|
5011
|
+
if (!this.current(session, connection) || !session.ready) return;
|
|
5012
|
+
if (!fits()) {
|
|
5013
|
+
this.addDropped(session, 1);
|
|
5014
|
+
this.report(session, "EVENTS_DROPPED");
|
|
5015
|
+
return;
|
|
5016
|
+
}
|
|
5017
|
+
session.queue.push(wire);
|
|
5018
|
+
if (session.queue.length === MAX_BATCH_EVENTS) this.flush(session, connection);
|
|
5019
|
+
else this.scheduleFlush(session, connection);
|
|
5020
|
+
}
|
|
5021
|
+
flush(session, connection) {
|
|
5022
|
+
if (!this.current(session, connection) || !session.ready || session.queue.length === 0 && session.dropped === 0) return;
|
|
5023
|
+
if (!this.connected(connection)) {
|
|
5024
|
+
this.fail(session, connection, "CONNECTION_LOST");
|
|
5025
|
+
return;
|
|
5026
|
+
}
|
|
5027
|
+
if (connection.pending.size > 0) return;
|
|
5028
|
+
clearTimeout(session.flushTimer);
|
|
5029
|
+
session.flushTimer = void 0;
|
|
5030
|
+
const batch = {
|
|
5031
|
+
events: session.queue,
|
|
5032
|
+
dropped: session.dropped
|
|
5033
|
+
};
|
|
5034
|
+
session.queue = [];
|
|
5035
|
+
session.dropped = 0;
|
|
5036
|
+
let finished = false;
|
|
5037
|
+
const pending = {
|
|
5038
|
+
timer: setTimeout(() => pending.finish("PERSISTENCE_UNKNOWN"), TIMEOUT_MS),
|
|
5039
|
+
finish: (code, knownRollback = false, flushNext = true) => {
|
|
5040
|
+
if (finished) return;
|
|
5041
|
+
finished = true;
|
|
5042
|
+
clearTimeout(pending.timer);
|
|
5043
|
+
connection.pending.delete(pending);
|
|
5044
|
+
if (knownRollback) this.addDropped(session, batch.events.length + batch.dropped, false);
|
|
5045
|
+
if (code && this.current(session, connection)) this.report(session, code);
|
|
5046
|
+
if (flushNext && this.current(session, connection) && session.ready && (session.queue.length > 0 || !knownRollback && session.dropped > 0)) this.flush(session, connection);
|
|
5047
|
+
}
|
|
5048
|
+
};
|
|
5049
|
+
connection.pending.add(pending);
|
|
5050
|
+
try {
|
|
5051
|
+
connection.channel.push("events", batch, TIMEOUT_MS).receive("ok", (payload) => {
|
|
5052
|
+
pending.finish(!isBatchAcknowledgement(payload, batch.events.length) ? "PERSISTENCE_UNKNOWN" : payload.rejected > 0 ? "EVENTS_REJECTED" : void 0);
|
|
5053
|
+
}).receive("error", (payload) => {
|
|
5054
|
+
if (!this.current(session, connection)) return;
|
|
5055
|
+
const reason = isObject(payload) ? payload.reason : void 0;
|
|
5056
|
+
if (reason === "unauthorized" || reason === "trajectory_mismatch") {
|
|
5057
|
+
pending.finish(void 0, true, false);
|
|
5058
|
+
this.fail(session, connection, reason.toUpperCase(), false);
|
|
5059
|
+
return;
|
|
5060
|
+
}
|
|
5061
|
+
const rollback = typeof reason === "string" && [
|
|
5062
|
+
"invalid_batch",
|
|
5063
|
+
"batch_too_large",
|
|
5064
|
+
"batch_too_many_events",
|
|
5065
|
+
"trajectory_over_share",
|
|
5066
|
+
"trajectory_outbox_full",
|
|
5067
|
+
"unsupported_event"
|
|
5068
|
+
].includes(reason);
|
|
5069
|
+
pending.finish(rollback ? reason.toUpperCase() : "PERSISTENCE_UNKNOWN", rollback);
|
|
5070
|
+
}).receive("timeout", () => pending.finish("PERSISTENCE_UNKNOWN"));
|
|
5071
|
+
} catch {
|
|
5072
|
+
pending.finish("PERSISTENCE_UNKNOWN");
|
|
5073
|
+
}
|
|
5074
|
+
}
|
|
5075
|
+
};
|
|
5076
|
+
|
|
5077
|
+
//#endregion
|
|
5078
|
+
//#region src/core/learning-bridge.ts
|
|
5079
|
+
const COPILOTKIT_OWN_URLS = ["https://telemetry.copilotkit.ai/", "https://cdn.copilotkit.ai/"];
|
|
5080
|
+
/**
|
|
5081
|
+
* Wires `@copilotkit/learning` into Core. Default capture uses Runtime and Phoenix;
|
|
5082
|
+
* explicit legacy sinks also use agent events and open-Thread attribution.
|
|
5083
|
+
*/
|
|
5084
|
+
var LearningBridge = class {
|
|
5085
|
+
collector = null;
|
|
5086
|
+
agents = /* @__PURE__ */ new Map();
|
|
5087
|
+
openThreads = /* @__PURE__ */ new Set();
|
|
5088
|
+
toolCalls = /* @__PURE__ */ new Map();
|
|
5089
|
+
executingTools = /* @__PURE__ */ new Map();
|
|
5090
|
+
lastRunIds = /* @__PURE__ */ new WeakMap();
|
|
5091
|
+
lastLinkedThreadId = null;
|
|
5092
|
+
coreSubscription = null;
|
|
5093
|
+
seq = 0;
|
|
5094
|
+
activeConfig;
|
|
5095
|
+
connection;
|
|
5096
|
+
constructor(core, config, onChange = () => {}) {
|
|
5097
|
+
this.core = core;
|
|
5098
|
+
this.config = config;
|
|
5099
|
+
this.connection = new TrajectoryConnection(core, onChange);
|
|
5100
|
+
this.setConfig(config);
|
|
5101
|
+
}
|
|
5102
|
+
setConfig(config) {
|
|
5103
|
+
this.config = config;
|
|
5104
|
+
if (config === void 0) this.stop();
|
|
5105
|
+
}
|
|
5106
|
+
get isConfigured() {
|
|
5107
|
+
return this.config !== void 0;
|
|
5108
|
+
}
|
|
5109
|
+
get isLegacyCaptureActive() {
|
|
5110
|
+
return this.collector !== null && this.collector.trajectoryId !== null;
|
|
5111
|
+
}
|
|
5112
|
+
startTracking() {
|
|
5113
|
+
if (this.coreSubscription !== null || !this.isLegacyCaptureActive) return;
|
|
5114
|
+
this.coreSubscription = this.core.subscribe({
|
|
5115
|
+
onAgentsChanged: ({ agents }) => {
|
|
5116
|
+
for (const agent of Object.values(agents)) this.track(agent);
|
|
5117
|
+
},
|
|
5118
|
+
onAgentRunStarted: ({ agent }) => this.track(agent),
|
|
5119
|
+
onToolExecutionStart: ({ toolCallId, agentId, toolName }) => {
|
|
5120
|
+
if (!this.isLegacyCaptureActive) return;
|
|
5121
|
+
this.executingTools.set(toolCallId, Date.now());
|
|
5122
|
+
this.emitToolCall("executing", toolCallId, agentId, toolName, {});
|
|
5123
|
+
},
|
|
5124
|
+
onToolExecutionEnd: ({ toolCallId, agentId, toolName, error }) => {
|
|
5125
|
+
if (!this.isLegacyCaptureActive) return;
|
|
5126
|
+
const startedAt = this.executingTools.get(toolCallId);
|
|
5127
|
+
this.executingTools.delete(toolCallId);
|
|
5128
|
+
this.emitToolCall("completed", toolCallId, agentId, toolName, {
|
|
5129
|
+
durationMs: startedAt === void 0 ? void 0 : Date.now() - startedAt,
|
|
5130
|
+
outcome: error === void 0 ? "ok" : "error"
|
|
5131
|
+
});
|
|
5132
|
+
this.toolCalls.delete(toolCallId);
|
|
5133
|
+
}
|
|
5134
|
+
});
|
|
5135
|
+
for (const agent of Object.values(this.core.agents)) this.track(agent);
|
|
5136
|
+
}
|
|
5137
|
+
get trajectoryId() {
|
|
5138
|
+
return this.connection.trajectoryId ?? this.collector?.trajectoryId ?? null;
|
|
5139
|
+
}
|
|
5140
|
+
start(options = {}) {
|
|
5141
|
+
const config = this.config;
|
|
5142
|
+
if (config === void 0) {
|
|
5143
|
+
console.warn("[CopilotKit] startTrajectory() needs the `learning` option on CopilotKitCore (or the CopilotKitProvider `learning` prop).");
|
|
5144
|
+
return Promise.resolve({
|
|
5145
|
+
status: "error",
|
|
5146
|
+
code: "LEARNING_DISABLED"
|
|
5147
|
+
});
|
|
5148
|
+
}
|
|
5149
|
+
if (config.sink === void 0) {
|
|
5150
|
+
if (options.learningContainerIds !== void 0) console.warn("[CopilotKit] learningContainerIds is supported only with a custom sink. Authenticated Trajectory capture does not assign Learning Containers; remove learningContainerIds from the capture options.");
|
|
5151
|
+
this.stopLegacy();
|
|
5152
|
+
return this.connection.start(options.trajectoryId, config);
|
|
5153
|
+
}
|
|
5154
|
+
this.connection.stop();
|
|
5155
|
+
const trajectoryId = options.trajectoryId ?? this.collector?.trajectoryId ?? crypto.randomUUID();
|
|
5156
|
+
if (this.collector === null) {
|
|
5157
|
+
this.activeConfig = config;
|
|
5158
|
+
const runtimeUrl = this.core.runtimeUrl;
|
|
5159
|
+
this.collector = (0, _copilotkit_learning.createCollector)({
|
|
5160
|
+
...config,
|
|
5161
|
+
beforeSend: (event) => {
|
|
5162
|
+
const sequenced = {
|
|
5163
|
+
...event,
|
|
5164
|
+
value: {
|
|
5165
|
+
...event.value,
|
|
5166
|
+
seq: ++this.seq
|
|
5167
|
+
}
|
|
5168
|
+
};
|
|
5169
|
+
return config.beforeSend === void 0 ? sequenced : config.beforeSend(sequenced);
|
|
5170
|
+
},
|
|
5171
|
+
ignoreUrls: [
|
|
5172
|
+
...config.ignoreUrls ?? [],
|
|
5173
|
+
...COPILOTKIT_OWN_URLS,
|
|
5174
|
+
...runtimeUrl === void 0 ? [] : [runtimeUrl]
|
|
5175
|
+
],
|
|
5176
|
+
enrich: (target) => this.enrich(target)
|
|
5177
|
+
});
|
|
5178
|
+
}
|
|
5179
|
+
const wasActive = this.collector.trajectoryId !== null;
|
|
5180
|
+
this.collector.start({
|
|
5181
|
+
...options,
|
|
5182
|
+
trajectoryId
|
|
5183
|
+
});
|
|
5184
|
+
if (wasActive) return Promise.resolve(this.collector.trajectoryId === trajectoryId ? {
|
|
5185
|
+
status: "started",
|
|
5186
|
+
trajectoryId
|
|
5187
|
+
} : {
|
|
5188
|
+
status: "error",
|
|
5189
|
+
code: "TRAJECTORY_ACTIVE"
|
|
5190
|
+
});
|
|
5191
|
+
this.startTracking();
|
|
5192
|
+
this.lastLinkedThreadId = null;
|
|
5193
|
+
for (const threadId of this.distinctOpenThreadIds()) this.emitThreadLinked(threadId, "start");
|
|
5194
|
+
return Promise.resolve({
|
|
5195
|
+
status: "started",
|
|
5196
|
+
trajectoryId
|
|
5197
|
+
});
|
|
5198
|
+
}
|
|
5199
|
+
stop() {
|
|
5200
|
+
this.connection.stop();
|
|
5201
|
+
this.stopLegacy();
|
|
5202
|
+
}
|
|
5203
|
+
stopLegacy() {
|
|
5204
|
+
this.collector?.stop();
|
|
5205
|
+
this.collector = null;
|
|
5206
|
+
this.activeConfig = void 0;
|
|
5207
|
+
this.coreSubscription?.unsubscribe();
|
|
5208
|
+
this.coreSubscription = null;
|
|
5209
|
+
for (const subscription of this.agents.values()) subscription.unsubscribe();
|
|
5210
|
+
this.agents.clear();
|
|
5211
|
+
this.toolCalls.clear();
|
|
5212
|
+
this.executingTools.clear();
|
|
5213
|
+
}
|
|
5214
|
+
/** A developer event. Only an explicit legacy sink adds open-Thread context. */
|
|
5215
|
+
emit(name, value) {
|
|
5216
|
+
this.connection.emit(name, value);
|
|
5217
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) this.collector?.emit(name, {
|
|
5218
|
+
...this.openThreadContext(),
|
|
5219
|
+
...value
|
|
5220
|
+
});
|
|
5221
|
+
}
|
|
5222
|
+
registerOpenThread(params) {
|
|
5223
|
+
const entry = { ...params };
|
|
5224
|
+
const isNew = !this.distinctOpenThreadIds().includes(entry.threadId);
|
|
5225
|
+
this.openThreads.add(entry);
|
|
5226
|
+
if (isNew) this.emitThreadLinked(entry.threadId, "open");
|
|
5227
|
+
return {
|
|
5228
|
+
update: (threadId) => {
|
|
5229
|
+
if (threadId === entry.threadId) return;
|
|
5230
|
+
entry.threadId = threadId;
|
|
5231
|
+
if (![...this.openThreads].filter((other) => other !== entry).some((other) => other.threadId === threadId)) this.emitThreadLinked(threadId, "switch");
|
|
5232
|
+
},
|
|
5233
|
+
unregister: () => {
|
|
5234
|
+
this.openThreads.delete(entry);
|
|
5235
|
+
}
|
|
5236
|
+
};
|
|
5237
|
+
}
|
|
5238
|
+
distinctOpenThreadIds() {
|
|
5239
|
+
return [...new Set([...this.openThreads].map((open) => open.threadId))];
|
|
5240
|
+
}
|
|
5241
|
+
emitThreadLinked(threadId, reason) {
|
|
5242
|
+
if (!(this.collector !== null && this.collector.trajectoryId !== null) || threadId === this.lastLinkedThreadId) return;
|
|
5243
|
+
this.lastLinkedThreadId = threadId;
|
|
5244
|
+
const owner = [...this.openThreads].find((open) => open.threadId === threadId);
|
|
5245
|
+
const agent = this.findAgentForThread(threadId);
|
|
5246
|
+
this.collector?.ɵemit("thread.linked", {
|
|
5247
|
+
threadId,
|
|
5248
|
+
agentId: owner?.agentId ?? agent?.agentId ?? null,
|
|
5249
|
+
hasMessages: (agent?.messages.length ?? 0) > 0,
|
|
5250
|
+
reason
|
|
5251
|
+
});
|
|
5252
|
+
}
|
|
5253
|
+
findAgentForThread(threadId) {
|
|
5254
|
+
return [...this.agents.keys()].find((agent) => agent.threadId === threadId);
|
|
5255
|
+
}
|
|
5256
|
+
track(agent) {
|
|
5257
|
+
const agentId = agent.agentId;
|
|
5258
|
+
if (!this.isLegacyCaptureActive || this.agents.has(agent) || agentId === void 0) return;
|
|
5259
|
+
this.agents.set(agent, agent.subscribe(this.agentSubscriber(agent, agentId)));
|
|
5260
|
+
}
|
|
5261
|
+
agentSubscriber(agent, agentId) {
|
|
5262
|
+
const runIdFor = () => this.lastRunIds.get(agent);
|
|
5263
|
+
return {
|
|
5264
|
+
onRunStartedEvent: ({ event }) => {
|
|
5265
|
+
this.lastRunIds.set(agent, event.runId);
|
|
5266
|
+
this.emitRun("started", agent, event.threadId, event.runId);
|
|
5267
|
+
},
|
|
5268
|
+
onRunFinishedEvent: ({ event }) => {
|
|
5269
|
+
this.emitRun("finished", agent, event.threadId, event.runId);
|
|
5270
|
+
},
|
|
5271
|
+
onRunErrorEvent: () => {
|
|
5272
|
+
this.emitRun("error", agent, agent.threadId, runIdFor() ?? null);
|
|
5273
|
+
},
|
|
5274
|
+
onToolCallStartEvent: ({ event }) => {
|
|
5275
|
+
if (!this.isLegacyCaptureActive) return;
|
|
5276
|
+
const origin = {
|
|
5277
|
+
agentId,
|
|
5278
|
+
threadId: agent.threadId,
|
|
5279
|
+
messageId: event.parentMessageId,
|
|
5280
|
+
runId: runIdFor()
|
|
5281
|
+
};
|
|
5282
|
+
this.toolCalls.set(event.toolCallId, origin);
|
|
5283
|
+
this.emitToolCall("started", event.toolCallId, origin.agentId, event.toolCallName, {});
|
|
5284
|
+
},
|
|
5285
|
+
onToolCallResultEvent: ({ event }) => {
|
|
5286
|
+
if (!this.executingTools.has(event.toolCallId)) this.toolCalls.delete(event.toolCallId);
|
|
5287
|
+
},
|
|
5288
|
+
onNewMessage: ({ message }) => this.emitMessage(agent, message)
|
|
5289
|
+
};
|
|
5290
|
+
}
|
|
5291
|
+
emitRun(phase, agent, threadId, runId) {
|
|
5292
|
+
this.collector?.ɵemit("agent.run", {
|
|
5293
|
+
phase,
|
|
5294
|
+
runId,
|
|
5295
|
+
threadId,
|
|
5296
|
+
agentId: agent.agentId
|
|
5297
|
+
});
|
|
5298
|
+
}
|
|
5299
|
+
emitMessage(agent, message) {
|
|
5300
|
+
const content = typeof message.content === "string" ? message.content : "";
|
|
5301
|
+
const includeText = this.activeConfig?.capture?.agentText !== false && typeof message.content === "string";
|
|
5302
|
+
this.collector?.ɵemit("agent.message", {
|
|
5303
|
+
messageId: message.id,
|
|
5304
|
+
runId: this.runIdForMessage(agent, message.id),
|
|
5305
|
+
threadId: agent.threadId,
|
|
5306
|
+
agentId: agent.agentId,
|
|
5307
|
+
role: message.role,
|
|
5308
|
+
textLength: content.length,
|
|
5309
|
+
...includeText ? { text: content } : {}
|
|
5310
|
+
});
|
|
5311
|
+
}
|
|
5312
|
+
emitToolCall(phase, toolCallId, agentId, toolName, extra) {
|
|
5313
|
+
const origin = this.toolCalls.get(toolCallId);
|
|
5314
|
+
this.collector?.ɵemit("tool.call", {
|
|
5315
|
+
phase,
|
|
5316
|
+
toolCallId,
|
|
5317
|
+
toolName,
|
|
5318
|
+
agentId,
|
|
5319
|
+
threadId: origin?.threadId ?? null,
|
|
5320
|
+
messageId: origin?.messageId,
|
|
5321
|
+
runId: origin?.runId,
|
|
5322
|
+
...extra
|
|
5323
|
+
});
|
|
5324
|
+
}
|
|
5325
|
+
runIdForMessage(agent, messageId) {
|
|
5326
|
+
const agentId = agent.agentId;
|
|
5327
|
+
if (agentId === void 0) return null;
|
|
5328
|
+
return this.core.getRunIdForMessage(agentId, agent.threadId, messageId) ?? this.lastRunIds.get(agent) ?? null;
|
|
5329
|
+
}
|
|
5330
|
+
findMessage(messageId) {
|
|
5331
|
+
for (const agent of this.agents.keys()) {
|
|
5332
|
+
const message = agent.messages.find((candidate) => candidate.id === messageId);
|
|
5333
|
+
if (message !== void 0) return {
|
|
5334
|
+
agent,
|
|
5335
|
+
message
|
|
5336
|
+
};
|
|
5337
|
+
}
|
|
5338
|
+
return null;
|
|
5339
|
+
}
|
|
5340
|
+
toolStatus(agent, toolCallId) {
|
|
5341
|
+
if (this.executingTools.has(toolCallId)) return "executing";
|
|
5342
|
+
return agent.messages.some((message) => message.role === "tool" && message.toolCallId === toolCallId) ? "complete" : "inProgress";
|
|
5343
|
+
}
|
|
5344
|
+
/** Adds agent context to a click: exact message first, then the single open Thread. */
|
|
5345
|
+
enrich(target) {
|
|
5346
|
+
const messageId = target.closest("[data-message-id]")?.getAttribute("data-message-id") ?? null;
|
|
5347
|
+
const toolCallId = target.closest("[data-tool-call-id]")?.getAttribute("data-tool-call-id") ?? null;
|
|
5348
|
+
const found = messageId === null ? null : this.findMessage(messageId);
|
|
5349
|
+
if (found !== null) {
|
|
5350
|
+
const { agent, message } = found;
|
|
5351
|
+
const toolCall = toolCallId !== null && message.role === "assistant" ? message.toolCalls?.find((call) => call.id === toolCallId) : void 0;
|
|
5352
|
+
return {
|
|
5353
|
+
threadId: agent.threadId,
|
|
5354
|
+
agentId: agent.agentId,
|
|
5355
|
+
messageId: message.id,
|
|
5356
|
+
runId: this.runIdForMessage(agent, message.id),
|
|
5357
|
+
...toolCall === void 0 ? {} : {
|
|
5358
|
+
toolCallId: toolCall.id,
|
|
5359
|
+
toolName: toolCall.function.name,
|
|
5360
|
+
toolStatus: this.toolStatus(agent, toolCall.id)
|
|
5361
|
+
}
|
|
5362
|
+
};
|
|
5363
|
+
}
|
|
5364
|
+
return this.openThreadContext();
|
|
5365
|
+
}
|
|
5366
|
+
/** The single open Thread, or `null` with the candidates when several are open. */
|
|
5367
|
+
openThreadContext() {
|
|
5368
|
+
const openIds = this.distinctOpenThreadIds();
|
|
5369
|
+
if (openIds.length > 1) return {
|
|
5370
|
+
threadId: null,
|
|
5371
|
+
threadAmbiguity: {
|
|
5372
|
+
reason: "multiple-open",
|
|
5373
|
+
candidates: openIds
|
|
5374
|
+
}
|
|
5375
|
+
};
|
|
5376
|
+
return { threadId: openIds[0] ?? null };
|
|
5377
|
+
}
|
|
5378
|
+
};
|
|
5379
|
+
|
|
4515
5380
|
//#endregion
|
|
4516
5381
|
//#region src/utils/micro-redux.ts
|
|
4517
5382
|
const INTERNAL_ACTION_TYPES = {
|
|
@@ -5664,6 +6529,9 @@ var CopilotKitCore = class {
|
|
|
5664
6529
|
runHandler;
|
|
5665
6530
|
stateManager;
|
|
5666
6531
|
threadStoreRegistry;
|
|
6532
|
+
learningBridge;
|
|
6533
|
+
notifiedTrajectoryId = null;
|
|
6534
|
+
learningConfiguredListeners = /* @__PURE__ */ new Set();
|
|
5667
6535
|
/**
|
|
5668
6536
|
* The single core-owned memory store, created lazily on first
|
|
5669
6537
|
* `getMemoryStore()` and kept user-scoped for the lifetime of the core.
|
|
@@ -5678,7 +6546,7 @@ var CopilotKitCore = class {
|
|
|
5678
6546
|
* store that was registered prior to that initial notification.
|
|
5679
6547
|
*/
|
|
5680
6548
|
previousAgentIds = /* @__PURE__ */ new Set();
|
|
5681
|
-
constructor({ runtimeUrl, runtimeTransport = "auto", deferInitialConnection = false, headers = {}, credentials, messageFilter, properties = {}, agents__unsafe_dev_only = {}, tools = [], suggestionsConfig = [], debug }) {
|
|
6549
|
+
constructor({ runtimeUrl, runtimeTransport = "auto", deferInitialConnection = false, headers = {}, credentials, messageFilter, properties = {}, agents__unsafe_dev_only = {}, tools = [], suggestionsConfig = [], debug, learning }) {
|
|
5682
6550
|
this._headers = normalizeHeaders(headers);
|
|
5683
6551
|
this._credentials = credentials;
|
|
5684
6552
|
this._messageFilter = messageFilter;
|
|
@@ -5694,6 +6562,7 @@ var CopilotKitCore = class {
|
|
|
5694
6562
|
this.runHandler.initialize(tools);
|
|
5695
6563
|
this.suggestionEngine.initialize(suggestionsConfig);
|
|
5696
6564
|
this.stateManager.initialize();
|
|
6565
|
+
this.learningBridge = new LearningBridge(this, learning, () => this.notifyTrajectoryChanged());
|
|
5697
6566
|
this.agentRegistry.setRuntimeTransport(runtimeTransport);
|
|
5698
6567
|
this.agentRegistry.setRuntimeUrl(runtimeUrl, { deferConnection: deferInitialConnection });
|
|
5699
6568
|
this.previousAgentIds = new Set(Object.keys(agents__unsafe_dev_only));
|
|
@@ -5845,6 +6714,10 @@ var CopilotKitCore = class {
|
|
|
5845
6714
|
get runtimeConnectionStatus() {
|
|
5846
6715
|
return this.agentRegistry.runtimeConnectionStatus;
|
|
5847
6716
|
}
|
|
6717
|
+
/** @internal The verbatim single-endpoint URL, including any trailing slash. */
|
|
6718
|
+
get ɵruntimeEndpointUrl() {
|
|
6719
|
+
return this.agentRegistry.runtimeEndpointUrl;
|
|
6720
|
+
}
|
|
5848
6721
|
get ɵruntimeFetch() {
|
|
5849
6722
|
return this.agentRegistry.createRuntimeFetch();
|
|
5850
6723
|
}
|
|
@@ -6004,6 +6877,83 @@ var CopilotKitCore = class {
|
|
|
6004
6877
|
getAgent(id) {
|
|
6005
6878
|
return this.agentRegistry.getAgent(id);
|
|
6006
6879
|
}
|
|
6880
|
+
/** Updates settings for the next capture. Removing the config stops capture. */
|
|
6881
|
+
setLearningConfig(config) {
|
|
6882
|
+
const wasConfigured = this.ɵlearningConfigured;
|
|
6883
|
+
this.learningBridge.setConfig(config);
|
|
6884
|
+
this.notifyTrajectoryChanged();
|
|
6885
|
+
if (this.ɵlearningConfigured === wasConfigured) return;
|
|
6886
|
+
for (const listener of this.learningConfiguredListeners) try {
|
|
6887
|
+
listener();
|
|
6888
|
+
} catch (error) {
|
|
6889
|
+
console.error("Learning configured listener error:", error);
|
|
6890
|
+
}
|
|
6891
|
+
}
|
|
6892
|
+
/**
|
|
6893
|
+
* @internal Whether a `learning` config is set. Unlike {@link trajectoryId},
|
|
6894
|
+
* it does not change with capture start, connection failures or reconnects.
|
|
6895
|
+
*/
|
|
6896
|
+
get ɵlearningConfigured() {
|
|
6897
|
+
return this.learningBridge.isConfigured;
|
|
6898
|
+
}
|
|
6899
|
+
/** @internal Calls `listener` when {@link ɵlearningConfigured} changes. */
|
|
6900
|
+
ɵsubscribeToLearningConfigured(listener) {
|
|
6901
|
+
this.learningConfiguredListeners.add(listener);
|
|
6902
|
+
return () => {
|
|
6903
|
+
this.learningConfiguredListeners.delete(listener);
|
|
6904
|
+
};
|
|
6905
|
+
}
|
|
6906
|
+
/** The active capture's Trajectory ID, or null while capture is stopped. */
|
|
6907
|
+
get trajectoryId() {
|
|
6908
|
+
return this.learningBridge.trajectoryId;
|
|
6909
|
+
}
|
|
6910
|
+
notifyTrajectoryChanged() {
|
|
6911
|
+
const trajectoryId = this.trajectoryId;
|
|
6912
|
+
if (trajectoryId === this.notifiedTrajectoryId) return;
|
|
6913
|
+
this.notifiedTrajectoryId = trajectoryId;
|
|
6914
|
+
this.notifySubscribers((subscriber) => subscriber.onTrajectoryChanged?.({
|
|
6915
|
+
copilotkit: this,
|
|
6916
|
+
trajectoryId
|
|
6917
|
+
}), "Subscriber onTrajectoryChanged error:");
|
|
6918
|
+
}
|
|
6919
|
+
/**
|
|
6920
|
+
* Starts one Trajectory after Runtime authorization and the Gateway join.
|
|
6921
|
+
* Omit the ID to create one. Repeated starts await the active connection;
|
|
6922
|
+
* a different ID stops the previous capture. Explicit sinks retain the
|
|
6923
|
+
* prototype's synchronous capture and require a stop before changing IDs.
|
|
6924
|
+
*
|
|
6925
|
+
* @example copilotkit.startTrajectory({ trajectoryId: crypto.randomUUID() })
|
|
6926
|
+
*/
|
|
6927
|
+
startTrajectory(options = {}) {
|
|
6928
|
+
const result = this.learningBridge.start(options);
|
|
6929
|
+
this.notifyTrajectoryChanged();
|
|
6930
|
+
return result;
|
|
6931
|
+
}
|
|
6932
|
+
/** Cancels pending work and stops capture, attempting one final batch without waiting for its ACK. */
|
|
6933
|
+
stopTrajectory() {
|
|
6934
|
+
this.learningBridge.stop();
|
|
6935
|
+
this.notifyTrajectoryChanged();
|
|
6936
|
+
}
|
|
6937
|
+
/**
|
|
6938
|
+
* Records an outcome that clicks cannot show, such as a saved report or an
|
|
6939
|
+
* approved deal. The wire adds `value.seq`; arrays, primitives, and objects
|
|
6940
|
+
* already containing `seq` are preserved under `value.data`. Explicit legacy
|
|
6941
|
+
* sinks retain object-only events with open-Thread enrichment.
|
|
6942
|
+
* No-op while no Trajectory runs. Events emitted during connection recovery
|
|
6943
|
+
* count as dropped. Built-in names such as `click` are rejected.
|
|
6944
|
+
*
|
|
6945
|
+
* @example copilotkit.emitTrajectoryEvent("deal.approved", { dealId: "deal-1" })
|
|
6946
|
+
*/
|
|
6947
|
+
emitTrajectoryEvent(name, value = {}) {
|
|
6948
|
+
this.learningBridge.emit(name, value);
|
|
6949
|
+
}
|
|
6950
|
+
/**
|
|
6951
|
+
* Tells Core that a view shows this Thread, so captured interactions can link to it.
|
|
6952
|
+
* Framework bindings call this; call it yourself only without a CopilotKit provider.
|
|
6953
|
+
*/
|
|
6954
|
+
registerOpenThread(params) {
|
|
6955
|
+
return this.learningBridge.registerOpenThread(params);
|
|
6956
|
+
}
|
|
6007
6957
|
/**
|
|
6008
6958
|
* Re-apply the current headers to a single agent (delegated to
|
|
6009
6959
|
* AgentRegistry). Core headers are merged on top of the agent's own
|