@nextclaw/ncp-toolkit 0.5.13 → 0.5.15
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 +25 -8
- package/dist/index.js +188 -66
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -72,7 +72,7 @@ declare class EventPublisher {
|
|
|
72
72
|
close(): void;
|
|
73
73
|
}
|
|
74
74
|
//#endregion
|
|
75
|
-
//#region src/agent/agent-backend/agent-backend
|
|
75
|
+
//#region src/agent/agent-backend/agent-backend.types.d.ts
|
|
76
76
|
type RuntimeFactoryParams = {
|
|
77
77
|
sessionId: string;
|
|
78
78
|
agentId?: string;
|
|
@@ -87,6 +87,14 @@ type AgentSessionRecord = {
|
|
|
87
87
|
sessionId: string;
|
|
88
88
|
agentId?: string;
|
|
89
89
|
messages: NcpMessage[];
|
|
90
|
+
createdAt?: string;
|
|
91
|
+
updatedAt: string;
|
|
92
|
+
metadata?: Record<string, unknown>;
|
|
93
|
+
};
|
|
94
|
+
type AgentSessionEventRecord = {
|
|
95
|
+
sessionId: string;
|
|
96
|
+
agentId?: string;
|
|
97
|
+
createdAt?: string;
|
|
90
98
|
updatedAt: string;
|
|
91
99
|
metadata?: Record<string, unknown>;
|
|
92
100
|
};
|
|
@@ -99,6 +107,7 @@ type LiveSessionExecution = {
|
|
|
99
107
|
type LiveSessionState = {
|
|
100
108
|
sessionId: string;
|
|
101
109
|
agentId?: string;
|
|
110
|
+
createdAt: string;
|
|
102
111
|
runtime: NcpAgentRuntime;
|
|
103
112
|
stateManager: NcpAgentConversationStateManager;
|
|
104
113
|
metadata: Record<string, unknown>;
|
|
@@ -108,19 +117,27 @@ type LiveSessionState = {
|
|
|
108
117
|
interface AgentSessionStore {
|
|
109
118
|
getSession(sessionId: string): Promise<AgentSessionRecord | null>;
|
|
110
119
|
listSessions(): Promise<AgentSessionRecord[]>;
|
|
120
|
+
getSessionSummary?(sessionId: string): Promise<NcpSessionSummary | null>;
|
|
121
|
+
listSessionSummaries?(): Promise<NcpSessionSummary[]>;
|
|
122
|
+
listSessionMessages?(sessionId: string): Promise<NcpMessage[]>;
|
|
123
|
+
appendSessionEvent?(params: {
|
|
124
|
+
session: AgentSessionEventRecord;
|
|
125
|
+
event: NcpEndpointEvent;
|
|
126
|
+
updatedAt: string;
|
|
127
|
+
}): Promise<void>;
|
|
111
128
|
saveSession(session: AgentSessionRecord): Promise<void>;
|
|
112
129
|
replaceSession(session: AgentSessionRecord): Promise<void>;
|
|
113
130
|
deleteSession(sessionId: string): Promise<AgentSessionRecord | null>;
|
|
114
131
|
}
|
|
115
132
|
//#endregion
|
|
116
|
-
//#region src/agent/agent-backend/agent-backend-session
|
|
133
|
+
//#region src/agent/agent-backend/agent-backend-session.utils.d.ts
|
|
117
134
|
type SessionContextWindowResolver = (params: {
|
|
118
135
|
messages: readonly NcpMessage[];
|
|
119
136
|
metadata: Record<string, unknown>;
|
|
120
137
|
sessionId: string;
|
|
121
138
|
}) => Record<string, unknown> | null;
|
|
122
139
|
//#endregion
|
|
123
|
-
//#region src/agent/agent-backend/agent-backend.d.ts
|
|
140
|
+
//#region src/agent/agent-backend/agent-backend.service.d.ts
|
|
124
141
|
type DefaultNcpAgentBackendConfig = {
|
|
125
142
|
createRuntime: CreateRuntimeFn;
|
|
126
143
|
sessionStore: AgentSessionStore;
|
|
@@ -159,6 +176,7 @@ declare class DefaultNcpAgentBackend implements NcpAgentServerEndpoint, NcpSessi
|
|
|
159
176
|
signal: AbortSignal;
|
|
160
177
|
}, opts?: NcpAgentRunStreamOptions) => AsyncIterable<NcpEndpointEvent>;
|
|
161
178
|
listSessions: () => Promise<NcpSessionSummary[]>;
|
|
179
|
+
isLiveSessionRunning: (sessionId: string) => boolean;
|
|
162
180
|
listSessionMessages: (sessionId: string) => Promise<NcpMessage[]>;
|
|
163
181
|
getSession: (sessionId: string) => Promise<NcpSessionSummary | null>;
|
|
164
182
|
appendMessage: (sessionId: string, message: NcpMessage) => Promise<NcpSessionSummary | null>;
|
|
@@ -169,16 +187,15 @@ declare class DefaultNcpAgentBackend implements NcpAgentServerEndpoint, NcpSessi
|
|
|
169
187
|
private startSessionExecution;
|
|
170
188
|
private finishSessionExecution;
|
|
171
189
|
private handleAbort;
|
|
172
|
-
private persistSession;
|
|
173
190
|
}
|
|
174
191
|
//#endregion
|
|
175
|
-
//#region src/agent/agent-backend/agent-run-executor.d.ts
|
|
192
|
+
//#region src/agent/agent-backend/agent-run-executor.service.d.ts
|
|
176
193
|
declare class AgentRunExecutor {
|
|
177
|
-
executeRun(session: LiveSessionState, envelope: NcpRequestEnvelope, controller: AbortController)
|
|
194
|
+
executeRun: (session: LiveSessionState, envelope: NcpRequestEnvelope, controller: AbortController) => AsyncGenerator<NcpEndpointEvent>;
|
|
178
195
|
private publishFailure;
|
|
179
196
|
}
|
|
180
197
|
//#endregion
|
|
181
|
-
//#region src/agent/agent-backend/in-memory-agent-session
|
|
198
|
+
//#region src/agent/agent-backend/in-memory-agent-session.store.d.ts
|
|
182
199
|
declare class InMemoryAgentSessionStore implements AgentSessionStore {
|
|
183
200
|
private readonly sessions;
|
|
184
201
|
getSession: (sessionId: string) => Promise<AgentSessionRecord | null>;
|
|
@@ -224,4 +241,4 @@ declare class NcpReplyConsumer {
|
|
|
224
241
|
consume: (input: NcpReplyInput) => Promise<void>;
|
|
225
242
|
}
|
|
226
243
|
//#endregion
|
|
227
|
-
export { AgentRunExecutor, type AgentSessionRecord, type AgentSessionStore, Chat, ChatTarget, type CreateRuntimeFn, DefaultNcpAgentBackend, type DefaultNcpAgentBackendConfig, DefaultNcpAgentConversationStateManager, EventPublisher, InMemoryAgentSessionStore, type LiveSessionExecution, type LiveSessionState, NcpErrorException, NcpEventStream, NcpReplyConsumer, NcpReplyInput, type RuntimeFactoryParams, createAgentClientFromServer };
|
|
244
|
+
export { AgentRunExecutor, type AgentSessionEventRecord, type AgentSessionRecord, type AgentSessionStore, Chat, ChatTarget, type CreateRuntimeFn, DefaultNcpAgentBackend, type DefaultNcpAgentBackendConfig, DefaultNcpAgentConversationStateManager, EventPublisher, InMemoryAgentSessionStore, type LiveSessionExecution, type LiveSessionState, NcpErrorException, NcpEventStream, NcpReplyConsumer, NcpReplyInput, type RuntimeFactoryParams, createAgentClientFromServer };
|
package/dist/index.js
CHANGED
|
@@ -574,6 +574,9 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
574
574
|
};
|
|
575
575
|
//#endregion
|
|
576
576
|
//#region src/agent/agent-client-from-server.ts
|
|
577
|
+
function assertMaterializedEnvelope(envelope) {
|
|
578
|
+
if (!envelope.sessionId?.trim() || !envelope.message.sessionId?.trim()) throw new Error("NCP request envelope must be materialized before server dispatch.");
|
|
579
|
+
}
|
|
577
580
|
/**
|
|
578
581
|
* Creates an NcpAgentClientEndpoint that forwards to an in-process NcpAgentServerEndpoint.
|
|
579
582
|
* Use when the agent runs in-process and you need to pass a client endpoint to the HTTP server.
|
|
@@ -592,6 +595,7 @@ function createAgentClientFromServer(server) {
|
|
|
592
595
|
async emit(event) {
|
|
593
596
|
switch (event.type) {
|
|
594
597
|
case NcpEventType.MessageRequest:
|
|
598
|
+
assertMaterializedEnvelope(event.payload);
|
|
595
599
|
await consume(server.send(event.payload));
|
|
596
600
|
return;
|
|
597
601
|
case NcpEventType.MessageStreamRequest:
|
|
@@ -607,6 +611,7 @@ function createAgentClientFromServer(server) {
|
|
|
607
611
|
return server.subscribe(listener);
|
|
608
612
|
},
|
|
609
613
|
async send(envelope) {
|
|
614
|
+
assertMaterializedEnvelope(envelope);
|
|
610
615
|
await consume(server.send(envelope));
|
|
611
616
|
},
|
|
612
617
|
async stream(payload) {
|
|
@@ -656,7 +661,7 @@ var EventPublisher = class {
|
|
|
656
661
|
}
|
|
657
662
|
};
|
|
658
663
|
//#endregion
|
|
659
|
-
//#region src/agent/agent-backend/agent-live-session-registry.ts
|
|
664
|
+
//#region src/agent/agent-backend/agent-live-session-registry.service.ts
|
|
660
665
|
function readOptionalAgentId$2(value) {
|
|
661
666
|
if (typeof value !== "string") return;
|
|
662
667
|
const trimmed = value.trim().toLowerCase();
|
|
@@ -695,6 +700,7 @@ var AgentLiveSessionRegistry = class {
|
|
|
695
700
|
const session = {
|
|
696
701
|
sessionId,
|
|
697
702
|
...sessionAgentId ? { agentId: sessionAgentId } : {},
|
|
703
|
+
createdAt: storedSession?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
698
704
|
stateManager,
|
|
699
705
|
metadata: sessionMetadata,
|
|
700
706
|
runtime: null,
|
|
@@ -747,7 +753,7 @@ var NcpErrorException = class extends Error {
|
|
|
747
753
|
}
|
|
748
754
|
};
|
|
749
755
|
//#endregion
|
|
750
|
-
//#region src/agent/agent-backend/agent-backend-execution
|
|
756
|
+
//#region src/agent/agent-backend/agent-backend-execution.utils.ts
|
|
751
757
|
function startAgentBackendSessionExecution(params) {
|
|
752
758
|
const { session, envelope, signal, onStatusChanged } = params;
|
|
753
759
|
if (session.activeExecution && !session.activeExecution.closed) throw new NcpErrorException("runtime-error", `Session ${session.sessionId} already has an active execution.`, { sessionId: session.sessionId });
|
|
@@ -775,22 +781,18 @@ function finishAgentBackendSessionExecution(params) {
|
|
|
775
781
|
status: "idle"
|
|
776
782
|
});
|
|
777
783
|
}
|
|
778
|
-
closeAgentBackendSessionExecution(execution);
|
|
779
|
-
}
|
|
780
|
-
function closeAgentBackendSessionExecution(execution) {
|
|
781
|
-
if (execution.closed) return;
|
|
782
|
-
execution.closed = true;
|
|
783
784
|
}
|
|
784
785
|
//#endregion
|
|
785
|
-
//#region src/agent/agent-backend/agent-run-executor.ts
|
|
786
|
+
//#region src/agent/agent-backend/agent-run-executor.service.ts
|
|
786
787
|
var AgentRunExecutor = class {
|
|
787
|
-
|
|
788
|
+
executeRun = (session, envelope, controller) => (async function* (self) {
|
|
788
789
|
if (!isHiddenNcpMessage(envelope.message)) {
|
|
789
790
|
const messageSent = {
|
|
790
791
|
type: NcpEventType.MessageSent,
|
|
791
792
|
payload: {
|
|
792
793
|
sessionId: envelope.sessionId,
|
|
793
794
|
message: structuredClone(envelope.message),
|
|
795
|
+
...envelope.correlationId ? { correlationId: envelope.correlationId } : {},
|
|
794
796
|
metadata: envelope.metadata
|
|
795
797
|
}
|
|
796
798
|
};
|
|
@@ -803,27 +805,39 @@ var AgentRunExecutor = class {
|
|
|
803
805
|
messages: [envelope.message],
|
|
804
806
|
correlationId: envelope.correlationId,
|
|
805
807
|
metadata: envelope.metadata
|
|
806
|
-
}, { signal: controller.signal })) yield
|
|
808
|
+
}, { signal: controller.signal })) yield normalizeRunEvent(event, envelope);
|
|
807
809
|
} catch (error) {
|
|
808
810
|
if (!controller.signal.aborted) {
|
|
809
|
-
const runErrorEvent = await
|
|
811
|
+
const runErrorEvent = await self.publishFailure(error, envelope, session);
|
|
810
812
|
yield structuredClone(runErrorEvent);
|
|
811
813
|
}
|
|
812
814
|
}
|
|
813
|
-
}
|
|
814
|
-
async
|
|
815
|
+
})(this);
|
|
816
|
+
publishFailure = async (error, envelope, session) => {
|
|
815
817
|
const message = error instanceof Error ? error.message : String(error);
|
|
816
818
|
const runErrorEvent = {
|
|
817
819
|
type: NcpEventType.RunError,
|
|
818
820
|
payload: {
|
|
819
821
|
sessionId: envelope.sessionId,
|
|
822
|
+
...envelope.correlationId ? { correlationId: envelope.correlationId } : {},
|
|
820
823
|
error: message
|
|
821
824
|
}
|
|
822
825
|
};
|
|
823
826
|
await session.stateManager.dispatch(runErrorEvent);
|
|
824
827
|
return runErrorEvent;
|
|
825
|
-
}
|
|
828
|
+
};
|
|
826
829
|
};
|
|
830
|
+
function normalizeRunEvent(event, envelope) {
|
|
831
|
+
if (!("payload" in event) || !event.payload || typeof event.payload !== "object") return structuredClone(event);
|
|
832
|
+
return structuredClone({
|
|
833
|
+
...event,
|
|
834
|
+
payload: {
|
|
835
|
+
...event.payload,
|
|
836
|
+
sessionId: "sessionId" in event.payload && typeof event.payload.sessionId === "string" ? event.payload.sessionId : envelope.sessionId,
|
|
837
|
+
...envelope.correlationId && (!("correlationId" in event.payload) || typeof event.payload.correlationId !== "string") ? { correlationId: envelope.correlationId } : {}
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
}
|
|
827
841
|
//#endregion
|
|
828
842
|
//#region src/agent/agent-backend/async-queue.ts
|
|
829
843
|
function createAsyncQueue() {
|
|
@@ -874,7 +888,7 @@ function createAsyncQueue() {
|
|
|
874
888
|
};
|
|
875
889
|
}
|
|
876
890
|
//#endregion
|
|
877
|
-
//#region src/agent/agent-backend/agent-backend-session-realtime.ts
|
|
891
|
+
//#region src/agent/agent-backend/agent-backend-session-realtime.service.ts
|
|
878
892
|
function readEventSessionId(event) {
|
|
879
893
|
const payload = "payload" in event ? event.payload : null;
|
|
880
894
|
if (!payload || typeof payload !== "object") return null;
|
|
@@ -888,7 +902,7 @@ var AgentBackendSessionRealtime = class {
|
|
|
888
902
|
if (options.dispatchToStateManager) await session.stateManager.dispatch(event);
|
|
889
903
|
this.params.publishEndpointEvent(event);
|
|
890
904
|
session.publisher.publish(event);
|
|
891
|
-
if (options.persistSession !== false) await this.params.
|
|
905
|
+
if (options.persistSession !== false) await this.params.persistSessionEvent(session, event);
|
|
892
906
|
};
|
|
893
907
|
streamSessionEvents = (payloadOrParams, opts) => {
|
|
894
908
|
return (async function* (self) {
|
|
@@ -958,7 +972,7 @@ var AgentBackendSessionRealtime = class {
|
|
|
958
972
|
};
|
|
959
973
|
};
|
|
960
974
|
//#endregion
|
|
961
|
-
//#region src/agent/agent-backend/agent-backend-session
|
|
975
|
+
//#region src/agent/agent-backend/agent-backend-session.utils.ts
|
|
962
976
|
const AUTO_SESSION_LABEL_MAX_LENGTH = 64;
|
|
963
977
|
function readOptionalAgentId$1(value) {
|
|
964
978
|
if (typeof value !== "string") return;
|
|
@@ -970,6 +984,9 @@ function readMessages(snapshot) {
|
|
|
970
984
|
if (snapshot.streamingMessage) messages.push(structuredClone(snapshot.streamingMessage));
|
|
971
985
|
return messages;
|
|
972
986
|
}
|
|
987
|
+
function readSessionActivityAt(summary) {
|
|
988
|
+
return summary.lastMessageAt ?? summary.createdAt ?? summary.updatedAt;
|
|
989
|
+
}
|
|
973
990
|
function toSessionSummary(session, liveSession) {
|
|
974
991
|
const metadata = withAutoSessionLabel({
|
|
975
992
|
metadata: session.metadata ? structuredClone({
|
|
@@ -982,6 +999,7 @@ function toSessionSummary(session, liveSession) {
|
|
|
982
999
|
sessionId: session.sessionId,
|
|
983
1000
|
...readOptionalAgentId$1(session.agentId) ? { agentId: readOptionalAgentId$1(session.agentId) } : {},
|
|
984
1001
|
messageCount: session.messages.length,
|
|
1002
|
+
...session.createdAt ? { createdAt: session.createdAt } : {},
|
|
985
1003
|
updatedAt: session.updatedAt,
|
|
986
1004
|
...session.messages.length > 0 ? { lastMessageAt: session.messages[session.messages.length - 1]?.timestamp ?? session.updatedAt } : {},
|
|
987
1005
|
status: liveSession?.activeExecution ? "running" : "idle",
|
|
@@ -999,6 +1017,7 @@ function toLiveSessionSummary(session) {
|
|
|
999
1017
|
sessionId: session.sessionId,
|
|
1000
1018
|
...readOptionalAgentId$1(session.agentId) ? { agentId: readOptionalAgentId$1(session.agentId) } : {},
|
|
1001
1019
|
messageCount: messages.length,
|
|
1020
|
+
createdAt: session.createdAt,
|
|
1002
1021
|
updatedAt,
|
|
1003
1022
|
...messages.length > 0 ? { lastMessageAt: messages[messages.length - 1]?.timestamp ?? updatedAt } : {},
|
|
1004
1023
|
status: session.activeExecution ? "running" : "idle",
|
|
@@ -1020,6 +1039,36 @@ function toLiveSessionSummaryWithContextWindow(session, resolver) {
|
|
|
1020
1039
|
const messages = readMessages(session.stateManager.getSnapshot());
|
|
1021
1040
|
return withSessionContextWindow(toLiveSessionSummary(session), messages, resolver);
|
|
1022
1041
|
}
|
|
1042
|
+
async function listBackendSessionSummaries(params) {
|
|
1043
|
+
const { liveSessions, sessionStore } = params;
|
|
1044
|
+
const summaries = sessionStore.listSessionSummaries ? await sessionStore.listSessionSummaries() : (await sessionStore.listSessions()).map((session) => toSessionSummary(session, liveSessions.find((liveSession) => liveSession.sessionId === session.sessionId) ?? null));
|
|
1045
|
+
for (const liveSession of liveSessions) {
|
|
1046
|
+
const existingIndex = summaries.findIndex((session) => session.sessionId === liveSession.sessionId);
|
|
1047
|
+
if (existingIndex >= 0) {
|
|
1048
|
+
summaries[existingIndex] = {
|
|
1049
|
+
...summaries[existingIndex],
|
|
1050
|
+
status: liveSession.activeExecution ? "running" : "idle"
|
|
1051
|
+
};
|
|
1052
|
+
continue;
|
|
1053
|
+
}
|
|
1054
|
+
summaries.push(toLiveSessionSummary(liveSession));
|
|
1055
|
+
}
|
|
1056
|
+
return summaries.sort((left, right) => readSessionActivityAt(right).localeCompare(readSessionActivityAt(left)));
|
|
1057
|
+
}
|
|
1058
|
+
async function listBackendSessionMessages(params) {
|
|
1059
|
+
const { liveSession, sessionId, sessionStore } = params;
|
|
1060
|
+
if (liveSession) return readMessages(liveSession.stateManager.getSnapshot());
|
|
1061
|
+
if (sessionStore.listSessionMessages) return sessionStore.listSessionMessages(sessionId);
|
|
1062
|
+
const session = await sessionStore.getSession(sessionId);
|
|
1063
|
+
return session ? session.messages.map((message) => structuredClone(message)) : [];
|
|
1064
|
+
}
|
|
1065
|
+
async function getBackendSessionSummary(params) {
|
|
1066
|
+
const { liveSession, resolveSessionContextWindow, sessionId, sessionStore } = params;
|
|
1067
|
+
if (!liveSession && sessionStore.getSessionSummary) return sessionStore.getSessionSummary(sessionId);
|
|
1068
|
+
const storedSession = await sessionStore.getSession(sessionId);
|
|
1069
|
+
const liveMessages = liveSession ? readMessages(liveSession.stateManager.getSnapshot()) : null;
|
|
1070
|
+
return storedSession ? withSessionContextWindow(toSessionSummary(storedSession, liveSession), liveMessages ?? storedSession.messages, resolveSessionContextWindow) : liveSession ? toLiveSessionSummaryWithContextWindow(liveSession, resolveSessionContextWindow) : null;
|
|
1071
|
+
}
|
|
1023
1072
|
function now() {
|
|
1024
1073
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1025
1074
|
}
|
|
@@ -1101,7 +1150,7 @@ function normalizeSendRunEvent(params) {
|
|
|
1101
1150
|
};
|
|
1102
1151
|
}
|
|
1103
1152
|
//#endregion
|
|
1104
|
-
//#region src/agent/agent-backend/agent-backend-session-persistence.ts
|
|
1153
|
+
//#region src/agent/agent-backend/agent-backend-session-persistence.utils.ts
|
|
1105
1154
|
function readOptionalAgentId(value) {
|
|
1106
1155
|
if (typeof value !== "string") return;
|
|
1107
1156
|
const trimmed = value.trim().toLowerCase();
|
|
@@ -1111,44 +1160,94 @@ function readAgentIdFromMetadata(metadata) {
|
|
|
1111
1160
|
return readOptionalAgentId(metadata?.agent_id) ?? readOptionalAgentId(metadata?.agentId);
|
|
1112
1161
|
}
|
|
1113
1162
|
function resolvePersistedAgentId(params) {
|
|
1114
|
-
|
|
1163
|
+
const { liveSession, storedSession } = params;
|
|
1164
|
+
return readOptionalAgentId(liveSession?.agentId) ?? readOptionalAgentId(storedSession?.agentId) ?? readAgentIdFromMetadata(liveSession?.metadata) ?? readAgentIdFromMetadata(storedSession?.metadata);
|
|
1115
1165
|
}
|
|
1116
1166
|
function buildUpdatedSessionRecord(params) {
|
|
1117
|
-
const
|
|
1118
|
-
|
|
1167
|
+
const { liveSession, patch, sessionId, storedSession, updatedAt } = params;
|
|
1168
|
+
const nextMetadata = patch.metadata === null ? {} : patch.metadata ? structuredClone(patch.metadata) : structuredClone(liveSession?.metadata ?? storedSession?.metadata ?? {});
|
|
1169
|
+
if (liveSession) liveSession.metadata = structuredClone(nextMetadata);
|
|
1170
|
+
const agentId = resolvePersistedAgentId({
|
|
1171
|
+
liveSession,
|
|
1172
|
+
storedSession
|
|
1173
|
+
});
|
|
1119
1174
|
return {
|
|
1120
|
-
sessionId
|
|
1121
|
-
...
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
liveSession: params.liveSession,
|
|
1126
|
-
storedSession: params.storedSession
|
|
1127
|
-
}) } : {},
|
|
1128
|
-
messages: params.liveSession ? readMessages(params.liveSession.stateManager.getSnapshot()) : params.storedSession?.messages.map((message) => structuredClone(message)) ?? [],
|
|
1129
|
-
updatedAt: params.updatedAt,
|
|
1175
|
+
sessionId,
|
|
1176
|
+
...agentId ? { agentId } : {},
|
|
1177
|
+
messages: liveSession ? readMessages(liveSession.stateManager.getSnapshot()) : storedSession?.messages.map((message) => structuredClone(message)) ?? [],
|
|
1178
|
+
createdAt: storedSession?.createdAt ?? liveSession?.createdAt ?? updatedAt,
|
|
1179
|
+
updatedAt,
|
|
1130
1180
|
metadata: nextMetadata
|
|
1131
1181
|
};
|
|
1132
1182
|
}
|
|
1133
1183
|
function buildPersistedLiveSessionRecord(params) {
|
|
1134
|
-
const
|
|
1184
|
+
const { session, sessionId, updatedAt } = params;
|
|
1185
|
+
const messages = readMessages(session.stateManager.getSnapshot());
|
|
1135
1186
|
const metadata = withAutoSessionLabel({
|
|
1136
1187
|
metadata: {
|
|
1137
|
-
...
|
|
1138
|
-
...
|
|
1188
|
+
...session.metadata ? structuredClone(session.metadata) : {},
|
|
1189
|
+
...session.activeExecution?.requestEnvelope.metadata ? structuredClone(session.activeExecution.requestEnvelope.metadata) : {}
|
|
1139
1190
|
},
|
|
1140
1191
|
messages
|
|
1141
1192
|
});
|
|
1193
|
+
const agentId = readOptionalAgentId(session.agentId) ?? readAgentIdFromMetadata(session.metadata) ?? readAgentIdFromMetadata(session.activeExecution?.requestEnvelope.metadata);
|
|
1142
1194
|
return {
|
|
1143
|
-
sessionId
|
|
1144
|
-
...
|
|
1195
|
+
sessionId,
|
|
1196
|
+
...agentId ? { agentId } : {},
|
|
1145
1197
|
messages,
|
|
1146
|
-
|
|
1198
|
+
createdAt: session.createdAt,
|
|
1199
|
+
updatedAt,
|
|
1200
|
+
metadata
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
function buildLiveSessionEventRecord(params) {
|
|
1204
|
+
const { session, updatedAt } = params;
|
|
1205
|
+
const metadata = {
|
|
1206
|
+
...session.metadata ? structuredClone(session.metadata) : {},
|
|
1207
|
+
...session.activeExecution?.requestEnvelope.metadata ? structuredClone(session.activeExecution.requestEnvelope.metadata) : {}
|
|
1208
|
+
};
|
|
1209
|
+
const agentId = readOptionalAgentId(session.agentId) ?? readAgentIdFromMetadata(session.metadata) ?? readAgentIdFromMetadata(session.activeExecution?.requestEnvelope.metadata);
|
|
1210
|
+
return {
|
|
1211
|
+
sessionId: session.sessionId,
|
|
1212
|
+
...agentId ? { agentId } : {},
|
|
1213
|
+
createdAt: session.createdAt,
|
|
1214
|
+
updatedAt,
|
|
1147
1215
|
metadata
|
|
1148
1216
|
};
|
|
1149
1217
|
}
|
|
1218
|
+
function shouldPersistRunEndSnapshot(sessionStore) {
|
|
1219
|
+
return !sessionStore.appendSessionEvent;
|
|
1220
|
+
}
|
|
1221
|
+
async function persistLiveSession(params) {
|
|
1222
|
+
const { session, sessionId, sessionStore, updatedAt } = params;
|
|
1223
|
+
if (!session) return;
|
|
1224
|
+
await sessionStore.saveSession(buildPersistedLiveSessionRecord({
|
|
1225
|
+
sessionId,
|
|
1226
|
+
session,
|
|
1227
|
+
updatedAt
|
|
1228
|
+
}));
|
|
1229
|
+
}
|
|
1230
|
+
async function persistLiveSessionEvent(params) {
|
|
1231
|
+
const { event, session, sessionStore, updatedAt } = params;
|
|
1232
|
+
if (sessionStore.appendSessionEvent) {
|
|
1233
|
+
await sessionStore.appendSessionEvent({
|
|
1234
|
+
session: buildLiveSessionEventRecord({
|
|
1235
|
+
session,
|
|
1236
|
+
updatedAt
|
|
1237
|
+
}),
|
|
1238
|
+
event,
|
|
1239
|
+
updatedAt
|
|
1240
|
+
});
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
await sessionStore.saveSession(buildPersistedLiveSessionRecord({
|
|
1244
|
+
sessionId: session.sessionId,
|
|
1245
|
+
session,
|
|
1246
|
+
updatedAt
|
|
1247
|
+
}));
|
|
1248
|
+
}
|
|
1150
1249
|
//#endregion
|
|
1151
|
-
//#region src/agent/agent-backend/agent-backend.ts
|
|
1250
|
+
//#region src/agent/agent-backend/agent-backend.service.ts
|
|
1152
1251
|
const DEFAULT_SUPPORTED_PART_TYPES = [
|
|
1153
1252
|
"text",
|
|
1154
1253
|
"file",
|
|
@@ -1161,6 +1260,9 @@ const DEFAULT_SUPPORTED_PART_TYPES = [
|
|
|
1161
1260
|
"action",
|
|
1162
1261
|
"extension"
|
|
1163
1262
|
];
|
|
1263
|
+
const disposeRuntime = async (runtime) => {
|
|
1264
|
+
await runtime.dispose?.();
|
|
1265
|
+
};
|
|
1164
1266
|
var DefaultNcpAgentBackend = class {
|
|
1165
1267
|
manifest;
|
|
1166
1268
|
sessionStore;
|
|
@@ -1183,7 +1285,18 @@ var DefaultNcpAgentBackend = class {
|
|
|
1183
1285
|
sessionStore: this.sessionStore,
|
|
1184
1286
|
publishEndpointEvent: (event) => this.publisher.publish(event),
|
|
1185
1287
|
subscribeEndpointEvent: (listener) => this.publisher.subscribe(listener),
|
|
1186
|
-
persistSession: (sessionId) =>
|
|
1288
|
+
persistSession: (sessionId) => persistLiveSession({
|
|
1289
|
+
sessionStore: this.sessionStore,
|
|
1290
|
+
session: this.sessionRegistry.getSession(sessionId),
|
|
1291
|
+
sessionId,
|
|
1292
|
+
updatedAt: now()
|
|
1293
|
+
}),
|
|
1294
|
+
persistSessionEvent: (session, event) => persistLiveSessionEvent({
|
|
1295
|
+
sessionStore: this.sessionStore,
|
|
1296
|
+
session,
|
|
1297
|
+
event,
|
|
1298
|
+
updatedAt: now()
|
|
1299
|
+
}),
|
|
1187
1300
|
getSessionSummary: (sessionId) => this.getSession(sessionId)
|
|
1188
1301
|
});
|
|
1189
1302
|
this.manifest = {
|
|
@@ -1211,12 +1324,14 @@ var DefaultNcpAgentBackend = class {
|
|
|
1211
1324
|
const execution = session.activeExecution;
|
|
1212
1325
|
if (!execution) {
|
|
1213
1326
|
session.publisher.close();
|
|
1327
|
+
await disposeRuntime(session.runtime);
|
|
1214
1328
|
continue;
|
|
1215
1329
|
}
|
|
1216
1330
|
execution.abortHandled = true;
|
|
1217
1331
|
execution.controller.abort();
|
|
1218
1332
|
this.finishSessionExecution(session, execution);
|
|
1219
1333
|
session.publisher.close();
|
|
1334
|
+
await disposeRuntime(session.runtime);
|
|
1220
1335
|
}
|
|
1221
1336
|
this.sessionRegistry.clear();
|
|
1222
1337
|
};
|
|
@@ -1224,7 +1339,15 @@ var DefaultNcpAgentBackend = class {
|
|
|
1224
1339
|
await this.ensureStarted();
|
|
1225
1340
|
switch (event.type) {
|
|
1226
1341
|
case NcpEventType.MessageRequest:
|
|
1227
|
-
|
|
1342
|
+
if (!event.payload.sessionId) throw new Error("MessageRequest requires a sessionId before it reaches the agent backend.");
|
|
1343
|
+
for await (const emittedEvent of this.send({
|
|
1344
|
+
...event.payload,
|
|
1345
|
+
sessionId: event.payload.sessionId,
|
|
1346
|
+
message: {
|
|
1347
|
+
...event.payload.message,
|
|
1348
|
+
sessionId: event.payload.sessionId
|
|
1349
|
+
}
|
|
1350
|
+
}));
|
|
1228
1351
|
return;
|
|
1229
1352
|
case NcpEventType.MessageStreamRequest:
|
|
1230
1353
|
await this.ensureStarted();
|
|
@@ -1266,31 +1389,38 @@ var DefaultNcpAgentBackend = class {
|
|
|
1266
1389
|
}
|
|
1267
1390
|
} finally {
|
|
1268
1391
|
self.finishSessionExecution(session, execution);
|
|
1269
|
-
|
|
1392
|
+
if (shouldPersistRunEndSnapshot(self.sessionStore)) await persistLiveSession({
|
|
1393
|
+
sessionStore: self.sessionStore,
|
|
1394
|
+
session,
|
|
1395
|
+
sessionId: session.sessionId,
|
|
1396
|
+
updatedAt: now()
|
|
1397
|
+
});
|
|
1270
1398
|
}
|
|
1271
1399
|
})(this);
|
|
1272
1400
|
};
|
|
1273
1401
|
abort = async (payload) => this.handleAbort(payload);
|
|
1274
1402
|
stream = (payloadOrParams, opts) => this.sessionRealtime.streamSessionEvents(payloadOrParams, opts);
|
|
1275
1403
|
listSessions = async () => {
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
}
|
|
1281
|
-
return summaries.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
1404
|
+
return listBackendSessionSummaries({
|
|
1405
|
+
sessionStore: this.sessionStore,
|
|
1406
|
+
liveSessions: this.sessionRegistry.listSessions()
|
|
1407
|
+
});
|
|
1282
1408
|
};
|
|
1409
|
+
isLiveSessionRunning = (sessionId) => Boolean(this.sessionRegistry.getSession(sessionId)?.activeExecution);
|
|
1283
1410
|
listSessionMessages = async (sessionId) => {
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1411
|
+
return listBackendSessionMessages({
|
|
1412
|
+
sessionStore: this.sessionStore,
|
|
1413
|
+
liveSession: this.sessionRegistry.getSession(sessionId),
|
|
1414
|
+
sessionId
|
|
1415
|
+
});
|
|
1288
1416
|
};
|
|
1289
1417
|
getSession = async (sessionId) => {
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1418
|
+
return getBackendSessionSummary({
|
|
1419
|
+
sessionStore: this.sessionStore,
|
|
1420
|
+
liveSession: this.sessionRegistry.getSession(sessionId),
|
|
1421
|
+
sessionId,
|
|
1422
|
+
resolveSessionContextWindow: this.resolveSessionContextWindow
|
|
1423
|
+
});
|
|
1294
1424
|
};
|
|
1295
1425
|
appendMessage = async (sessionId, message) => {
|
|
1296
1426
|
await this.ensureStarted();
|
|
@@ -1319,9 +1449,10 @@ var DefaultNcpAgentBackend = class {
|
|
|
1319
1449
|
if (execution) {
|
|
1320
1450
|
execution.abortHandled = true;
|
|
1321
1451
|
execution.controller.abort();
|
|
1322
|
-
|
|
1452
|
+
execution.closed = true;
|
|
1323
1453
|
}
|
|
1324
1454
|
liveSession?.publisher.close();
|
|
1455
|
+
if (liveSession) await disposeRuntime(liveSession.runtime);
|
|
1325
1456
|
await this.sessionStore.deleteSession(sessionId);
|
|
1326
1457
|
};
|
|
1327
1458
|
ensureStarted = async () => {
|
|
@@ -1354,18 +1485,9 @@ var DefaultNcpAgentBackend = class {
|
|
|
1354
1485
|
await this.sessionRealtime.publishSessionEvent(session, abortEvent, { dispatchToStateManager: true });
|
|
1355
1486
|
this.finishSessionExecution(session, execution);
|
|
1356
1487
|
};
|
|
1357
|
-
persistSession = async (sessionId) => {
|
|
1358
|
-
const session = this.sessionRegistry.getSession(sessionId);
|
|
1359
|
-
if (!session) return;
|
|
1360
|
-
await this.sessionStore.saveSession(buildPersistedLiveSessionRecord({
|
|
1361
|
-
sessionId,
|
|
1362
|
-
session,
|
|
1363
|
-
updatedAt: now()
|
|
1364
|
-
}));
|
|
1365
|
-
};
|
|
1366
1488
|
};
|
|
1367
1489
|
//#endregion
|
|
1368
|
-
//#region src/agent/agent-backend/in-memory-agent-session
|
|
1490
|
+
//#region src/agent/agent-backend/in-memory-agent-session.store.ts
|
|
1369
1491
|
var InMemoryAgentSessionStore = class {
|
|
1370
1492
|
sessions = /* @__PURE__ */ new Map();
|
|
1371
1493
|
getSession = async (sessionId) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/ncp-toolkit",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.15",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Toolkit implementations built on top of the NextClaw Communication Protocol.",
|
|
6
6
|
"type": "module",
|
|
@@ -15,14 +15,14 @@
|
|
|
15
15
|
"dist"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@nextclaw/ncp": "0.5.
|
|
18
|
+
"@nextclaw/ncp": "0.5.10"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@types/node": "^20.17.6",
|
|
22
22
|
"prettier": "^3.3.3",
|
|
23
23
|
"typescript": "^5.6.3",
|
|
24
24
|
"vitest": "^4.1.2",
|
|
25
|
-
"@nextclaw/ncp-agent-runtime": "0.3.
|
|
25
|
+
"@nextclaw/ncp-agent-runtime": "0.3.20"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "tsdown src/index.ts --dts --clean --target es2022 --no-fixedExtension",
|