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