@nextclaw/ncp-toolkit 0.5.17 → 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 +2 -67
- package/dist/index.js +17 -818
- 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 {
|
|
@@ -134,71 +134,6 @@ interface AgentSessionStore {
|
|
|
134
134
|
deleteSession(sessionId: string): Promise<AgentSessionRecord | null>;
|
|
135
135
|
}
|
|
136
136
|
//#endregion
|
|
137
|
-
//#region src/agent/agent-backend/agent-backend-session.utils.d.ts
|
|
138
|
-
type SessionContextWindowResolver = (params: {
|
|
139
|
-
messages: readonly NcpMessage[];
|
|
140
|
-
metadata: Record<string, unknown>;
|
|
141
|
-
sessionId: string;
|
|
142
|
-
}) => Record<string, unknown> | null;
|
|
143
|
-
//#endregion
|
|
144
|
-
//#region src/agent/agent-backend/agent-backend.service.d.ts
|
|
145
|
-
type DefaultNcpAgentBackendConfig = {
|
|
146
|
-
createRuntime: CreateRuntimeFn;
|
|
147
|
-
sessionStore: AgentSessionStore;
|
|
148
|
-
resolveSessionContextWindow?: SessionContextWindowResolver;
|
|
149
|
-
onSessionRunStatusChanged?: (payload: {
|
|
150
|
-
sessionKey: string;
|
|
151
|
-
status: "running" | "idle";
|
|
152
|
-
}) => void;
|
|
153
|
-
endpointId?: string;
|
|
154
|
-
version?: string;
|
|
155
|
-
metadata?: Record<string, unknown>;
|
|
156
|
-
supportedPartTypes?: NcpEndpointManifest["supportedPartTypes"];
|
|
157
|
-
expectedLatency?: NcpEndpointManifest["expectedLatency"];
|
|
158
|
-
};
|
|
159
|
-
declare class DefaultNcpAgentBackend implements NcpAgentServerEndpoint, NcpSessionApi, NcpAgentStreamProvider, NcpAgentRunApi {
|
|
160
|
-
readonly manifest: NcpEndpointManifest & {
|
|
161
|
-
endpointKind: "agent";
|
|
162
|
-
};
|
|
163
|
-
private readonly sessionStore;
|
|
164
|
-
private readonly onSessionRunStatusChanged;
|
|
165
|
-
private readonly sessionRegistry;
|
|
166
|
-
private readonly executor;
|
|
167
|
-
private readonly publisher;
|
|
168
|
-
private readonly sessionRealtime;
|
|
169
|
-
private readonly resolveSessionContextWindow;
|
|
170
|
-
private started;
|
|
171
|
-
constructor(config: DefaultNcpAgentBackendConfig);
|
|
172
|
-
start: () => Promise<void>;
|
|
173
|
-
stop: () => Promise<void>;
|
|
174
|
-
emit: (event: NcpEndpointEvent) => Promise<void>;
|
|
175
|
-
subscribe: (listener: (event: NcpEndpointEvent) => void) => (() => void);
|
|
176
|
-
send: (envelope: NcpRequestEnvelope, options?: NcpAgentRunSendOptions) => AsyncIterable<NcpEndpointEvent>;
|
|
177
|
-
abort: (payload: NcpMessageAbortPayload) => Promise<void>;
|
|
178
|
-
stream: (payloadOrParams: NcpStreamRequestPayload | {
|
|
179
|
-
payload: NcpStreamRequestPayload;
|
|
180
|
-
signal: AbortSignal;
|
|
181
|
-
}, opts?: NcpAgentRunStreamOptions) => AsyncIterable<NcpEndpointEvent>;
|
|
182
|
-
listSessions: () => Promise<NcpSessionSummary[]>;
|
|
183
|
-
isLiveSessionRunning: (sessionId: string) => boolean;
|
|
184
|
-
listSessionMessages: (sessionId: string) => Promise<NcpMessage[]>;
|
|
185
|
-
getSession: (sessionId: string) => Promise<NcpSessionSummary | null>;
|
|
186
|
-
appendMessage: (sessionId: string, message: NcpMessage) => Promise<NcpSessionSummary | null>;
|
|
187
|
-
updateToolCallResult: (sessionId: string, toolCallId: string, content: unknown) => Promise<NcpSessionSummary | null>;
|
|
188
|
-
updateSession: (sessionId: string, patch: NcpSessionPatch) => Promise<NcpSessionSummary | null>;
|
|
189
|
-
deleteSession: (sessionId: string) => Promise<void>;
|
|
190
|
-
private ensureStarted;
|
|
191
|
-
private startSessionExecution;
|
|
192
|
-
private finishSessionExecution;
|
|
193
|
-
private handleAbort;
|
|
194
|
-
}
|
|
195
|
-
//#endregion
|
|
196
|
-
//#region src/agent/agent-backend/agent-run-executor.service.d.ts
|
|
197
|
-
declare class AgentRunExecutor {
|
|
198
|
-
executeRun: (session: LiveSessionState, envelope: NcpRequestEnvelope, controller: AbortController) => AsyncGenerator<NcpEndpointEvent>;
|
|
199
|
-
private publishFailure;
|
|
200
|
-
}
|
|
201
|
-
//#endregion
|
|
202
137
|
//#region src/agent/agent-backend/in-memory-agent-session.store.d.ts
|
|
203
138
|
declare class InMemoryAgentSessionStore implements AgentSessionStore {
|
|
204
139
|
private readonly sessions;
|
|
@@ -249,4 +184,4 @@ declare class NcpReplyConsumer {
|
|
|
249
184
|
consume: (input: NcpReplyInput) => Promise<void>;
|
|
250
185
|
}
|
|
251
186
|
//#endregion
|
|
252
|
-
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,822 +662,6 @@ var EventPublisher = class {
|
|
|
662
662
|
}
|
|
663
663
|
};
|
|
664
664
|
//#endregion
|
|
665
|
-
//#region src/agent/agent-backend/agent-live-session-registry.service.ts
|
|
666
|
-
function readOptionalAgentId$2(value) {
|
|
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 {
|
|
675
|
-
sessions = /* @__PURE__ */ new Map();
|
|
676
|
-
constructor(sessionStore, createRuntime) {
|
|
677
|
-
this.sessionStore = sessionStore;
|
|
678
|
-
this.createRuntime = createRuntime;
|
|
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;
|
|
722
|
-
};
|
|
723
|
-
getSession = (sessionId) => {
|
|
724
|
-
return this.sessions.get(sessionId) ?? null;
|
|
725
|
-
};
|
|
726
|
-
deleteSession = (sessionId) => {
|
|
727
|
-
const session = this.sessions.get(sessionId) ?? null;
|
|
728
|
-
if (session) this.sessions.delete(sessionId);
|
|
729
|
-
return session;
|
|
730
|
-
};
|
|
731
|
-
clear = () => {
|
|
732
|
-
this.sessions.clear();
|
|
733
|
-
};
|
|
734
|
-
listSessions = () => {
|
|
735
|
-
return [...this.sessions.values()];
|
|
736
|
-
};
|
|
737
|
-
};
|
|
738
|
-
function cloneMessages(messages) {
|
|
739
|
-
return messages.map((message) => structuredClone(message));
|
|
740
|
-
}
|
|
741
|
-
//#endregion
|
|
742
|
-
//#region src/errors/ncp-error-exception.ts
|
|
743
|
-
/**
|
|
744
|
-
* Throwable form of protocol error for exception-based control flows.
|
|
745
|
-
*/
|
|
746
|
-
var NcpErrorException = class extends Error {
|
|
747
|
-
code;
|
|
748
|
-
details;
|
|
749
|
-
constructor(code, message, details) {
|
|
750
|
-
super(message);
|
|
751
|
-
this.name = "NcpErrorException";
|
|
752
|
-
this.code = code;
|
|
753
|
-
this.details = details;
|
|
754
|
-
}
|
|
755
|
-
};
|
|
756
|
-
//#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 buildPersistedLiveSessionRecord(params) {
|
|
1164
|
-
const { session, sessionId, updatedAt } = params;
|
|
1165
|
-
const messages = readMessages(session.stateManager.getSnapshot());
|
|
1166
|
-
const metadata = withAutoSessionLabel({
|
|
1167
|
-
metadata: {
|
|
1168
|
-
...session.metadata ? structuredClone(session.metadata) : {},
|
|
1169
|
-
...session.activeExecution?.requestEnvelope.metadata ? structuredClone(session.activeExecution.requestEnvelope.metadata) : {}
|
|
1170
|
-
},
|
|
1171
|
-
messages
|
|
1172
|
-
});
|
|
1173
|
-
const agentId = readOptionalAgentId(session.agentId) ?? readAgentIdFromMetadata(session.metadata) ?? readAgentIdFromMetadata(session.activeExecution?.requestEnvelope.metadata);
|
|
1174
|
-
return {
|
|
1175
|
-
sessionId,
|
|
1176
|
-
...agentId ? { agentId } : {},
|
|
1177
|
-
messages,
|
|
1178
|
-
createdAt: session.createdAt,
|
|
1179
|
-
updatedAt,
|
|
1180
|
-
metadata
|
|
1181
|
-
};
|
|
1182
|
-
}
|
|
1183
|
-
function buildLiveSessionEventRecord(params) {
|
|
1184
|
-
const { session, updatedAt } = params;
|
|
1185
|
-
const metadata = {
|
|
1186
|
-
...session.metadata ? structuredClone(session.metadata) : {},
|
|
1187
|
-
...session.activeExecution?.requestEnvelope.metadata ? structuredClone(session.activeExecution.requestEnvelope.metadata) : {}
|
|
1188
|
-
};
|
|
1189
|
-
const agentId = readOptionalAgentId(session.agentId) ?? readAgentIdFromMetadata(session.metadata) ?? readAgentIdFromMetadata(session.activeExecution?.requestEnvelope.metadata);
|
|
1190
|
-
return {
|
|
1191
|
-
sessionId: session.sessionId,
|
|
1192
|
-
...agentId ? { agentId } : {},
|
|
1193
|
-
createdAt: session.createdAt,
|
|
1194
|
-
updatedAt,
|
|
1195
|
-
metadata
|
|
1196
|
-
};
|
|
1197
|
-
}
|
|
1198
|
-
function shouldPersistRunEndSnapshot(sessionStore) {
|
|
1199
|
-
return !sessionStore.appendSessionEvent;
|
|
1200
|
-
}
|
|
1201
|
-
async function persistLiveSession(params) {
|
|
1202
|
-
const { session, sessionId, sessionStore, updatedAt } = params;
|
|
1203
|
-
if (!session) return;
|
|
1204
|
-
await sessionStore.saveSession(buildPersistedLiveSessionRecord({
|
|
1205
|
-
sessionId,
|
|
1206
|
-
session,
|
|
1207
|
-
updatedAt
|
|
1208
|
-
}));
|
|
1209
|
-
}
|
|
1210
|
-
async function persistLiveSessionEvent(params) {
|
|
1211
|
-
const { event, session, sessionStore, updatedAt } = params;
|
|
1212
|
-
if (sessionStore.appendSessionEvent) {
|
|
1213
|
-
await sessionStore.appendSessionEvent({
|
|
1214
|
-
session: buildLiveSessionEventRecord({
|
|
1215
|
-
session,
|
|
1216
|
-
updatedAt
|
|
1217
|
-
}),
|
|
1218
|
-
event,
|
|
1219
|
-
updatedAt
|
|
1220
|
-
});
|
|
1221
|
-
return;
|
|
1222
|
-
}
|
|
1223
|
-
await sessionStore.saveSession(buildPersistedLiveSessionRecord({
|
|
1224
|
-
sessionId: session.sessionId,
|
|
1225
|
-
session,
|
|
1226
|
-
updatedAt
|
|
1227
|
-
}));
|
|
1228
|
-
}
|
|
1229
|
-
//#endregion
|
|
1230
|
-
//#region src/agent/agent-backend/agent-backend.service.ts
|
|
1231
|
-
const DEFAULT_SUPPORTED_PART_TYPES = [
|
|
1232
|
-
"text",
|
|
1233
|
-
"file",
|
|
1234
|
-
"source",
|
|
1235
|
-
"step-start",
|
|
1236
|
-
"reasoning",
|
|
1237
|
-
"tool-invocation",
|
|
1238
|
-
"card",
|
|
1239
|
-
"rich-text",
|
|
1240
|
-
"action",
|
|
1241
|
-
"extension"
|
|
1242
|
-
];
|
|
1243
|
-
const disposeRuntime = async (runtime) => {
|
|
1244
|
-
await runtime.dispose?.();
|
|
1245
|
-
};
|
|
1246
|
-
function buildUpdatedMetadata(params) {
|
|
1247
|
-
const { liveSession, patch, storedSession } = params;
|
|
1248
|
-
if (patch.metadata === null) return {};
|
|
1249
|
-
if (patch.metadata) return structuredClone(patch.metadata);
|
|
1250
|
-
return structuredClone(liveSession?.metadata ?? storedSession?.metadata ?? {});
|
|
1251
|
-
}
|
|
1252
|
-
var DefaultNcpAgentBackend = class {
|
|
1253
|
-
manifest;
|
|
1254
|
-
sessionStore;
|
|
1255
|
-
onSessionRunStatusChanged;
|
|
1256
|
-
sessionRegistry;
|
|
1257
|
-
executor;
|
|
1258
|
-
publisher;
|
|
1259
|
-
sessionRealtime;
|
|
1260
|
-
resolveSessionContextWindow;
|
|
1261
|
-
started = false;
|
|
1262
|
-
constructor(config) {
|
|
1263
|
-
this.sessionStore = config.sessionStore;
|
|
1264
|
-
this.resolveSessionContextWindow = config.resolveSessionContextWindow;
|
|
1265
|
-
this.onSessionRunStatusChanged = config.onSessionRunStatusChanged;
|
|
1266
|
-
this.sessionRegistry = new AgentLiveSessionRegistry(this.sessionStore, config.createRuntime);
|
|
1267
|
-
this.executor = new AgentRunExecutor();
|
|
1268
|
-
this.publisher = new EventPublisher();
|
|
1269
|
-
this.sessionRealtime = new AgentBackendSessionRealtime({
|
|
1270
|
-
sessionRegistry: this.sessionRegistry,
|
|
1271
|
-
sessionStore: this.sessionStore,
|
|
1272
|
-
publishEndpointEvent: (event) => this.publisher.publish(event),
|
|
1273
|
-
subscribeEndpointEvent: (listener) => this.publisher.subscribe(listener),
|
|
1274
|
-
persistSession: (sessionId) => persistLiveSession({
|
|
1275
|
-
sessionStore: this.sessionStore,
|
|
1276
|
-
session: this.sessionRegistry.getSession(sessionId),
|
|
1277
|
-
sessionId,
|
|
1278
|
-
updatedAt: now()
|
|
1279
|
-
}),
|
|
1280
|
-
persistSessionEvent: (session, event) => persistLiveSessionEvent({
|
|
1281
|
-
sessionStore: this.sessionStore,
|
|
1282
|
-
session,
|
|
1283
|
-
event,
|
|
1284
|
-
updatedAt: now()
|
|
1285
|
-
}),
|
|
1286
|
-
getSessionSummary: (sessionId) => this.getSession(sessionId)
|
|
1287
|
-
});
|
|
1288
|
-
this.manifest = {
|
|
1289
|
-
endpointKind: "agent",
|
|
1290
|
-
endpointId: config.endpointId?.trim() || "ncp-agent-backend",
|
|
1291
|
-
version: config.version?.trim() || "0.1.0",
|
|
1292
|
-
supportsStreaming: true,
|
|
1293
|
-
supportsAbort: true,
|
|
1294
|
-
supportsProactiveMessages: false,
|
|
1295
|
-
supportsLiveSessionStream: true,
|
|
1296
|
-
supportedPartTypes: config.supportedPartTypes ?? DEFAULT_SUPPORTED_PART_TYPES,
|
|
1297
|
-
expectedLatency: config.expectedLatency ?? "seconds",
|
|
1298
|
-
metadata: config.metadata
|
|
1299
|
-
};
|
|
1300
|
-
}
|
|
1301
|
-
start = async () => {
|
|
1302
|
-
if (this.started) return;
|
|
1303
|
-
this.started = true;
|
|
1304
|
-
this.publisher.publish({ type: NcpEventType.EndpointReady });
|
|
1305
|
-
};
|
|
1306
|
-
stop = async () => {
|
|
1307
|
-
if (!this.started) return;
|
|
1308
|
-
this.started = false;
|
|
1309
|
-
for (const session of this.sessionRegistry.listSessions()) {
|
|
1310
|
-
const execution = session.activeExecution;
|
|
1311
|
-
if (!execution) {
|
|
1312
|
-
session.publisher.close();
|
|
1313
|
-
await disposeRuntime(session.runtime);
|
|
1314
|
-
continue;
|
|
1315
|
-
}
|
|
1316
|
-
execution.abortHandled = true;
|
|
1317
|
-
execution.controller.abort();
|
|
1318
|
-
this.finishSessionExecution(session, execution);
|
|
1319
|
-
session.publisher.close();
|
|
1320
|
-
await disposeRuntime(session.runtime);
|
|
1321
|
-
}
|
|
1322
|
-
this.sessionRegistry.clear();
|
|
1323
|
-
};
|
|
1324
|
-
emit = async (event) => {
|
|
1325
|
-
await this.ensureStarted();
|
|
1326
|
-
switch (event.type) {
|
|
1327
|
-
case NcpEventType.MessageRequest:
|
|
1328
|
-
if (!event.payload.sessionId) throw new Error("MessageRequest requires a sessionId before it reaches the agent backend.");
|
|
1329
|
-
for await (const emittedEvent of this.send({
|
|
1330
|
-
...event.payload,
|
|
1331
|
-
sessionId: event.payload.sessionId,
|
|
1332
|
-
message: {
|
|
1333
|
-
...event.payload.message,
|
|
1334
|
-
sessionId: event.payload.sessionId
|
|
1335
|
-
}
|
|
1336
|
-
}));
|
|
1337
|
-
return;
|
|
1338
|
-
case NcpEventType.MessageStreamRequest:
|
|
1339
|
-
await this.ensureStarted();
|
|
1340
|
-
return;
|
|
1341
|
-
case NcpEventType.MessageAbort:
|
|
1342
|
-
await this.handleAbort(event.payload);
|
|
1343
|
-
return;
|
|
1344
|
-
default: this.publisher.publish(event);
|
|
1345
|
-
}
|
|
1346
|
-
};
|
|
1347
|
-
subscribe = (listener) => this.publisher.subscribe(listener);
|
|
1348
|
-
send = (envelope, options) => {
|
|
1349
|
-
return (async function* (self) {
|
|
1350
|
-
await self.ensureStarted();
|
|
1351
|
-
const session = await self.sessionRegistry.ensureSession(envelope.sessionId, envelope.metadata);
|
|
1352
|
-
const execution = self.startSessionExecution(session, envelope, options?.signal);
|
|
1353
|
-
let completedMessageSeen = false;
|
|
1354
|
-
try {
|
|
1355
|
-
for await (const event of self.executor.executeRun(session, envelope, execution.controller)) {
|
|
1356
|
-
const normalized = normalizeSendRunEvent({
|
|
1357
|
-
session,
|
|
1358
|
-
event,
|
|
1359
|
-
completedMessageSeen
|
|
1360
|
-
});
|
|
1361
|
-
completedMessageSeen = normalized.completedMessageSeen;
|
|
1362
|
-
for (const normalizedEvent of normalized.eventsToPublish) {
|
|
1363
|
-
await self.sessionRealtime.publishSessionEvent(session, normalizedEvent);
|
|
1364
|
-
yield normalizedEvent;
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
if (execution.controller.signal.aborted && !execution.abortHandled) {
|
|
1368
|
-
const abortEvent = {
|
|
1369
|
-
type: NcpEventType.MessageAbort,
|
|
1370
|
-
payload: { sessionId: session.sessionId }
|
|
1371
|
-
};
|
|
1372
|
-
execution.abortHandled = true;
|
|
1373
|
-
await self.sessionRealtime.publishSessionEvent(session, abortEvent, { dispatchToStateManager: true });
|
|
1374
|
-
yield abortEvent;
|
|
1375
|
-
}
|
|
1376
|
-
} finally {
|
|
1377
|
-
self.finishSessionExecution(session, execution);
|
|
1378
|
-
if (shouldPersistRunEndSnapshot(self.sessionStore)) await persistLiveSession({
|
|
1379
|
-
sessionStore: self.sessionStore,
|
|
1380
|
-
session,
|
|
1381
|
-
sessionId: session.sessionId,
|
|
1382
|
-
updatedAt: now()
|
|
1383
|
-
});
|
|
1384
|
-
}
|
|
1385
|
-
})(this);
|
|
1386
|
-
};
|
|
1387
|
-
abort = async (payload) => this.handleAbort(payload);
|
|
1388
|
-
stream = (payloadOrParams, opts) => this.sessionRealtime.streamSessionEvents(payloadOrParams, opts);
|
|
1389
|
-
listSessions = async () => {
|
|
1390
|
-
return listBackendSessionSummaries({
|
|
1391
|
-
sessionStore: this.sessionStore,
|
|
1392
|
-
liveSessions: this.sessionRegistry.listSessions()
|
|
1393
|
-
});
|
|
1394
|
-
};
|
|
1395
|
-
isLiveSessionRunning = (sessionId) => Boolean(this.sessionRegistry.getSession(sessionId)?.activeExecution);
|
|
1396
|
-
listSessionMessages = async (sessionId) => {
|
|
1397
|
-
return listBackendSessionMessages({
|
|
1398
|
-
sessionStore: this.sessionStore,
|
|
1399
|
-
liveSession: this.sessionRegistry.getSession(sessionId),
|
|
1400
|
-
sessionId
|
|
1401
|
-
});
|
|
1402
|
-
};
|
|
1403
|
-
getSession = async (sessionId) => {
|
|
1404
|
-
return getBackendSessionSummary({
|
|
1405
|
-
sessionStore: this.sessionStore,
|
|
1406
|
-
liveSession: this.sessionRegistry.getSession(sessionId),
|
|
1407
|
-
sessionId,
|
|
1408
|
-
resolveSessionContextWindow: this.resolveSessionContextWindow
|
|
1409
|
-
});
|
|
1410
|
-
};
|
|
1411
|
-
appendMessage = async (sessionId, message) => {
|
|
1412
|
-
await this.ensureStarted();
|
|
1413
|
-
return this.sessionRealtime.appendMessage(sessionId, message);
|
|
1414
|
-
};
|
|
1415
|
-
updateToolCallResult = async (sessionId, toolCallId, content) => {
|
|
1416
|
-
await this.ensureStarted();
|
|
1417
|
-
return this.sessionRealtime.updateToolCallResult(sessionId, toolCallId, content);
|
|
1418
|
-
};
|
|
1419
|
-
updateSession = async (sessionId, patch) => {
|
|
1420
|
-
const liveSession = this.sessionRegistry.getSession(sessionId);
|
|
1421
|
-
const storedSession = await this.sessionStore.getSession(sessionId);
|
|
1422
|
-
if (!liveSession && !storedSession) return null;
|
|
1423
|
-
const metadata = buildUpdatedMetadata({
|
|
1424
|
-
liveSession,
|
|
1425
|
-
patch,
|
|
1426
|
-
storedSession
|
|
1427
|
-
});
|
|
1428
|
-
const updatedAt = now();
|
|
1429
|
-
if (liveSession) liveSession.metadata = structuredClone(metadata);
|
|
1430
|
-
await this.sessionStore.updateSessionMetadata({
|
|
1431
|
-
sessionId,
|
|
1432
|
-
metadata,
|
|
1433
|
-
updatedAt
|
|
1434
|
-
});
|
|
1435
|
-
return this.getSession(sessionId);
|
|
1436
|
-
};
|
|
1437
|
-
deleteSession = async (sessionId) => {
|
|
1438
|
-
const liveSession = this.sessionRegistry.deleteSession(sessionId);
|
|
1439
|
-
const execution = liveSession?.activeExecution;
|
|
1440
|
-
if (execution) {
|
|
1441
|
-
execution.abortHandled = true;
|
|
1442
|
-
execution.controller.abort();
|
|
1443
|
-
execution.closed = true;
|
|
1444
|
-
}
|
|
1445
|
-
liveSession?.publisher.close();
|
|
1446
|
-
if (liveSession) await disposeRuntime(liveSession.runtime);
|
|
1447
|
-
await this.sessionStore.deleteSession(sessionId);
|
|
1448
|
-
};
|
|
1449
|
-
ensureStarted = async () => {
|
|
1450
|
-
if (!this.started) await this.start();
|
|
1451
|
-
};
|
|
1452
|
-
startSessionExecution = (session, envelope, signal) => startAgentBackendSessionExecution({
|
|
1453
|
-
session,
|
|
1454
|
-
envelope,
|
|
1455
|
-
signal,
|
|
1456
|
-
onStatusChanged: this.onSessionRunStatusChanged
|
|
1457
|
-
});
|
|
1458
|
-
finishSessionExecution = (session, execution) => finishAgentBackendSessionExecution({
|
|
1459
|
-
session,
|
|
1460
|
-
execution,
|
|
1461
|
-
onStatusChanged: this.onSessionRunStatusChanged
|
|
1462
|
-
});
|
|
1463
|
-
handleAbort = async (payload) => {
|
|
1464
|
-
const session = this.sessionRegistry.getSession(payload.sessionId);
|
|
1465
|
-
const execution = session?.activeExecution;
|
|
1466
|
-
if (!session || !execution || execution.closed) return;
|
|
1467
|
-
execution.abortHandled = true;
|
|
1468
|
-
execution.controller.abort();
|
|
1469
|
-
const abortEvent = {
|
|
1470
|
-
type: NcpEventType.MessageAbort,
|
|
1471
|
-
payload: {
|
|
1472
|
-
sessionId: payload.sessionId,
|
|
1473
|
-
...payload.messageId ? { messageId: payload.messageId } : {}
|
|
1474
|
-
}
|
|
1475
|
-
};
|
|
1476
|
-
await this.sessionRealtime.publishSessionEvent(session, abortEvent, { dispatchToStateManager: true });
|
|
1477
|
-
this.finishSessionExecution(session, execution);
|
|
1478
|
-
};
|
|
1479
|
-
};
|
|
1480
|
-
//#endregion
|
|
1481
665
|
//#region src/agent/agent-backend/in-memory-agent-session.store.ts
|
|
1482
666
|
var InMemoryAgentSessionStore = class {
|
|
1483
667
|
sessions = /* @__PURE__ */ new Map();
|
|
@@ -1510,6 +694,21 @@ var InMemoryAgentSessionStore = class {
|
|
|
1510
694
|
};
|
|
1511
695
|
};
|
|
1512
696
|
//#endregion
|
|
697
|
+
//#region src/errors/ncp-error-exception.ts
|
|
698
|
+
/**
|
|
699
|
+
* Throwable form of protocol error for exception-based control flows.
|
|
700
|
+
*/
|
|
701
|
+
var NcpErrorException = class extends Error {
|
|
702
|
+
code;
|
|
703
|
+
details;
|
|
704
|
+
constructor(code, message, details) {
|
|
705
|
+
super(message);
|
|
706
|
+
this.name = "NcpErrorException";
|
|
707
|
+
this.code = code;
|
|
708
|
+
this.details = details;
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
//#endregion
|
|
1513
712
|
//#region src/chat/ncp-reply-consumer.ts
|
|
1514
713
|
function isTextLikePart(part) {
|
|
1515
714
|
return part.type === "text" || part.type === "rich-text";
|
|
@@ -1746,4 +945,4 @@ var NcpReplyConsumer = class {
|
|
|
1746
945
|
};
|
|
1747
946
|
};
|
|
1748
947
|
//#endregion
|
|
1749
|
-
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",
|