@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 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, NcpAgentRunApi, NcpAgentRunInput, NcpAgentRunSendOptions, NcpAgentRunStreamOptions, NcpAgentRuntime, NcpAgentServerEndpoint, NcpAgentStreamProvider, NcpContextWindowUpdatedPayload, NcpEndpointEvent, NcpEndpointManifest, NcpEndpointSubscriber, NcpError, NcpErrorCode, NcpMessage, NcpMessageAbortPayload, NcpMessagePart, NcpMessageSentPayload, NcpReasoningDeltaPayload, NcpReasoningEndPayload, NcpReasoningStartPayload, NcpRequestEnvelope, NcpRunErrorPayload, NcpRunFinishedPayload, NcpRunMetadataPayload, NcpRunStartedPayload, NcpSessionApi, NcpSessionPatch, NcpSessionSummary, NcpStreamRequestPayload, NcpTextDeltaPayload, NcpTextEndPayload, NcpTextStartPayload, NcpToolCallArgsDeltaPayload, NcpToolCallArgsPayload, NcpToolCallEndPayload, NcpToolCallResultPayload, NcpToolCallStartPayload, OpenAITool } from "@nextclaw/ncp";
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
- replaceSession: (session: AgentSessionRecord) => Promise<void>;
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 { AgentRunExecutor, type AgentSessionEventRecord, type AgentSessionRecord, type AgentSessionStore, Chat, ChatTarget, type CreateRuntimeFn, DefaultNcpAgentBackend, type DefaultNcpAgentBackendConfig, DefaultNcpAgentConversationStateManager, EventPublisher, InMemoryAgentSessionStore, type LiveSessionExecution, type LiveSessionState, NcpErrorException, NcpEventStream, NcpReplyConsumer, NcpReplyInput, type RuntimeFactoryParams, createAgentClientFromServer };
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, isHiddenNcpMessage, sanitizeAssistantReplyTags } from "@nextclaw/ncp";
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-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 {
665
+ //#region src/agent/agent-backend/in-memory-agent-session.store.ts
666
+ var InMemoryAgentSessionStore = class {
675
667
  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;
668
+ getSession = async (sessionId) => {
669
+ const session = this.sessions.get(sessionId);
670
+ return session ? structuredClone(session) : null;
722
671
  };
723
- getSession = (sessionId) => {
724
- return this.sessions.get(sessionId) ?? null;
672
+ listSessions = async () => {
673
+ return [...this.sessions.values()].map((session) => structuredClone(session));
725
674
  };
726
- deleteSession = (sessionId) => {
727
- const session = this.sessions.get(sessionId) ?? null;
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
- clear = () => {
732
- this.sessions.clear();
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
- listSessions = () => {
735
- return [...this.sessions.values()];
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 { AgentRunExecutor, DefaultNcpAgentBackend, DefaultNcpAgentConversationStateManager, EventPublisher, InMemoryAgentSessionStore, NcpErrorException, NcpReplyConsumer, createAgentClientFromServer };
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.16",
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.11"
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.21"
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",