@nextclaw/ncp-toolkit 0.5.16 → 0.5.18
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/README.md +0 -1
- package/dist/index.d.ts +12 -69
- package/dist/index.js +26 -829
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -12,7 +12,6 @@ pnpm -C packages/ncp-packages/nextclaw-ncp-toolkit build
|
|
|
12
12
|
|
|
13
13
|
- Reference conversation-state manager implementations
|
|
14
14
|
- Protocol-level helper logic that depends on `@nextclaw/ncp` contracts
|
|
15
|
-
- Composable agent backend building block: `DefaultNcpAgentBackend`
|
|
16
15
|
- Default in-memory adapter: `InMemoryAgentSessionStore`
|
|
17
16
|
- In-process adapter helper: `createAgentClientFromServer`
|
|
18
17
|
- Runtime throwable helper: `NcpErrorException`
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NcpAgentClientEndpoint, NcpAgentConversationHydrationParams, NcpAgentConversationSnapshot, NcpAgentConversationStateManager,
|
|
1
|
+
import { NcpAgentClientEndpoint, NcpAgentConversationHydrationParams, NcpAgentConversationSnapshot, NcpAgentConversationStateManager, NcpAgentRunInput, NcpAgentRuntime, NcpAgentServerEndpoint, NcpContextWindowUpdatedPayload, NcpEndpointEvent, NcpEndpointSubscriber, NcpError, NcpErrorCode, NcpMessage, NcpMessageAbortPayload, NcpMessagePart, NcpMessageSentPayload, NcpReasoningDeltaPayload, NcpReasoningEndPayload, NcpReasoningStartPayload, NcpRequestEnvelope, NcpRunErrorPayload, NcpRunFinishedPayload, NcpRunMetadataPayload, NcpRunStartedPayload, NcpSessionSummary, NcpTextDeltaPayload, NcpTextEndPayload, NcpTextStartPayload, NcpToolCallArgsDeltaPayload, NcpToolCallArgsPayload, NcpToolCallEndPayload, NcpToolCallResultPayload, NcpToolCallStartPayload, OpenAITool } from "@nextclaw/ncp";
|
|
2
2
|
|
|
3
3
|
//#region src/agent/agent-conversation-state-manager.d.ts
|
|
4
4
|
declare class DefaultNcpAgentConversationStateManager implements NcpAgentConversationStateManager {
|
|
@@ -125,83 +125,26 @@ interface AgentSessionStore {
|
|
|
125
125
|
event: NcpEndpointEvent;
|
|
126
126
|
updatedAt: string;
|
|
127
127
|
}): Promise<void>;
|
|
128
|
+
updateSessionMetadata(params: {
|
|
129
|
+
sessionId: string;
|
|
130
|
+
metadata: Record<string, unknown>;
|
|
131
|
+
updatedAt: string;
|
|
132
|
+
}): Promise<boolean>;
|
|
128
133
|
saveSession(session: AgentSessionRecord): Promise<void>;
|
|
129
|
-
replaceSession(session: AgentSessionRecord): Promise<void>;
|
|
130
134
|
deleteSession(sessionId: string): Promise<AgentSessionRecord | null>;
|
|
131
135
|
}
|
|
132
136
|
//#endregion
|
|
133
|
-
//#region src/agent/agent-backend/agent-backend-session.utils.d.ts
|
|
134
|
-
type SessionContextWindowResolver = (params: {
|
|
135
|
-
messages: readonly NcpMessage[];
|
|
136
|
-
metadata: Record<string, unknown>;
|
|
137
|
-
sessionId: string;
|
|
138
|
-
}) => Record<string, unknown> | null;
|
|
139
|
-
//#endregion
|
|
140
|
-
//#region src/agent/agent-backend/agent-backend.service.d.ts
|
|
141
|
-
type DefaultNcpAgentBackendConfig = {
|
|
142
|
-
createRuntime: CreateRuntimeFn;
|
|
143
|
-
sessionStore: AgentSessionStore;
|
|
144
|
-
resolveSessionContextWindow?: SessionContextWindowResolver;
|
|
145
|
-
onSessionRunStatusChanged?: (payload: {
|
|
146
|
-
sessionKey: string;
|
|
147
|
-
status: "running" | "idle";
|
|
148
|
-
}) => void;
|
|
149
|
-
endpointId?: string;
|
|
150
|
-
version?: string;
|
|
151
|
-
metadata?: Record<string, unknown>;
|
|
152
|
-
supportedPartTypes?: NcpEndpointManifest["supportedPartTypes"];
|
|
153
|
-
expectedLatency?: NcpEndpointManifest["expectedLatency"];
|
|
154
|
-
};
|
|
155
|
-
declare class DefaultNcpAgentBackend implements NcpAgentServerEndpoint, NcpSessionApi, NcpAgentStreamProvider, NcpAgentRunApi {
|
|
156
|
-
readonly manifest: NcpEndpointManifest & {
|
|
157
|
-
endpointKind: "agent";
|
|
158
|
-
};
|
|
159
|
-
private readonly sessionStore;
|
|
160
|
-
private readonly onSessionRunStatusChanged;
|
|
161
|
-
private readonly sessionRegistry;
|
|
162
|
-
private readonly executor;
|
|
163
|
-
private readonly publisher;
|
|
164
|
-
private readonly sessionRealtime;
|
|
165
|
-
private readonly resolveSessionContextWindow;
|
|
166
|
-
private started;
|
|
167
|
-
constructor(config: DefaultNcpAgentBackendConfig);
|
|
168
|
-
start: () => Promise<void>;
|
|
169
|
-
stop: () => Promise<void>;
|
|
170
|
-
emit: (event: NcpEndpointEvent) => Promise<void>;
|
|
171
|
-
subscribe: (listener: (event: NcpEndpointEvent) => void) => (() => void);
|
|
172
|
-
send: (envelope: NcpRequestEnvelope, options?: NcpAgentRunSendOptions) => AsyncIterable<NcpEndpointEvent>;
|
|
173
|
-
abort: (payload: NcpMessageAbortPayload) => Promise<void>;
|
|
174
|
-
stream: (payloadOrParams: NcpStreamRequestPayload | {
|
|
175
|
-
payload: NcpStreamRequestPayload;
|
|
176
|
-
signal: AbortSignal;
|
|
177
|
-
}, opts?: NcpAgentRunStreamOptions) => AsyncIterable<NcpEndpointEvent>;
|
|
178
|
-
listSessions: () => Promise<NcpSessionSummary[]>;
|
|
179
|
-
isLiveSessionRunning: (sessionId: string) => boolean;
|
|
180
|
-
listSessionMessages: (sessionId: string) => Promise<NcpMessage[]>;
|
|
181
|
-
getSession: (sessionId: string) => Promise<NcpSessionSummary | null>;
|
|
182
|
-
appendMessage: (sessionId: string, message: NcpMessage) => Promise<NcpSessionSummary | null>;
|
|
183
|
-
updateToolCallResult: (sessionId: string, toolCallId: string, content: unknown) => Promise<NcpSessionSummary | null>;
|
|
184
|
-
updateSession: (sessionId: string, patch: NcpSessionPatch) => Promise<NcpSessionSummary | null>;
|
|
185
|
-
deleteSession: (sessionId: string) => Promise<void>;
|
|
186
|
-
private ensureStarted;
|
|
187
|
-
private startSessionExecution;
|
|
188
|
-
private finishSessionExecution;
|
|
189
|
-
private handleAbort;
|
|
190
|
-
}
|
|
191
|
-
//#endregion
|
|
192
|
-
//#region src/agent/agent-backend/agent-run-executor.service.d.ts
|
|
193
|
-
declare class AgentRunExecutor {
|
|
194
|
-
executeRun: (session: LiveSessionState, envelope: NcpRequestEnvelope, controller: AbortController) => AsyncGenerator<NcpEndpointEvent>;
|
|
195
|
-
private publishFailure;
|
|
196
|
-
}
|
|
197
|
-
//#endregion
|
|
198
137
|
//#region src/agent/agent-backend/in-memory-agent-session.store.d.ts
|
|
199
138
|
declare class InMemoryAgentSessionStore implements AgentSessionStore {
|
|
200
139
|
private readonly sessions;
|
|
201
140
|
getSession: (sessionId: string) => Promise<AgentSessionRecord | null>;
|
|
202
141
|
listSessions: () => Promise<AgentSessionRecord[]>;
|
|
203
142
|
saveSession: (session: AgentSessionRecord) => Promise<void>;
|
|
204
|
-
|
|
143
|
+
updateSessionMetadata: (params: {
|
|
144
|
+
sessionId: string;
|
|
145
|
+
metadata: Record<string, unknown>;
|
|
146
|
+
updatedAt: string;
|
|
147
|
+
}) => Promise<boolean>;
|
|
205
148
|
deleteSession: (sessionId: string) => Promise<AgentSessionRecord | null>;
|
|
206
149
|
}
|
|
207
150
|
//#endregion
|
|
@@ -241,4 +184,4 @@ declare class NcpReplyConsumer {
|
|
|
241
184
|
consume: (input: NcpReplyInput) => Promise<void>;
|
|
242
185
|
}
|
|
243
186
|
//#endregion
|
|
244
|
-
export {
|
|
187
|
+
export { type AgentSessionEventRecord, type AgentSessionRecord, type AgentSessionStore, Chat, ChatTarget, type CreateRuntimeFn, DefaultNcpAgentConversationStateManager, EventPublisher, InMemoryAgentSessionStore, type LiveSessionExecution, type LiveSessionState, NcpErrorException, NcpEventStream, NcpReplyConsumer, NcpReplyInput, type RuntimeFactoryParams, createAgentClientFromServer };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NcpEventType, consumeNcpRunHandle, createNcpRunHandle,
|
|
1
|
+
import { NcpEventType, consumeNcpRunHandle, createNcpRunHandle, sanitizeAssistantReplyTags } from "@nextclaw/ncp";
|
|
2
2
|
//#region src/agent/agent-conversation-message-normalizer.ts
|
|
3
3
|
function cloneConversationMessage(message) {
|
|
4
4
|
return {
|
|
@@ -662,82 +662,37 @@ var EventPublisher = class {
|
|
|
662
662
|
}
|
|
663
663
|
};
|
|
664
664
|
//#endregion
|
|
665
|
-
//#region src/agent/agent-backend/agent-
|
|
666
|
-
|
|
667
|
-
if (typeof value !== "string") return;
|
|
668
|
-
const trimmed = value.trim().toLowerCase();
|
|
669
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
670
|
-
}
|
|
671
|
-
function readAgentIdFromMetadata$1(metadata) {
|
|
672
|
-
return readOptionalAgentId$2(metadata?.agent_id) ?? readOptionalAgentId$2(metadata?.agentId);
|
|
673
|
-
}
|
|
674
|
-
var AgentLiveSessionRegistry = class {
|
|
665
|
+
//#region src/agent/agent-backend/in-memory-agent-session.store.ts
|
|
666
|
+
var InMemoryAgentSessionStore = class {
|
|
675
667
|
sessions = /* @__PURE__ */ new Map();
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
}
|
|
680
|
-
ensureSession = async (sessionId, initialMetadata) => {
|
|
681
|
-
const existing = this.sessions.get(sessionId);
|
|
682
|
-
if (existing) {
|
|
683
|
-
if (!existing.agentId) existing.agentId = readAgentIdFromMetadata$1(initialMetadata) ?? existing.agentId;
|
|
684
|
-
if (initialMetadata && Object.keys(initialMetadata).length > 0) existing.metadata = {
|
|
685
|
-
...existing.metadata,
|
|
686
|
-
...structuredClone(initialMetadata)
|
|
687
|
-
};
|
|
688
|
-
return existing;
|
|
689
|
-
}
|
|
690
|
-
const storedSession = await this.sessionStore.getSession(sessionId);
|
|
691
|
-
const stateManager = new DefaultNcpAgentConversationStateManager();
|
|
692
|
-
stateManager.hydrate({
|
|
693
|
-
sessionId,
|
|
694
|
-
messages: cloneMessages(storedSession?.messages ?? [])
|
|
695
|
-
});
|
|
696
|
-
const sessionMetadata = {
|
|
697
|
-
...storedSession?.metadata ? structuredClone(storedSession.metadata) : {},
|
|
698
|
-
...initialMetadata ? structuredClone(initialMetadata) : {}
|
|
699
|
-
};
|
|
700
|
-
const sessionAgentId = readOptionalAgentId$2(storedSession?.agentId) ?? readAgentIdFromMetadata$1(initialMetadata);
|
|
701
|
-
const session = {
|
|
702
|
-
sessionId,
|
|
703
|
-
...sessionAgentId ? { agentId: sessionAgentId } : {},
|
|
704
|
-
createdAt: storedSession?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
705
|
-
stateManager,
|
|
706
|
-
metadata: sessionMetadata,
|
|
707
|
-
runtime: null,
|
|
708
|
-
publisher: new EventPublisher(),
|
|
709
|
-
activeExecution: null
|
|
710
|
-
};
|
|
711
|
-
session.runtime = this.createRuntime({
|
|
712
|
-
sessionId,
|
|
713
|
-
...sessionAgentId ? { agentId: sessionAgentId } : {},
|
|
714
|
-
stateManager,
|
|
715
|
-
sessionMetadata,
|
|
716
|
-
setSessionMetadata: (nextMetadata) => {
|
|
717
|
-
session.metadata = { ...structuredClone(nextMetadata) };
|
|
718
|
-
}
|
|
719
|
-
});
|
|
720
|
-
this.sessions.set(sessionId, session);
|
|
721
|
-
return session;
|
|
668
|
+
getSession = async (sessionId) => {
|
|
669
|
+
const session = this.sessions.get(sessionId);
|
|
670
|
+
return session ? structuredClone(session) : null;
|
|
722
671
|
};
|
|
723
|
-
|
|
724
|
-
return this.sessions.
|
|
672
|
+
listSessions = async () => {
|
|
673
|
+
return [...this.sessions.values()].map((session) => structuredClone(session));
|
|
725
674
|
};
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
if (session) this.sessions.delete(sessionId);
|
|
729
|
-
return session;
|
|
675
|
+
saveSession = async (session) => {
|
|
676
|
+
this.sessions.set(session.sessionId, structuredClone(session));
|
|
730
677
|
};
|
|
731
|
-
|
|
732
|
-
|
|
678
|
+
updateSessionMetadata = async (params) => {
|
|
679
|
+
const { metadata, sessionId, updatedAt } = params;
|
|
680
|
+
const session = this.sessions.get(sessionId);
|
|
681
|
+
if (!session) return false;
|
|
682
|
+
this.sessions.set(sessionId, {
|
|
683
|
+
...session,
|
|
684
|
+
metadata: structuredClone(metadata),
|
|
685
|
+
updatedAt
|
|
686
|
+
});
|
|
687
|
+
return true;
|
|
733
688
|
};
|
|
734
|
-
|
|
735
|
-
|
|
689
|
+
deleteSession = async (sessionId) => {
|
|
690
|
+
const session = this.sessions.get(sessionId);
|
|
691
|
+
if (!session) return null;
|
|
692
|
+
this.sessions.delete(sessionId);
|
|
693
|
+
return structuredClone(session);
|
|
736
694
|
};
|
|
737
695
|
};
|
|
738
|
-
function cloneMessages(messages) {
|
|
739
|
-
return messages.map((message) => structuredClone(message));
|
|
740
|
-
}
|
|
741
696
|
//#endregion
|
|
742
697
|
//#region src/errors/ncp-error-exception.ts
|
|
743
698
|
/**
|
|
@@ -754,764 +709,6 @@ var NcpErrorException = class extends Error {
|
|
|
754
709
|
}
|
|
755
710
|
};
|
|
756
711
|
//#endregion
|
|
757
|
-
//#region src/agent/agent-backend/agent-backend-execution.utils.ts
|
|
758
|
-
function startAgentBackendSessionExecution(params) {
|
|
759
|
-
const { session, envelope, signal, onStatusChanged } = params;
|
|
760
|
-
if (session.activeExecution && !session.activeExecution.closed) throw new NcpErrorException("runtime-error", `Session ${session.sessionId} already has an active execution.`, { sessionId: session.sessionId });
|
|
761
|
-
const controller = new AbortController();
|
|
762
|
-
if (signal) signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
763
|
-
const execution = {
|
|
764
|
-
controller,
|
|
765
|
-
requestEnvelope: structuredClone(envelope),
|
|
766
|
-
abortHandled: false,
|
|
767
|
-
closed: false
|
|
768
|
-
};
|
|
769
|
-
session.activeExecution = execution;
|
|
770
|
-
onStatusChanged?.({
|
|
771
|
-
sessionKey: session.sessionId,
|
|
772
|
-
status: "running"
|
|
773
|
-
});
|
|
774
|
-
return execution;
|
|
775
|
-
}
|
|
776
|
-
function finishAgentBackendSessionExecution(params) {
|
|
777
|
-
const { session, execution, onStatusChanged } = params;
|
|
778
|
-
if (session.activeExecution === execution) {
|
|
779
|
-
session.activeExecution = null;
|
|
780
|
-
onStatusChanged?.({
|
|
781
|
-
sessionKey: session.sessionId,
|
|
782
|
-
status: "idle"
|
|
783
|
-
});
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
//#endregion
|
|
787
|
-
//#region src/agent/agent-backend/agent-run-executor.service.ts
|
|
788
|
-
var AgentRunExecutor = class {
|
|
789
|
-
executeRun = (session, envelope, controller) => (async function* (self) {
|
|
790
|
-
if (!isHiddenNcpMessage(envelope.message)) {
|
|
791
|
-
const messageSent = {
|
|
792
|
-
type: NcpEventType.MessageSent,
|
|
793
|
-
payload: {
|
|
794
|
-
sessionId: envelope.sessionId,
|
|
795
|
-
message: structuredClone(envelope.message),
|
|
796
|
-
...envelope.correlationId ? { correlationId: envelope.correlationId } : {},
|
|
797
|
-
metadata: envelope.metadata
|
|
798
|
-
}
|
|
799
|
-
};
|
|
800
|
-
await session.stateManager.dispatch(messageSent);
|
|
801
|
-
yield structuredClone(messageSent);
|
|
802
|
-
}
|
|
803
|
-
try {
|
|
804
|
-
for await (const event of session.runtime.run({
|
|
805
|
-
sessionId: envelope.sessionId,
|
|
806
|
-
messages: [envelope.message],
|
|
807
|
-
correlationId: envelope.correlationId,
|
|
808
|
-
metadata: envelope.metadata
|
|
809
|
-
}, { signal: controller.signal })) yield normalizeRunEvent(event, envelope);
|
|
810
|
-
} catch (error) {
|
|
811
|
-
if (!controller.signal.aborted) {
|
|
812
|
-
const runErrorEvent = await self.publishFailure(error, envelope, session);
|
|
813
|
-
yield structuredClone(runErrorEvent);
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
})(this);
|
|
817
|
-
publishFailure = async (error, envelope, session) => {
|
|
818
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
819
|
-
const runErrorEvent = {
|
|
820
|
-
type: NcpEventType.RunError,
|
|
821
|
-
payload: {
|
|
822
|
-
sessionId: envelope.sessionId,
|
|
823
|
-
...envelope.correlationId ? { correlationId: envelope.correlationId } : {},
|
|
824
|
-
error: message
|
|
825
|
-
}
|
|
826
|
-
};
|
|
827
|
-
await session.stateManager.dispatch(runErrorEvent);
|
|
828
|
-
return runErrorEvent;
|
|
829
|
-
};
|
|
830
|
-
};
|
|
831
|
-
function normalizeRunEvent(event, envelope) {
|
|
832
|
-
if (!("payload" in event) || !event.payload || typeof event.payload !== "object") return structuredClone(event);
|
|
833
|
-
return structuredClone({
|
|
834
|
-
...event,
|
|
835
|
-
payload: {
|
|
836
|
-
...event.payload,
|
|
837
|
-
sessionId: "sessionId" in event.payload && typeof event.payload.sessionId === "string" ? event.payload.sessionId : envelope.sessionId,
|
|
838
|
-
...envelope.correlationId && (!("correlationId" in event.payload) || typeof event.payload.correlationId !== "string") ? { correlationId: envelope.correlationId } : {}
|
|
839
|
-
}
|
|
840
|
-
});
|
|
841
|
-
}
|
|
842
|
-
//#endregion
|
|
843
|
-
//#region src/agent/agent-backend/async-queue.ts
|
|
844
|
-
function createAsyncQueue() {
|
|
845
|
-
const items = [];
|
|
846
|
-
let closed = false;
|
|
847
|
-
let pendingResolve = null;
|
|
848
|
-
return {
|
|
849
|
-
push(item) {
|
|
850
|
-
if (closed) return;
|
|
851
|
-
if (pendingResolve) {
|
|
852
|
-
const resolve = pendingResolve;
|
|
853
|
-
pendingResolve = null;
|
|
854
|
-
resolve({
|
|
855
|
-
value: item,
|
|
856
|
-
done: false
|
|
857
|
-
});
|
|
858
|
-
return;
|
|
859
|
-
}
|
|
860
|
-
items.push(item);
|
|
861
|
-
},
|
|
862
|
-
close() {
|
|
863
|
-
if (closed) return;
|
|
864
|
-
closed = true;
|
|
865
|
-
if (pendingResolve) {
|
|
866
|
-
const resolve = pendingResolve;
|
|
867
|
-
pendingResolve = null;
|
|
868
|
-
resolve({
|
|
869
|
-
value: void 0,
|
|
870
|
-
done: true
|
|
871
|
-
});
|
|
872
|
-
}
|
|
873
|
-
},
|
|
874
|
-
iterable: { [Symbol.asyncIterator]() {
|
|
875
|
-
return { next: () => {
|
|
876
|
-
if (items.length > 0) return Promise.resolve({
|
|
877
|
-
value: items.shift(),
|
|
878
|
-
done: false
|
|
879
|
-
});
|
|
880
|
-
if (closed) return Promise.resolve({
|
|
881
|
-
value: void 0,
|
|
882
|
-
done: true
|
|
883
|
-
});
|
|
884
|
-
return new Promise((resolve) => {
|
|
885
|
-
pendingResolve = resolve;
|
|
886
|
-
});
|
|
887
|
-
} };
|
|
888
|
-
} }
|
|
889
|
-
};
|
|
890
|
-
}
|
|
891
|
-
//#endregion
|
|
892
|
-
//#region src/agent/agent-backend/agent-backend-session-realtime.service.ts
|
|
893
|
-
function readEventSessionId(event) {
|
|
894
|
-
const payload = "payload" in event ? event.payload : null;
|
|
895
|
-
if (!payload || typeof payload !== "object") return null;
|
|
896
|
-
return "sessionId" in payload && typeof payload.sessionId === "string" ? payload.sessionId : null;
|
|
897
|
-
}
|
|
898
|
-
var AgentBackendSessionRealtime = class {
|
|
899
|
-
constructor(params) {
|
|
900
|
-
this.params = params;
|
|
901
|
-
}
|
|
902
|
-
publishSessionEvent = async (session, event, options = {}) => {
|
|
903
|
-
if (options.dispatchToStateManager) await session.stateManager.dispatch(event);
|
|
904
|
-
if (options.persistSession !== false) await this.params.persistSessionEvent(session, event);
|
|
905
|
-
this.params.publishEndpointEvent(event);
|
|
906
|
-
session.publisher.publish(event);
|
|
907
|
-
};
|
|
908
|
-
streamSessionEvents = (payloadOrParams, opts) => {
|
|
909
|
-
return (async function* (self) {
|
|
910
|
-
const payload = "payload" in payloadOrParams && "signal" in payloadOrParams ? payloadOrParams.payload : payloadOrParams;
|
|
911
|
-
const signal = "payload" in payloadOrParams && "signal" in payloadOrParams ? payloadOrParams.signal : opts?.signal ?? new AbortController().signal;
|
|
912
|
-
const queue = createAsyncQueue();
|
|
913
|
-
const unsubscribe = self.params.subscribeEndpointEvent((event) => {
|
|
914
|
-
if (readEventSessionId(event) !== payload.sessionId) return;
|
|
915
|
-
queue.push(event);
|
|
916
|
-
});
|
|
917
|
-
const stop = () => {
|
|
918
|
-
unsubscribe();
|
|
919
|
-
queue.close();
|
|
920
|
-
signal.removeEventListener("abort", stop);
|
|
921
|
-
};
|
|
922
|
-
const liveSession = self.params.sessionRegistry.getSession(payload.sessionId);
|
|
923
|
-
const unsubscribeClose = liveSession ? liveSession.publisher.onClose(() => {
|
|
924
|
-
queue.close();
|
|
925
|
-
}) : () => void 0;
|
|
926
|
-
signal.addEventListener("abort", stop, { once: true });
|
|
927
|
-
try {
|
|
928
|
-
for await (const event of queue.iterable) {
|
|
929
|
-
if (signal.aborted) break;
|
|
930
|
-
yield event;
|
|
931
|
-
}
|
|
932
|
-
} finally {
|
|
933
|
-
unsubscribeClose();
|
|
934
|
-
stop();
|
|
935
|
-
}
|
|
936
|
-
})(this);
|
|
937
|
-
};
|
|
938
|
-
appendMessage = async (sessionId, message) => {
|
|
939
|
-
const normalizedSessionId = sessionId.trim();
|
|
940
|
-
if (!normalizedSessionId) return null;
|
|
941
|
-
let liveSession = this.params.sessionRegistry.getSession(normalizedSessionId);
|
|
942
|
-
if (!liveSession) {
|
|
943
|
-
if (!await this.params.sessionStore.getSession(normalizedSessionId)) return null;
|
|
944
|
-
liveSession = await this.params.sessionRegistry.ensureSession(normalizedSessionId);
|
|
945
|
-
}
|
|
946
|
-
const nextMessage = {
|
|
947
|
-
...structuredClone(message),
|
|
948
|
-
sessionId: normalizedSessionId
|
|
949
|
-
};
|
|
950
|
-
await this.publishSessionEvent(liveSession, {
|
|
951
|
-
type: NcpEventType.MessageSent,
|
|
952
|
-
payload: {
|
|
953
|
-
sessionId: normalizedSessionId,
|
|
954
|
-
message: nextMessage
|
|
955
|
-
}
|
|
956
|
-
}, { dispatchToStateManager: true });
|
|
957
|
-
return this.params.getSessionSummary(normalizedSessionId);
|
|
958
|
-
};
|
|
959
|
-
updateToolCallResult = async (sessionId, toolCallId, content) => {
|
|
960
|
-
const normalizedSessionId = sessionId.trim();
|
|
961
|
-
const normalizedToolCallId = toolCallId.trim();
|
|
962
|
-
if (!normalizedSessionId || !normalizedToolCallId) return null;
|
|
963
|
-
const liveSession = await this.params.sessionRegistry.ensureSession(normalizedSessionId);
|
|
964
|
-
await this.publishSessionEvent(liveSession, {
|
|
965
|
-
type: NcpEventType.MessageToolCallResult,
|
|
966
|
-
payload: {
|
|
967
|
-
sessionId: normalizedSessionId,
|
|
968
|
-
toolCallId: normalizedToolCallId,
|
|
969
|
-
content
|
|
970
|
-
}
|
|
971
|
-
}, { dispatchToStateManager: true });
|
|
972
|
-
return this.params.getSessionSummary(normalizedSessionId);
|
|
973
|
-
};
|
|
974
|
-
};
|
|
975
|
-
//#endregion
|
|
976
|
-
//#region src/agent/agent-backend/agent-backend-session.utils.ts
|
|
977
|
-
const AUTO_SESSION_LABEL_MAX_LENGTH = 64;
|
|
978
|
-
function readOptionalAgentId$1(value) {
|
|
979
|
-
if (typeof value !== "string") return;
|
|
980
|
-
const trimmed = value.trim().toLowerCase();
|
|
981
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
982
|
-
}
|
|
983
|
-
function readMessages(snapshot) {
|
|
984
|
-
const messages = snapshot.messages.map((message) => structuredClone(message));
|
|
985
|
-
if (snapshot.streamingMessage && !messages.some((message) => message.id === snapshot.streamingMessage?.id)) messages.push(structuredClone(snapshot.streamingMessage));
|
|
986
|
-
return messages;
|
|
987
|
-
}
|
|
988
|
-
function readSessionActivityAt(summary) {
|
|
989
|
-
return summary.lastMessageAt ?? summary.createdAt ?? summary.updatedAt;
|
|
990
|
-
}
|
|
991
|
-
function toSessionSummary(session, liveSession) {
|
|
992
|
-
const metadata = withAutoSessionLabel({
|
|
993
|
-
metadata: session.metadata ? structuredClone({
|
|
994
|
-
...session.metadata,
|
|
995
|
-
...liveSession?.metadata ? liveSession.metadata : {}
|
|
996
|
-
}) : liveSession?.metadata ? structuredClone(liveSession.metadata) : {},
|
|
997
|
-
messages: session.messages
|
|
998
|
-
});
|
|
999
|
-
return {
|
|
1000
|
-
sessionId: session.sessionId,
|
|
1001
|
-
...readOptionalAgentId$1(session.agentId) ? { agentId: readOptionalAgentId$1(session.agentId) } : {},
|
|
1002
|
-
messageCount: session.messages.length,
|
|
1003
|
-
...session.createdAt ? { createdAt: session.createdAt } : {},
|
|
1004
|
-
updatedAt: session.updatedAt,
|
|
1005
|
-
...session.messages.length > 0 ? { lastMessageAt: session.messages[session.messages.length - 1]?.timestamp ?? session.updatedAt } : {},
|
|
1006
|
-
status: liveSession?.activeExecution ? "running" : "idle",
|
|
1007
|
-
...Object.keys(metadata).length > 0 ? { metadata } : {}
|
|
1008
|
-
};
|
|
1009
|
-
}
|
|
1010
|
-
function toLiveSessionSummary(session) {
|
|
1011
|
-
const messages = readMessages(session.stateManager.getSnapshot());
|
|
1012
|
-
const updatedAt = now();
|
|
1013
|
-
const metadata = withAutoSessionLabel({
|
|
1014
|
-
metadata: Object.keys(session.metadata).length > 0 ? structuredClone(session.metadata) : session.activeExecution?.requestEnvelope.metadata ? structuredClone(session.activeExecution.requestEnvelope.metadata) : {},
|
|
1015
|
-
messages
|
|
1016
|
-
});
|
|
1017
|
-
return {
|
|
1018
|
-
sessionId: session.sessionId,
|
|
1019
|
-
...readOptionalAgentId$1(session.agentId) ? { agentId: readOptionalAgentId$1(session.agentId) } : {},
|
|
1020
|
-
messageCount: messages.length,
|
|
1021
|
-
createdAt: session.createdAt,
|
|
1022
|
-
updatedAt,
|
|
1023
|
-
...messages.length > 0 ? { lastMessageAt: messages[messages.length - 1]?.timestamp ?? updatedAt } : {},
|
|
1024
|
-
status: session.activeExecution ? "running" : "idle",
|
|
1025
|
-
...Object.keys(metadata).length > 0 ? { metadata } : {}
|
|
1026
|
-
};
|
|
1027
|
-
}
|
|
1028
|
-
function withSessionContextWindow(summary, messages, resolver) {
|
|
1029
|
-
const contextWindow = resolver?.({
|
|
1030
|
-
messages,
|
|
1031
|
-
metadata: summary.metadata ?? {},
|
|
1032
|
-
sessionId: summary.sessionId
|
|
1033
|
-
});
|
|
1034
|
-
return contextWindow ? {
|
|
1035
|
-
...summary,
|
|
1036
|
-
contextWindow
|
|
1037
|
-
} : summary;
|
|
1038
|
-
}
|
|
1039
|
-
function toLiveSessionSummaryWithContextWindow(session, resolver) {
|
|
1040
|
-
const messages = readMessages(session.stateManager.getSnapshot());
|
|
1041
|
-
return withSessionContextWindow(toLiveSessionSummary(session), messages, resolver);
|
|
1042
|
-
}
|
|
1043
|
-
async function listBackendSessionSummaries(params) {
|
|
1044
|
-
const { liveSessions, sessionStore } = params;
|
|
1045
|
-
const summaries = sessionStore.listSessionSummaries ? await sessionStore.listSessionSummaries() : (await sessionStore.listSessions()).map((session) => toSessionSummary(session, liveSessions.find((liveSession) => liveSession.sessionId === session.sessionId) ?? null));
|
|
1046
|
-
for (const liveSession of liveSessions) {
|
|
1047
|
-
const existingIndex = summaries.findIndex((session) => session.sessionId === liveSession.sessionId);
|
|
1048
|
-
if (existingIndex >= 0) {
|
|
1049
|
-
summaries[existingIndex] = {
|
|
1050
|
-
...summaries[existingIndex],
|
|
1051
|
-
status: liveSession.activeExecution ? "running" : "idle"
|
|
1052
|
-
};
|
|
1053
|
-
continue;
|
|
1054
|
-
}
|
|
1055
|
-
summaries.push(toLiveSessionSummary(liveSession));
|
|
1056
|
-
}
|
|
1057
|
-
return summaries.sort((left, right) => readSessionActivityAt(right).localeCompare(readSessionActivityAt(left)));
|
|
1058
|
-
}
|
|
1059
|
-
async function listBackendSessionMessages(params) {
|
|
1060
|
-
const { liveSession, sessionId, sessionStore } = params;
|
|
1061
|
-
if (liveSession) return readMessages(liveSession.stateManager.getSnapshot());
|
|
1062
|
-
if (sessionStore.listSessionMessages) return sessionStore.listSessionMessages(sessionId);
|
|
1063
|
-
const session = await sessionStore.getSession(sessionId);
|
|
1064
|
-
return session ? session.messages.map((message) => structuredClone(message)) : [];
|
|
1065
|
-
}
|
|
1066
|
-
async function getBackendSessionSummary(params) {
|
|
1067
|
-
const { liveSession, resolveSessionContextWindow, sessionId, sessionStore } = params;
|
|
1068
|
-
if (!liveSession && sessionStore.getSessionSummary) return sessionStore.getSessionSummary(sessionId);
|
|
1069
|
-
const storedSession = await sessionStore.getSession(sessionId);
|
|
1070
|
-
const liveMessages = liveSession ? readMessages(liveSession.stateManager.getSnapshot()) : null;
|
|
1071
|
-
return storedSession ? withSessionContextWindow(toSessionSummary(storedSession, liveSession), liveMessages ?? storedSession.messages, resolveSessionContextWindow) : liveSession ? toLiveSessionSummaryWithContextWindow(liveSession, resolveSessionContextWindow) : null;
|
|
1072
|
-
}
|
|
1073
|
-
function now() {
|
|
1074
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1075
|
-
}
|
|
1076
|
-
function readOptionalString$1(value) {
|
|
1077
|
-
if (typeof value !== "string") return null;
|
|
1078
|
-
const trimmed = value.trim();
|
|
1079
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
1080
|
-
}
|
|
1081
|
-
function truncateLabel(value) {
|
|
1082
|
-
const characters = Array.from(value);
|
|
1083
|
-
if (characters.length <= AUTO_SESSION_LABEL_MAX_LENGTH) return value;
|
|
1084
|
-
return `${characters.slice(0, AUTO_SESSION_LABEL_MAX_LENGTH).join("")}…`;
|
|
1085
|
-
}
|
|
1086
|
-
function resolveAutoSessionLabelFromMessages(messages) {
|
|
1087
|
-
for (const message of messages) {
|
|
1088
|
-
if (message.role !== "user") continue;
|
|
1089
|
-
for (const part of message.parts) if (part.type === "text" || part.type === "rich-text") {
|
|
1090
|
-
const text = readOptionalString$1(part.text);
|
|
1091
|
-
if (text) return truncateLabel(text);
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
return null;
|
|
1095
|
-
}
|
|
1096
|
-
function withAutoSessionLabel(params) {
|
|
1097
|
-
const { metadata, messages } = params;
|
|
1098
|
-
if (readOptionalString$1(metadata.label)) return metadata;
|
|
1099
|
-
const nextLabel = resolveAutoSessionLabelFromMessages(messages);
|
|
1100
|
-
if (!nextLabel) return metadata;
|
|
1101
|
-
return {
|
|
1102
|
-
...metadata,
|
|
1103
|
-
label: nextLabel
|
|
1104
|
-
};
|
|
1105
|
-
}
|
|
1106
|
-
function findFinalAssistantMessageById(session, messageId) {
|
|
1107
|
-
const normalizedMessageId = messageId.trim();
|
|
1108
|
-
if (!normalizedMessageId) return null;
|
|
1109
|
-
const snapshot = session.stateManager.getSnapshot();
|
|
1110
|
-
for (let index = snapshot.messages.length - 1; index >= 0; index -= 1) {
|
|
1111
|
-
const message = snapshot.messages[index];
|
|
1112
|
-
if (message?.id === normalizedMessageId && message.role === "assistant" && message.status === "final") return message;
|
|
1113
|
-
}
|
|
1114
|
-
return null;
|
|
1115
|
-
}
|
|
1116
|
-
function buildCompletedEventForFinishedRun(session, event) {
|
|
1117
|
-
const messageId = event.payload.messageId?.trim();
|
|
1118
|
-
if (!messageId) throw new Error(`Run finished without messageId for session "${session.sessionId}".`);
|
|
1119
|
-
const completedMessage = findFinalAssistantMessageById(session, messageId);
|
|
1120
|
-
if (!completedMessage) throw new Error(`Run finished without a final assistant message for session "${session.sessionId}" and message "${messageId}".`);
|
|
1121
|
-
return {
|
|
1122
|
-
type: NcpEventType.MessageCompleted,
|
|
1123
|
-
payload: {
|
|
1124
|
-
sessionId: session.sessionId,
|
|
1125
|
-
message: structuredClone(completedMessage)
|
|
1126
|
-
}
|
|
1127
|
-
};
|
|
1128
|
-
}
|
|
1129
|
-
function normalizeSendRunEvent(params) {
|
|
1130
|
-
const { session, event } = params;
|
|
1131
|
-
if (event.type === NcpEventType.MessageCompleted) {
|
|
1132
|
-
if (params.completedMessageSeen) throw new Error(`Multiple final assistant messages were emitted for session "${session.sessionId}".`);
|
|
1133
|
-
return {
|
|
1134
|
-
eventsToPublish: [event],
|
|
1135
|
-
completedMessageSeen: true
|
|
1136
|
-
};
|
|
1137
|
-
}
|
|
1138
|
-
if (event.type === NcpEventType.RunFinished) {
|
|
1139
|
-
if (params.completedMessageSeen) return {
|
|
1140
|
-
eventsToPublish: [event],
|
|
1141
|
-
completedMessageSeen: true
|
|
1142
|
-
};
|
|
1143
|
-
return {
|
|
1144
|
-
eventsToPublish: [buildCompletedEventForFinishedRun(session, event), event],
|
|
1145
|
-
completedMessageSeen: true
|
|
1146
|
-
};
|
|
1147
|
-
}
|
|
1148
|
-
return {
|
|
1149
|
-
eventsToPublish: [event],
|
|
1150
|
-
completedMessageSeen: params.completedMessageSeen
|
|
1151
|
-
};
|
|
1152
|
-
}
|
|
1153
|
-
//#endregion
|
|
1154
|
-
//#region src/agent/agent-backend/agent-backend-session-persistence.utils.ts
|
|
1155
|
-
function readOptionalAgentId(value) {
|
|
1156
|
-
if (typeof value !== "string") return;
|
|
1157
|
-
const trimmed = value.trim().toLowerCase();
|
|
1158
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
1159
|
-
}
|
|
1160
|
-
function readAgentIdFromMetadata(metadata) {
|
|
1161
|
-
return readOptionalAgentId(metadata?.agent_id) ?? readOptionalAgentId(metadata?.agentId);
|
|
1162
|
-
}
|
|
1163
|
-
function resolvePersistedAgentId(params) {
|
|
1164
|
-
const { liveSession, storedSession } = params;
|
|
1165
|
-
return readOptionalAgentId(liveSession?.agentId) ?? readOptionalAgentId(storedSession?.agentId) ?? readAgentIdFromMetadata(liveSession?.metadata) ?? readAgentIdFromMetadata(storedSession?.metadata);
|
|
1166
|
-
}
|
|
1167
|
-
function buildUpdatedSessionRecord(params) {
|
|
1168
|
-
const { liveSession, patch, sessionId, storedSession, updatedAt } = params;
|
|
1169
|
-
const nextMetadata = patch.metadata === null ? {} : patch.metadata ? structuredClone(patch.metadata) : structuredClone(liveSession?.metadata ?? storedSession?.metadata ?? {});
|
|
1170
|
-
if (liveSession) liveSession.metadata = structuredClone(nextMetadata);
|
|
1171
|
-
const agentId = resolvePersistedAgentId({
|
|
1172
|
-
liveSession,
|
|
1173
|
-
storedSession
|
|
1174
|
-
});
|
|
1175
|
-
return {
|
|
1176
|
-
sessionId,
|
|
1177
|
-
...agentId ? { agentId } : {},
|
|
1178
|
-
messages: liveSession ? readMessages(liveSession.stateManager.getSnapshot()) : storedSession?.messages.map((message) => structuredClone(message)) ?? [],
|
|
1179
|
-
createdAt: storedSession?.createdAt ?? liveSession?.createdAt ?? updatedAt,
|
|
1180
|
-
updatedAt,
|
|
1181
|
-
metadata: nextMetadata
|
|
1182
|
-
};
|
|
1183
|
-
}
|
|
1184
|
-
function buildPersistedLiveSessionRecord(params) {
|
|
1185
|
-
const { session, sessionId, updatedAt } = params;
|
|
1186
|
-
const messages = readMessages(session.stateManager.getSnapshot());
|
|
1187
|
-
const metadata = withAutoSessionLabel({
|
|
1188
|
-
metadata: {
|
|
1189
|
-
...session.metadata ? structuredClone(session.metadata) : {},
|
|
1190
|
-
...session.activeExecution?.requestEnvelope.metadata ? structuredClone(session.activeExecution.requestEnvelope.metadata) : {}
|
|
1191
|
-
},
|
|
1192
|
-
messages
|
|
1193
|
-
});
|
|
1194
|
-
const agentId = readOptionalAgentId(session.agentId) ?? readAgentIdFromMetadata(session.metadata) ?? readAgentIdFromMetadata(session.activeExecution?.requestEnvelope.metadata);
|
|
1195
|
-
return {
|
|
1196
|
-
sessionId,
|
|
1197
|
-
...agentId ? { agentId } : {},
|
|
1198
|
-
messages,
|
|
1199
|
-
createdAt: session.createdAt,
|
|
1200
|
-
updatedAt,
|
|
1201
|
-
metadata
|
|
1202
|
-
};
|
|
1203
|
-
}
|
|
1204
|
-
function buildLiveSessionEventRecord(params) {
|
|
1205
|
-
const { session, updatedAt } = params;
|
|
1206
|
-
const metadata = {
|
|
1207
|
-
...session.metadata ? structuredClone(session.metadata) : {},
|
|
1208
|
-
...session.activeExecution?.requestEnvelope.metadata ? structuredClone(session.activeExecution.requestEnvelope.metadata) : {}
|
|
1209
|
-
};
|
|
1210
|
-
const agentId = readOptionalAgentId(session.agentId) ?? readAgentIdFromMetadata(session.metadata) ?? readAgentIdFromMetadata(session.activeExecution?.requestEnvelope.metadata);
|
|
1211
|
-
return {
|
|
1212
|
-
sessionId: session.sessionId,
|
|
1213
|
-
...agentId ? { agentId } : {},
|
|
1214
|
-
createdAt: session.createdAt,
|
|
1215
|
-
updatedAt,
|
|
1216
|
-
metadata
|
|
1217
|
-
};
|
|
1218
|
-
}
|
|
1219
|
-
function shouldPersistRunEndSnapshot(sessionStore) {
|
|
1220
|
-
return !sessionStore.appendSessionEvent;
|
|
1221
|
-
}
|
|
1222
|
-
async function persistLiveSession(params) {
|
|
1223
|
-
const { session, sessionId, sessionStore, updatedAt } = params;
|
|
1224
|
-
if (!session) return;
|
|
1225
|
-
await sessionStore.saveSession(buildPersistedLiveSessionRecord({
|
|
1226
|
-
sessionId,
|
|
1227
|
-
session,
|
|
1228
|
-
updatedAt
|
|
1229
|
-
}));
|
|
1230
|
-
}
|
|
1231
|
-
async function persistLiveSessionEvent(params) {
|
|
1232
|
-
const { event, session, sessionStore, updatedAt } = params;
|
|
1233
|
-
if (sessionStore.appendSessionEvent) {
|
|
1234
|
-
await sessionStore.appendSessionEvent({
|
|
1235
|
-
session: buildLiveSessionEventRecord({
|
|
1236
|
-
session,
|
|
1237
|
-
updatedAt
|
|
1238
|
-
}),
|
|
1239
|
-
event,
|
|
1240
|
-
updatedAt
|
|
1241
|
-
});
|
|
1242
|
-
return;
|
|
1243
|
-
}
|
|
1244
|
-
await sessionStore.saveSession(buildPersistedLiveSessionRecord({
|
|
1245
|
-
sessionId: session.sessionId,
|
|
1246
|
-
session,
|
|
1247
|
-
updatedAt
|
|
1248
|
-
}));
|
|
1249
|
-
}
|
|
1250
|
-
//#endregion
|
|
1251
|
-
//#region src/agent/agent-backend/agent-backend.service.ts
|
|
1252
|
-
const DEFAULT_SUPPORTED_PART_TYPES = [
|
|
1253
|
-
"text",
|
|
1254
|
-
"file",
|
|
1255
|
-
"source",
|
|
1256
|
-
"step-start",
|
|
1257
|
-
"reasoning",
|
|
1258
|
-
"tool-invocation",
|
|
1259
|
-
"card",
|
|
1260
|
-
"rich-text",
|
|
1261
|
-
"action",
|
|
1262
|
-
"extension"
|
|
1263
|
-
];
|
|
1264
|
-
const disposeRuntime = async (runtime) => {
|
|
1265
|
-
await runtime.dispose?.();
|
|
1266
|
-
};
|
|
1267
|
-
var DefaultNcpAgentBackend = class {
|
|
1268
|
-
manifest;
|
|
1269
|
-
sessionStore;
|
|
1270
|
-
onSessionRunStatusChanged;
|
|
1271
|
-
sessionRegistry;
|
|
1272
|
-
executor;
|
|
1273
|
-
publisher;
|
|
1274
|
-
sessionRealtime;
|
|
1275
|
-
resolveSessionContextWindow;
|
|
1276
|
-
started = false;
|
|
1277
|
-
constructor(config) {
|
|
1278
|
-
this.sessionStore = config.sessionStore;
|
|
1279
|
-
this.resolveSessionContextWindow = config.resolveSessionContextWindow;
|
|
1280
|
-
this.onSessionRunStatusChanged = config.onSessionRunStatusChanged;
|
|
1281
|
-
this.sessionRegistry = new AgentLiveSessionRegistry(this.sessionStore, config.createRuntime);
|
|
1282
|
-
this.executor = new AgentRunExecutor();
|
|
1283
|
-
this.publisher = new EventPublisher();
|
|
1284
|
-
this.sessionRealtime = new AgentBackendSessionRealtime({
|
|
1285
|
-
sessionRegistry: this.sessionRegistry,
|
|
1286
|
-
sessionStore: this.sessionStore,
|
|
1287
|
-
publishEndpointEvent: (event) => this.publisher.publish(event),
|
|
1288
|
-
subscribeEndpointEvent: (listener) => this.publisher.subscribe(listener),
|
|
1289
|
-
persistSession: (sessionId) => persistLiveSession({
|
|
1290
|
-
sessionStore: this.sessionStore,
|
|
1291
|
-
session: this.sessionRegistry.getSession(sessionId),
|
|
1292
|
-
sessionId,
|
|
1293
|
-
updatedAt: now()
|
|
1294
|
-
}),
|
|
1295
|
-
persistSessionEvent: (session, event) => persistLiveSessionEvent({
|
|
1296
|
-
sessionStore: this.sessionStore,
|
|
1297
|
-
session,
|
|
1298
|
-
event,
|
|
1299
|
-
updatedAt: now()
|
|
1300
|
-
}),
|
|
1301
|
-
getSessionSummary: (sessionId) => this.getSession(sessionId)
|
|
1302
|
-
});
|
|
1303
|
-
this.manifest = {
|
|
1304
|
-
endpointKind: "agent",
|
|
1305
|
-
endpointId: config.endpointId?.trim() || "ncp-agent-backend",
|
|
1306
|
-
version: config.version?.trim() || "0.1.0",
|
|
1307
|
-
supportsStreaming: true,
|
|
1308
|
-
supportsAbort: true,
|
|
1309
|
-
supportsProactiveMessages: false,
|
|
1310
|
-
supportsLiveSessionStream: true,
|
|
1311
|
-
supportedPartTypes: config.supportedPartTypes ?? DEFAULT_SUPPORTED_PART_TYPES,
|
|
1312
|
-
expectedLatency: config.expectedLatency ?? "seconds",
|
|
1313
|
-
metadata: config.metadata
|
|
1314
|
-
};
|
|
1315
|
-
}
|
|
1316
|
-
start = async () => {
|
|
1317
|
-
if (this.started) return;
|
|
1318
|
-
this.started = true;
|
|
1319
|
-
this.publisher.publish({ type: NcpEventType.EndpointReady });
|
|
1320
|
-
};
|
|
1321
|
-
stop = async () => {
|
|
1322
|
-
if (!this.started) return;
|
|
1323
|
-
this.started = false;
|
|
1324
|
-
for (const session of this.sessionRegistry.listSessions()) {
|
|
1325
|
-
const execution = session.activeExecution;
|
|
1326
|
-
if (!execution) {
|
|
1327
|
-
session.publisher.close();
|
|
1328
|
-
await disposeRuntime(session.runtime);
|
|
1329
|
-
continue;
|
|
1330
|
-
}
|
|
1331
|
-
execution.abortHandled = true;
|
|
1332
|
-
execution.controller.abort();
|
|
1333
|
-
this.finishSessionExecution(session, execution);
|
|
1334
|
-
session.publisher.close();
|
|
1335
|
-
await disposeRuntime(session.runtime);
|
|
1336
|
-
}
|
|
1337
|
-
this.sessionRegistry.clear();
|
|
1338
|
-
};
|
|
1339
|
-
emit = async (event) => {
|
|
1340
|
-
await this.ensureStarted();
|
|
1341
|
-
switch (event.type) {
|
|
1342
|
-
case NcpEventType.MessageRequest:
|
|
1343
|
-
if (!event.payload.sessionId) throw new Error("MessageRequest requires a sessionId before it reaches the agent backend.");
|
|
1344
|
-
for await (const emittedEvent of this.send({
|
|
1345
|
-
...event.payload,
|
|
1346
|
-
sessionId: event.payload.sessionId,
|
|
1347
|
-
message: {
|
|
1348
|
-
...event.payload.message,
|
|
1349
|
-
sessionId: event.payload.sessionId
|
|
1350
|
-
}
|
|
1351
|
-
}));
|
|
1352
|
-
return;
|
|
1353
|
-
case NcpEventType.MessageStreamRequest:
|
|
1354
|
-
await this.ensureStarted();
|
|
1355
|
-
return;
|
|
1356
|
-
case NcpEventType.MessageAbort:
|
|
1357
|
-
await this.handleAbort(event.payload);
|
|
1358
|
-
return;
|
|
1359
|
-
default: this.publisher.publish(event);
|
|
1360
|
-
}
|
|
1361
|
-
};
|
|
1362
|
-
subscribe = (listener) => this.publisher.subscribe(listener);
|
|
1363
|
-
send = (envelope, options) => {
|
|
1364
|
-
return (async function* (self) {
|
|
1365
|
-
await self.ensureStarted();
|
|
1366
|
-
const session = await self.sessionRegistry.ensureSession(envelope.sessionId, envelope.metadata);
|
|
1367
|
-
const execution = self.startSessionExecution(session, envelope, options?.signal);
|
|
1368
|
-
let completedMessageSeen = false;
|
|
1369
|
-
try {
|
|
1370
|
-
for await (const event of self.executor.executeRun(session, envelope, execution.controller)) {
|
|
1371
|
-
const normalized = normalizeSendRunEvent({
|
|
1372
|
-
session,
|
|
1373
|
-
event,
|
|
1374
|
-
completedMessageSeen
|
|
1375
|
-
});
|
|
1376
|
-
completedMessageSeen = normalized.completedMessageSeen;
|
|
1377
|
-
for (const normalizedEvent of normalized.eventsToPublish) {
|
|
1378
|
-
await self.sessionRealtime.publishSessionEvent(session, normalizedEvent);
|
|
1379
|
-
yield normalizedEvent;
|
|
1380
|
-
}
|
|
1381
|
-
}
|
|
1382
|
-
if (execution.controller.signal.aborted && !execution.abortHandled) {
|
|
1383
|
-
const abortEvent = {
|
|
1384
|
-
type: NcpEventType.MessageAbort,
|
|
1385
|
-
payload: { sessionId: session.sessionId }
|
|
1386
|
-
};
|
|
1387
|
-
execution.abortHandled = true;
|
|
1388
|
-
await self.sessionRealtime.publishSessionEvent(session, abortEvent, { dispatchToStateManager: true });
|
|
1389
|
-
yield abortEvent;
|
|
1390
|
-
}
|
|
1391
|
-
} finally {
|
|
1392
|
-
self.finishSessionExecution(session, execution);
|
|
1393
|
-
if (shouldPersistRunEndSnapshot(self.sessionStore)) await persistLiveSession({
|
|
1394
|
-
sessionStore: self.sessionStore,
|
|
1395
|
-
session,
|
|
1396
|
-
sessionId: session.sessionId,
|
|
1397
|
-
updatedAt: now()
|
|
1398
|
-
});
|
|
1399
|
-
}
|
|
1400
|
-
})(this);
|
|
1401
|
-
};
|
|
1402
|
-
abort = async (payload) => this.handleAbort(payload);
|
|
1403
|
-
stream = (payloadOrParams, opts) => this.sessionRealtime.streamSessionEvents(payloadOrParams, opts);
|
|
1404
|
-
listSessions = async () => {
|
|
1405
|
-
return listBackendSessionSummaries({
|
|
1406
|
-
sessionStore: this.sessionStore,
|
|
1407
|
-
liveSessions: this.sessionRegistry.listSessions()
|
|
1408
|
-
});
|
|
1409
|
-
};
|
|
1410
|
-
isLiveSessionRunning = (sessionId) => Boolean(this.sessionRegistry.getSession(sessionId)?.activeExecution);
|
|
1411
|
-
listSessionMessages = async (sessionId) => {
|
|
1412
|
-
return listBackendSessionMessages({
|
|
1413
|
-
sessionStore: this.sessionStore,
|
|
1414
|
-
liveSession: this.sessionRegistry.getSession(sessionId),
|
|
1415
|
-
sessionId
|
|
1416
|
-
});
|
|
1417
|
-
};
|
|
1418
|
-
getSession = async (sessionId) => {
|
|
1419
|
-
return getBackendSessionSummary({
|
|
1420
|
-
sessionStore: this.sessionStore,
|
|
1421
|
-
liveSession: this.sessionRegistry.getSession(sessionId),
|
|
1422
|
-
sessionId,
|
|
1423
|
-
resolveSessionContextWindow: this.resolveSessionContextWindow
|
|
1424
|
-
});
|
|
1425
|
-
};
|
|
1426
|
-
appendMessage = async (sessionId, message) => {
|
|
1427
|
-
await this.ensureStarted();
|
|
1428
|
-
return this.sessionRealtime.appendMessage(sessionId, message);
|
|
1429
|
-
};
|
|
1430
|
-
updateToolCallResult = async (sessionId, toolCallId, content) => {
|
|
1431
|
-
await this.ensureStarted();
|
|
1432
|
-
return this.sessionRealtime.updateToolCallResult(sessionId, toolCallId, content);
|
|
1433
|
-
};
|
|
1434
|
-
updateSession = async (sessionId, patch) => {
|
|
1435
|
-
const liveSession = this.sessionRegistry.getSession(sessionId);
|
|
1436
|
-
const storedSession = await this.sessionStore.getSession(sessionId);
|
|
1437
|
-
if (!liveSession && !storedSession) return null;
|
|
1438
|
-
await this.sessionStore.replaceSession(buildUpdatedSessionRecord({
|
|
1439
|
-
sessionId,
|
|
1440
|
-
patch,
|
|
1441
|
-
liveSession,
|
|
1442
|
-
storedSession,
|
|
1443
|
-
updatedAt: now()
|
|
1444
|
-
}));
|
|
1445
|
-
return this.getSession(sessionId);
|
|
1446
|
-
};
|
|
1447
|
-
deleteSession = async (sessionId) => {
|
|
1448
|
-
const liveSession = this.sessionRegistry.deleteSession(sessionId);
|
|
1449
|
-
const execution = liveSession?.activeExecution;
|
|
1450
|
-
if (execution) {
|
|
1451
|
-
execution.abortHandled = true;
|
|
1452
|
-
execution.controller.abort();
|
|
1453
|
-
execution.closed = true;
|
|
1454
|
-
}
|
|
1455
|
-
liveSession?.publisher.close();
|
|
1456
|
-
if (liveSession) await disposeRuntime(liveSession.runtime);
|
|
1457
|
-
await this.sessionStore.deleteSession(sessionId);
|
|
1458
|
-
};
|
|
1459
|
-
ensureStarted = async () => {
|
|
1460
|
-
if (!this.started) await this.start();
|
|
1461
|
-
};
|
|
1462
|
-
startSessionExecution = (session, envelope, signal) => startAgentBackendSessionExecution({
|
|
1463
|
-
session,
|
|
1464
|
-
envelope,
|
|
1465
|
-
signal,
|
|
1466
|
-
onStatusChanged: this.onSessionRunStatusChanged
|
|
1467
|
-
});
|
|
1468
|
-
finishSessionExecution = (session, execution) => finishAgentBackendSessionExecution({
|
|
1469
|
-
session,
|
|
1470
|
-
execution,
|
|
1471
|
-
onStatusChanged: this.onSessionRunStatusChanged
|
|
1472
|
-
});
|
|
1473
|
-
handleAbort = async (payload) => {
|
|
1474
|
-
const session = this.sessionRegistry.getSession(payload.sessionId);
|
|
1475
|
-
const execution = session?.activeExecution;
|
|
1476
|
-
if (!session || !execution || execution.closed) return;
|
|
1477
|
-
execution.abortHandled = true;
|
|
1478
|
-
execution.controller.abort();
|
|
1479
|
-
const abortEvent = {
|
|
1480
|
-
type: NcpEventType.MessageAbort,
|
|
1481
|
-
payload: {
|
|
1482
|
-
sessionId: payload.sessionId,
|
|
1483
|
-
...payload.messageId ? { messageId: payload.messageId } : {}
|
|
1484
|
-
}
|
|
1485
|
-
};
|
|
1486
|
-
await this.sessionRealtime.publishSessionEvent(session, abortEvent, { dispatchToStateManager: true });
|
|
1487
|
-
this.finishSessionExecution(session, execution);
|
|
1488
|
-
};
|
|
1489
|
-
};
|
|
1490
|
-
//#endregion
|
|
1491
|
-
//#region src/agent/agent-backend/in-memory-agent-session.store.ts
|
|
1492
|
-
var InMemoryAgentSessionStore = class {
|
|
1493
|
-
sessions = /* @__PURE__ */ new Map();
|
|
1494
|
-
getSession = async (sessionId) => {
|
|
1495
|
-
const session = this.sessions.get(sessionId);
|
|
1496
|
-
return session ? structuredClone(session) : null;
|
|
1497
|
-
};
|
|
1498
|
-
listSessions = async () => {
|
|
1499
|
-
return [...this.sessions.values()].map((session) => structuredClone(session));
|
|
1500
|
-
};
|
|
1501
|
-
saveSession = async (session) => {
|
|
1502
|
-
this.sessions.set(session.sessionId, structuredClone(session));
|
|
1503
|
-
};
|
|
1504
|
-
replaceSession = async (session) => {
|
|
1505
|
-
this.sessions.set(session.sessionId, structuredClone(session));
|
|
1506
|
-
};
|
|
1507
|
-
deleteSession = async (sessionId) => {
|
|
1508
|
-
const session = this.sessions.get(sessionId);
|
|
1509
|
-
if (!session) return null;
|
|
1510
|
-
this.sessions.delete(sessionId);
|
|
1511
|
-
return structuredClone(session);
|
|
1512
|
-
};
|
|
1513
|
-
};
|
|
1514
|
-
//#endregion
|
|
1515
712
|
//#region src/chat/ncp-reply-consumer.ts
|
|
1516
713
|
function isTextLikePart(part) {
|
|
1517
714
|
return part.type === "text" || part.type === "rich-text";
|
|
@@ -1748,4 +945,4 @@ var NcpReplyConsumer = class {
|
|
|
1748
945
|
};
|
|
1749
946
|
};
|
|
1750
947
|
//#endregion
|
|
1751
|
-
export {
|
|
948
|
+
export { DefaultNcpAgentConversationStateManager, EventPublisher, InMemoryAgentSessionStore, NcpErrorException, NcpReplyConsumer, createAgentClientFromServer };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/ncp-toolkit",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.18",
|
|
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.13"
|
|
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.23"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "tsdown src/index.ts --dts --clean --target es2022 --no-fixedExtension",
|