@nextclaw/ncp-toolkit 0.4.16 → 0.5.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.d.ts +7 -9
- package/dist/index.js +426 -371
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -69,6 +69,7 @@ declare class EventPublisher {
|
|
|
69
69
|
|
|
70
70
|
type RuntimeFactoryParams = {
|
|
71
71
|
sessionId: string;
|
|
72
|
+
agentId?: string;
|
|
72
73
|
stateManager: NcpAgentConversationStateManager;
|
|
73
74
|
sessionMetadata: Record<string, unknown>;
|
|
74
75
|
setSessionMetadata: (nextMetadata: Record<string, unknown>) => void;
|
|
@@ -76,22 +77,24 @@ type RuntimeFactoryParams = {
|
|
|
76
77
|
type CreateRuntimeFn = (params: RuntimeFactoryParams) => NcpAgentRuntime;
|
|
77
78
|
type AgentSessionRecord = {
|
|
78
79
|
sessionId: string;
|
|
80
|
+
agentId?: string;
|
|
79
81
|
messages: NcpMessage[];
|
|
80
82
|
updatedAt: string;
|
|
81
83
|
metadata?: Record<string, unknown>;
|
|
82
84
|
};
|
|
83
85
|
type LiveSessionExecution = {
|
|
84
86
|
controller: AbortController;
|
|
85
|
-
publisher: EventPublisher;
|
|
86
87
|
requestEnvelope: NcpRequestEnvelope;
|
|
87
88
|
abortHandled: boolean;
|
|
88
89
|
closed: boolean;
|
|
89
90
|
};
|
|
90
91
|
type LiveSessionState = {
|
|
91
92
|
sessionId: string;
|
|
93
|
+
agentId?: string;
|
|
92
94
|
runtime: NcpAgentRuntime;
|
|
93
95
|
stateManager: NcpAgentConversationStateManager;
|
|
94
96
|
metadata: Record<string, unknown>;
|
|
97
|
+
publisher: EventPublisher;
|
|
95
98
|
activeExecution: LiveSessionExecution | null;
|
|
96
99
|
};
|
|
97
100
|
interface AgentSessionStore {
|
|
@@ -124,12 +127,13 @@ declare class DefaultNcpAgentBackend implements NcpAgentServerEndpoint, NcpSessi
|
|
|
124
127
|
private readonly sessionRegistry;
|
|
125
128
|
private readonly executor;
|
|
126
129
|
private readonly publisher;
|
|
130
|
+
private readonly sessionRealtime;
|
|
127
131
|
private started;
|
|
128
132
|
constructor(config: DefaultNcpAgentBackendConfig);
|
|
129
133
|
start: () => Promise<void>;
|
|
130
134
|
stop: () => Promise<void>;
|
|
131
135
|
emit: (event: NcpEndpointEvent) => Promise<void>;
|
|
132
|
-
subscribe: (listener: (event: NcpEndpointEvent) => void) => () => void;
|
|
136
|
+
subscribe: (listener: (event: NcpEndpointEvent) => void) => (() => void);
|
|
133
137
|
send: (envelope: NcpRequestEnvelope, options?: NcpAgentRunSendOptions) => AsyncIterable<NcpEndpointEvent>;
|
|
134
138
|
abort: (payload: NcpMessageAbortPayload) => Promise<void>;
|
|
135
139
|
stream: (payloadOrParams: NcpStreamRequestPayload | {
|
|
@@ -146,18 +150,12 @@ declare class DefaultNcpAgentBackend implements NcpAgentServerEndpoint, NcpSessi
|
|
|
146
150
|
private ensureStarted;
|
|
147
151
|
private startSessionExecution;
|
|
148
152
|
private finishSessionExecution;
|
|
149
|
-
private
|
|
150
|
-
private publishLiveEvent;
|
|
151
|
-
private handleRequest;
|
|
152
|
-
private streamToSubscribers;
|
|
153
|
+
private publishEndpointEvent;
|
|
153
154
|
private handleAbort;
|
|
154
|
-
private createAbortEvent;
|
|
155
155
|
private persistSession;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
declare class AgentRunExecutor {
|
|
159
|
-
private readonly persistSession;
|
|
160
|
-
constructor(persistSession: (sessionId: string) => Promise<void>);
|
|
161
159
|
executeRun(session: LiveSessionState, envelope: NcpRequestEnvelope, controller: AbortController): AsyncGenerator<NcpEndpointEvent>;
|
|
162
160
|
private publishFailure;
|
|
163
161
|
}
|
package/dist/index.js
CHANGED
|
@@ -702,31 +702,77 @@ async function consume(events) {
|
|
|
702
702
|
|
|
703
703
|
// src/agent/agent-backend/agent-backend.ts
|
|
704
704
|
import {
|
|
705
|
-
NcpEventType as
|
|
705
|
+
NcpEventType as NcpEventType6
|
|
706
706
|
} from "@nextclaw/ncp";
|
|
707
707
|
|
|
708
|
-
// src/
|
|
709
|
-
var
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
this.
|
|
715
|
-
|
|
716
|
-
|
|
708
|
+
// src/agent/agent-backend/event-publisher.ts
|
|
709
|
+
var EventPublisher = class {
|
|
710
|
+
listeners = /* @__PURE__ */ new Set();
|
|
711
|
+
closeListeners = /* @__PURE__ */ new Set();
|
|
712
|
+
closed = false;
|
|
713
|
+
subscribe(listener) {
|
|
714
|
+
if (this.closed) {
|
|
715
|
+
return () => void 0;
|
|
716
|
+
}
|
|
717
|
+
this.listeners.add(listener);
|
|
718
|
+
return () => {
|
|
719
|
+
this.listeners.delete(listener);
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
onClose(listener) {
|
|
723
|
+
if (this.closed) {
|
|
724
|
+
listener();
|
|
725
|
+
return () => void 0;
|
|
726
|
+
}
|
|
727
|
+
this.closeListeners.add(listener);
|
|
728
|
+
return () => {
|
|
729
|
+
this.closeListeners.delete(listener);
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
publish(event) {
|
|
733
|
+
if (this.closed) {
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
for (const listener of this.listeners) {
|
|
737
|
+
listener(structuredClone(event));
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
close() {
|
|
741
|
+
if (this.closed) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
this.closed = true;
|
|
745
|
+
this.listeners.clear();
|
|
746
|
+
for (const listener of this.closeListeners) {
|
|
747
|
+
listener();
|
|
748
|
+
}
|
|
749
|
+
this.closeListeners.clear();
|
|
717
750
|
}
|
|
718
751
|
};
|
|
719
752
|
|
|
720
753
|
// src/agent/agent-backend/agent-live-session-registry.ts
|
|
754
|
+
function readOptionalAgentId(value) {
|
|
755
|
+
if (typeof value !== "string") {
|
|
756
|
+
return void 0;
|
|
757
|
+
}
|
|
758
|
+
const trimmed = value.trim().toLowerCase();
|
|
759
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
760
|
+
}
|
|
761
|
+
function readAgentIdFromMetadata(metadata) {
|
|
762
|
+
return readOptionalAgentId(metadata?.agent_id) ?? readOptionalAgentId(metadata?.agentId);
|
|
763
|
+
}
|
|
721
764
|
var AgentLiveSessionRegistry = class {
|
|
722
765
|
constructor(sessionStore, createRuntime) {
|
|
723
766
|
this.sessionStore = sessionStore;
|
|
724
767
|
this.createRuntime = createRuntime;
|
|
725
768
|
}
|
|
726
769
|
sessions = /* @__PURE__ */ new Map();
|
|
727
|
-
async
|
|
770
|
+
ensureSession = async (sessionId, initialMetadata) => {
|
|
728
771
|
const existing = this.sessions.get(sessionId);
|
|
729
772
|
if (existing) {
|
|
773
|
+
if (!existing.agentId) {
|
|
774
|
+
existing.agentId = readAgentIdFromMetadata(initialMetadata) ?? existing.agentId;
|
|
775
|
+
}
|
|
730
776
|
if (initialMetadata && Object.keys(initialMetadata).length > 0) {
|
|
731
777
|
existing.metadata = {
|
|
732
778
|
...existing.metadata,
|
|
@@ -745,15 +791,19 @@ var AgentLiveSessionRegistry = class {
|
|
|
745
791
|
...storedSession?.metadata ? structuredClone(storedSession.metadata) : {},
|
|
746
792
|
...initialMetadata ? structuredClone(initialMetadata) : {}
|
|
747
793
|
};
|
|
794
|
+
const sessionAgentId = readOptionalAgentId(storedSession?.agentId) ?? readAgentIdFromMetadata(initialMetadata);
|
|
748
795
|
const session = {
|
|
749
796
|
sessionId,
|
|
797
|
+
...sessionAgentId ? { agentId: sessionAgentId } : {},
|
|
750
798
|
stateManager,
|
|
751
799
|
metadata: sessionMetadata,
|
|
752
800
|
runtime: null,
|
|
801
|
+
publisher: new EventPublisher(),
|
|
753
802
|
activeExecution: null
|
|
754
803
|
};
|
|
755
804
|
session.runtime = this.createRuntime({
|
|
756
805
|
sessionId,
|
|
806
|
+
...sessionAgentId ? { agentId: sessionAgentId } : {},
|
|
757
807
|
stateManager,
|
|
758
808
|
sessionMetadata,
|
|
759
809
|
setSessionMetadata: (nextMetadata) => {
|
|
@@ -764,37 +814,87 @@ var AgentLiveSessionRegistry = class {
|
|
|
764
814
|
});
|
|
765
815
|
this.sessions.set(sessionId, session);
|
|
766
816
|
return session;
|
|
767
|
-
}
|
|
768
|
-
getSession(sessionId) {
|
|
817
|
+
};
|
|
818
|
+
getSession = (sessionId) => {
|
|
769
819
|
return this.sessions.get(sessionId) ?? null;
|
|
770
|
-
}
|
|
771
|
-
deleteSession(sessionId) {
|
|
820
|
+
};
|
|
821
|
+
deleteSession = (sessionId) => {
|
|
772
822
|
const session = this.sessions.get(sessionId) ?? null;
|
|
773
823
|
if (session) {
|
|
774
824
|
this.sessions.delete(sessionId);
|
|
775
825
|
}
|
|
776
826
|
return session;
|
|
777
|
-
}
|
|
778
|
-
clear() {
|
|
827
|
+
};
|
|
828
|
+
clear = () => {
|
|
779
829
|
this.sessions.clear();
|
|
780
|
-
}
|
|
781
|
-
listSessions() {
|
|
830
|
+
};
|
|
831
|
+
listSessions = () => {
|
|
782
832
|
return [...this.sessions.values()];
|
|
783
|
-
}
|
|
833
|
+
};
|
|
784
834
|
};
|
|
785
835
|
function cloneMessages(messages) {
|
|
786
836
|
return messages.map((message) => structuredClone(message));
|
|
787
837
|
}
|
|
788
838
|
|
|
839
|
+
// src/errors/ncp-error-exception.ts
|
|
840
|
+
var NcpErrorException = class extends Error {
|
|
841
|
+
code;
|
|
842
|
+
details;
|
|
843
|
+
constructor(code, message, details) {
|
|
844
|
+
super(message);
|
|
845
|
+
this.name = "NcpErrorException";
|
|
846
|
+
this.code = code;
|
|
847
|
+
this.details = details;
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
// src/agent/agent-backend/agent-backend-execution-utils.ts
|
|
852
|
+
function startAgentBackendSessionExecution(params) {
|
|
853
|
+
const { session, envelope, signal, onStatusChanged } = params;
|
|
854
|
+
if (session.activeExecution && !session.activeExecution.closed) {
|
|
855
|
+
throw new NcpErrorException(
|
|
856
|
+
"runtime-error",
|
|
857
|
+
`Session ${session.sessionId} already has an active execution.`,
|
|
858
|
+
{ sessionId: session.sessionId }
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
const controller = new AbortController();
|
|
862
|
+
if (signal) {
|
|
863
|
+
signal.addEventListener("abort", () => controller.abort(), {
|
|
864
|
+
once: true
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
const execution = {
|
|
868
|
+
controller,
|
|
869
|
+
requestEnvelope: structuredClone(envelope),
|
|
870
|
+
abortHandled: false,
|
|
871
|
+
closed: false
|
|
872
|
+
};
|
|
873
|
+
session.activeExecution = execution;
|
|
874
|
+
onStatusChanged?.({ sessionKey: session.sessionId, status: "running" });
|
|
875
|
+
return execution;
|
|
876
|
+
}
|
|
877
|
+
function finishAgentBackendSessionExecution(params) {
|
|
878
|
+
const { session, execution, onStatusChanged } = params;
|
|
879
|
+
if (session.activeExecution === execution) {
|
|
880
|
+
session.activeExecution = null;
|
|
881
|
+
onStatusChanged?.({ sessionKey: session.sessionId, status: "idle" });
|
|
882
|
+
}
|
|
883
|
+
closeAgentBackendSessionExecution(execution);
|
|
884
|
+
}
|
|
885
|
+
function closeAgentBackendSessionExecution(execution) {
|
|
886
|
+
if (execution.closed) {
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
execution.closed = true;
|
|
890
|
+
}
|
|
891
|
+
|
|
789
892
|
// src/agent/agent-backend/agent-run-executor.ts
|
|
790
893
|
import {
|
|
791
894
|
isHiddenNcpMessage
|
|
792
895
|
} from "@nextclaw/ncp";
|
|
793
896
|
import { NcpEventType as NcpEventType3 } from "@nextclaw/ncp";
|
|
794
897
|
var AgentRunExecutor = class {
|
|
795
|
-
constructor(persistSession) {
|
|
796
|
-
this.persistSession = persistSession;
|
|
797
|
-
}
|
|
798
898
|
async *executeRun(session, envelope, controller) {
|
|
799
899
|
if (!isHiddenNcpMessage(envelope.message)) {
|
|
800
900
|
const messageSent = {
|
|
@@ -806,7 +906,6 @@ var AgentRunExecutor = class {
|
|
|
806
906
|
}
|
|
807
907
|
};
|
|
808
908
|
await session.stateManager.dispatch(messageSent);
|
|
809
|
-
await this.persistSession(envelope.sessionId);
|
|
810
909
|
yield structuredClone(messageSent);
|
|
811
910
|
}
|
|
812
911
|
try {
|
|
@@ -819,16 +918,17 @@ var AgentRunExecutor = class {
|
|
|
819
918
|
},
|
|
820
919
|
{ signal: controller.signal }
|
|
821
920
|
)) {
|
|
822
|
-
await this.persistSession(envelope.sessionId);
|
|
823
921
|
yield structuredClone(event);
|
|
824
922
|
}
|
|
825
923
|
} catch (error) {
|
|
826
924
|
if (!controller.signal.aborted) {
|
|
827
|
-
const runErrorEvent = await this.publishFailure(
|
|
925
|
+
const runErrorEvent = await this.publishFailure(
|
|
926
|
+
error,
|
|
927
|
+
envelope,
|
|
928
|
+
session
|
|
929
|
+
);
|
|
828
930
|
yield structuredClone(runErrorEvent);
|
|
829
931
|
}
|
|
830
|
-
} finally {
|
|
831
|
-
await this.persistSession(envelope.sessionId);
|
|
832
932
|
}
|
|
833
933
|
}
|
|
834
934
|
async publishFailure(error, envelope, session) {
|
|
@@ -841,14 +941,187 @@ var AgentRunExecutor = class {
|
|
|
841
941
|
}
|
|
842
942
|
};
|
|
843
943
|
await session.stateManager.dispatch(runErrorEvent);
|
|
844
|
-
await this.persistSession(envelope.sessionId);
|
|
845
944
|
return runErrorEvent;
|
|
846
945
|
}
|
|
847
946
|
};
|
|
848
947
|
|
|
849
|
-
// src/agent/agent-backend/agent-backend-session-
|
|
948
|
+
// src/agent/agent-backend/agent-backend-session-realtime.ts
|
|
850
949
|
import { NcpEventType as NcpEventType4 } from "@nextclaw/ncp";
|
|
950
|
+
|
|
951
|
+
// src/agent/agent-backend/async-queue.ts
|
|
952
|
+
function createAsyncQueue() {
|
|
953
|
+
const items = [];
|
|
954
|
+
let closed = false;
|
|
955
|
+
let pendingResolve = null;
|
|
956
|
+
const iterable = {
|
|
957
|
+
[Symbol.asyncIterator]() {
|
|
958
|
+
return {
|
|
959
|
+
next: () => {
|
|
960
|
+
if (items.length > 0) {
|
|
961
|
+
return Promise.resolve({
|
|
962
|
+
value: items.shift(),
|
|
963
|
+
done: false
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
if (closed) {
|
|
967
|
+
return Promise.resolve({
|
|
968
|
+
value: void 0,
|
|
969
|
+
done: true
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
return new Promise((resolve) => {
|
|
973
|
+
pendingResolve = resolve;
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
return {
|
|
980
|
+
push(item) {
|
|
981
|
+
if (closed) {
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
if (pendingResolve) {
|
|
985
|
+
const resolve = pendingResolve;
|
|
986
|
+
pendingResolve = null;
|
|
987
|
+
resolve({ value: item, done: false });
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
items.push(item);
|
|
991
|
+
},
|
|
992
|
+
close() {
|
|
993
|
+
if (closed) {
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
closed = true;
|
|
997
|
+
if (pendingResolve) {
|
|
998
|
+
const resolve = pendingResolve;
|
|
999
|
+
pendingResolve = null;
|
|
1000
|
+
resolve({
|
|
1001
|
+
value: void 0,
|
|
1002
|
+
done: true
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
},
|
|
1006
|
+
iterable
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// src/agent/agent-backend/agent-backend-session-realtime.ts
|
|
1011
|
+
var AgentBackendSessionRealtime = class {
|
|
1012
|
+
constructor(params) {
|
|
1013
|
+
this.params = params;
|
|
1014
|
+
}
|
|
1015
|
+
publishSessionEvent = async (session, event, options = {}) => {
|
|
1016
|
+
if (options.dispatchToStateManager) {
|
|
1017
|
+
await session.stateManager.dispatch(event);
|
|
1018
|
+
}
|
|
1019
|
+
this.params.publishEndpointEvent(event);
|
|
1020
|
+
session.publisher.publish(event);
|
|
1021
|
+
if (options.persistSession !== false) {
|
|
1022
|
+
await this.params.persistSession(session.sessionId);
|
|
1023
|
+
}
|
|
1024
|
+
};
|
|
1025
|
+
streamSessionEvents = (payloadOrParams, opts) => {
|
|
1026
|
+
return (async function* (self) {
|
|
1027
|
+
const payload = "payload" in payloadOrParams && "signal" in payloadOrParams ? payloadOrParams.payload : payloadOrParams;
|
|
1028
|
+
const signal = "payload" in payloadOrParams && "signal" in payloadOrParams ? payloadOrParams.signal : opts?.signal ?? new AbortController().signal;
|
|
1029
|
+
const session = await self.params.sessionRegistry.ensureSession(
|
|
1030
|
+
payload.sessionId
|
|
1031
|
+
);
|
|
1032
|
+
const queue = createAsyncQueue();
|
|
1033
|
+
const unsubscribe = session.publisher.subscribe((event) => {
|
|
1034
|
+
queue.push(event);
|
|
1035
|
+
});
|
|
1036
|
+
const unsubscribeClose = session.publisher.onClose(() => {
|
|
1037
|
+
queue.close();
|
|
1038
|
+
});
|
|
1039
|
+
const stop = () => {
|
|
1040
|
+
unsubscribe();
|
|
1041
|
+
unsubscribeClose();
|
|
1042
|
+
queue.close();
|
|
1043
|
+
signal.removeEventListener("abort", stop);
|
|
1044
|
+
};
|
|
1045
|
+
signal.addEventListener("abort", stop, { once: true });
|
|
1046
|
+
try {
|
|
1047
|
+
for await (const event of queue.iterable) {
|
|
1048
|
+
if (signal.aborted) {
|
|
1049
|
+
break;
|
|
1050
|
+
}
|
|
1051
|
+
yield event;
|
|
1052
|
+
}
|
|
1053
|
+
} finally {
|
|
1054
|
+
stop();
|
|
1055
|
+
}
|
|
1056
|
+
})(this);
|
|
1057
|
+
};
|
|
1058
|
+
appendMessage = async (sessionId, message) => {
|
|
1059
|
+
const normalizedSessionId = sessionId.trim();
|
|
1060
|
+
if (!normalizedSessionId) {
|
|
1061
|
+
return null;
|
|
1062
|
+
}
|
|
1063
|
+
let liveSession = this.params.sessionRegistry.getSession(normalizedSessionId);
|
|
1064
|
+
if (!liveSession) {
|
|
1065
|
+
const storedSession = await this.params.sessionStore.getSession(normalizedSessionId);
|
|
1066
|
+
if (!storedSession) {
|
|
1067
|
+
return null;
|
|
1068
|
+
}
|
|
1069
|
+
liveSession = await this.params.sessionRegistry.ensureSession(normalizedSessionId);
|
|
1070
|
+
}
|
|
1071
|
+
const nextMessage = {
|
|
1072
|
+
...structuredClone(message),
|
|
1073
|
+
sessionId: normalizedSessionId
|
|
1074
|
+
};
|
|
1075
|
+
await this.publishSessionEvent(
|
|
1076
|
+
liveSession,
|
|
1077
|
+
{
|
|
1078
|
+
type: NcpEventType4.MessageSent,
|
|
1079
|
+
payload: {
|
|
1080
|
+
sessionId: normalizedSessionId,
|
|
1081
|
+
message: nextMessage
|
|
1082
|
+
}
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
dispatchToStateManager: true
|
|
1086
|
+
}
|
|
1087
|
+
);
|
|
1088
|
+
return this.params.getSessionSummary(normalizedSessionId);
|
|
1089
|
+
};
|
|
1090
|
+
updateToolCallResult = async (sessionId, toolCallId, content) => {
|
|
1091
|
+
const normalizedSessionId = sessionId.trim();
|
|
1092
|
+
const normalizedToolCallId = toolCallId.trim();
|
|
1093
|
+
if (!normalizedSessionId || !normalizedToolCallId) {
|
|
1094
|
+
return null;
|
|
1095
|
+
}
|
|
1096
|
+
const liveSession = await this.params.sessionRegistry.ensureSession(normalizedSessionId);
|
|
1097
|
+
await this.publishSessionEvent(
|
|
1098
|
+
liveSession,
|
|
1099
|
+
{
|
|
1100
|
+
type: NcpEventType4.MessageToolCallResult,
|
|
1101
|
+
payload: {
|
|
1102
|
+
sessionId: normalizedSessionId,
|
|
1103
|
+
toolCallId: normalizedToolCallId,
|
|
1104
|
+
content
|
|
1105
|
+
}
|
|
1106
|
+
},
|
|
1107
|
+
{
|
|
1108
|
+
dispatchToStateManager: true
|
|
1109
|
+
}
|
|
1110
|
+
);
|
|
1111
|
+
return this.params.getSessionSummary(normalizedSessionId);
|
|
1112
|
+
};
|
|
1113
|
+
};
|
|
1114
|
+
|
|
1115
|
+
// src/agent/agent-backend/agent-backend-session-utils.ts
|
|
1116
|
+
import { NcpEventType as NcpEventType5 } from "@nextclaw/ncp";
|
|
851
1117
|
var AUTO_SESSION_LABEL_MAX_LENGTH = 64;
|
|
1118
|
+
function readOptionalAgentId2(value) {
|
|
1119
|
+
if (typeof value !== "string") {
|
|
1120
|
+
return void 0;
|
|
1121
|
+
}
|
|
1122
|
+
const trimmed = value.trim().toLowerCase();
|
|
1123
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
1124
|
+
}
|
|
852
1125
|
function readMessages(snapshot) {
|
|
853
1126
|
const messages = snapshot.messages.map((message) => structuredClone(message));
|
|
854
1127
|
if (snapshot.streamingMessage) {
|
|
@@ -866,6 +1139,7 @@ function toSessionSummary(session, liveSession) {
|
|
|
866
1139
|
});
|
|
867
1140
|
return {
|
|
868
1141
|
sessionId: session.sessionId,
|
|
1142
|
+
...readOptionalAgentId2(session.agentId) ? { agentId: readOptionalAgentId2(session.agentId) } : {},
|
|
869
1143
|
messageCount: session.messages.length,
|
|
870
1144
|
updatedAt: session.updatedAt,
|
|
871
1145
|
status: liveSession?.activeExecution ? "running" : "idle",
|
|
@@ -881,6 +1155,7 @@ function toLiveSessionSummary(session) {
|
|
|
881
1155
|
});
|
|
882
1156
|
return {
|
|
883
1157
|
sessionId: session.sessionId,
|
|
1158
|
+
...readOptionalAgentId2(session.agentId) ? { agentId: readOptionalAgentId2(session.agentId) } : {},
|
|
884
1159
|
messageCount: messages.length,
|
|
885
1160
|
updatedAt: now(),
|
|
886
1161
|
status: session.activeExecution ? "running" : "idle",
|
|
@@ -934,18 +1209,21 @@ function withAutoSessionLabel(params) {
|
|
|
934
1209
|
label: nextLabel
|
|
935
1210
|
};
|
|
936
1211
|
}
|
|
937
|
-
function isTerminalEvent(event) {
|
|
938
|
-
switch (event.type) {
|
|
939
|
-
case NcpEventType4.MessageAbort:
|
|
940
|
-
case NcpEventType4.RunFinished:
|
|
941
|
-
case NcpEventType4.RunError:
|
|
942
|
-
return true;
|
|
943
|
-
default:
|
|
944
|
-
return false;
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
1212
|
|
|
948
1213
|
// src/agent/agent-backend/agent-backend-session-persistence.ts
|
|
1214
|
+
function readOptionalAgentId3(value) {
|
|
1215
|
+
if (typeof value !== "string") {
|
|
1216
|
+
return void 0;
|
|
1217
|
+
}
|
|
1218
|
+
const trimmed = value.trim().toLowerCase();
|
|
1219
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
1220
|
+
}
|
|
1221
|
+
function readAgentIdFromMetadata2(metadata) {
|
|
1222
|
+
return readOptionalAgentId3(metadata?.agent_id) ?? readOptionalAgentId3(metadata?.agentId);
|
|
1223
|
+
}
|
|
1224
|
+
function resolvePersistedAgentId(params) {
|
|
1225
|
+
return readOptionalAgentId3(params.liveSession?.agentId) ?? readOptionalAgentId3(params.storedSession?.agentId) ?? readAgentIdFromMetadata2(params.liveSession?.metadata) ?? readAgentIdFromMetadata2(params.storedSession?.metadata);
|
|
1226
|
+
}
|
|
949
1227
|
function buildUpdatedSessionRecord(params) {
|
|
950
1228
|
const nextMetadata = params.patch.metadata === null ? {} : params.patch.metadata ? structuredClone(params.patch.metadata) : structuredClone(params.liveSession?.metadata ?? params.storedSession?.metadata ?? {});
|
|
951
1229
|
if (params.liveSession) {
|
|
@@ -953,6 +1231,15 @@ function buildUpdatedSessionRecord(params) {
|
|
|
953
1231
|
}
|
|
954
1232
|
return {
|
|
955
1233
|
sessionId: params.sessionId,
|
|
1234
|
+
...resolvePersistedAgentId({
|
|
1235
|
+
liveSession: params.liveSession,
|
|
1236
|
+
storedSession: params.storedSession
|
|
1237
|
+
}) ? {
|
|
1238
|
+
agentId: resolvePersistedAgentId({
|
|
1239
|
+
liveSession: params.liveSession,
|
|
1240
|
+
storedSession: params.storedSession
|
|
1241
|
+
})
|
|
1242
|
+
} : {},
|
|
956
1243
|
messages: params.liveSession ? readMessages(params.liveSession.stateManager.getSnapshot()) : params.storedSession?.messages.map((message) => structuredClone(message)) ?? [],
|
|
957
1244
|
updatedAt: params.updatedAt,
|
|
958
1245
|
metadata: nextMetadata
|
|
@@ -969,228 +1256,28 @@ function buildPersistedLiveSessionRecord(params) {
|
|
|
969
1256
|
});
|
|
970
1257
|
return {
|
|
971
1258
|
sessionId: params.sessionId,
|
|
1259
|
+
...readOptionalAgentId3(params.session.agentId) ?? readAgentIdFromMetadata2(params.session.metadata) ?? readAgentIdFromMetadata2(params.session.activeExecution?.requestEnvelope.metadata) ? {
|
|
1260
|
+
agentId: readOptionalAgentId3(params.session.agentId) ?? readAgentIdFromMetadata2(params.session.metadata) ?? readAgentIdFromMetadata2(params.session.activeExecution?.requestEnvelope.metadata)
|
|
1261
|
+
} : {},
|
|
972
1262
|
messages,
|
|
973
1263
|
updatedAt: params.updatedAt,
|
|
974
1264
|
metadata
|
|
975
1265
|
};
|
|
976
1266
|
}
|
|
977
1267
|
|
|
978
|
-
// src/agent/agent-backend/agent-backend-update-tool-call-result.ts
|
|
979
|
-
import { NcpEventType as NcpEventType5 } from "@nextclaw/ncp";
|
|
980
|
-
async function updateAgentBackendToolCallResult(params) {
|
|
981
|
-
const normalizedSessionId = params.sessionId.trim();
|
|
982
|
-
const normalizedToolCallId = params.toolCallId.trim();
|
|
983
|
-
if (!normalizedSessionId || !normalizedToolCallId) {
|
|
984
|
-
return null;
|
|
985
|
-
}
|
|
986
|
-
const liveSession = await params.sessionRegistry.ensureSession(normalizedSessionId);
|
|
987
|
-
const event = {
|
|
988
|
-
type: NcpEventType5.MessageToolCallResult,
|
|
989
|
-
payload: {
|
|
990
|
-
sessionId: normalizedSessionId,
|
|
991
|
-
toolCallId: normalizedToolCallId,
|
|
992
|
-
content: params.content
|
|
993
|
-
}
|
|
994
|
-
};
|
|
995
|
-
await liveSession.stateManager.dispatch(event);
|
|
996
|
-
params.publisher.publish(event);
|
|
997
|
-
if (liveSession.activeExecution && !liveSession.activeExecution.closed) {
|
|
998
|
-
liveSession.activeExecution.publisher.publish(event);
|
|
999
|
-
}
|
|
1000
|
-
await params.persistSession(normalizedSessionId);
|
|
1001
|
-
return params.getSession(normalizedSessionId);
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
// src/agent/agent-backend/agent-backend-append-message.ts
|
|
1005
|
-
import { NcpEventType as NcpEventType6 } from "@nextclaw/ncp";
|
|
1006
|
-
async function appendAgentBackendMessage(params) {
|
|
1007
|
-
const normalizedSessionId = params.sessionId.trim();
|
|
1008
|
-
if (!normalizedSessionId) {
|
|
1009
|
-
return null;
|
|
1010
|
-
}
|
|
1011
|
-
let liveSession = params.sessionRegistry.getSession(normalizedSessionId);
|
|
1012
|
-
if (!liveSession) {
|
|
1013
|
-
const storedSession = await params.sessionStore.getSession(normalizedSessionId);
|
|
1014
|
-
if (!storedSession) {
|
|
1015
|
-
return null;
|
|
1016
|
-
}
|
|
1017
|
-
liveSession = await params.sessionRegistry.ensureSession(normalizedSessionId);
|
|
1018
|
-
}
|
|
1019
|
-
const nextMessage = {
|
|
1020
|
-
...structuredClone(params.message),
|
|
1021
|
-
sessionId: normalizedSessionId
|
|
1022
|
-
};
|
|
1023
|
-
const event = {
|
|
1024
|
-
type: NcpEventType6.MessageSent,
|
|
1025
|
-
payload: {
|
|
1026
|
-
sessionId: normalizedSessionId,
|
|
1027
|
-
message: nextMessage
|
|
1028
|
-
}
|
|
1029
|
-
};
|
|
1030
|
-
await liveSession.stateManager.dispatch(event);
|
|
1031
|
-
params.publisher.publish(event);
|
|
1032
|
-
if (liveSession.activeExecution && !liveSession.activeExecution.closed) {
|
|
1033
|
-
liveSession.activeExecution.publisher.publish(event);
|
|
1034
|
-
}
|
|
1035
|
-
await params.persistSession(normalizedSessionId);
|
|
1036
|
-
return params.getSession(normalizedSessionId);
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
// src/agent/agent-backend/agent-backend-stream.ts
|
|
1040
|
-
import { NcpEventType as NcpEventType7 } from "@nextclaw/ncp";
|
|
1041
|
-
|
|
1042
|
-
// src/agent/agent-backend/async-queue.ts
|
|
1043
|
-
function createAsyncQueue() {
|
|
1044
|
-
const items = [];
|
|
1045
|
-
let closed = false;
|
|
1046
|
-
let pendingResolve = null;
|
|
1047
|
-
const iterable = {
|
|
1048
|
-
[Symbol.asyncIterator]() {
|
|
1049
|
-
return {
|
|
1050
|
-
next: () => {
|
|
1051
|
-
if (items.length > 0) {
|
|
1052
|
-
return Promise.resolve({
|
|
1053
|
-
value: items.shift(),
|
|
1054
|
-
done: false
|
|
1055
|
-
});
|
|
1056
|
-
}
|
|
1057
|
-
if (closed) {
|
|
1058
|
-
return Promise.resolve({
|
|
1059
|
-
value: void 0,
|
|
1060
|
-
done: true
|
|
1061
|
-
});
|
|
1062
|
-
}
|
|
1063
|
-
return new Promise((resolve) => {
|
|
1064
|
-
pendingResolve = resolve;
|
|
1065
|
-
});
|
|
1066
|
-
}
|
|
1067
|
-
};
|
|
1068
|
-
}
|
|
1069
|
-
};
|
|
1070
|
-
return {
|
|
1071
|
-
push(item) {
|
|
1072
|
-
if (closed) {
|
|
1073
|
-
return;
|
|
1074
|
-
}
|
|
1075
|
-
if (pendingResolve) {
|
|
1076
|
-
const resolve = pendingResolve;
|
|
1077
|
-
pendingResolve = null;
|
|
1078
|
-
resolve({ value: item, done: false });
|
|
1079
|
-
return;
|
|
1080
|
-
}
|
|
1081
|
-
items.push(item);
|
|
1082
|
-
},
|
|
1083
|
-
close() {
|
|
1084
|
-
if (closed) {
|
|
1085
|
-
return;
|
|
1086
|
-
}
|
|
1087
|
-
closed = true;
|
|
1088
|
-
if (pendingResolve) {
|
|
1089
|
-
const resolve = pendingResolve;
|
|
1090
|
-
pendingResolve = null;
|
|
1091
|
-
resolve({
|
|
1092
|
-
value: void 0,
|
|
1093
|
-
done: true
|
|
1094
|
-
});
|
|
1095
|
-
}
|
|
1096
|
-
},
|
|
1097
|
-
iterable
|
|
1098
|
-
};
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
// src/agent/agent-backend/agent-backend-stream.ts
|
|
1102
|
-
async function* streamAgentBackendExecution(params) {
|
|
1103
|
-
const payload = "payload" in params.payloadOrParams && "signal" in params.payloadOrParams ? params.payloadOrParams.payload : params.payloadOrParams;
|
|
1104
|
-
const signal = "payload" in params.payloadOrParams && "signal" in params.payloadOrParams ? params.payloadOrParams.signal : params.opts?.signal ?? new AbortController().signal;
|
|
1105
|
-
const session = params.sessionRegistry.getSession(payload.sessionId);
|
|
1106
|
-
const execution = session?.activeExecution;
|
|
1107
|
-
if (!session || !execution || execution.closed) {
|
|
1108
|
-
if (session) {
|
|
1109
|
-
yield {
|
|
1110
|
-
type: NcpEventType7.RunFinished,
|
|
1111
|
-
payload: {
|
|
1112
|
-
sessionId: payload.sessionId
|
|
1113
|
-
}
|
|
1114
|
-
};
|
|
1115
|
-
}
|
|
1116
|
-
return;
|
|
1117
|
-
}
|
|
1118
|
-
const queue = createAsyncQueue();
|
|
1119
|
-
const unsubscribe = execution.publisher.subscribe((event) => {
|
|
1120
|
-
queue.push(event);
|
|
1121
|
-
});
|
|
1122
|
-
const unsubscribeClose = execution.publisher.onClose(() => {
|
|
1123
|
-
queue.close();
|
|
1124
|
-
});
|
|
1125
|
-
const stop = () => {
|
|
1126
|
-
unsubscribe();
|
|
1127
|
-
unsubscribeClose();
|
|
1128
|
-
queue.close();
|
|
1129
|
-
signal.removeEventListener("abort", stop);
|
|
1130
|
-
};
|
|
1131
|
-
signal.addEventListener("abort", stop, { once: true });
|
|
1132
|
-
try {
|
|
1133
|
-
for await (const event of queue.iterable) {
|
|
1134
|
-
if (signal.aborted) {
|
|
1135
|
-
break;
|
|
1136
|
-
}
|
|
1137
|
-
yield event;
|
|
1138
|
-
if (isTerminalEvent(event)) {
|
|
1139
|
-
break;
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
} finally {
|
|
1143
|
-
stop();
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
// src/agent/agent-backend/event-publisher.ts
|
|
1148
|
-
var EventPublisher = class {
|
|
1149
|
-
listeners = /* @__PURE__ */ new Set();
|
|
1150
|
-
closeListeners = /* @__PURE__ */ new Set();
|
|
1151
|
-
closed = false;
|
|
1152
|
-
subscribe(listener) {
|
|
1153
|
-
if (this.closed) {
|
|
1154
|
-
return () => void 0;
|
|
1155
|
-
}
|
|
1156
|
-
this.listeners.add(listener);
|
|
1157
|
-
return () => {
|
|
1158
|
-
this.listeners.delete(listener);
|
|
1159
|
-
};
|
|
1160
|
-
}
|
|
1161
|
-
onClose(listener) {
|
|
1162
|
-
if (this.closed) {
|
|
1163
|
-
listener();
|
|
1164
|
-
return () => void 0;
|
|
1165
|
-
}
|
|
1166
|
-
this.closeListeners.add(listener);
|
|
1167
|
-
return () => {
|
|
1168
|
-
this.closeListeners.delete(listener);
|
|
1169
|
-
};
|
|
1170
|
-
}
|
|
1171
|
-
publish(event) {
|
|
1172
|
-
if (this.closed) {
|
|
1173
|
-
return;
|
|
1174
|
-
}
|
|
1175
|
-
for (const listener of this.listeners) {
|
|
1176
|
-
listener(structuredClone(event));
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
close() {
|
|
1180
|
-
if (this.closed) {
|
|
1181
|
-
return;
|
|
1182
|
-
}
|
|
1183
|
-
this.closed = true;
|
|
1184
|
-
this.listeners.clear();
|
|
1185
|
-
for (const listener of this.closeListeners) {
|
|
1186
|
-
listener();
|
|
1187
|
-
}
|
|
1188
|
-
this.closeListeners.clear();
|
|
1189
|
-
}
|
|
1190
|
-
};
|
|
1191
|
-
|
|
1192
1268
|
// src/agent/agent-backend/agent-backend.ts
|
|
1193
|
-
var DEFAULT_SUPPORTED_PART_TYPES = [
|
|
1269
|
+
var DEFAULT_SUPPORTED_PART_TYPES = [
|
|
1270
|
+
"text",
|
|
1271
|
+
"file",
|
|
1272
|
+
"source",
|
|
1273
|
+
"step-start",
|
|
1274
|
+
"reasoning",
|
|
1275
|
+
"tool-invocation",
|
|
1276
|
+
"card",
|
|
1277
|
+
"rich-text",
|
|
1278
|
+
"action",
|
|
1279
|
+
"extension"
|
|
1280
|
+
];
|
|
1194
1281
|
var DefaultNcpAgentBackend = class {
|
|
1195
1282
|
manifest;
|
|
1196
1283
|
sessionStore;
|
|
@@ -1198,13 +1285,24 @@ var DefaultNcpAgentBackend = class {
|
|
|
1198
1285
|
sessionRegistry;
|
|
1199
1286
|
executor;
|
|
1200
1287
|
publisher;
|
|
1288
|
+
sessionRealtime;
|
|
1201
1289
|
started = false;
|
|
1202
1290
|
constructor(config) {
|
|
1203
1291
|
this.sessionStore = config.sessionStore;
|
|
1204
1292
|
this.onSessionRunStatusChanged = config.onSessionRunStatusChanged;
|
|
1205
|
-
this.sessionRegistry = new AgentLiveSessionRegistry(
|
|
1206
|
-
|
|
1293
|
+
this.sessionRegistry = new AgentLiveSessionRegistry(
|
|
1294
|
+
this.sessionStore,
|
|
1295
|
+
config.createRuntime
|
|
1296
|
+
);
|
|
1297
|
+
this.executor = new AgentRunExecutor();
|
|
1207
1298
|
this.publisher = new EventPublisher();
|
|
1299
|
+
this.sessionRealtime = new AgentBackendSessionRealtime({
|
|
1300
|
+
sessionRegistry: this.sessionRegistry,
|
|
1301
|
+
sessionStore: this.sessionStore,
|
|
1302
|
+
publishEndpointEvent: (event) => this.publishEndpointEvent(event),
|
|
1303
|
+
persistSession: (sessionId) => this.persistSession(sessionId),
|
|
1304
|
+
getSessionSummary: (sessionId) => this.getSession(sessionId)
|
|
1305
|
+
});
|
|
1208
1306
|
this.manifest = {
|
|
1209
1307
|
endpointKind: "agent",
|
|
1210
1308
|
endpointId: config.endpointId?.trim() || "ncp-agent-backend",
|
|
@@ -1223,7 +1321,7 @@ var DefaultNcpAgentBackend = class {
|
|
|
1223
1321
|
return;
|
|
1224
1322
|
}
|
|
1225
1323
|
this.started = true;
|
|
1226
|
-
this.publisher.publish({ type:
|
|
1324
|
+
this.publisher.publish({ type: NcpEventType6.EndpointReady });
|
|
1227
1325
|
};
|
|
1228
1326
|
stop = async () => {
|
|
1229
1327
|
if (!this.started) {
|
|
@@ -1233,24 +1331,28 @@ var DefaultNcpAgentBackend = class {
|
|
|
1233
1331
|
for (const session of this.sessionRegistry.listSessions()) {
|
|
1234
1332
|
const execution = session.activeExecution;
|
|
1235
1333
|
if (!execution) {
|
|
1334
|
+
session.publisher.close();
|
|
1236
1335
|
continue;
|
|
1237
1336
|
}
|
|
1238
1337
|
execution.abortHandled = true;
|
|
1239
1338
|
execution.controller.abort();
|
|
1240
1339
|
this.finishSessionExecution(session, execution);
|
|
1340
|
+
session.publisher.close();
|
|
1241
1341
|
}
|
|
1242
1342
|
this.sessionRegistry.clear();
|
|
1243
1343
|
};
|
|
1244
1344
|
emit = async (event) => {
|
|
1245
1345
|
await this.ensureStarted();
|
|
1246
1346
|
switch (event.type) {
|
|
1247
|
-
case
|
|
1248
|
-
await this.
|
|
1347
|
+
case NcpEventType6.MessageRequest:
|
|
1348
|
+
for await (const emittedEvent of this.send(event.payload)) {
|
|
1349
|
+
void emittedEvent;
|
|
1350
|
+
}
|
|
1249
1351
|
return;
|
|
1250
|
-
case
|
|
1251
|
-
await this.
|
|
1352
|
+
case NcpEventType6.MessageStreamRequest:
|
|
1353
|
+
await this.ensureStarted();
|
|
1252
1354
|
return;
|
|
1253
|
-
case
|
|
1355
|
+
case NcpEventType6.MessageAbort:
|
|
1254
1356
|
await this.handleAbort(event.payload);
|
|
1255
1357
|
return;
|
|
1256
1358
|
default:
|
|
@@ -1278,13 +1380,20 @@ var DefaultNcpAgentBackend = class {
|
|
|
1278
1380
|
envelope,
|
|
1279
1381
|
execution.controller
|
|
1280
1382
|
)) {
|
|
1281
|
-
self.
|
|
1383
|
+
await self.sessionRealtime.publishSessionEvent(session, event);
|
|
1282
1384
|
yield event;
|
|
1283
1385
|
}
|
|
1284
1386
|
if (execution.controller.signal.aborted && !execution.abortHandled) {
|
|
1285
|
-
const abortEvent =
|
|
1387
|
+
const abortEvent = {
|
|
1388
|
+
type: NcpEventType6.MessageAbort,
|
|
1389
|
+
payload: {
|
|
1390
|
+
sessionId: session.sessionId
|
|
1391
|
+
}
|
|
1392
|
+
};
|
|
1286
1393
|
execution.abortHandled = true;
|
|
1287
|
-
self.
|
|
1394
|
+
await self.sessionRealtime.publishSessionEvent(session, abortEvent, {
|
|
1395
|
+
dispatchToStateManager: true
|
|
1396
|
+
});
|
|
1288
1397
|
yield abortEvent;
|
|
1289
1398
|
}
|
|
1290
1399
|
} finally {
|
|
@@ -1296,15 +1405,14 @@ var DefaultNcpAgentBackend = class {
|
|
|
1296
1405
|
abort = async (payload) => {
|
|
1297
1406
|
await this.handleAbort(payload);
|
|
1298
1407
|
};
|
|
1299
|
-
stream = (payloadOrParams, opts) =>
|
|
1300
|
-
payloadOrParams,
|
|
1301
|
-
opts,
|
|
1302
|
-
sessionRegistry: this.sessionRegistry
|
|
1303
|
-
});
|
|
1408
|
+
stream = (payloadOrParams, opts) => this.sessionRealtime.streamSessionEvents(payloadOrParams, opts);
|
|
1304
1409
|
listSessions = async () => {
|
|
1305
1410
|
const storedSessions = await this.sessionStore.listSessions();
|
|
1306
1411
|
const summaries = storedSessions.map(
|
|
1307
|
-
(session) => toSessionSummary(
|
|
1412
|
+
(session) => toSessionSummary(
|
|
1413
|
+
session,
|
|
1414
|
+
this.sessionRegistry.getSession(session.sessionId)
|
|
1415
|
+
)
|
|
1308
1416
|
);
|
|
1309
1417
|
for (const liveSession of this.sessionRegistry.listSessions()) {
|
|
1310
1418
|
if (summaries.some((session) => session.sessionId === liveSession.sessionId)) {
|
|
@@ -1312,11 +1420,14 @@ var DefaultNcpAgentBackend = class {
|
|
|
1312
1420
|
}
|
|
1313
1421
|
summaries.push(toLiveSessionSummary(liveSession));
|
|
1314
1422
|
}
|
|
1315
|
-
return summaries.sort(
|
|
1423
|
+
return summaries.sort(
|
|
1424
|
+
(left, right) => right.updatedAt.localeCompare(left.updatedAt)
|
|
1425
|
+
);
|
|
1316
1426
|
};
|
|
1317
1427
|
listSessionMessages = async (sessionId) => {
|
|
1318
1428
|
const liveSession = this.sessionRegistry.getSession(sessionId);
|
|
1319
|
-
if (liveSession)
|
|
1429
|
+
if (liveSession)
|
|
1430
|
+
return readMessages(liveSession.stateManager.getSnapshot());
|
|
1320
1431
|
const session = await this.sessionStore.getSession(sessionId);
|
|
1321
1432
|
return session ? session.messages.map((message) => structuredClone(message)) : [];
|
|
1322
1433
|
};
|
|
@@ -1327,39 +1438,29 @@ var DefaultNcpAgentBackend = class {
|
|
|
1327
1438
|
};
|
|
1328
1439
|
appendMessage = async (sessionId, message) => {
|
|
1329
1440
|
await this.ensureStarted();
|
|
1330
|
-
return
|
|
1331
|
-
sessionId,
|
|
1332
|
-
message,
|
|
1333
|
-
sessionRegistry: this.sessionRegistry,
|
|
1334
|
-
sessionStore: this.sessionStore,
|
|
1335
|
-
publisher: this.publisher,
|
|
1336
|
-
persistSession: async (nextSessionId) => this.persistSession(nextSessionId),
|
|
1337
|
-
getSession: async (nextSessionId) => this.getSession(nextSessionId)
|
|
1338
|
-
});
|
|
1441
|
+
return this.sessionRealtime.appendMessage(sessionId, message);
|
|
1339
1442
|
};
|
|
1340
1443
|
updateToolCallResult = async (sessionId, toolCallId, content) => {
|
|
1341
1444
|
await this.ensureStarted();
|
|
1342
|
-
return
|
|
1445
|
+
return this.sessionRealtime.updateToolCallResult(
|
|
1343
1446
|
sessionId,
|
|
1344
1447
|
toolCallId,
|
|
1345
|
-
content
|
|
1346
|
-
|
|
1347
|
-
publisher: this.publisher,
|
|
1348
|
-
persistSession: async (nextSessionId) => this.persistSession(nextSessionId),
|
|
1349
|
-
getSession: async (nextSessionId) => this.getSession(nextSessionId)
|
|
1350
|
-
});
|
|
1448
|
+
content
|
|
1449
|
+
);
|
|
1351
1450
|
};
|
|
1352
1451
|
updateSession = async (sessionId, patch) => {
|
|
1353
1452
|
const liveSession = this.sessionRegistry.getSession(sessionId);
|
|
1354
1453
|
const storedSession = await this.sessionStore.getSession(sessionId);
|
|
1355
1454
|
if (!liveSession && !storedSession) return null;
|
|
1356
|
-
await this.sessionStore.replaceSession(
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1455
|
+
await this.sessionStore.replaceSession(
|
|
1456
|
+
buildUpdatedSessionRecord({
|
|
1457
|
+
sessionId,
|
|
1458
|
+
patch,
|
|
1459
|
+
liveSession,
|
|
1460
|
+
storedSession,
|
|
1461
|
+
updatedAt: now()
|
|
1462
|
+
})
|
|
1463
|
+
);
|
|
1363
1464
|
return this.getSession(sessionId);
|
|
1364
1465
|
};
|
|
1365
1466
|
deleteSession = async (sessionId) => {
|
|
@@ -1368,8 +1469,9 @@ var DefaultNcpAgentBackend = class {
|
|
|
1368
1469
|
if (execution) {
|
|
1369
1470
|
execution.abortHandled = true;
|
|
1370
1471
|
execution.controller.abort();
|
|
1371
|
-
|
|
1472
|
+
closeAgentBackendSessionExecution(execution);
|
|
1372
1473
|
}
|
|
1474
|
+
liveSession?.publisher.close();
|
|
1373
1475
|
await this.sessionStore.deleteSession(sessionId);
|
|
1374
1476
|
};
|
|
1375
1477
|
ensureStarted = async () => {
|
|
@@ -1377,61 +1479,19 @@ var DefaultNcpAgentBackend = class {
|
|
|
1377
1479
|
await this.start();
|
|
1378
1480
|
}
|
|
1379
1481
|
};
|
|
1380
|
-
startSessionExecution = (session, envelope, signal) => {
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
});
|
|
1393
|
-
}
|
|
1394
|
-
const execution = {
|
|
1395
|
-
controller,
|
|
1396
|
-
publisher: new EventPublisher(),
|
|
1397
|
-
requestEnvelope: structuredClone(envelope),
|
|
1398
|
-
abortHandled: false,
|
|
1399
|
-
closed: false
|
|
1400
|
-
};
|
|
1401
|
-
session.activeExecution = execution;
|
|
1402
|
-
this.onSessionRunStatusChanged?.({ sessionKey: session.sessionId, status: "running" });
|
|
1403
|
-
return execution;
|
|
1404
|
-
};
|
|
1405
|
-
finishSessionExecution = (session, execution) => {
|
|
1406
|
-
if (session.activeExecution === execution) {
|
|
1407
|
-
session.activeExecution = null;
|
|
1408
|
-
this.onSessionRunStatusChanged?.({ sessionKey: session.sessionId, status: "idle" });
|
|
1409
|
-
}
|
|
1410
|
-
this.closeExecution(execution);
|
|
1411
|
-
};
|
|
1412
|
-
closeExecution = (execution) => {
|
|
1413
|
-
if (execution.closed) {
|
|
1414
|
-
return;
|
|
1415
|
-
}
|
|
1416
|
-
execution.closed = true;
|
|
1417
|
-
execution.publisher.close();
|
|
1418
|
-
};
|
|
1419
|
-
publishLiveEvent = (execution, event) => {
|
|
1482
|
+
startSessionExecution = (session, envelope, signal) => startAgentBackendSessionExecution({
|
|
1483
|
+
session,
|
|
1484
|
+
envelope,
|
|
1485
|
+
signal,
|
|
1486
|
+
onStatusChanged: this.onSessionRunStatusChanged
|
|
1487
|
+
});
|
|
1488
|
+
finishSessionExecution = (session, execution) => finishAgentBackendSessionExecution({
|
|
1489
|
+
session,
|
|
1490
|
+
execution,
|
|
1491
|
+
onStatusChanged: this.onSessionRunStatusChanged
|
|
1492
|
+
});
|
|
1493
|
+
publishEndpointEvent = (event) => {
|
|
1420
1494
|
this.publisher.publish(event);
|
|
1421
|
-
if (!execution.closed) {
|
|
1422
|
-
execution.publisher.publish(event);
|
|
1423
|
-
}
|
|
1424
|
-
};
|
|
1425
|
-
handleRequest = async (envelope) => {
|
|
1426
|
-
for await (const event of this.send(envelope)) {
|
|
1427
|
-
void event;
|
|
1428
|
-
}
|
|
1429
|
-
};
|
|
1430
|
-
streamToSubscribers = async (payload) => {
|
|
1431
|
-
const signal = new AbortController().signal;
|
|
1432
|
-
for await (const event of this.stream({ payload, signal })) {
|
|
1433
|
-
void event;
|
|
1434
|
-
}
|
|
1435
1495
|
};
|
|
1436
1496
|
handleAbort = async (payload) => {
|
|
1437
1497
|
const session = this.sessionRegistry.getSession(payload.sessionId);
|
|
@@ -1441,33 +1501,28 @@ var DefaultNcpAgentBackend = class {
|
|
|
1441
1501
|
}
|
|
1442
1502
|
execution.abortHandled = true;
|
|
1443
1503
|
execution.controller.abort();
|
|
1444
|
-
const abortEvent = await this.createAbortEvent(payload.sessionId, payload.messageId);
|
|
1445
|
-
this.publishLiveEvent(execution, abortEvent);
|
|
1446
|
-
this.finishSessionExecution(session, execution);
|
|
1447
|
-
};
|
|
1448
|
-
createAbortEvent = async (sessionId, messageId) => {
|
|
1449
1504
|
const abortEvent = {
|
|
1450
|
-
type:
|
|
1505
|
+
type: NcpEventType6.MessageAbort,
|
|
1451
1506
|
payload: {
|
|
1452
|
-
sessionId,
|
|
1453
|
-
...messageId ? { messageId } : {}
|
|
1507
|
+
sessionId: payload.sessionId,
|
|
1508
|
+
...payload.messageId ? { messageId: payload.messageId } : {}
|
|
1454
1509
|
}
|
|
1455
1510
|
};
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
await this.persistSession(sessionId);
|
|
1461
|
-
return abortEvent;
|
|
1511
|
+
await this.sessionRealtime.publishSessionEvent(session, abortEvent, {
|
|
1512
|
+
dispatchToStateManager: true
|
|
1513
|
+
});
|
|
1514
|
+
this.finishSessionExecution(session, execution);
|
|
1462
1515
|
};
|
|
1463
1516
|
persistSession = async (sessionId) => {
|
|
1464
1517
|
const session = this.sessionRegistry.getSession(sessionId);
|
|
1465
1518
|
if (!session) return;
|
|
1466
|
-
await this.sessionStore.saveSession(
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1519
|
+
await this.sessionStore.saveSession(
|
|
1520
|
+
buildPersistedLiveSessionRecord({
|
|
1521
|
+
sessionId,
|
|
1522
|
+
session,
|
|
1523
|
+
updatedAt: now()
|
|
1524
|
+
})
|
|
1525
|
+
);
|
|
1471
1526
|
};
|
|
1472
1527
|
};
|
|
1473
1528
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/ncp-toolkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Toolkit implementations built on top of the NextClaw Communication Protocol.",
|
|
6
6
|
"type": "module",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"dist"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@nextclaw/ncp": "0.
|
|
18
|
+
"@nextclaw/ncp": "0.5.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@types/node": "^20.17.6",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"tsup": "^8.3.5",
|
|
24
24
|
"typescript": "^5.6.3",
|
|
25
25
|
"vitest": "^4.1.2",
|
|
26
|
-
"@nextclaw/ncp-agent-runtime": "0.3.
|
|
26
|
+
"@nextclaw/ncp-agent-runtime": "0.3.7"
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
29
|
"build": "tsup src/index.ts --format esm --dts --out-dir dist",
|