@copilotkit/core 1.76.0 → 1.77.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +881 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +93 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +93 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +881 -14
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +912 -18
- package/dist/index.umd.js.map +1 -1
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { BehaviorSubject, EMPTY, NEVER, Notification, Observable, Subject, Subsc
|
|
|
5
5
|
import { catchError, concatMap, concatWith, delay, distinctUntilChanged, endWith, filter, finalize, ignoreElements, map, mergeMap, observeOn as observeOn$1, scan, share, shareReplay, startWith, switchMap as switchMap$1, take, takeUntil, tap, timeout, withLatestFrom } from "rxjs/operators";
|
|
6
6
|
import { Socket } from "phoenix";
|
|
7
7
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
8
|
+
import { createCollector, createTrajectoryCollector } from "@copilotkit/learning";
|
|
8
9
|
|
|
9
10
|
//#region src/utils/http-agent.ts
|
|
10
11
|
/**
|
|
@@ -1906,6 +1907,10 @@ var AgentRegistry = class {
|
|
|
1906
1907
|
this.applyMessageFilterToAgent(agent);
|
|
1907
1908
|
});
|
|
1908
1909
|
}
|
|
1910
|
+
/** Preserve the exact configured URL when posting to a single endpoint. */
|
|
1911
|
+
get runtimeEndpointUrl() {
|
|
1912
|
+
return this._runtimeEndpointUrl;
|
|
1913
|
+
}
|
|
1909
1914
|
createRuntimeFetch() {
|
|
1910
1915
|
if (!this.runtimeFetch) this.runtimeFetch = (async (input, init) => {
|
|
1911
1916
|
const meta = () => runtimeRequestMeta(init);
|
|
@@ -2930,6 +2935,68 @@ function normalizeToolResultContent(content) {
|
|
|
2930
2935
|
function isForwardedToClientPlaceholder(content) {
|
|
2931
2936
|
return normalizeToolResultContent(content) === FORWARDED_TO_CLIENT;
|
|
2932
2937
|
}
|
|
2938
|
+
const MEDIA_PART_TYPES = new Set([
|
|
2939
|
+
"image",
|
|
2940
|
+
"audio",
|
|
2941
|
+
"video",
|
|
2942
|
+
"document"
|
|
2943
|
+
]);
|
|
2944
|
+
function isOptionalString(value) {
|
|
2945
|
+
return value === void 0 || typeof value === "string";
|
|
2946
|
+
}
|
|
2947
|
+
function isPartSource(value) {
|
|
2948
|
+
if (!value || typeof value !== "object") return false;
|
|
2949
|
+
const source = value;
|
|
2950
|
+
if (typeof source.value !== "string") return false;
|
|
2951
|
+
switch (source.type) {
|
|
2952
|
+
case "data": return typeof source.mimeType === "string";
|
|
2953
|
+
case "url": return isOptionalString(source.mimeType);
|
|
2954
|
+
case "file": return isOptionalString(source.provider) && isOptionalString(source.mimeType);
|
|
2955
|
+
default: return false;
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
/**
|
|
2959
|
+
* Mirrors `ContentPartSchema` from `@ag-ui/core/schemas`. The runtime parses
|
|
2960
|
+
* the request with that schema, and one invalid part there fails the whole run.
|
|
2961
|
+
* Core does not import the schema because the UMD build has no global for it.
|
|
2962
|
+
* A test checks this function against the schema.
|
|
2963
|
+
*/
|
|
2964
|
+
function isContentPart(value) {
|
|
2965
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2966
|
+
const part = value;
|
|
2967
|
+
if (!isOptionalString(part.id) || part.metadata === null) return false;
|
|
2968
|
+
if (part.type === "text") return typeof part.text === "string";
|
|
2969
|
+
return typeof part.type === "string" && MEDIA_PART_TYPES.has(part.type) && isPartSource(part.source);
|
|
2970
|
+
}
|
|
2971
|
+
/**
|
|
2972
|
+
* Converts what a frontend tool handler returned into tool message content.
|
|
2973
|
+
* A non-empty array of AG-UI content parts passes through unchanged, so an
|
|
2974
|
+
* image reaches the model as media. Everything else becomes a string, as before.
|
|
2975
|
+
*
|
|
2976
|
+
* `text` is the string form, for surfaces typed as `result: string`
|
|
2977
|
+
* (`runTool`, `onToolExecutionEnd`). For content parts it is the JSON text they
|
|
2978
|
+
* received before. It is computed here, so a part that cannot be serialized
|
|
2979
|
+
* throws inside the caller's handler error boundary.
|
|
2980
|
+
*/
|
|
2981
|
+
function toToolResultContent(result) {
|
|
2982
|
+
if (result === void 0 || result === null) return {
|
|
2983
|
+
content: "",
|
|
2984
|
+
text: ""
|
|
2985
|
+
};
|
|
2986
|
+
if (typeof result === "string") return {
|
|
2987
|
+
content: result,
|
|
2988
|
+
text: result
|
|
2989
|
+
};
|
|
2990
|
+
const text = JSON.stringify(result);
|
|
2991
|
+
if (Array.isArray(result) && result.length > 0 && result.every(isContentPart)) return {
|
|
2992
|
+
content: result,
|
|
2993
|
+
text
|
|
2994
|
+
};
|
|
2995
|
+
return {
|
|
2996
|
+
content: text,
|
|
2997
|
+
text
|
|
2998
|
+
};
|
|
2999
|
+
}
|
|
2933
3000
|
|
|
2934
3001
|
//#endregion
|
|
2935
3002
|
//#region src/core/tool-schema.ts
|
|
@@ -3643,6 +3710,7 @@ var RunHandler = class {
|
|
|
3643
3710
|
*/
|
|
3644
3711
|
async executeToolHandler({ tool, toolCall, agent, agentId, handlerArgs, toolType, messageId, signal = this._runAbortController?.signal, discardOnAbort = false }) {
|
|
3645
3712
|
let toolCallResult = "";
|
|
3713
|
+
let toolCallText = "";
|
|
3646
3714
|
let errorMessage;
|
|
3647
3715
|
let isArgumentError = false;
|
|
3648
3716
|
let parsedArgs;
|
|
@@ -3678,9 +3746,7 @@ var RunHandler = class {
|
|
|
3678
3746
|
agent,
|
|
3679
3747
|
signal
|
|
3680
3748
|
});
|
|
3681
|
-
|
|
3682
|
-
else if (typeof result === "string") toolCallResult = result;
|
|
3683
|
-
else toolCallResult = JSON.stringify(result);
|
|
3749
|
+
({content: toolCallResult, text: toolCallText} = toToolResultContent(result));
|
|
3684
3750
|
} catch (error) {
|
|
3685
3751
|
const handlerError = error instanceof Error ? error : new Error(String(error));
|
|
3686
3752
|
errorMessage = handlerError.message;
|
|
@@ -3697,17 +3763,18 @@ var RunHandler = class {
|
|
|
3697
3763
|
}
|
|
3698
3764
|
});
|
|
3699
3765
|
}
|
|
3700
|
-
if (errorMessage) toolCallResult = `Error: ${errorMessage}`;
|
|
3766
|
+
if (errorMessage) toolCallResult = toolCallText = `Error: ${errorMessage}`;
|
|
3701
3767
|
await this._internal.notifySubscribers((subscriber) => subscriber.onToolExecutionEnd?.({
|
|
3702
3768
|
copilotkit: this.core,
|
|
3703
3769
|
toolCallId: toolCall.id,
|
|
3704
3770
|
agentId,
|
|
3705
3771
|
toolName: toolCall.function.name,
|
|
3706
|
-
result: errorMessage ? "" :
|
|
3772
|
+
result: errorMessage ? "" : toolCallText,
|
|
3707
3773
|
error: errorMessage
|
|
3708
3774
|
}), "Subscriber onToolExecutionEnd error:");
|
|
3709
3775
|
return {
|
|
3710
|
-
|
|
3776
|
+
content: toolCallResult,
|
|
3777
|
+
result: toolCallText,
|
|
3711
3778
|
error: errorMessage,
|
|
3712
3779
|
isArgumentError
|
|
3713
3780
|
};
|
|
@@ -3719,6 +3786,7 @@ var RunHandler = class {
|
|
|
3719
3786
|
const threadId = agent.threadId;
|
|
3720
3787
|
if (tool?.agentId && tool.agentId !== agent.agentId) return false;
|
|
3721
3788
|
let handlerResult = {
|
|
3789
|
+
content: "",
|
|
3722
3790
|
result: "",
|
|
3723
3791
|
error: void 0,
|
|
3724
3792
|
isArgumentError: false
|
|
@@ -3743,7 +3811,7 @@ var RunHandler = class {
|
|
|
3743
3811
|
id: randomUUID$1(),
|
|
3744
3812
|
role: "tool",
|
|
3745
3813
|
toolCallId: toolCall.id,
|
|
3746
|
-
content: handlerResult.
|
|
3814
|
+
content: handlerResult.content
|
|
3747
3815
|
};
|
|
3748
3816
|
agent.messages.splice(insertAt, 0, toolMessage);
|
|
3749
3817
|
if (!handlerResult.error && tool?.followUp !== false) return true;
|
|
@@ -3760,6 +3828,7 @@ var RunHandler = class {
|
|
|
3760
3828
|
const threadId = agent.threadId;
|
|
3761
3829
|
if (wildcardTool?.agentId && wildcardTool.agentId !== agent.agentId) return false;
|
|
3762
3830
|
let toolCallResult = "";
|
|
3831
|
+
let toolCallText = "";
|
|
3763
3832
|
let errorMessage;
|
|
3764
3833
|
if (wildcardTool?.handler) {
|
|
3765
3834
|
let parsedArgs;
|
|
@@ -3798,9 +3867,7 @@ var RunHandler = class {
|
|
|
3798
3867
|
agent,
|
|
3799
3868
|
signal
|
|
3800
3869
|
});
|
|
3801
|
-
|
|
3802
|
-
else if (typeof result === "string") toolCallResult = result;
|
|
3803
|
-
else toolCallResult = JSON.stringify(result);
|
|
3870
|
+
({content: toolCallResult, text: toolCallText} = toToolResultContent(result));
|
|
3804
3871
|
} catch (error) {
|
|
3805
3872
|
const handlerError = error instanceof Error ? error : new Error(String(error));
|
|
3806
3873
|
errorMessage = handlerError.message;
|
|
@@ -3817,13 +3884,13 @@ var RunHandler = class {
|
|
|
3817
3884
|
}
|
|
3818
3885
|
});
|
|
3819
3886
|
}
|
|
3820
|
-
if (errorMessage) toolCallResult = `Error: ${errorMessage}`;
|
|
3887
|
+
if (errorMessage) toolCallResult = toolCallText = `Error: ${errorMessage}`;
|
|
3821
3888
|
await this._internal.notifySubscribers((subscriber) => subscriber.onToolExecutionEnd?.({
|
|
3822
3889
|
copilotkit: this.core,
|
|
3823
3890
|
toolCallId: toolCall.id,
|
|
3824
3891
|
agentId,
|
|
3825
3892
|
toolName: toolCall.function.name,
|
|
3826
|
-
result: errorMessage ? "" :
|
|
3893
|
+
result: errorMessage ? "" : toolCallText,
|
|
3827
3894
|
error: errorMessage
|
|
3828
3895
|
}), "Subscriber onToolExecutionEnd error:");
|
|
3829
3896
|
}
|
|
@@ -3894,6 +3961,7 @@ var RunHandler = class {
|
|
|
3894
3961
|
};
|
|
3895
3962
|
agent.messages.push(assistantMessage);
|
|
3896
3963
|
let handlerResult = {
|
|
3964
|
+
content: "",
|
|
3897
3965
|
result: "",
|
|
3898
3966
|
error: void 0,
|
|
3899
3967
|
isArgumentError: false
|
|
@@ -3910,7 +3978,7 @@ var RunHandler = class {
|
|
|
3910
3978
|
id: randomUUID$1(),
|
|
3911
3979
|
role: "tool",
|
|
3912
3980
|
toolCallId,
|
|
3913
|
-
content: handlerResult.
|
|
3981
|
+
content: handlerResult.content
|
|
3914
3982
|
};
|
|
3915
3983
|
const assistantIndex = agent.messages.findIndex((m) => m.id === assistantMessage.id);
|
|
3916
3984
|
if (assistantIndex !== -1) agent.messages.splice(assistantIndex + 1, 0, toolResultMessage);
|
|
@@ -4511,6 +4579,720 @@ var ThreadStoreRegistry = class {
|
|
|
4511
4579
|
}
|
|
4512
4580
|
};
|
|
4513
4581
|
|
|
4582
|
+
//#endregion
|
|
4583
|
+
//#region src/core/trajectory-connection.ts
|
|
4584
|
+
const TIMEOUT_MS = 1e4;
|
|
4585
|
+
const BATCH_INTERVAL_MS = 2e3;
|
|
4586
|
+
const MAX_BATCH_EVENTS = 50;
|
|
4587
|
+
const MAX_BATCH_BYTES = 64 * 1024;
|
|
4588
|
+
const MAX_EVENT_BYTES = 16 * 1024;
|
|
4589
|
+
const byteLength = (value) => new TextEncoder().encode(JSON.stringify(value)).byteLength;
|
|
4590
|
+
const reconnectDelay = phoenixExponentialBackoff(1e3, 1e4);
|
|
4591
|
+
function isObject(value) {
|
|
4592
|
+
return typeof value === "object" && value !== null;
|
|
4593
|
+
}
|
|
4594
|
+
function isNonNegativeInteger(value) {
|
|
4595
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
4596
|
+
}
|
|
4597
|
+
function isBatchAcknowledgement(value, eventCount) {
|
|
4598
|
+
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);
|
|
4599
|
+
}
|
|
4600
|
+
function isGrant(value) {
|
|
4601
|
+
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;
|
|
4602
|
+
}
|
|
4603
|
+
function errorCode(value, fallback) {
|
|
4604
|
+
return isObject(value) && typeof value.code === "string" && /^[A-Z][A-Z0-9_]{0,63}$/.test(value.code) ? value.code : fallback;
|
|
4605
|
+
}
|
|
4606
|
+
/** Owns bounded, connected-only capture batches. Failed batches are never resent. */
|
|
4607
|
+
var TrajectoryConnection = class {
|
|
4608
|
+
session;
|
|
4609
|
+
nextSeq = Math.floor(Date.now()) * 1e3;
|
|
4610
|
+
constructor(core, onChange) {
|
|
4611
|
+
this.core = core;
|
|
4612
|
+
this.onChange = onChange;
|
|
4613
|
+
}
|
|
4614
|
+
get trajectoryId() {
|
|
4615
|
+
return this.session?.ready ? this.session.trajectoryId : null;
|
|
4616
|
+
}
|
|
4617
|
+
start(trajectoryId, config) {
|
|
4618
|
+
if (this.session && (trajectoryId === void 0 || trajectoryId === this.session.trajectoryId)) return this.session.promise;
|
|
4619
|
+
this.stop();
|
|
4620
|
+
const id = trajectoryId ?? crypto.randomUUID();
|
|
4621
|
+
let resolveStart;
|
|
4622
|
+
const promise = new Promise((resolve) => {
|
|
4623
|
+
resolveStart = resolve;
|
|
4624
|
+
});
|
|
4625
|
+
const session = {
|
|
4626
|
+
trajectoryId: id,
|
|
4627
|
+
config,
|
|
4628
|
+
promise,
|
|
4629
|
+
resolve: resolveStart,
|
|
4630
|
+
retries: 0,
|
|
4631
|
+
queue: [],
|
|
4632
|
+
dropped: 0,
|
|
4633
|
+
ready: false,
|
|
4634
|
+
started: false,
|
|
4635
|
+
offline: typeof navigator !== "undefined" && navigator.onLine === false,
|
|
4636
|
+
onOffline: () => {
|
|
4637
|
+
if (this.session !== session) return;
|
|
4638
|
+
session.offline = true;
|
|
4639
|
+
if (session.connection) this.fail(session, session.connection, "CONNECTION_LOST");
|
|
4640
|
+
clearTimeout(session.retry);
|
|
4641
|
+
session.retry = void 0;
|
|
4642
|
+
},
|
|
4643
|
+
onOnline: () => {
|
|
4644
|
+
if (this.session !== session) return;
|
|
4645
|
+
session.offline = false;
|
|
4646
|
+
if (!session.connection) {
|
|
4647
|
+
clearTimeout(session.retry);
|
|
4648
|
+
session.retry = void 0;
|
|
4649
|
+
this.connect(session);
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
};
|
|
4653
|
+
this.session = session;
|
|
4654
|
+
if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
|
|
4655
|
+
window.addEventListener("offline", session.onOffline);
|
|
4656
|
+
window.addEventListener("online", session.onOnline);
|
|
4657
|
+
}
|
|
4658
|
+
if (session.offline) {
|
|
4659
|
+
this.report(session, "CONNECTION_LOST");
|
|
4660
|
+
this.end(session, {
|
|
4661
|
+
status: "error",
|
|
4662
|
+
code: "CONNECTION_LOST"
|
|
4663
|
+
});
|
|
4664
|
+
} else this.connect(session);
|
|
4665
|
+
return promise;
|
|
4666
|
+
}
|
|
4667
|
+
stop() {
|
|
4668
|
+
const session = this.session;
|
|
4669
|
+
if (!session) return;
|
|
4670
|
+
if (session.ready && session.connection) this.flush(session, session.connection);
|
|
4671
|
+
this.end(session, {
|
|
4672
|
+
status: "error",
|
|
4673
|
+
code: "CANCELLED"
|
|
4674
|
+
});
|
|
4675
|
+
}
|
|
4676
|
+
emit(name, value) {
|
|
4677
|
+
const session = this.session;
|
|
4678
|
+
if (session?.ready) session.collector?.emit(name, value);
|
|
4679
|
+
else if (session?.started) this.addDropped(session, 1, false);
|
|
4680
|
+
}
|
|
4681
|
+
report(session, code) {
|
|
4682
|
+
try {
|
|
4683
|
+
session.config.onError?.({
|
|
4684
|
+
code,
|
|
4685
|
+
message: `Trajectory capture: ${code}.`
|
|
4686
|
+
});
|
|
4687
|
+
} catch {}
|
|
4688
|
+
}
|
|
4689
|
+
current(session, connection) {
|
|
4690
|
+
return this.session === session && session.connection === connection;
|
|
4691
|
+
}
|
|
4692
|
+
end(session, result) {
|
|
4693
|
+
if (this.session !== session) return;
|
|
4694
|
+
this.session = void 0;
|
|
4695
|
+
clearTimeout(session.retry);
|
|
4696
|
+
session.collector?.stop();
|
|
4697
|
+
session.ready = false;
|
|
4698
|
+
if (session.queue.length > 0) this.report(session, "EVENTS_DROPPED");
|
|
4699
|
+
this.discardQueue(session);
|
|
4700
|
+
if (session.connection) {
|
|
4701
|
+
for (const pending of session.connection.pending) {
|
|
4702
|
+
pending.finish("PERSISTENCE_UNKNOWN");
|
|
4703
|
+
this.report(session, "PERSISTENCE_UNKNOWN");
|
|
4704
|
+
}
|
|
4705
|
+
this.cleanup(session, session.connection);
|
|
4706
|
+
}
|
|
4707
|
+
if (typeof window !== "undefined" && typeof window.removeEventListener === "function") {
|
|
4708
|
+
window.removeEventListener("offline", session.onOffline);
|
|
4709
|
+
window.removeEventListener("online", session.onOnline);
|
|
4710
|
+
}
|
|
4711
|
+
session.resolve(result);
|
|
4712
|
+
this.onChange();
|
|
4713
|
+
}
|
|
4714
|
+
cleanup(session, connection) {
|
|
4715
|
+
session.connection = void 0;
|
|
4716
|
+
clearTimeout(connection.timer);
|
|
4717
|
+
connection.abort.abort();
|
|
4718
|
+
for (const pending of connection.pending) clearTimeout(pending.timer);
|
|
4719
|
+
connection.pending.clear();
|
|
4720
|
+
const { socket, channel } = connection;
|
|
4721
|
+
try {
|
|
4722
|
+
if (channel) {
|
|
4723
|
+
for (const [event, ref] of connection.channelRefs) channel.off(event, ref);
|
|
4724
|
+
channel.leave();
|
|
4725
|
+
}
|
|
4726
|
+
} finally {
|
|
4727
|
+
if (socket) {
|
|
4728
|
+
socket.off(connection.socketRefs);
|
|
4729
|
+
socket.disconnect();
|
|
4730
|
+
}
|
|
4731
|
+
}
|
|
4732
|
+
}
|
|
4733
|
+
fail(session, connection, code, retryable = true) {
|
|
4734
|
+
if (!this.current(session, connection)) return;
|
|
4735
|
+
session.collector?.stop();
|
|
4736
|
+
session.collector = void 0;
|
|
4737
|
+
if (session.ready && session.started) session.promise = new Promise((resolve) => {
|
|
4738
|
+
session.resolve = resolve;
|
|
4739
|
+
});
|
|
4740
|
+
session.ready = false;
|
|
4741
|
+
this.discardQueue(session);
|
|
4742
|
+
for (const pending of connection.pending) pending.finish("PERSISTENCE_UNKNOWN");
|
|
4743
|
+
if (!this.current(session, connection)) return;
|
|
4744
|
+
this.cleanup(session, connection);
|
|
4745
|
+
this.onChange();
|
|
4746
|
+
this.report(session, code);
|
|
4747
|
+
if (this.session !== session) return;
|
|
4748
|
+
if (!session.started || !retryable) {
|
|
4749
|
+
this.end(session, {
|
|
4750
|
+
status: "error",
|
|
4751
|
+
code
|
|
4752
|
+
});
|
|
4753
|
+
return;
|
|
4754
|
+
}
|
|
4755
|
+
if (session.offline || this.session !== session) return;
|
|
4756
|
+
session.retry = setTimeout(() => {
|
|
4757
|
+
session.retry = void 0;
|
|
4758
|
+
if (this.session === session && !session.offline) this.connect(session);
|
|
4759
|
+
}, reconnectDelay(++session.retries));
|
|
4760
|
+
}
|
|
4761
|
+
async connect(session) {
|
|
4762
|
+
if (this.session !== session || session.offline || session.connection) return;
|
|
4763
|
+
const connection = {
|
|
4764
|
+
abort: new AbortController(),
|
|
4765
|
+
socketRefs: [],
|
|
4766
|
+
channelRefs: [],
|
|
4767
|
+
pending: /* @__PURE__ */ new Set()
|
|
4768
|
+
};
|
|
4769
|
+
session.connection = connection;
|
|
4770
|
+
connection.timer = setTimeout(() => this.fail(session, connection, "CONNECTION_TIMEOUT"), TIMEOUT_MS);
|
|
4771
|
+
try {
|
|
4772
|
+
const runtimeUrl = this.core.runtimeUrl;
|
|
4773
|
+
if (!runtimeUrl) {
|
|
4774
|
+
this.fail(session, connection, "RUNTIME_REQUIRED", false);
|
|
4775
|
+
return;
|
|
4776
|
+
}
|
|
4777
|
+
let transport = this.core.runtimeTransport;
|
|
4778
|
+
if (transport === "auto") {
|
|
4779
|
+
transport = await this.detectedTransport(connection.abort.signal);
|
|
4780
|
+
if (!this.current(session, connection)) return;
|
|
4781
|
+
}
|
|
4782
|
+
const rest = transport === "rest";
|
|
4783
|
+
const response = await fetch(rest ? `${runtimeUrl}/trajectory/${encodeURIComponent(session.trajectoryId)}/connect` : this.core.ɵruntimeEndpointUrl ?? runtimeUrl, {
|
|
4784
|
+
method: "POST",
|
|
4785
|
+
redirect: "error",
|
|
4786
|
+
signal: connection.abort.signal,
|
|
4787
|
+
headers: {
|
|
4788
|
+
...this.core.headers,
|
|
4789
|
+
"Content-Type": "application/json"
|
|
4790
|
+
},
|
|
4791
|
+
credentials: this.core.credentials,
|
|
4792
|
+
body: JSON.stringify(rest ? {} : {
|
|
4793
|
+
method: "trajectory/connect",
|
|
4794
|
+
params: { trajectoryId: session.trajectoryId },
|
|
4795
|
+
body: {}
|
|
4796
|
+
})
|
|
4797
|
+
});
|
|
4798
|
+
if (!this.current(session, connection)) return;
|
|
4799
|
+
const payload = await response.json().catch(() => void 0);
|
|
4800
|
+
if (!this.current(session, connection)) return;
|
|
4801
|
+
if (!response.ok) {
|
|
4802
|
+
this.fail(session, connection, errorCode(payload, "CONNECTION_FAILED"), response.status >= 500 || response.status === 408 || response.status === 429);
|
|
4803
|
+
return;
|
|
4804
|
+
}
|
|
4805
|
+
if (!isGrant(payload)) {
|
|
4806
|
+
this.fail(session, connection, "INVALID_GRANT", false);
|
|
4807
|
+
return;
|
|
4808
|
+
}
|
|
4809
|
+
const socket = new Socket(payload.realtime.clientUrl, {
|
|
4810
|
+
params: { join_token: payload.joinToken },
|
|
4811
|
+
timeout: TIMEOUT_MS
|
|
4812
|
+
});
|
|
4813
|
+
connection.socket = socket;
|
|
4814
|
+
const lost = () => this.fail(session, connection, "CONNECTION_LOST");
|
|
4815
|
+
connection.socketRefs.push(socket.onError(lost), socket.onClose(lost));
|
|
4816
|
+
const channel = socket.channel(payload.realtime.topic, {});
|
|
4817
|
+
connection.channel = channel;
|
|
4818
|
+
connection.channelRefs.push(["phx_error", channel.onError(lost)], ["phx_close", channel.onClose(lost)]);
|
|
4819
|
+
socket.connect();
|
|
4820
|
+
if (!this.current(session, connection)) return;
|
|
4821
|
+
channel.join(TIMEOUT_MS).receive("ok", () => {
|
|
4822
|
+
if (!this.current(session, connection)) return;
|
|
4823
|
+
try {
|
|
4824
|
+
clearTimeout(connection.timer);
|
|
4825
|
+
session.ready = true;
|
|
4826
|
+
session.retries = 0;
|
|
4827
|
+
session.collector = createTrajectoryCollector({
|
|
4828
|
+
...session.config,
|
|
4829
|
+
ignoreUrls: [
|
|
4830
|
+
...session.config.ignoreUrls ?? [],
|
|
4831
|
+
...this.core.runtimeUrl ? [this.core.runtimeUrl] : [],
|
|
4832
|
+
...this.core.ɵruntimeEndpointUrl ? [this.core.ɵruntimeEndpointUrl] : [],
|
|
4833
|
+
"https://telemetry.copilotkit.ai/",
|
|
4834
|
+
"https://cdn.copilotkit.ai/"
|
|
4835
|
+
],
|
|
4836
|
+
onError: (error) => {
|
|
4837
|
+
if (error.code === "CAPTURE_FAILED") this.fail(session, connection, error.code, false);
|
|
4838
|
+
else {
|
|
4839
|
+
if (error.code === "INVALID_EVENT" || error.code === "EVENT_TOO_LARGE") this.addDropped(session, 1);
|
|
4840
|
+
session.config.onError?.(error);
|
|
4841
|
+
}
|
|
4842
|
+
},
|
|
4843
|
+
send: (event) => this.send(session, connection, event)
|
|
4844
|
+
});
|
|
4845
|
+
session.collector.start();
|
|
4846
|
+
if (!this.current(session, connection)) return;
|
|
4847
|
+
if (session.dropped > 0) this.scheduleFlush(session, connection);
|
|
4848
|
+
session.started = true;
|
|
4849
|
+
session.resolve({
|
|
4850
|
+
status: "started",
|
|
4851
|
+
trajectoryId: session.trajectoryId
|
|
4852
|
+
});
|
|
4853
|
+
this.onChange();
|
|
4854
|
+
} catch {
|
|
4855
|
+
this.fail(session, connection, "CAPTURE_FAILED", false);
|
|
4856
|
+
}
|
|
4857
|
+
}).receive("error", (joinError) => this.fail(session, connection, errorCode(joinError, "JOIN_FAILED"))).receive("timeout", () => this.fail(session, connection, "CONNECTION_TIMEOUT"));
|
|
4858
|
+
} catch {
|
|
4859
|
+
this.fail(session, connection, "CONNECTION_FAILED");
|
|
4860
|
+
}
|
|
4861
|
+
}
|
|
4862
|
+
/** Resolves once auto-detection picks a transport, or with "auto" when the attempt is aborted. */
|
|
4863
|
+
detectedTransport(signal) {
|
|
4864
|
+
return new Promise((resolve) => {
|
|
4865
|
+
const finish = () => {
|
|
4866
|
+
subscription.unsubscribe();
|
|
4867
|
+
signal.removeEventListener("abort", finish);
|
|
4868
|
+
resolve(this.core.runtimeTransport);
|
|
4869
|
+
};
|
|
4870
|
+
const subscription = this.core.subscribe({ onRuntimeConnectionStatusChanged: () => {
|
|
4871
|
+
if (this.core.runtimeTransport !== "auto") finish();
|
|
4872
|
+
} });
|
|
4873
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
4874
|
+
});
|
|
4875
|
+
}
|
|
4876
|
+
addDropped(session, count, schedule = true) {
|
|
4877
|
+
session.dropped = Math.min(Number.MAX_SAFE_INTEGER, session.dropped + count);
|
|
4878
|
+
if (schedule && count > 0 && session.ready && session.connection) this.scheduleFlush(session, session.connection);
|
|
4879
|
+
}
|
|
4880
|
+
scheduleFlush(session, connection) {
|
|
4881
|
+
if (session.flushTimer !== void 0 || !this.current(session, connection)) return;
|
|
4882
|
+
session.flushTimer = setTimeout(() => {
|
|
4883
|
+
session.flushTimer = void 0;
|
|
4884
|
+
this.flush(session, connection);
|
|
4885
|
+
}, BATCH_INTERVAL_MS);
|
|
4886
|
+
}
|
|
4887
|
+
discardQueue(session) {
|
|
4888
|
+
clearTimeout(session.flushTimer);
|
|
4889
|
+
session.flushTimer = void 0;
|
|
4890
|
+
this.addDropped(session, session.queue.length, false);
|
|
4891
|
+
session.queue = [];
|
|
4892
|
+
}
|
|
4893
|
+
connected(connection) {
|
|
4894
|
+
return connection.socket?.isConnected() === true && connection.channel?.state === "joined";
|
|
4895
|
+
}
|
|
4896
|
+
send(session, connection, event) {
|
|
4897
|
+
if (!this.current(session, connection) || !session.ready) return;
|
|
4898
|
+
if (!this.connected(connection)) {
|
|
4899
|
+
this.addDropped(session, 1);
|
|
4900
|
+
this.fail(session, connection, "CONNECTION_LOST");
|
|
4901
|
+
return;
|
|
4902
|
+
}
|
|
4903
|
+
if (!Number.isSafeInteger(this.nextSeq)) {
|
|
4904
|
+
this.addDropped(session, 1);
|
|
4905
|
+
this.report(session, "SEQUENCE_EXHAUSTED");
|
|
4906
|
+
return;
|
|
4907
|
+
}
|
|
4908
|
+
const original = event.value;
|
|
4909
|
+
const fields = isObject(original) && !Array.isArray(original) && !Object.hasOwn(original, "seq") ? original : { data: original };
|
|
4910
|
+
const wire = {
|
|
4911
|
+
...event,
|
|
4912
|
+
value: {
|
|
4913
|
+
...fields,
|
|
4914
|
+
seq: this.nextSeq++
|
|
4915
|
+
}
|
|
4916
|
+
};
|
|
4917
|
+
if (byteLength(wire) > MAX_EVENT_BYTES) {
|
|
4918
|
+
this.addDropped(session, 1);
|
|
4919
|
+
this.report(session, "EVENT_TOO_LARGE");
|
|
4920
|
+
return;
|
|
4921
|
+
}
|
|
4922
|
+
const fits = () => session.queue.length < MAX_BATCH_EVENTS && byteLength({
|
|
4923
|
+
events: [...session.queue, wire],
|
|
4924
|
+
dropped: Number.MAX_SAFE_INTEGER
|
|
4925
|
+
}) <= MAX_BATCH_BYTES;
|
|
4926
|
+
if (!fits()) this.flush(session, connection);
|
|
4927
|
+
if (!this.current(session, connection) || !session.ready) return;
|
|
4928
|
+
if (!fits()) {
|
|
4929
|
+
this.addDropped(session, 1);
|
|
4930
|
+
this.report(session, "EVENTS_DROPPED");
|
|
4931
|
+
return;
|
|
4932
|
+
}
|
|
4933
|
+
session.queue.push(wire);
|
|
4934
|
+
if (session.queue.length === MAX_BATCH_EVENTS) this.flush(session, connection);
|
|
4935
|
+
else this.scheduleFlush(session, connection);
|
|
4936
|
+
}
|
|
4937
|
+
flush(session, connection) {
|
|
4938
|
+
if (!this.current(session, connection) || !session.ready || session.queue.length === 0 && session.dropped === 0) return;
|
|
4939
|
+
if (!this.connected(connection)) {
|
|
4940
|
+
this.fail(session, connection, "CONNECTION_LOST");
|
|
4941
|
+
return;
|
|
4942
|
+
}
|
|
4943
|
+
if (connection.pending.size > 0) return;
|
|
4944
|
+
clearTimeout(session.flushTimer);
|
|
4945
|
+
session.flushTimer = void 0;
|
|
4946
|
+
const batch = {
|
|
4947
|
+
events: session.queue,
|
|
4948
|
+
dropped: session.dropped
|
|
4949
|
+
};
|
|
4950
|
+
session.queue = [];
|
|
4951
|
+
session.dropped = 0;
|
|
4952
|
+
let finished = false;
|
|
4953
|
+
const pending = {
|
|
4954
|
+
timer: setTimeout(() => pending.finish("PERSISTENCE_UNKNOWN"), TIMEOUT_MS),
|
|
4955
|
+
finish: (code, knownRollback = false, flushNext = true) => {
|
|
4956
|
+
if (finished) return;
|
|
4957
|
+
finished = true;
|
|
4958
|
+
clearTimeout(pending.timer);
|
|
4959
|
+
connection.pending.delete(pending);
|
|
4960
|
+
if (knownRollback) this.addDropped(session, batch.events.length + batch.dropped, false);
|
|
4961
|
+
if (code && this.current(session, connection)) this.report(session, code);
|
|
4962
|
+
if (flushNext && this.current(session, connection) && session.ready && (session.queue.length > 0 || !knownRollback && session.dropped > 0)) this.flush(session, connection);
|
|
4963
|
+
}
|
|
4964
|
+
};
|
|
4965
|
+
connection.pending.add(pending);
|
|
4966
|
+
try {
|
|
4967
|
+
connection.channel.push("events", batch, TIMEOUT_MS).receive("ok", (payload) => {
|
|
4968
|
+
pending.finish(!isBatchAcknowledgement(payload, batch.events.length) ? "PERSISTENCE_UNKNOWN" : payload.rejected > 0 ? "EVENTS_REJECTED" : void 0);
|
|
4969
|
+
}).receive("error", (payload) => {
|
|
4970
|
+
if (!this.current(session, connection)) return;
|
|
4971
|
+
const reason = isObject(payload) ? payload.reason : void 0;
|
|
4972
|
+
if (reason === "unauthorized" || reason === "trajectory_mismatch") {
|
|
4973
|
+
pending.finish(void 0, true, false);
|
|
4974
|
+
this.fail(session, connection, reason.toUpperCase(), false);
|
|
4975
|
+
return;
|
|
4976
|
+
}
|
|
4977
|
+
const rollback = typeof reason === "string" && [
|
|
4978
|
+
"invalid_batch",
|
|
4979
|
+
"batch_too_large",
|
|
4980
|
+
"batch_too_many_events",
|
|
4981
|
+
"trajectory_over_share",
|
|
4982
|
+
"trajectory_outbox_full",
|
|
4983
|
+
"unsupported_event"
|
|
4984
|
+
].includes(reason);
|
|
4985
|
+
pending.finish(rollback ? reason.toUpperCase() : "PERSISTENCE_UNKNOWN", rollback);
|
|
4986
|
+
}).receive("timeout", () => pending.finish("PERSISTENCE_UNKNOWN"));
|
|
4987
|
+
} catch {
|
|
4988
|
+
pending.finish("PERSISTENCE_UNKNOWN");
|
|
4989
|
+
}
|
|
4990
|
+
}
|
|
4991
|
+
};
|
|
4992
|
+
|
|
4993
|
+
//#endregion
|
|
4994
|
+
//#region src/core/learning-bridge.ts
|
|
4995
|
+
const COPILOTKIT_OWN_URLS = ["https://telemetry.copilotkit.ai/", "https://cdn.copilotkit.ai/"];
|
|
4996
|
+
/**
|
|
4997
|
+
* Wires `@copilotkit/learning` into Core. Default capture uses Runtime and Phoenix;
|
|
4998
|
+
* explicit legacy sinks also use agent events and open-Thread attribution.
|
|
4999
|
+
*/
|
|
5000
|
+
var LearningBridge = class {
|
|
5001
|
+
collector = null;
|
|
5002
|
+
agents = /* @__PURE__ */ new Map();
|
|
5003
|
+
openThreads = /* @__PURE__ */ new Set();
|
|
5004
|
+
toolCalls = /* @__PURE__ */ new Map();
|
|
5005
|
+
executingTools = /* @__PURE__ */ new Map();
|
|
5006
|
+
lastRunIds = /* @__PURE__ */ new WeakMap();
|
|
5007
|
+
lastLinkedThreadId = null;
|
|
5008
|
+
coreSubscription = null;
|
|
5009
|
+
seq = 0;
|
|
5010
|
+
activeConfig;
|
|
5011
|
+
connection;
|
|
5012
|
+
constructor(core, config, onChange = () => {}) {
|
|
5013
|
+
this.core = core;
|
|
5014
|
+
this.config = config;
|
|
5015
|
+
this.connection = new TrajectoryConnection(core, onChange);
|
|
5016
|
+
this.setConfig(config);
|
|
5017
|
+
}
|
|
5018
|
+
setConfig(config) {
|
|
5019
|
+
this.config = config;
|
|
5020
|
+
if (config === void 0) this.stop();
|
|
5021
|
+
}
|
|
5022
|
+
get isConfigured() {
|
|
5023
|
+
return this.config !== void 0;
|
|
5024
|
+
}
|
|
5025
|
+
get isLegacyCaptureActive() {
|
|
5026
|
+
return this.collector !== null && this.collector.trajectoryId !== null;
|
|
5027
|
+
}
|
|
5028
|
+
startTracking() {
|
|
5029
|
+
if (this.coreSubscription !== null || !this.isLegacyCaptureActive) return;
|
|
5030
|
+
this.coreSubscription = this.core.subscribe({
|
|
5031
|
+
onAgentsChanged: ({ agents }) => {
|
|
5032
|
+
for (const agent of Object.values(agents)) this.track(agent);
|
|
5033
|
+
},
|
|
5034
|
+
onAgentRunStarted: ({ agent }) => this.track(agent),
|
|
5035
|
+
onToolExecutionStart: ({ toolCallId, agentId, toolName }) => {
|
|
5036
|
+
if (!this.isLegacyCaptureActive) return;
|
|
5037
|
+
this.executingTools.set(toolCallId, Date.now());
|
|
5038
|
+
this.emitToolCall("executing", toolCallId, agentId, toolName, {});
|
|
5039
|
+
},
|
|
5040
|
+
onToolExecutionEnd: ({ toolCallId, agentId, toolName, error }) => {
|
|
5041
|
+
if (!this.isLegacyCaptureActive) return;
|
|
5042
|
+
const startedAt = this.executingTools.get(toolCallId);
|
|
5043
|
+
this.executingTools.delete(toolCallId);
|
|
5044
|
+
this.emitToolCall("completed", toolCallId, agentId, toolName, {
|
|
5045
|
+
durationMs: startedAt === void 0 ? void 0 : Date.now() - startedAt,
|
|
5046
|
+
outcome: error === void 0 ? "ok" : "error"
|
|
5047
|
+
});
|
|
5048
|
+
this.toolCalls.delete(toolCallId);
|
|
5049
|
+
}
|
|
5050
|
+
});
|
|
5051
|
+
for (const agent of Object.values(this.core.agents)) this.track(agent);
|
|
5052
|
+
}
|
|
5053
|
+
get trajectoryId() {
|
|
5054
|
+
return this.connection.trajectoryId ?? this.collector?.trajectoryId ?? null;
|
|
5055
|
+
}
|
|
5056
|
+
start(options = {}) {
|
|
5057
|
+
const config = this.config;
|
|
5058
|
+
if (config === void 0) {
|
|
5059
|
+
console.warn("[CopilotKit] startTrajectory() needs the `learning` option on CopilotKitCore (or the CopilotKitProvider `learning` prop).");
|
|
5060
|
+
return Promise.resolve({
|
|
5061
|
+
status: "error",
|
|
5062
|
+
code: "LEARNING_DISABLED"
|
|
5063
|
+
});
|
|
5064
|
+
}
|
|
5065
|
+
if (config.sink === void 0) {
|
|
5066
|
+
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.");
|
|
5067
|
+
this.stopLegacy();
|
|
5068
|
+
return this.connection.start(options.trajectoryId, config);
|
|
5069
|
+
}
|
|
5070
|
+
this.connection.stop();
|
|
5071
|
+
const trajectoryId = options.trajectoryId ?? this.collector?.trajectoryId ?? crypto.randomUUID();
|
|
5072
|
+
if (this.collector === null) {
|
|
5073
|
+
this.activeConfig = config;
|
|
5074
|
+
const runtimeUrl = this.core.runtimeUrl;
|
|
5075
|
+
this.collector = createCollector({
|
|
5076
|
+
...config,
|
|
5077
|
+
beforeSend: (event) => {
|
|
5078
|
+
const sequenced = {
|
|
5079
|
+
...event,
|
|
5080
|
+
value: {
|
|
5081
|
+
...event.value,
|
|
5082
|
+
seq: ++this.seq
|
|
5083
|
+
}
|
|
5084
|
+
};
|
|
5085
|
+
return config.beforeSend === void 0 ? sequenced : config.beforeSend(sequenced);
|
|
5086
|
+
},
|
|
5087
|
+
ignoreUrls: [
|
|
5088
|
+
...config.ignoreUrls ?? [],
|
|
5089
|
+
...COPILOTKIT_OWN_URLS,
|
|
5090
|
+
...runtimeUrl === void 0 ? [] : [runtimeUrl]
|
|
5091
|
+
],
|
|
5092
|
+
enrich: (target) => this.enrich(target)
|
|
5093
|
+
});
|
|
5094
|
+
}
|
|
5095
|
+
const wasActive = this.collector.trajectoryId !== null;
|
|
5096
|
+
this.collector.start({
|
|
5097
|
+
...options,
|
|
5098
|
+
trajectoryId
|
|
5099
|
+
});
|
|
5100
|
+
if (wasActive) return Promise.resolve(this.collector.trajectoryId === trajectoryId ? {
|
|
5101
|
+
status: "started",
|
|
5102
|
+
trajectoryId
|
|
5103
|
+
} : {
|
|
5104
|
+
status: "error",
|
|
5105
|
+
code: "TRAJECTORY_ACTIVE"
|
|
5106
|
+
});
|
|
5107
|
+
this.startTracking();
|
|
5108
|
+
this.lastLinkedThreadId = null;
|
|
5109
|
+
for (const threadId of this.distinctOpenThreadIds()) this.emitThreadLinked(threadId, "start");
|
|
5110
|
+
return Promise.resolve({
|
|
5111
|
+
status: "started",
|
|
5112
|
+
trajectoryId
|
|
5113
|
+
});
|
|
5114
|
+
}
|
|
5115
|
+
stop() {
|
|
5116
|
+
this.connection.stop();
|
|
5117
|
+
this.stopLegacy();
|
|
5118
|
+
}
|
|
5119
|
+
stopLegacy() {
|
|
5120
|
+
this.collector?.stop();
|
|
5121
|
+
this.collector = null;
|
|
5122
|
+
this.activeConfig = void 0;
|
|
5123
|
+
this.coreSubscription?.unsubscribe();
|
|
5124
|
+
this.coreSubscription = null;
|
|
5125
|
+
for (const subscription of this.agents.values()) subscription.unsubscribe();
|
|
5126
|
+
this.agents.clear();
|
|
5127
|
+
this.toolCalls.clear();
|
|
5128
|
+
this.executingTools.clear();
|
|
5129
|
+
}
|
|
5130
|
+
/** A developer event. Only an explicit legacy sink adds open-Thread context. */
|
|
5131
|
+
emit(name, value) {
|
|
5132
|
+
this.connection.emit(name, value);
|
|
5133
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) this.collector?.emit(name, {
|
|
5134
|
+
...this.openThreadContext(),
|
|
5135
|
+
...value
|
|
5136
|
+
});
|
|
5137
|
+
}
|
|
5138
|
+
registerOpenThread(params) {
|
|
5139
|
+
const entry = { ...params };
|
|
5140
|
+
const isNew = !this.distinctOpenThreadIds().includes(entry.threadId);
|
|
5141
|
+
this.openThreads.add(entry);
|
|
5142
|
+
if (isNew) this.emitThreadLinked(entry.threadId, "open");
|
|
5143
|
+
return {
|
|
5144
|
+
update: (threadId) => {
|
|
5145
|
+
if (threadId === entry.threadId) return;
|
|
5146
|
+
entry.threadId = threadId;
|
|
5147
|
+
if (![...this.openThreads].filter((other) => other !== entry).some((other) => other.threadId === threadId)) this.emitThreadLinked(threadId, "switch");
|
|
5148
|
+
},
|
|
5149
|
+
unregister: () => {
|
|
5150
|
+
this.openThreads.delete(entry);
|
|
5151
|
+
}
|
|
5152
|
+
};
|
|
5153
|
+
}
|
|
5154
|
+
distinctOpenThreadIds() {
|
|
5155
|
+
return [...new Set([...this.openThreads].map((open) => open.threadId))];
|
|
5156
|
+
}
|
|
5157
|
+
emitThreadLinked(threadId, reason) {
|
|
5158
|
+
if (!(this.collector !== null && this.collector.trajectoryId !== null) || threadId === this.lastLinkedThreadId) return;
|
|
5159
|
+
this.lastLinkedThreadId = threadId;
|
|
5160
|
+
const owner = [...this.openThreads].find((open) => open.threadId === threadId);
|
|
5161
|
+
const agent = this.findAgentForThread(threadId);
|
|
5162
|
+
this.collector?.ɵemit("thread.linked", {
|
|
5163
|
+
threadId,
|
|
5164
|
+
agentId: owner?.agentId ?? agent?.agentId ?? null,
|
|
5165
|
+
hasMessages: (agent?.messages.length ?? 0) > 0,
|
|
5166
|
+
reason
|
|
5167
|
+
});
|
|
5168
|
+
}
|
|
5169
|
+
findAgentForThread(threadId) {
|
|
5170
|
+
return [...this.agents.keys()].find((agent) => agent.threadId === threadId);
|
|
5171
|
+
}
|
|
5172
|
+
track(agent) {
|
|
5173
|
+
const agentId = agent.agentId;
|
|
5174
|
+
if (!this.isLegacyCaptureActive || this.agents.has(agent) || agentId === void 0) return;
|
|
5175
|
+
this.agents.set(agent, agent.subscribe(this.agentSubscriber(agent, agentId)));
|
|
5176
|
+
}
|
|
5177
|
+
agentSubscriber(agent, agentId) {
|
|
5178
|
+
const runIdFor = () => this.lastRunIds.get(agent);
|
|
5179
|
+
return {
|
|
5180
|
+
onRunStartedEvent: ({ event }) => {
|
|
5181
|
+
this.lastRunIds.set(agent, event.runId);
|
|
5182
|
+
this.emitRun("started", agent, event.threadId, event.runId);
|
|
5183
|
+
},
|
|
5184
|
+
onRunFinishedEvent: ({ event }) => {
|
|
5185
|
+
this.emitRun("finished", agent, event.threadId, event.runId);
|
|
5186
|
+
},
|
|
5187
|
+
onRunErrorEvent: () => {
|
|
5188
|
+
this.emitRun("error", agent, agent.threadId, runIdFor() ?? null);
|
|
5189
|
+
},
|
|
5190
|
+
onToolCallStartEvent: ({ event }) => {
|
|
5191
|
+
if (!this.isLegacyCaptureActive) return;
|
|
5192
|
+
const origin = {
|
|
5193
|
+
agentId,
|
|
5194
|
+
threadId: agent.threadId,
|
|
5195
|
+
messageId: event.parentMessageId,
|
|
5196
|
+
runId: runIdFor()
|
|
5197
|
+
};
|
|
5198
|
+
this.toolCalls.set(event.toolCallId, origin);
|
|
5199
|
+
this.emitToolCall("started", event.toolCallId, origin.agentId, event.toolCallName, {});
|
|
5200
|
+
},
|
|
5201
|
+
onToolCallResultEvent: ({ event }) => {
|
|
5202
|
+
if (!this.executingTools.has(event.toolCallId)) this.toolCalls.delete(event.toolCallId);
|
|
5203
|
+
},
|
|
5204
|
+
onNewMessage: ({ message }) => this.emitMessage(agent, message)
|
|
5205
|
+
};
|
|
5206
|
+
}
|
|
5207
|
+
emitRun(phase, agent, threadId, runId) {
|
|
5208
|
+
this.collector?.ɵemit("agent.run", {
|
|
5209
|
+
phase,
|
|
5210
|
+
runId,
|
|
5211
|
+
threadId,
|
|
5212
|
+
agentId: agent.agentId
|
|
5213
|
+
});
|
|
5214
|
+
}
|
|
5215
|
+
emitMessage(agent, message) {
|
|
5216
|
+
const content = typeof message.content === "string" ? message.content : "";
|
|
5217
|
+
const includeText = this.activeConfig?.capture?.agentText !== false && typeof message.content === "string";
|
|
5218
|
+
this.collector?.ɵemit("agent.message", {
|
|
5219
|
+
messageId: message.id,
|
|
5220
|
+
runId: this.runIdForMessage(agent, message.id),
|
|
5221
|
+
threadId: agent.threadId,
|
|
5222
|
+
agentId: agent.agentId,
|
|
5223
|
+
role: message.role,
|
|
5224
|
+
textLength: content.length,
|
|
5225
|
+
...includeText ? { text: content } : {}
|
|
5226
|
+
});
|
|
5227
|
+
}
|
|
5228
|
+
emitToolCall(phase, toolCallId, agentId, toolName, extra) {
|
|
5229
|
+
const origin = this.toolCalls.get(toolCallId);
|
|
5230
|
+
this.collector?.ɵemit("tool.call", {
|
|
5231
|
+
phase,
|
|
5232
|
+
toolCallId,
|
|
5233
|
+
toolName,
|
|
5234
|
+
agentId,
|
|
5235
|
+
threadId: origin?.threadId ?? null,
|
|
5236
|
+
messageId: origin?.messageId,
|
|
5237
|
+
runId: origin?.runId,
|
|
5238
|
+
...extra
|
|
5239
|
+
});
|
|
5240
|
+
}
|
|
5241
|
+
runIdForMessage(agent, messageId) {
|
|
5242
|
+
const agentId = agent.agentId;
|
|
5243
|
+
if (agentId === void 0) return null;
|
|
5244
|
+
return this.core.getRunIdForMessage(agentId, agent.threadId, messageId) ?? this.lastRunIds.get(agent) ?? null;
|
|
5245
|
+
}
|
|
5246
|
+
findMessage(messageId) {
|
|
5247
|
+
for (const agent of this.agents.keys()) {
|
|
5248
|
+
const message = agent.messages.find((candidate) => candidate.id === messageId);
|
|
5249
|
+
if (message !== void 0) return {
|
|
5250
|
+
agent,
|
|
5251
|
+
message
|
|
5252
|
+
};
|
|
5253
|
+
}
|
|
5254
|
+
return null;
|
|
5255
|
+
}
|
|
5256
|
+
toolStatus(agent, toolCallId) {
|
|
5257
|
+
if (this.executingTools.has(toolCallId)) return "executing";
|
|
5258
|
+
return agent.messages.some((message) => message.role === "tool" && message.toolCallId === toolCallId) ? "complete" : "inProgress";
|
|
5259
|
+
}
|
|
5260
|
+
/** Adds agent context to a click: exact message first, then the single open Thread. */
|
|
5261
|
+
enrich(target) {
|
|
5262
|
+
const messageId = target.closest("[data-message-id]")?.getAttribute("data-message-id") ?? null;
|
|
5263
|
+
const toolCallId = target.closest("[data-tool-call-id]")?.getAttribute("data-tool-call-id") ?? null;
|
|
5264
|
+
const found = messageId === null ? null : this.findMessage(messageId);
|
|
5265
|
+
if (found !== null) {
|
|
5266
|
+
const { agent, message } = found;
|
|
5267
|
+
const toolCall = toolCallId !== null && message.role === "assistant" ? message.toolCalls?.find((call) => call.id === toolCallId) : void 0;
|
|
5268
|
+
return {
|
|
5269
|
+
threadId: agent.threadId,
|
|
5270
|
+
agentId: agent.agentId,
|
|
5271
|
+
messageId: message.id,
|
|
5272
|
+
runId: this.runIdForMessage(agent, message.id),
|
|
5273
|
+
...toolCall === void 0 ? {} : {
|
|
5274
|
+
toolCallId: toolCall.id,
|
|
5275
|
+
toolName: toolCall.function.name,
|
|
5276
|
+
toolStatus: this.toolStatus(agent, toolCall.id)
|
|
5277
|
+
}
|
|
5278
|
+
};
|
|
5279
|
+
}
|
|
5280
|
+
return this.openThreadContext();
|
|
5281
|
+
}
|
|
5282
|
+
/** The single open Thread, or `null` with the candidates when several are open. */
|
|
5283
|
+
openThreadContext() {
|
|
5284
|
+
const openIds = this.distinctOpenThreadIds();
|
|
5285
|
+
if (openIds.length > 1) return {
|
|
5286
|
+
threadId: null,
|
|
5287
|
+
threadAmbiguity: {
|
|
5288
|
+
reason: "multiple-open",
|
|
5289
|
+
candidates: openIds
|
|
5290
|
+
}
|
|
5291
|
+
};
|
|
5292
|
+
return { threadId: openIds[0] ?? null };
|
|
5293
|
+
}
|
|
5294
|
+
};
|
|
5295
|
+
|
|
4514
5296
|
//#endregion
|
|
4515
5297
|
//#region src/utils/micro-redux.ts
|
|
4516
5298
|
const INTERNAL_ACTION_TYPES = {
|
|
@@ -5663,6 +6445,9 @@ var CopilotKitCore = class {
|
|
|
5663
6445
|
runHandler;
|
|
5664
6446
|
stateManager;
|
|
5665
6447
|
threadStoreRegistry;
|
|
6448
|
+
learningBridge;
|
|
6449
|
+
notifiedTrajectoryId = null;
|
|
6450
|
+
learningConfiguredListeners = /* @__PURE__ */ new Set();
|
|
5666
6451
|
/**
|
|
5667
6452
|
* The single core-owned memory store, created lazily on first
|
|
5668
6453
|
* `getMemoryStore()` and kept user-scoped for the lifetime of the core.
|
|
@@ -5677,7 +6462,7 @@ var CopilotKitCore = class {
|
|
|
5677
6462
|
* store that was registered prior to that initial notification.
|
|
5678
6463
|
*/
|
|
5679
6464
|
previousAgentIds = /* @__PURE__ */ new Set();
|
|
5680
|
-
constructor({ runtimeUrl, runtimeTransport = "auto", deferInitialConnection = false, headers = {}, credentials, messageFilter, properties = {}, agents__unsafe_dev_only = {}, tools = [], suggestionsConfig = [], debug }) {
|
|
6465
|
+
constructor({ runtimeUrl, runtimeTransport = "auto", deferInitialConnection = false, headers = {}, credentials, messageFilter, properties = {}, agents__unsafe_dev_only = {}, tools = [], suggestionsConfig = [], debug, learning }) {
|
|
5681
6466
|
this._headers = normalizeHeaders(headers);
|
|
5682
6467
|
this._credentials = credentials;
|
|
5683
6468
|
this._messageFilter = messageFilter;
|
|
@@ -5693,6 +6478,7 @@ var CopilotKitCore = class {
|
|
|
5693
6478
|
this.runHandler.initialize(tools);
|
|
5694
6479
|
this.suggestionEngine.initialize(suggestionsConfig);
|
|
5695
6480
|
this.stateManager.initialize();
|
|
6481
|
+
this.learningBridge = new LearningBridge(this, learning, () => this.notifyTrajectoryChanged());
|
|
5696
6482
|
this.agentRegistry.setRuntimeTransport(runtimeTransport);
|
|
5697
6483
|
this.agentRegistry.setRuntimeUrl(runtimeUrl, { deferConnection: deferInitialConnection });
|
|
5698
6484
|
this.previousAgentIds = new Set(Object.keys(agents__unsafe_dev_only));
|
|
@@ -5844,6 +6630,10 @@ var CopilotKitCore = class {
|
|
|
5844
6630
|
get runtimeConnectionStatus() {
|
|
5845
6631
|
return this.agentRegistry.runtimeConnectionStatus;
|
|
5846
6632
|
}
|
|
6633
|
+
/** @internal The verbatim single-endpoint URL, including any trailing slash. */
|
|
6634
|
+
get ɵruntimeEndpointUrl() {
|
|
6635
|
+
return this.agentRegistry.runtimeEndpointUrl;
|
|
6636
|
+
}
|
|
5847
6637
|
get ɵruntimeFetch() {
|
|
5848
6638
|
return this.agentRegistry.createRuntimeFetch();
|
|
5849
6639
|
}
|
|
@@ -6003,6 +6793,83 @@ var CopilotKitCore = class {
|
|
|
6003
6793
|
getAgent(id) {
|
|
6004
6794
|
return this.agentRegistry.getAgent(id);
|
|
6005
6795
|
}
|
|
6796
|
+
/** Updates settings for the next capture. Removing the config stops capture. */
|
|
6797
|
+
setLearningConfig(config) {
|
|
6798
|
+
const wasConfigured = this.ɵlearningConfigured;
|
|
6799
|
+
this.learningBridge.setConfig(config);
|
|
6800
|
+
this.notifyTrajectoryChanged();
|
|
6801
|
+
if (this.ɵlearningConfigured === wasConfigured) return;
|
|
6802
|
+
for (const listener of this.learningConfiguredListeners) try {
|
|
6803
|
+
listener();
|
|
6804
|
+
} catch (error) {
|
|
6805
|
+
console.error("Learning configured listener error:", error);
|
|
6806
|
+
}
|
|
6807
|
+
}
|
|
6808
|
+
/**
|
|
6809
|
+
* @internal Whether a `learning` config is set. Unlike {@link trajectoryId},
|
|
6810
|
+
* it does not change with capture start, connection failures or reconnects.
|
|
6811
|
+
*/
|
|
6812
|
+
get ɵlearningConfigured() {
|
|
6813
|
+
return this.learningBridge.isConfigured;
|
|
6814
|
+
}
|
|
6815
|
+
/** @internal Calls `listener` when {@link ɵlearningConfigured} changes. */
|
|
6816
|
+
ɵsubscribeToLearningConfigured(listener) {
|
|
6817
|
+
this.learningConfiguredListeners.add(listener);
|
|
6818
|
+
return () => {
|
|
6819
|
+
this.learningConfiguredListeners.delete(listener);
|
|
6820
|
+
};
|
|
6821
|
+
}
|
|
6822
|
+
/** The active capture's Trajectory ID, or null while capture is stopped. */
|
|
6823
|
+
get trajectoryId() {
|
|
6824
|
+
return this.learningBridge.trajectoryId;
|
|
6825
|
+
}
|
|
6826
|
+
notifyTrajectoryChanged() {
|
|
6827
|
+
const trajectoryId = this.trajectoryId;
|
|
6828
|
+
if (trajectoryId === this.notifiedTrajectoryId) return;
|
|
6829
|
+
this.notifiedTrajectoryId = trajectoryId;
|
|
6830
|
+
this.notifySubscribers((subscriber) => subscriber.onTrajectoryChanged?.({
|
|
6831
|
+
copilotkit: this,
|
|
6832
|
+
trajectoryId
|
|
6833
|
+
}), "Subscriber onTrajectoryChanged error:");
|
|
6834
|
+
}
|
|
6835
|
+
/**
|
|
6836
|
+
* Starts one Trajectory after Runtime authorization and the Gateway join.
|
|
6837
|
+
* Omit the ID to create one. Repeated starts await the active connection;
|
|
6838
|
+
* a different ID stops the previous capture. Explicit sinks retain the
|
|
6839
|
+
* prototype's synchronous capture and require a stop before changing IDs.
|
|
6840
|
+
*
|
|
6841
|
+
* @example copilotkit.startTrajectory({ trajectoryId: crypto.randomUUID() })
|
|
6842
|
+
*/
|
|
6843
|
+
startTrajectory(options = {}) {
|
|
6844
|
+
const result = this.learningBridge.start(options);
|
|
6845
|
+
this.notifyTrajectoryChanged();
|
|
6846
|
+
return result;
|
|
6847
|
+
}
|
|
6848
|
+
/** Cancels pending work and stops capture, attempting one final batch without waiting for its ACK. */
|
|
6849
|
+
stopTrajectory() {
|
|
6850
|
+
this.learningBridge.stop();
|
|
6851
|
+
this.notifyTrajectoryChanged();
|
|
6852
|
+
}
|
|
6853
|
+
/**
|
|
6854
|
+
* Records an outcome that clicks cannot show, such as a saved report or an
|
|
6855
|
+
* approved deal. The wire adds `value.seq`; arrays, primitives, and objects
|
|
6856
|
+
* already containing `seq` are preserved under `value.data`. Explicit legacy
|
|
6857
|
+
* sinks retain object-only events with open-Thread enrichment.
|
|
6858
|
+
* No-op while no Trajectory runs. Events emitted during connection recovery
|
|
6859
|
+
* count as dropped. Built-in names such as `click` are rejected.
|
|
6860
|
+
*
|
|
6861
|
+
* @example copilotkit.emitTrajectoryEvent("deal.approved", { dealId: "deal-1" })
|
|
6862
|
+
*/
|
|
6863
|
+
emitTrajectoryEvent(name, value = {}) {
|
|
6864
|
+
this.learningBridge.emit(name, value);
|
|
6865
|
+
}
|
|
6866
|
+
/**
|
|
6867
|
+
* Tells Core that a view shows this Thread, so captured interactions can link to it.
|
|
6868
|
+
* Framework bindings call this; call it yourself only without a CopilotKit provider.
|
|
6869
|
+
*/
|
|
6870
|
+
registerOpenThread(params) {
|
|
6871
|
+
return this.learningBridge.registerOpenThread(params);
|
|
6872
|
+
}
|
|
6006
6873
|
/**
|
|
6007
6874
|
* Re-apply the current headers to a single agent (delegated to
|
|
6008
6875
|
* AgentRegistry). Core headers are merged on top of the agent's own
|