@copilotkit/core 1.73.0 → 1.73.2-canary.1790068297
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 +438 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +141 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +141 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +438 -58
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +448 -62
- package/dist/index.umd.js.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -379,16 +379,24 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
|
|
|
379
379
|
requestJoinCredentials$(mode, input, replayCursor) {
|
|
380
380
|
return defer(async () => {
|
|
381
381
|
try {
|
|
382
|
-
const
|
|
382
|
+
const requestFetch = this.config.fetch ?? globalFetch;
|
|
383
|
+
const body = {
|
|
384
|
+
...input,
|
|
385
|
+
...mode === "connect" ? { lastSeenEventId: replayCursor === void 0 ? this.getReconnectCursor(input) : replayCursor } : {}
|
|
386
|
+
};
|
|
387
|
+
const single = this.config.transport === "single";
|
|
388
|
+
const response = await requestFetch(single ? this.config.runtimeUrl : this.buildRuntimeUrl(mode), {
|
|
383
389
|
method: "POST",
|
|
390
|
+
redirect: "error",
|
|
384
391
|
headers: {
|
|
385
392
|
"Content-Type": "application/json",
|
|
386
393
|
...this.headers
|
|
387
394
|
},
|
|
388
|
-
body: JSON.stringify({
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
395
|
+
body: JSON.stringify(single ? {
|
|
396
|
+
method: `agent/${mode}`,
|
|
397
|
+
params: { agentId: this.config.agentId },
|
|
398
|
+
body
|
|
399
|
+
} : body),
|
|
392
400
|
...this.credentials ? { credentials: this.credentials } : {}
|
|
393
401
|
});
|
|
394
402
|
if (response.status === 204 && mode === "connect") return null;
|
|
@@ -622,6 +630,90 @@ function isRuntimeInfoRequestError(error) {
|
|
|
622
630
|
return typeof error === "object" && error !== null && typeof error.runtimeInfoStatus === "number";
|
|
623
631
|
}
|
|
624
632
|
|
|
633
|
+
//#endregion
|
|
634
|
+
//#region src/core/message-filter.ts
|
|
635
|
+
function toolCallIdsOf(message) {
|
|
636
|
+
const calls = message.toolCalls;
|
|
637
|
+
if (!Array.isArray(calls)) return [];
|
|
638
|
+
return calls.map((call) => call.id).filter((id) => Boolean(id));
|
|
639
|
+
}
|
|
640
|
+
function resultCallIdOf(message) {
|
|
641
|
+
if (message.role !== "tool") return void 0;
|
|
642
|
+
return message.toolCallId;
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Repair the tool-call pairs a filter broke.
|
|
646
|
+
*
|
|
647
|
+
* Every provider rejects a half pair. A tool result whose call is gone reads as
|
|
648
|
+
* a reply to nothing, and an assistant tool call whose result is gone leaves
|
|
649
|
+
* the turn unanswered — the failure reporters hit on #1482 when a trimmed
|
|
650
|
+
* history landed mid-HITL and Anthropic answered "a tool result is expected".
|
|
651
|
+
* A filter written as `messages.slice(-1)` cannot know this, so the repair runs
|
|
652
|
+
* for every filter rather than being something each caller opts into.
|
|
653
|
+
*
|
|
654
|
+
* Both repairs restore, never discard, and both are anchored on the untrimmed
|
|
655
|
+
* thread:
|
|
656
|
+
*
|
|
657
|
+
* - A kept tool result whose assistant call was trimmed away gets that call put
|
|
658
|
+
* back in front of it. Dropping the result instead would be protocol-safe and
|
|
659
|
+
* semantically wrong: on a human-in-the-loop turn the result IS the new
|
|
660
|
+
* information, and a backend that already holds the call would be asked to
|
|
661
|
+
* resume with nothing.
|
|
662
|
+
* - A kept assistant call whose result was trimmed gets that result restored,
|
|
663
|
+
* placed directly after the call.
|
|
664
|
+
*
|
|
665
|
+
* Restoring a call pulls in every result for that call, not only the one the
|
|
666
|
+
* filter kept. Providers reject an assistant turn with a parallel tool call
|
|
667
|
+
* left unanswered, so a half-answered set is as invalid as a missing one.
|
|
668
|
+
*
|
|
669
|
+
* A call with no result anywhere in `full` is left alone. That is an open call
|
|
670
|
+
* (a pending frontend tool, an unanswered interrupt), not a broken pair. A
|
|
671
|
+
* result whose call is nowhere in `full` is left alone too: it was already
|
|
672
|
+
* unpaired before the filter ran, so it is not this function's to fix.
|
|
673
|
+
*
|
|
674
|
+
* The filter's own ordering is preserved. Restorations are the only insertions,
|
|
675
|
+
* and messages the filter synthesized (absent from `full`) pass through
|
|
676
|
+
* untouched.
|
|
677
|
+
*/
|
|
678
|
+
function ɵrepairToolCallPairs(kept, full) {
|
|
679
|
+
const callIdToIssuer = /* @__PURE__ */ new Map();
|
|
680
|
+
const callIdToResult = /* @__PURE__ */ new Map();
|
|
681
|
+
for (const message of full) {
|
|
682
|
+
for (const callId of toolCallIdsOf(message)) callIdToIssuer.set(callId, message);
|
|
683
|
+
const resultCallId = resultCallIdOf(message);
|
|
684
|
+
if (resultCallId !== void 0) callIdToResult.set(resultCallId, message);
|
|
685
|
+
}
|
|
686
|
+
const repaired = [];
|
|
687
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
688
|
+
const emit = (message) => {
|
|
689
|
+
if (emitted.has(message.id)) return;
|
|
690
|
+
emitted.add(message.id);
|
|
691
|
+
repaired.push(message);
|
|
692
|
+
};
|
|
693
|
+
/** Emit an assistant turn together with every result it is owed. */
|
|
694
|
+
const emitWithResults = (message) => {
|
|
695
|
+
emit(message);
|
|
696
|
+
for (const callId of toolCallIdsOf(message)) {
|
|
697
|
+
const result = callIdToResult.get(callId);
|
|
698
|
+
if (result) emit(result);
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
for (const message of kept) {
|
|
702
|
+
const resultCallId = resultCallIdOf(message);
|
|
703
|
+
if (resultCallId === void 0) {
|
|
704
|
+
emitWithResults(message);
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
const issuer = callIdToIssuer.get(resultCallId);
|
|
708
|
+
if (issuer) {
|
|
709
|
+
emitWithResults(issuer);
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
emit(message);
|
|
713
|
+
}
|
|
714
|
+
return repaired;
|
|
715
|
+
}
|
|
716
|
+
|
|
625
717
|
//#endregion
|
|
626
718
|
//#region src/agent.ts
|
|
627
719
|
function hasHeaders(agent) {
|
|
@@ -660,6 +752,18 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
|
|
|
660
752
|
_capabilities;
|
|
661
753
|
delegate;
|
|
662
754
|
runtimeInfoPromise;
|
|
755
|
+
_messageFilter;
|
|
756
|
+
/**
|
|
757
|
+
* The HTTP `run` this agent started and has not yet seen finish, as the
|
|
758
|
+
* exact `{ threadId, runId }` it POSTed. `abortRun` narrows `/stop` to this
|
|
759
|
+
* run only while `threadId` still matches: `threadId` is a public field the
|
|
760
|
+
* host may reassign under a live run, and a runId from another thread must
|
|
761
|
+
* not be sent as if it were this thread's. Released when the run stream
|
|
762
|
+
* finalizes, cleared on `connect` (a reconnected thread may run under a
|
|
763
|
+
* runId this client never saw) and never copied by `clone`. Without a
|
|
764
|
+
* provable active run the stop stays thread-wide.
|
|
765
|
+
*/
|
|
766
|
+
activeRun;
|
|
663
767
|
constructor(config) {
|
|
664
768
|
const normalizedRuntimeUrl = config.runtimeUrl ? config.runtimeUrl.replace(/\/$/, "") : void 0;
|
|
665
769
|
const transport = config.transport ?? "auto";
|
|
@@ -678,10 +782,47 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
|
|
|
678
782
|
this.runtimeMode = config.runtimeMode ?? RUNTIME_MODE_SSE;
|
|
679
783
|
this.intelligence = config.intelligence;
|
|
680
784
|
this._capabilities = config.capabilities;
|
|
785
|
+
this._messageFilter = config.messageFilter;
|
|
681
786
|
if (config.debug) this.debug = config.debug;
|
|
682
787
|
if (this.transport === "single") this.singleEndpointUrl = this.runtimeEndpointUrl;
|
|
683
788
|
}
|
|
684
789
|
/**
|
|
790
|
+
* Adopt the runtime mode a fresh `/info` just reported.
|
|
791
|
+
*
|
|
792
|
+
* A live proxy outlives the runtime configuration that minted it: a
|
|
793
|
+
* redeploy, an env-var change or a rollback can flip `intelligence` → `sse`
|
|
794
|
+
* under a page that is already open. The registry preserves the proxy across
|
|
795
|
+
* that re-sync on purpose — it is backing an open conversation — so the new
|
|
796
|
+
* mode is pushed onto the instance here instead of replacing it.
|
|
797
|
+
*
|
|
798
|
+
* Without this the mode was decided once, at construction, and `run()` kept
|
|
799
|
+
* taking the delegate path against a runtime serving plain SSE, where
|
|
800
|
+
* `response.json()` on a `text/event-stream` body throws for the rest of the
|
|
801
|
+
* page's life. See #7130.
|
|
802
|
+
*
|
|
803
|
+
* A delegate built for the old mode is torn down: it speaks a protocol the
|
|
804
|
+
* runtime no longer serves, and it may be holding a websocket open. The
|
|
805
|
+
* memoized `/info` promise goes with it — it answered for the old
|
|
806
|
+
* configuration, so a later `ensureRuntimeConfiguration()` must re-ask
|
|
807
|
+
* rather than treat that answer as current.
|
|
808
|
+
*
|
|
809
|
+
* Only `wsUrl` is compared on the Intelligence metadata, because that is the
|
|
810
|
+
* one field the delegate bakes in at construction.
|
|
811
|
+
*/
|
|
812
|
+
adoptRuntimeMode(runtimeMode, intelligence) {
|
|
813
|
+
const unchanged = this.runtimeMode === runtimeMode && this.intelligence?.wsUrl === intelligence?.wsUrl;
|
|
814
|
+
this.runtimeMode = runtimeMode;
|
|
815
|
+
this.intelligence = intelligence;
|
|
816
|
+
if (unchanged) return;
|
|
817
|
+
this.runtimeInfoPromise = void 0;
|
|
818
|
+
const staleDelegate = this.delegate;
|
|
819
|
+
this.delegate = void 0;
|
|
820
|
+
if (staleDelegate) {
|
|
821
|
+
staleDelegate.abortRun();
|
|
822
|
+
this.detachActiveRun();
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
685
826
|
* The agent id used for outbound runtime requests — `runtimeAgentId` when
|
|
686
827
|
* set (manually-registered proxy), otherwise `agentId` (registry id
|
|
687
828
|
* matches runtime id). Subscriber bookkeeping keeps using `agentId`
|
|
@@ -699,6 +840,67 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
|
|
|
699
840
|
get capabilities() {
|
|
700
841
|
return this._capabilities;
|
|
701
842
|
}
|
|
843
|
+
/**
|
|
844
|
+
* The filter applied to the outbound message list on every run.
|
|
845
|
+
*
|
|
846
|
+
* Registry-owned. `AgentRegistry` writes it whenever the core-level filter
|
|
847
|
+
* changes, so an agent discovered before the app configured one still picks
|
|
848
|
+
* it up — and so a value written here by hand is replaced on the registry's
|
|
849
|
+
* next sweep. Configure it through `CopilotKitCore` instead.
|
|
850
|
+
*/
|
|
851
|
+
get messageFilter() {
|
|
852
|
+
return this._messageFilter;
|
|
853
|
+
}
|
|
854
|
+
set messageFilter(filter) {
|
|
855
|
+
this._messageFilter = filter;
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Narrow the outbound payload with the configured message filter.
|
|
859
|
+
*
|
|
860
|
+
* **Called from the HTTP paths only, and that placement is the Intelligence
|
|
861
|
+
* exemption.** An earlier revision applied the filter in
|
|
862
|
+
* `prepareRunAgentInput`, which runs before the runtime mode is resolved:
|
|
863
|
+
* with the default `"auto"` transport, or on a proxy still `"pending"` its
|
|
864
|
+
* first `/info`, `run()` prepared the input, *then* resolved the mode, then
|
|
865
|
+
* handed that already-filtered input to `#runViaDelegate` — so a managed
|
|
866
|
+
* runtime received a truncated thread despite the exemption. Filtering here
|
|
867
|
+
* makes that unreachable by construction: `#runViaHttp` and
|
|
868
|
+
* `#connectViaHttp` are only entered once the mode is known not to be
|
|
869
|
+
* Intelligence. Do not move this back up the call chain.
|
|
870
|
+
*
|
|
871
|
+
* The managed runtime is the store of record for the thread — the threads
|
|
872
|
+
* drawer and the Slack transcript read from it — so a client-side truncation
|
|
873
|
+
* there has a blast radius nobody asked for. Every reporter on #1482 is
|
|
874
|
+
* self-hosted.
|
|
875
|
+
*
|
|
876
|
+
* `prepareRunAgentInput` has already deep-cloned the thread and stripped
|
|
877
|
+
* `activity` messages by the time the input reaches here, so the filter
|
|
878
|
+
* cannot reach the messages the UI renders no matter what it does with the
|
|
879
|
+
* array it is handed. The filter gets a *second* deep clone on top of that,
|
|
880
|
+
* because `input.messages` is also the repair baseline and the untrimmed
|
|
881
|
+
* fallback: sharing the message objects let a filter that mutates a kept
|
|
882
|
+
* message's `toolCallId` corrupt the baseline, and an orphaned tool result
|
|
883
|
+
* then went out on the wire — a payload the provider rejects, which is
|
|
884
|
+
* exactly the failure this whole mechanism exists to prevent.
|
|
885
|
+
*/
|
|
886
|
+
#applyMessageFilter(input) {
|
|
887
|
+
const filter = this._messageFilter;
|
|
888
|
+
if (!filter) return input;
|
|
889
|
+
try {
|
|
890
|
+
const kept = filter(structuredClone_(input.messages), { agentId: this.agentId ?? "" });
|
|
891
|
+
if (!Array.isArray(kept)) {
|
|
892
|
+
console.warn("ProxiedCopilotRuntimeAgent: messageFilter returned a non-array value; sending the full message history instead.");
|
|
893
|
+
return input;
|
|
894
|
+
}
|
|
895
|
+
return {
|
|
896
|
+
...input,
|
|
897
|
+
messages: ɵrepairToolCallPairs(kept, input.messages)
|
|
898
|
+
};
|
|
899
|
+
} catch (error) {
|
|
900
|
+
console.warn("ProxiedCopilotRuntimeAgent: messageFilter failed; sending the full message history instead.", error);
|
|
901
|
+
return input;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
702
904
|
requestInit(input) {
|
|
703
905
|
return {
|
|
704
906
|
...super.requestInit(input),
|
|
@@ -722,6 +924,7 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
|
|
|
722
924
|
if (!this.agentId || !this.threadId) return;
|
|
723
925
|
if (typeof fetch === "undefined") return;
|
|
724
926
|
const routedId = this.routedAgentId();
|
|
927
|
+
const runId = this.activeRun?.threadId === this.threadId ? this.activeRun.runId : void 0;
|
|
725
928
|
if (this.transport === "single") {
|
|
726
929
|
if (!this.singleEndpointUrl) return;
|
|
727
930
|
const headers = new Headers({
|
|
@@ -736,7 +939,8 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
|
|
|
736
939
|
params: {
|
|
737
940
|
agentId: routedId,
|
|
738
941
|
threadId: this.threadId
|
|
739
|
-
}
|
|
942
|
+
},
|
|
943
|
+
...runId === void 0 ? {} : { body: { runId } }
|
|
740
944
|
}),
|
|
741
945
|
...this.credentials ? { credentials: this.credentials } : {}
|
|
742
946
|
}).catch((error) => {
|
|
@@ -755,6 +959,7 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
|
|
|
755
959
|
"Content-Type": "application/json",
|
|
756
960
|
...this.headers
|
|
757
961
|
},
|
|
962
|
+
...runId === void 0 ? {} : { body: JSON.stringify({ runId }) },
|
|
758
963
|
...this.credentials ? { credentials: this.credentials } : {}
|
|
759
964
|
}).catch((error) => {
|
|
760
965
|
console.error("ProxiedCopilotRuntimeAgent: stop request failed", error);
|
|
@@ -810,7 +1015,9 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
|
|
|
810
1015
|
#connectViaDelegate(input) {
|
|
811
1016
|
return defer(() => from(this.resolveDelegate())).pipe(switchMap$1((delegate) => withAbortErrorHandling(delegate.connect(input))));
|
|
812
1017
|
}
|
|
813
|
-
#connectViaHttp(
|
|
1018
|
+
#connectViaHttp(unfiltered) {
|
|
1019
|
+
const input = this.#applyMessageFilter(unfiltered);
|
|
1020
|
+
this.activeRun = void 0;
|
|
814
1021
|
const routedId = this.routedAgentId();
|
|
815
1022
|
if (this.transport === "single") {
|
|
816
1023
|
if (!this.singleEndpointUrl) throw new Error("Single endpoint transport requires a runtimeUrl");
|
|
@@ -824,13 +1031,24 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
|
|
|
824
1031
|
#runViaDelegate(input) {
|
|
825
1032
|
return defer(() => from(this.resolveDelegate())).pipe(switchMap$1((delegate) => withAbortErrorHandling(delegate.run(input))));
|
|
826
1033
|
}
|
|
827
|
-
#runViaHttp(
|
|
1034
|
+
#runViaHttp(unfiltered) {
|
|
1035
|
+
const input = this.#applyMessageFilter(unfiltered);
|
|
1036
|
+
const activeRun = {
|
|
1037
|
+
threadId: input.threadId,
|
|
1038
|
+
runId: input.runId
|
|
1039
|
+
};
|
|
1040
|
+
const trackActiveRun = (source) => {
|
|
1041
|
+
this.activeRun = activeRun;
|
|
1042
|
+
return source.pipe(finalize(() => {
|
|
1043
|
+
if (this.activeRun === activeRun) this.activeRun = void 0;
|
|
1044
|
+
}));
|
|
1045
|
+
};
|
|
828
1046
|
if (this.transport === "single") {
|
|
829
1047
|
if (!this.singleEndpointUrl) throw new Error("Single endpoint transport requires a runtimeUrl");
|
|
830
1048
|
const requestInit = this.createSingleRouteRequestInit(input, "agent/run", { agentId: this.routedAgentId() });
|
|
831
|
-
return withAbortErrorHandling(transformHttpEventStream(runHttpRequest(() => this.fetch(this.singleEndpointUrl, requestInit))));
|
|
1049
|
+
return trackActiveRun(withAbortErrorHandling(transformHttpEventStream(runHttpRequest(() => this.fetch(this.singleEndpointUrl, requestInit)))));
|
|
832
1050
|
}
|
|
833
|
-
return withAbortErrorHandling(super.run(input));
|
|
1051
|
+
return trackActiveRun(withAbortErrorHandling(super.run(input)));
|
|
834
1052
|
}
|
|
835
1053
|
clone() {
|
|
836
1054
|
const cloned = new ProxiedCopilotRuntimeAgent({
|
|
@@ -845,7 +1063,8 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
|
|
|
845
1063
|
intelligence: this.intelligence,
|
|
846
1064
|
capabilities: this._capabilities,
|
|
847
1065
|
debug: this.debug,
|
|
848
|
-
fetch: this.fetch
|
|
1066
|
+
fetch: this.fetch,
|
|
1067
|
+
messageFilter: this._messageFilter
|
|
849
1068
|
});
|
|
850
1069
|
cloned.threadId = this.threadId;
|
|
851
1070
|
cloned.setState(this.state);
|
|
@@ -973,9 +1192,11 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
|
|
|
973
1192
|
createIntelligenceDelegate() {
|
|
974
1193
|
const routedId = this.routedAgentId();
|
|
975
1194
|
if (!this.runtimeUrl || !routedId || !this.intelligence?.wsUrl) throw new Error("Intelligence mode requires runtimeUrl, agentId, and intelligence websocket metadata");
|
|
1195
|
+
const single = this.transport === "single";
|
|
976
1196
|
return new IntelligenceAgent({
|
|
977
1197
|
url: this.intelligence.wsUrl,
|
|
978
|
-
runtimeUrl: this.runtimeUrl,
|
|
1198
|
+
runtimeUrl: single ? this.runtimeEndpointUrl ?? this.runtimeUrl : this.runtimeUrl,
|
|
1199
|
+
transport: single ? "single" : "rest",
|
|
979
1200
|
agentId: routedId,
|
|
980
1201
|
headers: { ...this.headers },
|
|
981
1202
|
credentials: this.credentials,
|
|
@@ -1238,6 +1459,7 @@ var AgentRegistry = class {
|
|
|
1238
1459
|
this.localAgents = this.assignAgentIds(agents);
|
|
1239
1460
|
this.applyHeadersToAgents(this.localAgents);
|
|
1240
1461
|
this.applyCredentialsToAgents(this.localAgents);
|
|
1462
|
+
this.applyMessageFilterToAgents(this.localAgents);
|
|
1241
1463
|
this.applyRuntimeFetchToAgents(this.localAgents);
|
|
1242
1464
|
this._agents = this.localAgents;
|
|
1243
1465
|
}
|
|
@@ -1329,6 +1551,7 @@ var AgentRegistry = class {
|
|
|
1329
1551
|
};
|
|
1330
1552
|
this.applyHeadersToAgents(this._agents);
|
|
1331
1553
|
this.applyCredentialsToAgents(this._agents);
|
|
1554
|
+
this.applyMessageFilterToAgents(this._agents);
|
|
1332
1555
|
this.applyRuntimeFetchToAgents(this._agents);
|
|
1333
1556
|
this.notifyAgentsChanged();
|
|
1334
1557
|
}
|
|
@@ -1381,7 +1604,8 @@ var AgentRegistry = class {
|
|
|
1381
1604
|
credentials: friends.credentials,
|
|
1382
1605
|
runtimeMode: this._runtimeUrl ? this._runtimeConnectionStatus === CopilotKitCoreRuntimeConnectionStatus.Connected ? this._runtimeMode : "pending" : RUNTIME_MODE_SSE,
|
|
1383
1606
|
intelligence: this._intelligence,
|
|
1384
|
-
debug: debug ? resolveDebugConfig(debug) : void 0
|
|
1607
|
+
debug: debug ? resolveDebugConfig(debug) : void 0,
|
|
1608
|
+
messageFilter: friends.messageFilter
|
|
1385
1609
|
});
|
|
1386
1610
|
this.applyHeadersToAgent(agent);
|
|
1387
1611
|
this.applyRuntimeFetchToAgent(agent);
|
|
@@ -1450,6 +1674,45 @@ var AgentRegistry = class {
|
|
|
1450
1674
|
this.applyCredentialsToAgent(agent);
|
|
1451
1675
|
});
|
|
1452
1676
|
}
|
|
1677
|
+
/**
|
|
1678
|
+
* Carry the mode a fresh `/info` reported onto a proxy this re-sync kept.
|
|
1679
|
+
*
|
|
1680
|
+
* `canReuseRuntimeAgent` deliberately ignores the mode: a preserved proxy is
|
|
1681
|
+
* backing an open conversation, and replacing the instance to change one
|
|
1682
|
+
* field would drop it. The mode is therefore pushed onto the survivor here,
|
|
1683
|
+
* the same way headers and credentials are. Without it the mode was fixed at
|
|
1684
|
+
* construction, so a runtime that flipped `intelligence` → `sse` under an
|
|
1685
|
+
* open page left every later run on the delegate path. See #7130.
|
|
1686
|
+
*/
|
|
1687
|
+
applyRuntimeModeToAgent(agent, runtimeInfo) {
|
|
1688
|
+
if (agent instanceof ProxiedCopilotRuntimeAgent) agent.adoptRuntimeMode(runtimeInfo.mode ?? RUNTIME_MODE_SSE, runtimeInfo.intelligence);
|
|
1689
|
+
}
|
|
1690
|
+
/**
|
|
1691
|
+
* Apply the core's message filter to an agent.
|
|
1692
|
+
*
|
|
1693
|
+
* The test is the class, not which bucket the agent is registered in: every
|
|
1694
|
+
* `ProxiedCopilotRuntimeAgent` gets the core filter, whether it came from
|
|
1695
|
+
* `/info` or from `registerProxiedAgent` (which files its proxy under
|
|
1696
|
+
* `localAgents`). Agents of any other class are left untouched — an
|
|
1697
|
+
* `AbstractAgent` the app built has no such hook, and its owner already has
|
|
1698
|
+
* the AG-UI middleware seam.
|
|
1699
|
+
*
|
|
1700
|
+
* This is the sole writer of `agent.messageFilter` in normal operation. The
|
|
1701
|
+
* public setter exists for the registry and for tests; a value written
|
|
1702
|
+
* directly onto an agent is replaced the next time the registry sweeps, the
|
|
1703
|
+
* same way per-agent credentials are.
|
|
1704
|
+
*/
|
|
1705
|
+
applyMessageFilterToAgent(agent) {
|
|
1706
|
+
if (agent instanceof ProxiedCopilotRuntimeAgent) agent.messageFilter = this.core.messageFilter;
|
|
1707
|
+
}
|
|
1708
|
+
/**
|
|
1709
|
+
* Apply the core's message filter to all agents
|
|
1710
|
+
*/
|
|
1711
|
+
applyMessageFilterToAgents(agents) {
|
|
1712
|
+
Object.values(agents).forEach((agent) => {
|
|
1713
|
+
this.applyMessageFilterToAgent(agent);
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1453
1716
|
createRuntimeFetch() {
|
|
1454
1717
|
if (!this.runtimeFetch) this.runtimeFetch = (async (input, init) => {
|
|
1455
1718
|
const meta = () => runtimeRequestMeta(init);
|
|
@@ -1837,7 +2100,9 @@ var AgentRegistry = class {
|
|
|
1837
2100
|
if (existing instanceof ProxiedCopilotRuntimeAgent && this.canReuseRuntimeAgent(existing, runtimeUrl, this._runtimeTransport)) {
|
|
1838
2101
|
this.applyHeadersToAgent(existing);
|
|
1839
2102
|
this.applyCredentialsToAgent(existing);
|
|
2103
|
+
this.applyMessageFilterToAgent(existing);
|
|
1840
2104
|
this.applyRuntimeFetchToAgent(existing);
|
|
2105
|
+
this.applyRuntimeModeToAgent(existing, runtimeInfoResponse);
|
|
1841
2106
|
return [id, existing];
|
|
1842
2107
|
}
|
|
1843
2108
|
const agent = new ProxiedCopilotRuntimeAgent({
|
|
@@ -1849,7 +2114,8 @@ var AgentRegistry = class {
|
|
|
1849
2114
|
runtimeMode: runtimeInfoResponse.mode ?? RUNTIME_MODE_SSE,
|
|
1850
2115
|
intelligence: runtimeInfoResponse.intelligence,
|
|
1851
2116
|
capabilities,
|
|
1852
|
-
debug: rawDebug ? resolveDebugConfig(rawDebug) : void 0
|
|
2117
|
+
debug: rawDebug ? resolveDebugConfig(rawDebug) : void 0,
|
|
2118
|
+
messageFilter: this.core.messageFilter
|
|
1853
2119
|
});
|
|
1854
2120
|
this.applyHeadersToAgent(agent);
|
|
1855
2121
|
this.applyRuntimeFetchToAgent(agent);
|
|
@@ -2241,7 +2507,10 @@ var SuggestionEngine = class {
|
|
|
2241
2507
|
credentials
|
|
2242
2508
|
}) } : {}
|
|
2243
2509
|
});
|
|
2244
|
-
} else
|
|
2510
|
+
} else {
|
|
2511
|
+
suggestionAgent = suggestionsProviderAgent.clone();
|
|
2512
|
+
if (suggestionAgent instanceof ProxiedCopilotRuntimeAgent) suggestionAgent.messageFilter = void 0;
|
|
2513
|
+
}
|
|
2245
2514
|
suggestionAgent.threadId = suggestionId;
|
|
2246
2515
|
suggestionAgent.setMessages(seededMessages);
|
|
2247
2516
|
suggestionAgent.setState(seededState);
|
|
@@ -2603,6 +2872,19 @@ var WebMCPRegistry = class {
|
|
|
2603
2872
|
//#endregion
|
|
2604
2873
|
//#region src/core/run-handler.ts
|
|
2605
2874
|
/**
|
|
2875
|
+
* Live approvals cancelled by stopping their own run, keyed by the handler's
|
|
2876
|
+
* signal. The value reports whether that stop is still current: no reconnect
|
|
2877
|
+
* has started and the thread is unchanged, so nothing will restore the call.
|
|
2878
|
+
*
|
|
2879
|
+
* Kept here rather than in `AbortSignal.reason`: React Native's global
|
|
2880
|
+
* `AbortController` is the `abort-controller@3` polyfill, whose `abort()`
|
|
2881
|
+
* takes no reason and whose signals never expose one.
|
|
2882
|
+
*/
|
|
2883
|
+
const liveHITLStops = /* @__PURE__ */ new WeakMap();
|
|
2884
|
+
function discardAbortedResult(signal, discardOnAbort, errorMessage) {
|
|
2885
|
+
return !!(discardOnAbort && signal?.aborted && !(liveHITLStops.get(signal)?.() && errorMessage));
|
|
2886
|
+
}
|
|
2887
|
+
/**
|
|
2606
2888
|
* Absolute safety cap on the depth of recursive follow-up runs triggered by
|
|
2607
2889
|
* `processAgentResult`. Without this, any scenario that keeps
|
|
2608
2890
|
* `needsFollowUp = true` (e.g. the LLM repeatedly calling the same tool, a
|
|
@@ -2715,6 +2997,10 @@ var RunHandler = class {
|
|
|
2715
2997
|
_lastConnectedThreadIdsByAgent = /* @__PURE__ */ new Map();
|
|
2716
2998
|
_anonymousAgentIds = /* @__PURE__ */ new WeakMap();
|
|
2717
2999
|
_nextAnonymousAgentId = 0;
|
|
3000
|
+
/** Prevent reconnects from starting an interaction already executing locally. */
|
|
3001
|
+
_executingToolCalls = /* @__PURE__ */ new WeakMap();
|
|
3002
|
+
_connectionGenerations = /* @__PURE__ */ new WeakMap();
|
|
3003
|
+
_interactionAbortControllers = /* @__PURE__ */ new WeakMap();
|
|
2718
3004
|
constructor(core) {
|
|
2719
3005
|
this.core = core;
|
|
2720
3006
|
}
|
|
@@ -2723,8 +3009,14 @@ var RunHandler = class {
|
|
|
2723
3009
|
* that in-flight tool handlers should stop and `processAgentResult` should
|
|
2724
3010
|
* not start a follow-up run.
|
|
2725
3011
|
*/
|
|
2726
|
-
abortCurrentRun() {
|
|
3012
|
+
abortCurrentRun(agent) {
|
|
2727
3013
|
this._runAbortController?.abort();
|
|
3014
|
+
const replay = this._interactionAbortControllers.get(agent);
|
|
3015
|
+
if (replay) {
|
|
3016
|
+
for (const controller of replay.controllers) controller.abort();
|
|
3017
|
+
this._interactionAbortControllers.delete(agent);
|
|
3018
|
+
this._executingToolCalls.delete(agent);
|
|
3019
|
+
}
|
|
2728
3020
|
}
|
|
2729
3021
|
/**
|
|
2730
3022
|
* Typed access to CopilotKitCore's internal ("friend") methods.
|
|
@@ -2880,7 +3172,14 @@ var RunHandler = class {
|
|
|
2880
3172
|
* Connect an agent (establish initial connection)
|
|
2881
3173
|
*/
|
|
2882
3174
|
async connectAgent({ agent }) {
|
|
3175
|
+
this._connectionGenerations.set(agent, {});
|
|
2883
3176
|
const incomingThreadId = agent.threadId ?? null;
|
|
3177
|
+
const previousReplay = this._interactionAbortControllers.get(agent);
|
|
3178
|
+
if (previousReplay && previousReplay.threadId !== incomingThreadId) {
|
|
3179
|
+
for (const controller of previousReplay.controllers) controller.abort();
|
|
3180
|
+
this._interactionAbortControllers.delete(agent);
|
|
3181
|
+
this._executingToolCalls.delete(agent);
|
|
3182
|
+
}
|
|
2884
3183
|
try {
|
|
2885
3184
|
const restoreKey = this.getConnectRestoreKey(agent);
|
|
2886
3185
|
const isFreshRestore = incomingThreadId !== (this._lastConnectedThreadIdsByAgent.get(restoreKey) ?? null);
|
|
@@ -2905,11 +3204,33 @@ var RunHandler = class {
|
|
|
2905
3204
|
tools: this.buildFrontendTools(agent.agentId),
|
|
2906
3205
|
context: this._internal.getContextForAgent(agent.agentId)
|
|
2907
3206
|
}, this.createAgentErrorSubscriber(agent));
|
|
2908
|
-
return
|
|
3207
|
+
if ((agent.threadId ?? null) !== incomingThreadId) return runAgentResult;
|
|
3208
|
+
const controller = new AbortController();
|
|
3209
|
+
const replay = this._interactionAbortControllers.get(agent) ?? {
|
|
3210
|
+
threadId: incomingThreadId,
|
|
3211
|
+
controllers: /* @__PURE__ */ new Set()
|
|
3212
|
+
};
|
|
3213
|
+
replay.controllers.add(controller);
|
|
3214
|
+
this._interactionAbortControllers.set(agent, replay);
|
|
3215
|
+
this.processAgentResult({
|
|
2909
3216
|
runAgentResult,
|
|
2910
3217
|
agent,
|
|
2911
|
-
|
|
3218
|
+
toolExecutionMode: "human-in-the-loop",
|
|
3219
|
+
signal: controller.signal
|
|
3220
|
+
}).catch(async (error) => {
|
|
3221
|
+
await this._internal.emitError({
|
|
3222
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
3223
|
+
code: CopilotKitCoreErrorCode.AGENT_CONNECT_FAILED,
|
|
3224
|
+
context: {
|
|
3225
|
+
agentId: agent.agentId,
|
|
3226
|
+
threadId: incomingThreadId
|
|
3227
|
+
}
|
|
3228
|
+
});
|
|
3229
|
+
}).finally(() => {
|
|
3230
|
+
replay.controllers.delete(controller);
|
|
3231
|
+
if (replay.controllers.size === 0 && this._interactionAbortControllers.get(agent) === replay) this._interactionAbortControllers.delete(agent);
|
|
2912
3232
|
});
|
|
3233
|
+
return runAgentResult;
|
|
2913
3234
|
} catch (error) {
|
|
2914
3235
|
const connectError = error instanceof Error ? error : new Error(String(error));
|
|
2915
3236
|
if (!isAbortError$1(connectError)) {
|
|
@@ -3011,45 +3332,83 @@ var RunHandler = class {
|
|
|
3011
3332
|
/**
|
|
3012
3333
|
* Process agent result and execute tools
|
|
3013
3334
|
*/
|
|
3014
|
-
async processAgentResult({ runAgentResult, agent, runId,
|
|
3335
|
+
async processAgentResult({ runAgentResult, agent, runId, toolExecutionMode = "all", signal = this._runAbortController?.signal }) {
|
|
3015
3336
|
const { newMessages } = runAgentResult;
|
|
3016
3337
|
const agentId = agent.agentId;
|
|
3017
3338
|
let needsFollowUp = false;
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3339
|
+
const threadId = agent.threadId;
|
|
3340
|
+
const executing = this._executingToolCalls.get(agent) ?? /* @__PURE__ */ new Set();
|
|
3341
|
+
this._executingToolCalls.set(agent, executing);
|
|
3342
|
+
if (toolExecutionMode === "human-in-the-loop" && agent.messages.some((message) => message.role === "assistant" && message.toolCalls?.some((call) => executing.has(JSON.stringify([threadId, call.id])) && !agent.messages.some((result) => result.role === "tool" && result.toolCallId === call.id && !this.isFrontendPlaceholderResult(result))))) return runAgentResult;
|
|
3343
|
+
const messagesToProcess = toolExecutionMode === "human-in-the-loop" ? [...agent.messages] : newMessages;
|
|
3344
|
+
for (const message of messagesToProcess) if (message.role === "assistant") for (const toolCall of message.toolCalls || []) {
|
|
3345
|
+
const tool = this.getTool({
|
|
3346
|
+
toolName: toolCall.function.name,
|
|
3347
|
+
agentId: agent.agentId
|
|
3348
|
+
});
|
|
3349
|
+
let wildcardTool;
|
|
3350
|
+
const getWildcardTool = () => {
|
|
3351
|
+
if (tool || wildcardTool) return wildcardTool;
|
|
3352
|
+
wildcardTool = this.getTool({
|
|
3353
|
+
toolName: WILDCARD_TOOL_NAME,
|
|
3022
3354
|
agentId: agent.agentId
|
|
3023
3355
|
});
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3356
|
+
return wildcardTool;
|
|
3357
|
+
};
|
|
3358
|
+
const executableTool = tool ?? getWildcardTool();
|
|
3359
|
+
if (toolExecutionMode === "human-in-the-loop" && (executableTool?.type !== "human-in-the-loop" || !executableTool.handler)) continue;
|
|
3360
|
+
const executionKey = JSON.stringify([threadId, toolCall.id]);
|
|
3361
|
+
if (signal?.aborted || agent.threadId !== threadId || executing.has(executionKey)) continue;
|
|
3362
|
+
const existingResultIndex = newMessages.findIndex((m) => m.role === "tool" && m.toolCallId === toolCall.id);
|
|
3363
|
+
let existingResult = agent.messages.find((m) => m.role === "tool" && m.toolCallId === toolCall.id) ?? newMessages[existingResultIndex];
|
|
3364
|
+
if (existingResult && executableTool?.handler && this.isFrontendPlaceholderResult(existingResult)) {
|
|
3365
|
+
if (existingResultIndex !== -1) newMessages.splice(existingResultIndex, 1);
|
|
3366
|
+
existingResult = void 0;
|
|
3367
|
+
const agentMsgIdx = agent.messages.findIndex((m) => m.role === "tool" && m.toolCallId === toolCall.id);
|
|
3368
|
+
if (agentMsgIdx !== -1) agent.messages.splice(agentMsgIdx, 1);
|
|
3369
|
+
}
|
|
3370
|
+
if (!existingResult) {
|
|
3371
|
+
executing.add(executionKey);
|
|
3372
|
+
const interactionController = toolExecutionMode === "all" && executableTool?.type === "human-in-the-loop" ? new AbortController() : void 0;
|
|
3373
|
+
const interactions = this._interactionAbortControllers.get(agent) ?? {
|
|
3374
|
+
threadId: threadId ?? null,
|
|
3375
|
+
controllers: /* @__PURE__ */ new Set()
|
|
3376
|
+
};
|
|
3377
|
+
const abortInteraction = () => {
|
|
3378
|
+
if (!interactionController) return;
|
|
3379
|
+
const connectionGeneration = this._connectionGenerations.get(agent);
|
|
3380
|
+
liveHITLStops.set(interactionController.signal, () => this._connectionGenerations.get(agent) === connectionGeneration && agent.threadId === threadId);
|
|
3381
|
+
interactionController.abort();
|
|
3032
3382
|
};
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
existingResultIndex = -1;
|
|
3039
|
-
const agentMsgIdx = agent.messages.findIndex((m) => m.role === "tool" && m.toolCallId === toolCall.id);
|
|
3040
|
-
if (agentMsgIdx !== -1) agent.messages.splice(agentMsgIdx, 1);
|
|
3383
|
+
if (interactionController) {
|
|
3384
|
+
interactions.controllers.add(interactionController);
|
|
3385
|
+
this._interactionAbortControllers.set(agent, interactions);
|
|
3386
|
+
signal?.addEventListener("abort", abortInteraction, { once: true });
|
|
3387
|
+
if (signal?.aborted) abortInteraction();
|
|
3041
3388
|
}
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3389
|
+
const executionSignal = interactionController?.signal ?? signal;
|
|
3390
|
+
const discardOnAbort = toolExecutionMode === "human-in-the-loop" || !!interactionController;
|
|
3391
|
+
try {
|
|
3392
|
+
if (tool) {
|
|
3393
|
+
if (await this.executeSpecificTool(tool, toolCall, message, agent, agentId, executionSignal, discardOnAbort)) needsFollowUp = true;
|
|
3394
|
+
} else {
|
|
3395
|
+
const fallbackTool = getWildcardTool();
|
|
3396
|
+
if (fallbackTool) {
|
|
3397
|
+
if (await this.executeWildcardTool(fallbackTool, toolCall, message, agent, agentId, executionSignal, discardOnAbort)) needsFollowUp = true;
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
} finally {
|
|
3401
|
+
executing.delete(executionKey);
|
|
3402
|
+
if (interactionController) {
|
|
3403
|
+
signal?.removeEventListener("abort", abortInteraction);
|
|
3404
|
+
interactions.controllers.delete(interactionController);
|
|
3405
|
+
if (interactions.controllers.size === 0 && this._interactionAbortControllers.get(agent) === interactions) this._interactionAbortControllers.delete(agent);
|
|
3048
3406
|
}
|
|
3049
3407
|
}
|
|
3408
|
+
if (interactionController?.signal.aborted) return runAgentResult;
|
|
3050
3409
|
}
|
|
3051
3410
|
}
|
|
3052
|
-
if (needsFollowUp && !
|
|
3411
|
+
if (needsFollowUp && agent.threadId === threadId && !signal?.aborted) if (this._runDepth >= MAX_FOLLOW_UP_DEPTH) logger.warn(`[CopilotKit] Follow-up depth limit (${MAX_FOLLOW_UP_DEPTH}) reached for agent "${agentId}". Stopping recursive follow-up runs to prevent an infinite loop. This usually indicates a tool that keeps requesting a follow-up (e.g. the LLM repeatedly calling the same tool). Consider setting "followUp: false" on the offending tool.`);
|
|
3053
3412
|
else {
|
|
3054
3413
|
await this._internal.waitForPendingFrameworkUpdates();
|
|
3055
3414
|
const continuationHandoff = this._internal.stateManager.markNextRunAsContinuation(agent, runId);
|
|
@@ -3069,7 +3428,7 @@ var RunHandler = class {
|
|
|
3069
3428
|
* Handles arg parsing, subscriber notifications, handler invocation, result stringification,
|
|
3070
3429
|
* and error handling.
|
|
3071
3430
|
*/
|
|
3072
|
-
async executeToolHandler({ tool, toolCall, agent, agentId, handlerArgs, toolType, messageId }) {
|
|
3431
|
+
async executeToolHandler({ tool, toolCall, agent, agentId, handlerArgs, toolType, messageId, signal = this._runAbortController?.signal, discardOnAbort = false }) {
|
|
3073
3432
|
let toolCallResult = "";
|
|
3074
3433
|
let errorMessage;
|
|
3075
3434
|
let isArgumentError = false;
|
|
@@ -3104,7 +3463,7 @@ var RunHandler = class {
|
|
|
3104
3463
|
const result = await tool.handler(parsedArgs, {
|
|
3105
3464
|
toolCall,
|
|
3106
3465
|
agent,
|
|
3107
|
-
signal
|
|
3466
|
+
signal
|
|
3108
3467
|
});
|
|
3109
3468
|
if (result === void 0 || result === null) toolCallResult = "";
|
|
3110
3469
|
else if (typeof result === "string") toolCallResult = result;
|
|
@@ -3112,7 +3471,7 @@ var RunHandler = class {
|
|
|
3112
3471
|
} catch (error) {
|
|
3113
3472
|
const handlerError = error instanceof Error ? error : new Error(String(error));
|
|
3114
3473
|
errorMessage = handlerError.message;
|
|
3115
|
-
await this._internal.emitError({
|
|
3474
|
+
if (!discardAbortedResult(signal, discardOnAbort, errorMessage)) await this._internal.emitError({
|
|
3116
3475
|
error: handlerError,
|
|
3117
3476
|
code: CopilotKitCoreErrorCode.TOOL_HANDLER_FAILED,
|
|
3118
3477
|
context: {
|
|
@@ -3143,7 +3502,8 @@ var RunHandler = class {
|
|
|
3143
3502
|
/**
|
|
3144
3503
|
* Execute a specific tool
|
|
3145
3504
|
*/
|
|
3146
|
-
async executeSpecificTool(tool, toolCall, message, agent, agentId) {
|
|
3505
|
+
async executeSpecificTool(tool, toolCall, message, agent, agentId, signal, discardOnAbort = false) {
|
|
3506
|
+
const threadId = agent.threadId;
|
|
3147
3507
|
if (tool?.agentId && tool.agentId !== agent.agentId) return false;
|
|
3148
3508
|
let handlerResult = {
|
|
3149
3509
|
result: "",
|
|
@@ -3157,11 +3517,13 @@ var RunHandler = class {
|
|
|
3157
3517
|
agentId,
|
|
3158
3518
|
handlerArgs: toolCall.function.arguments,
|
|
3159
3519
|
toolType: "specific",
|
|
3160
|
-
messageId: message.id
|
|
3520
|
+
messageId: message.id,
|
|
3521
|
+
signal,
|
|
3522
|
+
discardOnAbort
|
|
3161
3523
|
});
|
|
3162
3524
|
{
|
|
3163
3525
|
const messageIndex = agent.messages.findIndex((m) => m.id === message.id);
|
|
3164
|
-
if (messageIndex === -1) return false;
|
|
3526
|
+
if (discardAbortedResult(signal, discardOnAbort, handlerResult.error) || agent.threadId !== threadId || messageIndex === -1 || agent.messages.some((m) => m.role === "tool" && m.toolCallId === toolCall.id && !this.isFrontendPlaceholderResult(m))) return false;
|
|
3165
3527
|
let insertAt = messageIndex + 1;
|
|
3166
3528
|
while (insertAt < agent.messages.length && agent.messages[insertAt]?.role === "tool") insertAt++;
|
|
3167
3529
|
const toolMessage = {
|
|
@@ -3181,7 +3543,8 @@ var RunHandler = class {
|
|
|
3181
3543
|
* specific tools, so this method keeps its own arg-wrapping logic rather than
|
|
3182
3544
|
* delegating to `executeToolHandler`.
|
|
3183
3545
|
*/
|
|
3184
|
-
async executeWildcardTool(wildcardTool, toolCall, message, agent, agentId) {
|
|
3546
|
+
async executeWildcardTool(wildcardTool, toolCall, message, agent, agentId, signal, discardOnAbort = false) {
|
|
3547
|
+
const threadId = agent.threadId;
|
|
3185
3548
|
if (wildcardTool?.agentId && wildcardTool.agentId !== agent.agentId) return false;
|
|
3186
3549
|
let toolCallResult = "";
|
|
3187
3550
|
let errorMessage;
|
|
@@ -3219,7 +3582,8 @@ var RunHandler = class {
|
|
|
3219
3582
|
if (!errorMessage) try {
|
|
3220
3583
|
const result = await wildcardTool.handler(wildcardArgs, {
|
|
3221
3584
|
toolCall,
|
|
3222
|
-
agent
|
|
3585
|
+
agent,
|
|
3586
|
+
signal
|
|
3223
3587
|
});
|
|
3224
3588
|
if (result === void 0 || result === null) toolCallResult = "";
|
|
3225
3589
|
else if (typeof result === "string") toolCallResult = result;
|
|
@@ -3227,7 +3591,7 @@ var RunHandler = class {
|
|
|
3227
3591
|
} catch (error) {
|
|
3228
3592
|
const handlerError = error instanceof Error ? error : new Error(String(error));
|
|
3229
3593
|
errorMessage = handlerError.message;
|
|
3230
|
-
await this._internal.emitError({
|
|
3594
|
+
if (!discardAbortedResult(signal, discardOnAbort, errorMessage)) await this._internal.emitError({
|
|
3231
3595
|
error: handlerError,
|
|
3232
3596
|
code: CopilotKitCoreErrorCode.TOOL_HANDLER_FAILED,
|
|
3233
3597
|
context: {
|
|
@@ -3252,7 +3616,7 @@ var RunHandler = class {
|
|
|
3252
3616
|
}
|
|
3253
3617
|
{
|
|
3254
3618
|
const messageIndex = agent.messages.findIndex((m) => m.id === message.id);
|
|
3255
|
-
if (messageIndex === -1) return false;
|
|
3619
|
+
if (discardAbortedResult(signal, discardOnAbort, errorMessage) || agent.threadId !== threadId || messageIndex === -1 || agent.messages.some((m) => m.role === "tool" && m.toolCallId === toolCall.id && !this.isFrontendPlaceholderResult(m))) return false;
|
|
3256
3620
|
let insertAt = messageIndex + 1;
|
|
3257
3621
|
while (insertAt < agent.messages.length && agent.messages[insertAt]?.role === "tool") insertAt++;
|
|
3258
3622
|
const toolMessage = {
|
|
@@ -5037,6 +5401,7 @@ function normalizeHeaders(headers) {
|
|
|
5037
5401
|
var CopilotKitCore = class {
|
|
5038
5402
|
_headers;
|
|
5039
5403
|
_credentials;
|
|
5404
|
+
_messageFilter;
|
|
5040
5405
|
_properties;
|
|
5041
5406
|
_defaultThrottleMs;
|
|
5042
5407
|
_debug;
|
|
@@ -5061,9 +5426,10 @@ var CopilotKitCore = class {
|
|
|
5061
5426
|
* store that was registered prior to that initial notification.
|
|
5062
5427
|
*/
|
|
5063
5428
|
previousAgentIds = /* @__PURE__ */ new Set();
|
|
5064
|
-
constructor({ runtimeUrl, runtimeTransport = "auto", deferInitialConnection = false, headers = {}, credentials, properties = {}, agents__unsafe_dev_only = {}, tools = [], suggestionsConfig = [], debug }) {
|
|
5429
|
+
constructor({ runtimeUrl, runtimeTransport = "auto", deferInitialConnection = false, headers = {}, credentials, messageFilter, properties = {}, agents__unsafe_dev_only = {}, tools = [], suggestionsConfig = [], debug }) {
|
|
5065
5430
|
this._headers = normalizeHeaders(headers);
|
|
5066
5431
|
this._credentials = credentials;
|
|
5432
|
+
this._messageFilter = messageFilter;
|
|
5067
5433
|
this._properties = properties;
|
|
5068
5434
|
this._debug = debug;
|
|
5069
5435
|
this.agentRegistry = new AgentRegistry(this);
|
|
@@ -5183,6 +5549,9 @@ var CopilotKitCore = class {
|
|
|
5183
5549
|
get credentials() {
|
|
5184
5550
|
return this._credentials;
|
|
5185
5551
|
}
|
|
5552
|
+
get messageFilter() {
|
|
5553
|
+
return this._messageFilter;
|
|
5554
|
+
}
|
|
5186
5555
|
get properties() {
|
|
5187
5556
|
return this._properties;
|
|
5188
5557
|
}
|
|
@@ -5328,6 +5697,17 @@ var CopilotKitCore = class {
|
|
|
5328
5697
|
this.agentRegistry.applyCredentialsToAgents(this.agentRegistry.agents);
|
|
5329
5698
|
this.agentRegistry.handleCredentialsChanged();
|
|
5330
5699
|
}
|
|
5700
|
+
/**
|
|
5701
|
+
* Replace the message filter applied to every runtime agent.
|
|
5702
|
+
*
|
|
5703
|
+
* Applies to agents already discovered as well as ones discovered later, so
|
|
5704
|
+
* a filter set before `/info` lands is not lost. Pass `undefined` to go back
|
|
5705
|
+
* to sending the full thread.
|
|
5706
|
+
*/
|
|
5707
|
+
setMessageFilter(messageFilter) {
|
|
5708
|
+
this._messageFilter = messageFilter;
|
|
5709
|
+
this.agentRegistry.applyMessageFilterToAgents(this.agentRegistry.agents);
|
|
5710
|
+
}
|
|
5331
5711
|
setProperties(properties) {
|
|
5332
5712
|
this._properties = properties;
|
|
5333
5713
|
this.notifySubscribers((subscriber) => subscriber.onPropertiesChanged?.({
|
|
@@ -5676,7 +6056,7 @@ var CopilotKitCore = class {
|
|
|
5676
6056
|
return this.runHandler.connectAgent(params);
|
|
5677
6057
|
}
|
|
5678
6058
|
stopAgent(params) {
|
|
5679
|
-
this.runHandler.abortCurrentRun();
|
|
6059
|
+
this.runHandler.abortCurrentRun(params.agent);
|
|
5680
6060
|
params.agent.abortRun();
|
|
5681
6061
|
}
|
|
5682
6062
|
async runAgent(params) {
|