@nextclaw/ncp-toolkit 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/index.d.ts +129 -13
- package/dist/index.js +784 -153
- package/package.json +4 -7
package/README.md
CHANGED
|
@@ -5,10 +5,14 @@ Toolkit implementations built on top of `@nextclaw/ncp` protocol contracts.
|
|
|
5
5
|
## Build
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
pnpm -C packages/nextclaw-ncp-toolkit build
|
|
8
|
+
pnpm -C packages/ncp-packages/nextclaw-ncp-toolkit build
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Scope
|
|
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
|
+
- Default in-memory adapter: `InMemoryAgentSessionStore`
|
|
17
|
+
- In-process adapter helper: `createAgentClientFromServer`
|
|
18
|
+
- Runtime throwable helper: `NcpErrorException`
|
package/dist/index.d.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { NcpAgentConversationStateManager,
|
|
1
|
+
import { NcpAgentConversationStateManager, NcpAgentConversationSnapshot, NcpAgentConversationHydrationParams, NcpEndpointEvent, NcpMessageSentPayload, NcpMessageAbortPayload, NcpTextStartPayload, NcpTextDeltaPayload, NcpTextEndPayload, NcpReasoningStartPayload, NcpReasoningDeltaPayload, NcpReasoningEndPayload, NcpToolCallStartPayload, NcpToolCallArgsPayload, NcpToolCallArgsDeltaPayload, NcpToolCallEndPayload, NcpToolCallResultPayload, NcpRunStartedPayload, NcpRunFinishedPayload, NcpRunErrorPayload, NcpRunMetadataPayload, NcpError, NcpAgentServerEndpoint, NcpAgentClientEndpoint, NcpEndpointSubscriber, NcpAgentRuntime, NcpRequestEnvelope, NcpMessage, NcpSessionApi, NcpAgentStreamProvider, NcpAgentRunApi, NcpEndpointManifest, NcpAgentRunSendOptions, NcpStreamRequestPayload, NcpAgentRunStreamOptions, NcpSessionSummary, NcpErrorCode } from '@nextclaw/ncp';
|
|
2
2
|
|
|
3
3
|
declare class DefaultNcpAgentConversationStateManager implements NcpAgentConversationStateManager {
|
|
4
4
|
private messages;
|
|
5
5
|
private streamingMessage;
|
|
6
6
|
private error;
|
|
7
|
+
private activeRun;
|
|
7
8
|
private readonly listeners;
|
|
8
9
|
private readonly toolCallMessageIdByCallId;
|
|
9
10
|
private readonly toolCallArgsRawByCallId;
|
|
11
|
+
private snapshotCache;
|
|
12
|
+
private snapshotVersion;
|
|
10
13
|
private stateVersion;
|
|
11
|
-
getSnapshot():
|
|
12
|
-
subscribe(listener: (snapshot:
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
getSnapshot(): NcpAgentConversationSnapshot;
|
|
15
|
+
subscribe(listener: (snapshot: NcpAgentConversationSnapshot) => void): () => boolean;
|
|
16
|
+
reset(): void;
|
|
17
|
+
hydrate(payload: NcpAgentConversationHydrationParams): void;
|
|
18
|
+
dispatch(event: NcpEndpointEvent): Promise<void>;
|
|
15
19
|
handleMessageSent(payload: NcpMessageSentPayload): void;
|
|
16
|
-
handleMessageAccepted(_payload: NcpMessageAcceptedPayload): void;
|
|
17
|
-
handleMessageIncoming(payload: NcpResponseEnvelope): void;
|
|
18
|
-
handleMessageCompleted(payload: NcpCompletedEnvelope): void;
|
|
19
|
-
handleMessageFailed(payload: NcpFailedEnvelope): void;
|
|
20
20
|
handleMessageAbort(payload: NcpMessageAbortPayload): void;
|
|
21
21
|
handleMessageTextStart(payload: NcpTextStartPayload): void;
|
|
22
22
|
handleMessageTextDelta(payload: NcpTextDeltaPayload): void;
|
|
@@ -29,10 +29,10 @@ declare class DefaultNcpAgentConversationStateManager implements NcpAgentConvers
|
|
|
29
29
|
handleMessageToolCallArgsDelta(payload: NcpToolCallArgsDeltaPayload): void;
|
|
30
30
|
handleMessageToolCallEnd(payload: NcpToolCallEndPayload): void;
|
|
31
31
|
handleMessageToolCallResult(payload: NcpToolCallResultPayload): void;
|
|
32
|
-
handleRunStarted(
|
|
32
|
+
handleRunStarted(payload: NcpRunStartedPayload): void;
|
|
33
33
|
handleRunFinished(_payload: NcpRunFinishedPayload): void;
|
|
34
34
|
handleRunError(payload: NcpRunErrorPayload): void;
|
|
35
|
-
handleRunMetadata(
|
|
35
|
+
handleRunMetadata(payload: NcpRunMetadataPayload): void;
|
|
36
36
|
handleEndpointError(payload: NcpError): void;
|
|
37
37
|
private applyToolCallArgs;
|
|
38
38
|
private ensureStreamingMessage;
|
|
@@ -41,11 +41,127 @@ declare class DefaultNcpAgentConversationStateManager implements NcpAgentConvers
|
|
|
41
41
|
private findToolInvocationPart;
|
|
42
42
|
private findToolNameByCallId;
|
|
43
43
|
private upsertToolInvocationPart;
|
|
44
|
-
private findLastReasoningPartIndex;
|
|
45
44
|
private upsertMessage;
|
|
46
45
|
private replaceStreamingMessage;
|
|
47
46
|
private setError;
|
|
47
|
+
private clearActiveRun;
|
|
48
48
|
private clearToolCallTrackingByMessageId;
|
|
49
|
+
private notifyListeners;
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Creates an NcpAgentClientEndpoint that forwards to an in-process NcpAgentServerEndpoint.
|
|
54
|
+
* Use when the agent runs in-process and you need to pass a client endpoint to the HTTP server.
|
|
55
|
+
*/
|
|
56
|
+
declare function createAgentClientFromServer(server: NcpAgentServerEndpoint): NcpAgentClientEndpoint;
|
|
57
|
+
|
|
58
|
+
declare class EventPublisher {
|
|
59
|
+
private readonly listeners;
|
|
60
|
+
private readonly closeListeners;
|
|
61
|
+
private closed;
|
|
62
|
+
subscribe(listener: NcpEndpointSubscriber): () => void;
|
|
63
|
+
onClose(listener: () => void): () => void;
|
|
64
|
+
publish(event: NcpEndpointEvent): void;
|
|
65
|
+
close(): void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
type RuntimeFactoryParams = {
|
|
69
|
+
sessionId: string;
|
|
70
|
+
stateManager: NcpAgentConversationStateManager;
|
|
71
|
+
};
|
|
72
|
+
type CreateRuntimeFn = (params: RuntimeFactoryParams) => NcpAgentRuntime;
|
|
73
|
+
type AgentSessionRecord = {
|
|
74
|
+
sessionId: string;
|
|
75
|
+
messages: NcpMessage[];
|
|
76
|
+
updatedAt: string;
|
|
77
|
+
};
|
|
78
|
+
type LiveSessionExecution = {
|
|
79
|
+
controller: AbortController;
|
|
80
|
+
publisher: EventPublisher;
|
|
81
|
+
requestEnvelope: NcpRequestEnvelope;
|
|
82
|
+
abortHandled: boolean;
|
|
83
|
+
closed: boolean;
|
|
84
|
+
};
|
|
85
|
+
type LiveSessionState = {
|
|
86
|
+
sessionId: string;
|
|
87
|
+
runtime: NcpAgentRuntime;
|
|
88
|
+
stateManager: NcpAgentConversationStateManager;
|
|
89
|
+
activeExecution: LiveSessionExecution | null;
|
|
90
|
+
};
|
|
91
|
+
interface AgentSessionStore {
|
|
92
|
+
getSession(sessionId: string): Promise<AgentSessionRecord | null>;
|
|
93
|
+
listSessions(): Promise<AgentSessionRecord[]>;
|
|
94
|
+
saveSession(session: AgentSessionRecord): Promise<void>;
|
|
95
|
+
deleteSession(sessionId: string): Promise<AgentSessionRecord | null>;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
type DefaultNcpAgentBackendConfig = {
|
|
99
|
+
createRuntime: CreateRuntimeFn;
|
|
100
|
+
sessionStore: AgentSessionStore;
|
|
101
|
+
endpointId?: string;
|
|
102
|
+
version?: string;
|
|
103
|
+
metadata?: Record<string, unknown>;
|
|
104
|
+
supportedPartTypes?: NcpEndpointManifest["supportedPartTypes"];
|
|
105
|
+
expectedLatency?: NcpEndpointManifest["expectedLatency"];
|
|
106
|
+
};
|
|
107
|
+
declare class DefaultNcpAgentBackend implements NcpAgentServerEndpoint, NcpSessionApi, NcpAgentStreamProvider, NcpAgentRunApi {
|
|
108
|
+
readonly manifest: NcpEndpointManifest & {
|
|
109
|
+
endpointKind: "agent";
|
|
110
|
+
};
|
|
111
|
+
private readonly sessionStore;
|
|
112
|
+
private readonly sessionRegistry;
|
|
113
|
+
private readonly executor;
|
|
114
|
+
private readonly publisher;
|
|
115
|
+
private started;
|
|
116
|
+
constructor(config: DefaultNcpAgentBackendConfig);
|
|
117
|
+
start(): Promise<void>;
|
|
118
|
+
stop(): Promise<void>;
|
|
119
|
+
emit(event: NcpEndpointEvent): Promise<void>;
|
|
120
|
+
subscribe(listener: (event: NcpEndpointEvent) => void): () => void;
|
|
121
|
+
send(envelope: NcpRequestEnvelope, options?: NcpAgentRunSendOptions): AsyncIterable<NcpEndpointEvent>;
|
|
122
|
+
abort(payload: NcpMessageAbortPayload): Promise<void>;
|
|
123
|
+
stream(payloadOrParams: NcpStreamRequestPayload | {
|
|
124
|
+
payload: NcpStreamRequestPayload;
|
|
125
|
+
signal: AbortSignal;
|
|
126
|
+
}, opts?: NcpAgentRunStreamOptions): AsyncIterable<NcpEndpointEvent>;
|
|
127
|
+
listSessions(): Promise<NcpSessionSummary[]>;
|
|
128
|
+
listSessionMessages(sessionId: string): Promise<NcpMessage[]>;
|
|
129
|
+
getSession(sessionId: string): Promise<NcpSessionSummary | null>;
|
|
130
|
+
deleteSession(sessionId: string): Promise<void>;
|
|
131
|
+
private ensureStarted;
|
|
132
|
+
private startSessionExecution;
|
|
133
|
+
private finishSessionExecution;
|
|
134
|
+
private closeExecution;
|
|
135
|
+
private publishLiveEvent;
|
|
136
|
+
private handleRequest;
|
|
137
|
+
private streamToSubscribers;
|
|
138
|
+
private handleAbort;
|
|
139
|
+
private createAbortEvent;
|
|
140
|
+
private persistSession;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
declare class AgentRunExecutor {
|
|
144
|
+
private readonly persistSession;
|
|
145
|
+
constructor(persistSession: (sessionId: string) => Promise<void>);
|
|
146
|
+
executeRun(session: LiveSessionState, envelope: NcpRequestEnvelope, controller: AbortController): AsyncGenerator<NcpEndpointEvent>;
|
|
147
|
+
private publishFailure;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
declare class InMemoryAgentSessionStore implements AgentSessionStore {
|
|
151
|
+
private readonly sessions;
|
|
152
|
+
getSession(sessionId: string): Promise<AgentSessionRecord | null>;
|
|
153
|
+
listSessions(): Promise<AgentSessionRecord[]>;
|
|
154
|
+
saveSession(session: AgentSessionRecord): Promise<void>;
|
|
155
|
+
deleteSession(sessionId: string): Promise<AgentSessionRecord | null>;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Throwable form of protocol error for exception-based control flows.
|
|
160
|
+
*/
|
|
161
|
+
declare class NcpErrorException extends Error {
|
|
162
|
+
readonly code: NcpErrorCode;
|
|
163
|
+
readonly details?: Record<string, unknown>;
|
|
164
|
+
constructor(code: NcpErrorCode, message: string, details?: Record<string, unknown>);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export { AgentRunExecutor, type AgentSessionRecord, type AgentSessionStore, type CreateRuntimeFn, DefaultNcpAgentBackend, type DefaultNcpAgentBackendConfig, DefaultNcpAgentConversationStateManager, EventPublisher, InMemoryAgentSessionStore, type LiveSessionExecution, type LiveSessionState, NcpErrorException, type RuntimeFactoryParams, createAgentClientFromServer };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
// src/agent/agent-conversation-state-manager.ts
|
|
2
|
+
import {
|
|
3
|
+
NcpEventType
|
|
4
|
+
} from "@nextclaw/ncp";
|
|
2
5
|
var DEFAULT_ASSISTANT_ROLE = "assistant";
|
|
3
6
|
var cloneMessage = (message) => {
|
|
4
7
|
return {
|
|
@@ -13,7 +16,7 @@ var buildRuntimeError = (payload) => {
|
|
|
13
16
|
code: "runtime-error",
|
|
14
17
|
message: message && message.length > 0 ? message : "Agent run failed.",
|
|
15
18
|
details: {
|
|
16
|
-
|
|
19
|
+
sessionId: payload.sessionId,
|
|
17
20
|
messageId: payload.messageId,
|
|
18
21
|
threadId: payload.threadId,
|
|
19
22
|
runId: payload.runId
|
|
@@ -24,205 +27,153 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
24
27
|
messages = [];
|
|
25
28
|
streamingMessage = null;
|
|
26
29
|
error = null;
|
|
30
|
+
activeRun = null;
|
|
27
31
|
listeners = /* @__PURE__ */ new Set();
|
|
28
32
|
toolCallMessageIdByCallId = /* @__PURE__ */ new Map();
|
|
29
33
|
toolCallArgsRawByCallId = /* @__PURE__ */ new Map();
|
|
34
|
+
snapshotCache = null;
|
|
35
|
+
snapshotVersion = -1;
|
|
30
36
|
stateVersion = 0;
|
|
31
37
|
getSnapshot() {
|
|
32
|
-
|
|
38
|
+
if (this.snapshotCache && this.snapshotVersion === this.stateVersion) {
|
|
39
|
+
return this.snapshotCache;
|
|
40
|
+
}
|
|
41
|
+
const snapshot = {
|
|
33
42
|
messages: this.messages.map((message) => cloneMessage(message)),
|
|
34
43
|
streamingMessage: this.streamingMessage ? cloneMessage(this.streamingMessage) : null,
|
|
35
|
-
error: this.error ? { ...this.error, details: this.error.details ? { ...this.error.details } : void 0 } : null
|
|
44
|
+
error: this.error ? { ...this.error, details: this.error.details ? { ...this.error.details } : void 0 } : null,
|
|
45
|
+
activeRun: this.activeRun ? { ...this.activeRun } : null
|
|
36
46
|
};
|
|
47
|
+
this.snapshotCache = snapshot;
|
|
48
|
+
this.snapshotVersion = this.stateVersion;
|
|
49
|
+
return snapshot;
|
|
37
50
|
}
|
|
38
51
|
subscribe(listener) {
|
|
39
52
|
this.listeners.add(listener);
|
|
40
53
|
return () => this.listeners.delete(listener);
|
|
41
54
|
}
|
|
42
|
-
|
|
55
|
+
reset() {
|
|
56
|
+
if (this.messages.length === 0 && !this.streamingMessage && !this.error && !this.activeRun && this.toolCallMessageIdByCallId.size === 0 && this.toolCallArgsRawByCallId.size === 0) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
this.messages = [];
|
|
60
|
+
this.streamingMessage = null;
|
|
61
|
+
this.error = null;
|
|
62
|
+
this.activeRun = null;
|
|
63
|
+
this.toolCallMessageIdByCallId.clear();
|
|
64
|
+
this.toolCallArgsRawByCallId.clear();
|
|
65
|
+
this.stateVersion += 1;
|
|
66
|
+
this.notifyListeners();
|
|
67
|
+
}
|
|
68
|
+
hydrate(payload) {
|
|
69
|
+
this.messages = payload.messages.map((message) => cloneMessage(message));
|
|
70
|
+
this.streamingMessage = null;
|
|
71
|
+
this.error = null;
|
|
72
|
+
this.activeRun = payload.activeRun ? {
|
|
73
|
+
...payload.activeRun,
|
|
74
|
+
sessionId: payload.activeRun.sessionId ?? payload.sessionId,
|
|
75
|
+
abortDisabledReason: payload.activeRun.abortDisabledReason ?? null
|
|
76
|
+
} : null;
|
|
77
|
+
this.toolCallMessageIdByCallId.clear();
|
|
78
|
+
this.toolCallArgsRawByCallId.clear();
|
|
79
|
+
this.stateVersion += 1;
|
|
80
|
+
this.notifyListeners();
|
|
81
|
+
}
|
|
82
|
+
async dispatch(event) {
|
|
43
83
|
const versionBeforeDispatch = this.stateVersion;
|
|
44
84
|
switch (event.type) {
|
|
45
|
-
case
|
|
46
|
-
this.handleMessageRequest(event.payload);
|
|
47
|
-
break;
|
|
48
|
-
case "message.sent":
|
|
85
|
+
case NcpEventType.MessageSent:
|
|
49
86
|
this.handleMessageSent(event.payload);
|
|
50
87
|
break;
|
|
51
|
-
case
|
|
52
|
-
this.handleMessageAccepted(event.payload);
|
|
53
|
-
break;
|
|
54
|
-
case "message.incoming":
|
|
55
|
-
this.handleMessageIncoming(event.payload);
|
|
56
|
-
break;
|
|
57
|
-
case "message.completed":
|
|
58
|
-
this.handleMessageCompleted(event.payload);
|
|
59
|
-
break;
|
|
60
|
-
case "message.failed":
|
|
61
|
-
this.handleMessageFailed(event.payload);
|
|
62
|
-
break;
|
|
63
|
-
case "message.abort":
|
|
88
|
+
case NcpEventType.MessageAbort:
|
|
64
89
|
this.handleMessageAbort(event.payload);
|
|
65
90
|
break;
|
|
66
|
-
case
|
|
91
|
+
case NcpEventType.MessageTextStart:
|
|
67
92
|
this.handleMessageTextStart(event.payload);
|
|
68
93
|
break;
|
|
69
|
-
case
|
|
94
|
+
case NcpEventType.MessageTextDelta:
|
|
70
95
|
this.handleMessageTextDelta(event.payload);
|
|
71
96
|
break;
|
|
72
|
-
case
|
|
97
|
+
case NcpEventType.MessageTextEnd:
|
|
73
98
|
this.handleMessageTextEnd(event.payload);
|
|
74
99
|
break;
|
|
75
|
-
case
|
|
100
|
+
case NcpEventType.MessageReasoningStart:
|
|
76
101
|
this.handleMessageReasoningStart(event.payload);
|
|
77
102
|
break;
|
|
78
|
-
case
|
|
103
|
+
case NcpEventType.MessageReasoningDelta:
|
|
79
104
|
this.handleMessageReasoningDelta(event.payload);
|
|
80
105
|
break;
|
|
81
|
-
case
|
|
106
|
+
case NcpEventType.MessageReasoningEnd:
|
|
82
107
|
this.handleMessageReasoningEnd(event.payload);
|
|
83
108
|
break;
|
|
84
|
-
case
|
|
109
|
+
case NcpEventType.MessageToolCallStart:
|
|
85
110
|
this.handleMessageToolCallStart(event.payload);
|
|
86
111
|
break;
|
|
87
|
-
case
|
|
112
|
+
case NcpEventType.MessageToolCallArgs:
|
|
88
113
|
this.handleMessageToolCallArgs(event.payload);
|
|
89
114
|
break;
|
|
90
|
-
case
|
|
115
|
+
case NcpEventType.MessageToolCallArgsDelta:
|
|
91
116
|
this.handleMessageToolCallArgsDelta(event.payload);
|
|
92
117
|
break;
|
|
93
|
-
case
|
|
118
|
+
case NcpEventType.MessageToolCallEnd:
|
|
94
119
|
this.handleMessageToolCallEnd(event.payload);
|
|
95
120
|
break;
|
|
96
|
-
case
|
|
121
|
+
case NcpEventType.MessageToolCallResult:
|
|
97
122
|
this.handleMessageToolCallResult(event.payload);
|
|
98
123
|
break;
|
|
99
|
-
case
|
|
124
|
+
case NcpEventType.RunStarted:
|
|
100
125
|
this.handleRunStarted(event.payload);
|
|
101
126
|
break;
|
|
102
|
-
case
|
|
127
|
+
case NcpEventType.RunFinished:
|
|
103
128
|
this.handleRunFinished(event.payload);
|
|
104
129
|
break;
|
|
105
|
-
case
|
|
130
|
+
case NcpEventType.RunError:
|
|
106
131
|
this.handleRunError(event.payload);
|
|
107
132
|
break;
|
|
108
|
-
case
|
|
133
|
+
case NcpEventType.RunMetadata:
|
|
109
134
|
this.handleRunMetadata(event.payload);
|
|
110
135
|
break;
|
|
111
|
-
case
|
|
136
|
+
case NcpEventType.EndpointError:
|
|
112
137
|
this.handleEndpointError(event.payload);
|
|
113
138
|
break;
|
|
114
|
-
case "endpoint.ready":
|
|
115
|
-
case "typing.start":
|
|
116
|
-
case "typing.end":
|
|
117
|
-
case "presence.updated":
|
|
118
|
-
case "message.read":
|
|
119
|
-
case "message.delivered":
|
|
120
|
-
case "message.recalled":
|
|
121
|
-
case "message.reaction":
|
|
122
|
-
break;
|
|
123
139
|
default:
|
|
124
140
|
break;
|
|
125
141
|
}
|
|
126
142
|
if (this.stateVersion !== versionBeforeDispatch) {
|
|
127
|
-
|
|
128
|
-
for (const listener of this.listeners) {
|
|
129
|
-
listener(snapshot);
|
|
130
|
-
}
|
|
143
|
+
this.notifyListeners();
|
|
131
144
|
}
|
|
132
145
|
}
|
|
133
|
-
handleMessageRequest(payload) {
|
|
134
|
-
this.upsertMessage(payload.message);
|
|
135
|
-
this.setError(null);
|
|
136
|
-
}
|
|
137
146
|
handleMessageSent(payload) {
|
|
138
147
|
this.upsertMessage(payload.message);
|
|
139
148
|
this.setError(null);
|
|
140
149
|
}
|
|
141
|
-
handleMessageAccepted(_payload) {
|
|
142
|
-
}
|
|
143
|
-
handleMessageIncoming(payload) {
|
|
144
|
-
const incomingMessage = cloneMessage(payload.message);
|
|
145
|
-
if (incomingMessage.status === "streaming" || incomingMessage.status === "pending") {
|
|
146
|
-
this.replaceStreamingMessage(incomingMessage);
|
|
147
|
-
this.setError(null);
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
this.upsertMessage(incomingMessage);
|
|
151
|
-
if (this.streamingMessage?.id === incomingMessage.id) {
|
|
152
|
-
this.replaceStreamingMessage(null);
|
|
153
|
-
this.clearToolCallTrackingByMessageId(incomingMessage.id);
|
|
154
|
-
}
|
|
155
|
-
this.setError(null);
|
|
156
|
-
}
|
|
157
|
-
handleMessageCompleted(payload) {
|
|
158
|
-
const completedMessage = {
|
|
159
|
-
...cloneMessage(payload.message),
|
|
160
|
-
status: "final"
|
|
161
|
-
};
|
|
162
|
-
this.upsertMessage(completedMessage);
|
|
163
|
-
if (this.streamingMessage?.id === completedMessage.id) {
|
|
164
|
-
this.replaceStreamingMessage(null);
|
|
165
|
-
}
|
|
166
|
-
this.clearToolCallTrackingByMessageId(completedMessage.id);
|
|
167
|
-
this.setError(null);
|
|
168
|
-
}
|
|
169
|
-
handleMessageFailed(payload) {
|
|
170
|
-
this.setError(payload.error);
|
|
171
|
-
const targetMessageId = payload.messageId?.trim();
|
|
172
|
-
if (!targetMessageId) {
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
if (this.streamingMessage?.id === targetMessageId) {
|
|
176
|
-
this.upsertMessage({
|
|
177
|
-
...this.streamingMessage,
|
|
178
|
-
status: "error"
|
|
179
|
-
});
|
|
180
|
-
this.replaceStreamingMessage(null);
|
|
181
|
-
this.clearToolCallTrackingByMessageId(targetMessageId);
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
const messageIndex = this.messages.findIndex((message) => message.id === targetMessageId);
|
|
185
|
-
if (messageIndex < 0) {
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
const nextMessages = [...this.messages];
|
|
189
|
-
nextMessages[messageIndex] = {
|
|
190
|
-
...nextMessages[messageIndex],
|
|
191
|
-
status: "error"
|
|
192
|
-
};
|
|
193
|
-
this.messages = nextMessages;
|
|
194
|
-
this.stateVersion += 1;
|
|
195
|
-
}
|
|
196
150
|
handleMessageAbort(payload) {
|
|
197
151
|
const targetMessageId = payload.messageId?.trim();
|
|
198
|
-
this.
|
|
199
|
-
|
|
200
|
-
message: "Message aborted.",
|
|
201
|
-
details: {
|
|
202
|
-
messageId: targetMessageId,
|
|
203
|
-
correlationId: payload.correlationId
|
|
204
|
-
}
|
|
205
|
-
});
|
|
152
|
+
this.clearActiveRun();
|
|
153
|
+
this.setError(null);
|
|
206
154
|
if (this.streamingMessage && (!targetMessageId || this.streamingMessage.id === targetMessageId)) {
|
|
155
|
+
const streamingMessageId = this.streamingMessage.id;
|
|
207
156
|
this.upsertMessage({
|
|
208
157
|
...this.streamingMessage,
|
|
209
|
-
status: "
|
|
158
|
+
status: "final"
|
|
210
159
|
});
|
|
211
160
|
this.replaceStreamingMessage(null);
|
|
212
161
|
if (targetMessageId) {
|
|
213
162
|
this.clearToolCallTrackingByMessageId(targetMessageId);
|
|
163
|
+
} else {
|
|
164
|
+
this.clearToolCallTrackingByMessageId(streamingMessageId);
|
|
214
165
|
}
|
|
215
166
|
}
|
|
216
167
|
}
|
|
217
168
|
handleMessageTextStart(payload) {
|
|
218
|
-
this.ensureStreamingMessage(payload.
|
|
169
|
+
this.ensureStreamingMessage(payload.sessionId, payload.messageId, "streaming");
|
|
219
170
|
this.setError(null);
|
|
220
171
|
}
|
|
221
172
|
handleMessageTextDelta(payload) {
|
|
222
173
|
if (!payload.delta) {
|
|
223
174
|
return;
|
|
224
175
|
}
|
|
225
|
-
const targetMessage = this.ensureStreamingMessage(payload.
|
|
176
|
+
const targetMessage = this.ensureStreamingMessage(payload.sessionId, payload.messageId, "streaming");
|
|
226
177
|
const nextParts = [...targetMessage.parts];
|
|
227
178
|
const lastPart = nextParts[nextParts.length - 1];
|
|
228
179
|
if (lastPart?.type === "text") {
|
|
@@ -252,23 +203,20 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
252
203
|
});
|
|
253
204
|
}
|
|
254
205
|
handleMessageReasoningStart(payload) {
|
|
255
|
-
this.ensureStreamingMessage(payload.
|
|
206
|
+
this.ensureStreamingMessage(payload.sessionId, payload.messageId, "streaming");
|
|
256
207
|
}
|
|
257
208
|
handleMessageReasoningDelta(payload) {
|
|
258
209
|
if (!payload.delta) {
|
|
259
210
|
return;
|
|
260
211
|
}
|
|
261
|
-
const targetMessage = this.ensureStreamingMessage(payload.
|
|
212
|
+
const targetMessage = this.ensureStreamingMessage(payload.sessionId, payload.messageId, "streaming");
|
|
262
213
|
const nextParts = [...targetMessage.parts];
|
|
263
|
-
const
|
|
264
|
-
if (
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
text: `${existingPart.text}${payload.delta}`
|
|
270
|
-
};
|
|
271
|
-
}
|
|
214
|
+
const lastPart = nextParts[nextParts.length - 1];
|
|
215
|
+
if (lastPart?.type === "reasoning") {
|
|
216
|
+
nextParts[nextParts.length - 1] = {
|
|
217
|
+
type: "reasoning",
|
|
218
|
+
text: `${lastPart.text}${payload.delta}`
|
|
219
|
+
};
|
|
272
220
|
} else {
|
|
273
221
|
nextParts.push({ type: "reasoning", text: payload.delta });
|
|
274
222
|
}
|
|
@@ -285,7 +233,7 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
285
233
|
}
|
|
286
234
|
handleMessageToolCallStart(payload) {
|
|
287
235
|
const targetMessage = this.resolveToolCallTargetMessage(
|
|
288
|
-
payload.
|
|
236
|
+
payload.sessionId,
|
|
289
237
|
payload.toolCallId,
|
|
290
238
|
payload.messageId
|
|
291
239
|
);
|
|
@@ -306,16 +254,16 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
306
254
|
}
|
|
307
255
|
handleMessageToolCallArgs(payload) {
|
|
308
256
|
this.toolCallArgsRawByCallId.set(payload.toolCallId, payload.args);
|
|
309
|
-
this.applyToolCallArgs(payload.
|
|
257
|
+
this.applyToolCallArgs(payload.sessionId, payload.toolCallId, payload.args);
|
|
310
258
|
}
|
|
311
259
|
handleMessageToolCallArgsDelta(payload) {
|
|
312
260
|
const currentArgs = this.toolCallArgsRawByCallId.get(payload.toolCallId) ?? "";
|
|
313
261
|
const nextArgs = `${currentArgs}${payload.delta}`;
|
|
314
262
|
this.toolCallArgsRawByCallId.set(payload.toolCallId, nextArgs);
|
|
315
|
-
this.applyToolCallArgs(payload.
|
|
263
|
+
this.applyToolCallArgs(payload.sessionId, payload.toolCallId, nextArgs, payload.messageId);
|
|
316
264
|
}
|
|
317
265
|
handleMessageToolCallEnd(payload) {
|
|
318
|
-
const targetMessage = this.resolveToolCallTargetMessage(payload.
|
|
266
|
+
const targetMessage = this.resolveToolCallTargetMessage(payload.sessionId, payload.toolCallId);
|
|
319
267
|
const args = this.toolCallArgsRawByCallId.get(payload.toolCallId) ?? "";
|
|
320
268
|
const nextParts = this.upsertToolInvocationPart(targetMessage.parts, {
|
|
321
269
|
type: "tool-invocation",
|
|
@@ -343,7 +291,7 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
343
291
|
return this.upsertToolInvocationPart(targetMessage.parts, mergedPart);
|
|
344
292
|
});
|
|
345
293
|
if (!updated) {
|
|
346
|
-
const fallbackMessage = this.resolveToolCallTargetMessage(payload.
|
|
294
|
+
const fallbackMessage = this.resolveToolCallTargetMessage(payload.sessionId, payload.toolCallId);
|
|
347
295
|
const nextParts = this.upsertToolInvocationPart(fallbackMessage.parts, {
|
|
348
296
|
type: "tool-invocation",
|
|
349
297
|
toolCallId: payload.toolCallId,
|
|
@@ -358,21 +306,59 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
358
306
|
});
|
|
359
307
|
}
|
|
360
308
|
}
|
|
361
|
-
handleRunStarted(
|
|
309
|
+
handleRunStarted(payload) {
|
|
362
310
|
this.setError(null);
|
|
311
|
+
this.activeRun = {
|
|
312
|
+
runId: payload.runId ?? null,
|
|
313
|
+
sessionId: payload.sessionId
|
|
314
|
+
};
|
|
315
|
+
this.stateVersion += 1;
|
|
363
316
|
}
|
|
364
317
|
handleRunFinished(_payload) {
|
|
318
|
+
if (this.streamingMessage) {
|
|
319
|
+
const finalizedMessage = {
|
|
320
|
+
...this.streamingMessage,
|
|
321
|
+
status: "final"
|
|
322
|
+
};
|
|
323
|
+
this.upsertMessage(finalizedMessage);
|
|
324
|
+
this.replaceStreamingMessage(null);
|
|
325
|
+
this.clearToolCallTrackingByMessageId(finalizedMessage.id);
|
|
326
|
+
}
|
|
327
|
+
this.setError(null);
|
|
328
|
+
this.clearActiveRun();
|
|
365
329
|
}
|
|
366
330
|
handleRunError(payload) {
|
|
331
|
+
if (this.streamingMessage) {
|
|
332
|
+
const failedMessage = {
|
|
333
|
+
...this.streamingMessage,
|
|
334
|
+
status: "error"
|
|
335
|
+
};
|
|
336
|
+
this.upsertMessage(failedMessage);
|
|
337
|
+
this.replaceStreamingMessage(null);
|
|
338
|
+
this.clearToolCallTrackingByMessageId(failedMessage.id);
|
|
339
|
+
}
|
|
367
340
|
this.setError(buildRuntimeError(payload));
|
|
341
|
+
this.clearActiveRun();
|
|
368
342
|
}
|
|
369
|
-
handleRunMetadata(
|
|
343
|
+
handleRunMetadata(payload) {
|
|
344
|
+
const m = payload.metadata;
|
|
345
|
+
if (m?.kind === "ready") {
|
|
346
|
+
const ready = m;
|
|
347
|
+
this.activeRun = {
|
|
348
|
+
runId: ready.runId ?? this.activeRun?.runId ?? null,
|
|
349
|
+
sessionId: ready.sessionId ?? this.activeRun?.sessionId,
|
|
350
|
+
abortDisabledReason: ready.supportsAbort === false ? ready.abortDisabledReason ?? "Unsupported" : null
|
|
351
|
+
};
|
|
352
|
+
this.stateVersion += 1;
|
|
353
|
+
} else if (m?.kind === "final") {
|
|
354
|
+
this.clearActiveRun();
|
|
355
|
+
}
|
|
370
356
|
}
|
|
371
357
|
handleEndpointError(payload) {
|
|
372
358
|
this.setError(payload);
|
|
373
359
|
}
|
|
374
|
-
applyToolCallArgs(
|
|
375
|
-
const targetMessage = this.resolveToolCallTargetMessage(
|
|
360
|
+
applyToolCallArgs(sessionId, toolCallId, args, messageId) {
|
|
361
|
+
const targetMessage = this.resolveToolCallTargetMessage(sessionId, toolCallId, messageId);
|
|
376
362
|
const toolName = this.findToolNameByCallId(targetMessage.parts, toolCallId) ?? "unknown";
|
|
377
363
|
const nextParts = this.upsertToolInvocationPart(targetMessage.parts, {
|
|
378
364
|
type: "tool-invocation",
|
|
@@ -387,7 +373,7 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
387
373
|
status: "streaming"
|
|
388
374
|
});
|
|
389
375
|
}
|
|
390
|
-
ensureStreamingMessage(
|
|
376
|
+
ensureStreamingMessage(sessionId, messageId, status) {
|
|
391
377
|
if (this.streamingMessage?.id === messageId) {
|
|
392
378
|
if (this.streamingMessage.status === status) {
|
|
393
379
|
return this.streamingMessage;
|
|
@@ -399,9 +385,24 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
399
385
|
this.replaceStreamingMessage(nextStreamingMessage2);
|
|
400
386
|
return nextStreamingMessage2;
|
|
401
387
|
}
|
|
388
|
+
const messageIndex = this.messages.findIndex((message) => message.id === messageId);
|
|
389
|
+
if (messageIndex >= 0) {
|
|
390
|
+
const existingMessage = cloneMessage(this.messages[messageIndex]);
|
|
391
|
+
const nextMessages = [...this.messages];
|
|
392
|
+
nextMessages.splice(messageIndex, 1);
|
|
393
|
+
this.messages = nextMessages;
|
|
394
|
+
this.stateVersion += 1;
|
|
395
|
+
const nextStreamingMessage2 = {
|
|
396
|
+
...existingMessage,
|
|
397
|
+
sessionId,
|
|
398
|
+
status
|
|
399
|
+
};
|
|
400
|
+
this.replaceStreamingMessage(nextStreamingMessage2);
|
|
401
|
+
return nextStreamingMessage2;
|
|
402
|
+
}
|
|
402
403
|
const nextStreamingMessage = {
|
|
403
404
|
id: messageId,
|
|
404
|
-
|
|
405
|
+
sessionId,
|
|
405
406
|
role: DEFAULT_ASSISTANT_ROLE,
|
|
406
407
|
status,
|
|
407
408
|
parts: [],
|
|
@@ -410,10 +411,10 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
410
411
|
this.replaceStreamingMessage(nextStreamingMessage);
|
|
411
412
|
return nextStreamingMessage;
|
|
412
413
|
}
|
|
413
|
-
resolveToolCallTargetMessage(
|
|
414
|
+
resolveToolCallTargetMessage(sessionId, toolCallId, messageId) {
|
|
414
415
|
const preferredMessageId = messageId?.trim() || this.toolCallMessageIdByCallId.get(toolCallId) || this.streamingMessage?.id || `tool-${toolCallId}`;
|
|
415
416
|
this.toolCallMessageIdByCallId.set(toolCallId, preferredMessageId);
|
|
416
|
-
return this.ensureStreamingMessage(
|
|
417
|
+
return this.ensureStreamingMessage(sessionId, preferredMessageId, "streaming");
|
|
417
418
|
}
|
|
418
419
|
updateMessageContainingToolCall(toolCallId, updater) {
|
|
419
420
|
if (this.streamingMessage) {
|
|
@@ -469,14 +470,6 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
469
470
|
nextParts.push(toolPart);
|
|
470
471
|
return nextParts;
|
|
471
472
|
}
|
|
472
|
-
findLastReasoningPartIndex(parts) {
|
|
473
|
-
for (let index = parts.length - 1; index >= 0; index -= 1) {
|
|
474
|
-
if (parts[index]?.type === "reasoning") {
|
|
475
|
-
return index;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
return -1;
|
|
479
|
-
}
|
|
480
473
|
upsertMessage(message) {
|
|
481
474
|
const normalizedMessage = cloneMessage(message);
|
|
482
475
|
const messageIndex = this.messages.findIndex((item) => item.id === normalizedMessage.id);
|
|
@@ -505,6 +498,13 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
505
498
|
this.error = nextError ? { ...nextError, details: nextError.details ? { ...nextError.details } : void 0 } : null;
|
|
506
499
|
this.stateVersion += 1;
|
|
507
500
|
}
|
|
501
|
+
clearActiveRun() {
|
|
502
|
+
if (!this.activeRun) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
this.activeRun = null;
|
|
506
|
+
this.stateVersion += 1;
|
|
507
|
+
}
|
|
508
508
|
clearToolCallTrackingByMessageId(messageId) {
|
|
509
509
|
for (const [toolCallId, trackedMessageId] of this.toolCallMessageIdByCallId) {
|
|
510
510
|
if (trackedMessageId !== messageId) {
|
|
@@ -514,7 +514,638 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
514
514
|
this.toolCallArgsRawByCallId.delete(toolCallId);
|
|
515
515
|
}
|
|
516
516
|
}
|
|
517
|
+
notifyListeners() {
|
|
518
|
+
const snapshot = this.getSnapshot();
|
|
519
|
+
for (const listener of this.listeners) {
|
|
520
|
+
listener(snapshot);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
// src/agent/agent-client-from-server.ts
|
|
526
|
+
import {
|
|
527
|
+
NcpEventType as NcpEventType2
|
|
528
|
+
} from "@nextclaw/ncp";
|
|
529
|
+
function createAgentClientFromServer(server) {
|
|
530
|
+
return {
|
|
531
|
+
get manifest() {
|
|
532
|
+
return server.manifest;
|
|
533
|
+
},
|
|
534
|
+
async start() {
|
|
535
|
+
await server.start();
|
|
536
|
+
},
|
|
537
|
+
async stop() {
|
|
538
|
+
await server.stop();
|
|
539
|
+
},
|
|
540
|
+
async emit(event) {
|
|
541
|
+
switch (event.type) {
|
|
542
|
+
case NcpEventType2.MessageRequest:
|
|
543
|
+
await consume(server.send(event.payload));
|
|
544
|
+
return;
|
|
545
|
+
case NcpEventType2.MessageStreamRequest:
|
|
546
|
+
await consume(server.stream(event.payload));
|
|
547
|
+
return;
|
|
548
|
+
case NcpEventType2.MessageAbort:
|
|
549
|
+
await server.abort(event.payload);
|
|
550
|
+
return;
|
|
551
|
+
default:
|
|
552
|
+
await server.emit(event);
|
|
553
|
+
}
|
|
554
|
+
},
|
|
555
|
+
subscribe(listener) {
|
|
556
|
+
return server.subscribe(listener);
|
|
557
|
+
},
|
|
558
|
+
async send(envelope) {
|
|
559
|
+
await consume(server.send(envelope));
|
|
560
|
+
},
|
|
561
|
+
async stream(payload) {
|
|
562
|
+
await consume(server.stream(payload));
|
|
563
|
+
},
|
|
564
|
+
async abort(payload) {
|
|
565
|
+
await server.abort(payload);
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
async function consume(events) {
|
|
570
|
+
for await (const event of events) {
|
|
571
|
+
void event;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// src/agent/agent-backend/agent-backend.ts
|
|
576
|
+
import {
|
|
577
|
+
NcpEventType as NcpEventType4
|
|
578
|
+
} from "@nextclaw/ncp";
|
|
579
|
+
|
|
580
|
+
// src/errors/ncp-error-exception.ts
|
|
581
|
+
var NcpErrorException = class extends Error {
|
|
582
|
+
code;
|
|
583
|
+
details;
|
|
584
|
+
constructor(code, message, details) {
|
|
585
|
+
super(message);
|
|
586
|
+
this.name = "NcpErrorException";
|
|
587
|
+
this.code = code;
|
|
588
|
+
this.details = details;
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
// src/agent/agent-backend/agent-live-session-registry.ts
|
|
593
|
+
var AgentLiveSessionRegistry = class {
|
|
594
|
+
constructor(sessionStore, createRuntime) {
|
|
595
|
+
this.sessionStore = sessionStore;
|
|
596
|
+
this.createRuntime = createRuntime;
|
|
597
|
+
}
|
|
598
|
+
sessions = /* @__PURE__ */ new Map();
|
|
599
|
+
async ensureSession(sessionId) {
|
|
600
|
+
const existing = this.sessions.get(sessionId);
|
|
601
|
+
if (existing) {
|
|
602
|
+
return existing;
|
|
603
|
+
}
|
|
604
|
+
const storedSession = await this.sessionStore.getSession(sessionId);
|
|
605
|
+
const stateManager = new DefaultNcpAgentConversationStateManager();
|
|
606
|
+
stateManager.hydrate({
|
|
607
|
+
sessionId,
|
|
608
|
+
messages: cloneMessages(storedSession?.messages ?? [])
|
|
609
|
+
});
|
|
610
|
+
const session = {
|
|
611
|
+
sessionId,
|
|
612
|
+
stateManager,
|
|
613
|
+
runtime: this.createRuntime({ sessionId, stateManager }),
|
|
614
|
+
activeExecution: null
|
|
615
|
+
};
|
|
616
|
+
this.sessions.set(sessionId, session);
|
|
617
|
+
return session;
|
|
618
|
+
}
|
|
619
|
+
getSession(sessionId) {
|
|
620
|
+
return this.sessions.get(sessionId) ?? null;
|
|
621
|
+
}
|
|
622
|
+
deleteSession(sessionId) {
|
|
623
|
+
const session = this.sessions.get(sessionId) ?? null;
|
|
624
|
+
if (session) {
|
|
625
|
+
this.sessions.delete(sessionId);
|
|
626
|
+
}
|
|
627
|
+
return session;
|
|
628
|
+
}
|
|
629
|
+
clear() {
|
|
630
|
+
this.sessions.clear();
|
|
631
|
+
}
|
|
632
|
+
listSessions() {
|
|
633
|
+
return [...this.sessions.values()];
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
function cloneMessages(messages) {
|
|
637
|
+
return messages.map((message) => structuredClone(message));
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// src/agent/agent-backend/agent-run-executor.ts
|
|
641
|
+
import { NcpEventType as NcpEventType3 } from "@nextclaw/ncp";
|
|
642
|
+
var AgentRunExecutor = class {
|
|
643
|
+
constructor(persistSession) {
|
|
644
|
+
this.persistSession = persistSession;
|
|
645
|
+
}
|
|
646
|
+
async *executeRun(session, envelope, controller) {
|
|
647
|
+
const messageSent = {
|
|
648
|
+
type: NcpEventType3.MessageSent,
|
|
649
|
+
payload: {
|
|
650
|
+
sessionId: envelope.sessionId,
|
|
651
|
+
message: structuredClone(envelope.message),
|
|
652
|
+
metadata: envelope.metadata
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
let messageSentPublished = false;
|
|
656
|
+
try {
|
|
657
|
+
for await (const event of session.runtime.run(
|
|
658
|
+
{
|
|
659
|
+
sessionId: envelope.sessionId,
|
|
660
|
+
messages: [envelope.message],
|
|
661
|
+
correlationId: envelope.correlationId
|
|
662
|
+
},
|
|
663
|
+
{ signal: controller.signal }
|
|
664
|
+
)) {
|
|
665
|
+
if (!messageSentPublished) {
|
|
666
|
+
messageSentPublished = true;
|
|
667
|
+
await this.persistSession(envelope.sessionId);
|
|
668
|
+
yield structuredClone(messageSent);
|
|
669
|
+
}
|
|
670
|
+
await this.persistSession(envelope.sessionId);
|
|
671
|
+
yield structuredClone(event);
|
|
672
|
+
}
|
|
673
|
+
} catch (error) {
|
|
674
|
+
if (!controller.signal.aborted) {
|
|
675
|
+
const runErrorEvent = await this.publishFailure(error, envelope, session);
|
|
676
|
+
yield structuredClone(runErrorEvent);
|
|
677
|
+
}
|
|
678
|
+
} finally {
|
|
679
|
+
await this.persistSession(envelope.sessionId);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
async publishFailure(error, envelope, session) {
|
|
683
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
684
|
+
const runErrorEvent = {
|
|
685
|
+
type: NcpEventType3.RunError,
|
|
686
|
+
payload: {
|
|
687
|
+
sessionId: envelope.sessionId,
|
|
688
|
+
error: message
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
await session.stateManager.dispatch(runErrorEvent);
|
|
692
|
+
await this.persistSession(envelope.sessionId);
|
|
693
|
+
return runErrorEvent;
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
// src/agent/agent-backend/async-queue.ts
|
|
698
|
+
function createAsyncQueue() {
|
|
699
|
+
const items = [];
|
|
700
|
+
let closed = false;
|
|
701
|
+
let pendingResolve = null;
|
|
702
|
+
const iterable = {
|
|
703
|
+
[Symbol.asyncIterator]() {
|
|
704
|
+
return {
|
|
705
|
+
next: () => {
|
|
706
|
+
if (items.length > 0) {
|
|
707
|
+
return Promise.resolve({
|
|
708
|
+
value: items.shift(),
|
|
709
|
+
done: false
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
if (closed) {
|
|
713
|
+
return Promise.resolve({
|
|
714
|
+
value: void 0,
|
|
715
|
+
done: true
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
return new Promise((resolve) => {
|
|
719
|
+
pendingResolve = resolve;
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
return {
|
|
726
|
+
push(item) {
|
|
727
|
+
if (closed) {
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
if (pendingResolve) {
|
|
731
|
+
const resolve = pendingResolve;
|
|
732
|
+
pendingResolve = null;
|
|
733
|
+
resolve({ value: item, done: false });
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
items.push(item);
|
|
737
|
+
},
|
|
738
|
+
close() {
|
|
739
|
+
if (closed) {
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
closed = true;
|
|
743
|
+
if (pendingResolve) {
|
|
744
|
+
const resolve = pendingResolve;
|
|
745
|
+
pendingResolve = null;
|
|
746
|
+
resolve({
|
|
747
|
+
value: void 0,
|
|
748
|
+
done: true
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
},
|
|
752
|
+
iterable
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// src/agent/agent-backend/event-publisher.ts
|
|
757
|
+
var EventPublisher = class {
|
|
758
|
+
listeners = /* @__PURE__ */ new Set();
|
|
759
|
+
closeListeners = /* @__PURE__ */ new Set();
|
|
760
|
+
closed = false;
|
|
761
|
+
subscribe(listener) {
|
|
762
|
+
if (this.closed) {
|
|
763
|
+
return () => void 0;
|
|
764
|
+
}
|
|
765
|
+
this.listeners.add(listener);
|
|
766
|
+
return () => {
|
|
767
|
+
this.listeners.delete(listener);
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
onClose(listener) {
|
|
771
|
+
if (this.closed) {
|
|
772
|
+
listener();
|
|
773
|
+
return () => void 0;
|
|
774
|
+
}
|
|
775
|
+
this.closeListeners.add(listener);
|
|
776
|
+
return () => {
|
|
777
|
+
this.closeListeners.delete(listener);
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
publish(event) {
|
|
781
|
+
if (this.closed) {
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
for (const listener of this.listeners) {
|
|
785
|
+
listener(structuredClone(event));
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
close() {
|
|
789
|
+
if (this.closed) {
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
this.closed = true;
|
|
793
|
+
this.listeners.clear();
|
|
794
|
+
for (const listener of this.closeListeners) {
|
|
795
|
+
listener();
|
|
796
|
+
}
|
|
797
|
+
this.closeListeners.clear();
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
|
|
801
|
+
// src/agent/agent-backend/agent-backend.ts
|
|
802
|
+
var DEFAULT_SUPPORTED_PART_TYPES = [
|
|
803
|
+
"text",
|
|
804
|
+
"file",
|
|
805
|
+
"source",
|
|
806
|
+
"step-start",
|
|
807
|
+
"reasoning",
|
|
808
|
+
"tool-invocation",
|
|
809
|
+
"card",
|
|
810
|
+
"rich-text",
|
|
811
|
+
"action",
|
|
812
|
+
"extension"
|
|
813
|
+
];
|
|
814
|
+
var DefaultNcpAgentBackend = class {
|
|
815
|
+
manifest;
|
|
816
|
+
sessionStore;
|
|
817
|
+
sessionRegistry;
|
|
818
|
+
executor;
|
|
819
|
+
publisher;
|
|
820
|
+
started = false;
|
|
821
|
+
constructor(config) {
|
|
822
|
+
this.sessionStore = config.sessionStore;
|
|
823
|
+
this.sessionRegistry = new AgentLiveSessionRegistry(
|
|
824
|
+
this.sessionStore,
|
|
825
|
+
config.createRuntime
|
|
826
|
+
);
|
|
827
|
+
this.executor = new AgentRunExecutor(async (sessionId) => this.persistSession(sessionId));
|
|
828
|
+
this.publisher = new EventPublisher();
|
|
829
|
+
this.manifest = {
|
|
830
|
+
endpointKind: "agent",
|
|
831
|
+
endpointId: config.endpointId?.trim() || "ncp-agent-backend",
|
|
832
|
+
version: config.version?.trim() || "0.1.0",
|
|
833
|
+
supportsStreaming: true,
|
|
834
|
+
supportsAbort: true,
|
|
835
|
+
supportsProactiveMessages: false,
|
|
836
|
+
supportsLiveSessionStream: true,
|
|
837
|
+
supportedPartTypes: config.supportedPartTypes ?? DEFAULT_SUPPORTED_PART_TYPES,
|
|
838
|
+
expectedLatency: config.expectedLatency ?? "seconds",
|
|
839
|
+
metadata: config.metadata
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
async start() {
|
|
843
|
+
if (this.started) {
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
this.started = true;
|
|
847
|
+
this.publisher.publish({ type: NcpEventType4.EndpointReady });
|
|
848
|
+
}
|
|
849
|
+
async stop() {
|
|
850
|
+
if (!this.started) {
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
this.started = false;
|
|
854
|
+
for (const session of this.sessionRegistry.listSessions()) {
|
|
855
|
+
const execution = session.activeExecution;
|
|
856
|
+
if (!execution) {
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
859
|
+
execution.abortHandled = true;
|
|
860
|
+
execution.controller.abort();
|
|
861
|
+
this.finishSessionExecution(session, execution);
|
|
862
|
+
}
|
|
863
|
+
this.sessionRegistry.clear();
|
|
864
|
+
}
|
|
865
|
+
async emit(event) {
|
|
866
|
+
await this.ensureStarted();
|
|
867
|
+
switch (event.type) {
|
|
868
|
+
case NcpEventType4.MessageRequest:
|
|
869
|
+
await this.handleRequest(event.payload);
|
|
870
|
+
return;
|
|
871
|
+
case NcpEventType4.MessageStreamRequest:
|
|
872
|
+
await this.streamToSubscribers(event.payload);
|
|
873
|
+
return;
|
|
874
|
+
case NcpEventType4.MessageAbort:
|
|
875
|
+
await this.handleAbort(event.payload);
|
|
876
|
+
return;
|
|
877
|
+
default:
|
|
878
|
+
this.publisher.publish(event);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
subscribe(listener) {
|
|
882
|
+
return this.publisher.subscribe(listener);
|
|
883
|
+
}
|
|
884
|
+
async *send(envelope, options) {
|
|
885
|
+
await this.ensureStarted();
|
|
886
|
+
const session = await this.sessionRegistry.ensureSession(envelope.sessionId);
|
|
887
|
+
const execution = this.startSessionExecution(session, envelope, options?.signal);
|
|
888
|
+
try {
|
|
889
|
+
for await (const event of this.executor.executeRun(session, envelope, execution.controller)) {
|
|
890
|
+
this.publishLiveEvent(execution, event);
|
|
891
|
+
yield event;
|
|
892
|
+
}
|
|
893
|
+
if (execution.controller.signal.aborted && !execution.abortHandled) {
|
|
894
|
+
const abortEvent = await this.createAbortEvent(session.sessionId);
|
|
895
|
+
execution.abortHandled = true;
|
|
896
|
+
this.publishLiveEvent(execution, abortEvent);
|
|
897
|
+
yield abortEvent;
|
|
898
|
+
}
|
|
899
|
+
} finally {
|
|
900
|
+
this.finishSessionExecution(session, execution);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
async abort(payload) {
|
|
904
|
+
await this.handleAbort(payload);
|
|
905
|
+
}
|
|
906
|
+
async *stream(payloadOrParams, opts) {
|
|
907
|
+
const payload = "payload" in payloadOrParams && "signal" in payloadOrParams ? payloadOrParams.payload : payloadOrParams;
|
|
908
|
+
const signal = "payload" in payloadOrParams && "signal" in payloadOrParams ? payloadOrParams.signal : opts?.signal ?? new AbortController().signal;
|
|
909
|
+
const session = this.sessionRegistry.getSession(payload.sessionId);
|
|
910
|
+
const execution = session?.activeExecution;
|
|
911
|
+
if (!session || !execution || execution.closed) {
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
const queue = createAsyncQueue();
|
|
915
|
+
const unsubscribe = execution.publisher.subscribe((event) => {
|
|
916
|
+
queue.push(event);
|
|
917
|
+
});
|
|
918
|
+
const unsubscribeClose = execution.publisher.onClose(() => {
|
|
919
|
+
queue.close();
|
|
920
|
+
});
|
|
921
|
+
const stop = () => {
|
|
922
|
+
unsubscribe();
|
|
923
|
+
unsubscribeClose();
|
|
924
|
+
queue.close();
|
|
925
|
+
signal.removeEventListener("abort", stop);
|
|
926
|
+
};
|
|
927
|
+
signal.addEventListener("abort", stop, { once: true });
|
|
928
|
+
try {
|
|
929
|
+
for await (const event of queue.iterable) {
|
|
930
|
+
if (signal.aborted) {
|
|
931
|
+
break;
|
|
932
|
+
}
|
|
933
|
+
yield event;
|
|
934
|
+
if (isTerminalEvent(event)) {
|
|
935
|
+
break;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
} finally {
|
|
939
|
+
stop();
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
async listSessions() {
|
|
943
|
+
const storedSessions = await this.sessionStore.listSessions();
|
|
944
|
+
const summaries = storedSessions.map(
|
|
945
|
+
(session) => toSessionSummary(session, this.sessionRegistry.getSession(session.sessionId))
|
|
946
|
+
);
|
|
947
|
+
for (const liveSession of this.sessionRegistry.listSessions()) {
|
|
948
|
+
if (summaries.some((session) => session.sessionId === liveSession.sessionId)) {
|
|
949
|
+
continue;
|
|
950
|
+
}
|
|
951
|
+
summaries.push(toLiveSessionSummary(liveSession));
|
|
952
|
+
}
|
|
953
|
+
return summaries.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
954
|
+
}
|
|
955
|
+
async listSessionMessages(sessionId) {
|
|
956
|
+
const liveSession = this.sessionRegistry.getSession(sessionId);
|
|
957
|
+
if (liveSession) {
|
|
958
|
+
return readMessages(liveSession.stateManager.getSnapshot());
|
|
959
|
+
}
|
|
960
|
+
const session = await this.sessionStore.getSession(sessionId);
|
|
961
|
+
return session ? session.messages.map((message) => structuredClone(message)) : [];
|
|
962
|
+
}
|
|
963
|
+
async getSession(sessionId) {
|
|
964
|
+
const liveSession = this.sessionRegistry.getSession(sessionId);
|
|
965
|
+
const storedSession = await this.sessionStore.getSession(sessionId);
|
|
966
|
+
if (storedSession) {
|
|
967
|
+
return toSessionSummary(storedSession, liveSession);
|
|
968
|
+
}
|
|
969
|
+
if (liveSession) {
|
|
970
|
+
return toLiveSessionSummary(liveSession);
|
|
971
|
+
}
|
|
972
|
+
return null;
|
|
973
|
+
}
|
|
974
|
+
async deleteSession(sessionId) {
|
|
975
|
+
const liveSession = this.sessionRegistry.deleteSession(sessionId);
|
|
976
|
+
const execution = liveSession?.activeExecution;
|
|
977
|
+
if (execution) {
|
|
978
|
+
execution.abortHandled = true;
|
|
979
|
+
execution.controller.abort();
|
|
980
|
+
this.closeExecution(execution);
|
|
981
|
+
}
|
|
982
|
+
await this.sessionStore.deleteSession(sessionId);
|
|
983
|
+
}
|
|
984
|
+
async ensureStarted() {
|
|
985
|
+
if (!this.started) {
|
|
986
|
+
await this.start();
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
startSessionExecution(session, envelope, signal) {
|
|
990
|
+
if (session.activeExecution && !session.activeExecution.closed) {
|
|
991
|
+
throw new NcpErrorException(
|
|
992
|
+
"runtime-error",
|
|
993
|
+
`Session ${session.sessionId} already has an active execution.`,
|
|
994
|
+
{ sessionId: session.sessionId }
|
|
995
|
+
);
|
|
996
|
+
}
|
|
997
|
+
const controller = new AbortController();
|
|
998
|
+
if (signal) {
|
|
999
|
+
signal.addEventListener("abort", () => controller.abort(), {
|
|
1000
|
+
once: true
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
const execution = {
|
|
1004
|
+
controller,
|
|
1005
|
+
publisher: new EventPublisher(),
|
|
1006
|
+
requestEnvelope: structuredClone(envelope),
|
|
1007
|
+
abortHandled: false,
|
|
1008
|
+
closed: false
|
|
1009
|
+
};
|
|
1010
|
+
session.activeExecution = execution;
|
|
1011
|
+
return execution;
|
|
1012
|
+
}
|
|
1013
|
+
finishSessionExecution(session, execution) {
|
|
1014
|
+
if (session.activeExecution === execution) {
|
|
1015
|
+
session.activeExecution = null;
|
|
1016
|
+
}
|
|
1017
|
+
this.closeExecution(execution);
|
|
1018
|
+
}
|
|
1019
|
+
closeExecution(execution) {
|
|
1020
|
+
if (execution.closed) {
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
execution.closed = true;
|
|
1024
|
+
execution.publisher.close();
|
|
1025
|
+
}
|
|
1026
|
+
publishLiveEvent(execution, event) {
|
|
1027
|
+
this.publisher.publish(event);
|
|
1028
|
+
if (!execution.closed) {
|
|
1029
|
+
execution.publisher.publish(event);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
async handleRequest(envelope) {
|
|
1033
|
+
for await (const event of this.send(envelope)) {
|
|
1034
|
+
void event;
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
async streamToSubscribers(payload) {
|
|
1038
|
+
const signal = new AbortController().signal;
|
|
1039
|
+
for await (const event of this.stream({ payload, signal })) {
|
|
1040
|
+
void event;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
async handleAbort(payload) {
|
|
1044
|
+
const session = this.sessionRegistry.getSession(payload.sessionId);
|
|
1045
|
+
const execution = session?.activeExecution;
|
|
1046
|
+
if (!session || !execution || execution.closed) {
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
execution.abortHandled = true;
|
|
1050
|
+
execution.controller.abort();
|
|
1051
|
+
const abortEvent = await this.createAbortEvent(payload.sessionId, payload.messageId);
|
|
1052
|
+
this.publishLiveEvent(execution, abortEvent);
|
|
1053
|
+
this.finishSessionExecution(session, execution);
|
|
1054
|
+
}
|
|
1055
|
+
async createAbortEvent(sessionId, messageId) {
|
|
1056
|
+
const abortEvent = {
|
|
1057
|
+
type: NcpEventType4.MessageAbort,
|
|
1058
|
+
payload: {
|
|
1059
|
+
sessionId,
|
|
1060
|
+
...messageId ? { messageId } : {}
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1063
|
+
const liveSession = this.sessionRegistry.getSession(sessionId);
|
|
1064
|
+
if (liveSession) {
|
|
1065
|
+
await liveSession.stateManager.dispatch(abortEvent);
|
|
1066
|
+
}
|
|
1067
|
+
await this.persistSession(sessionId);
|
|
1068
|
+
return abortEvent;
|
|
1069
|
+
}
|
|
1070
|
+
async persistSession(sessionId) {
|
|
1071
|
+
const session = this.sessionRegistry.getSession(sessionId);
|
|
1072
|
+
if (!session) {
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
const snapshot = session.stateManager.getSnapshot();
|
|
1076
|
+
await this.sessionStore.saveSession({
|
|
1077
|
+
sessionId,
|
|
1078
|
+
messages: readMessages(snapshot),
|
|
1079
|
+
updatedAt: now()
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
};
|
|
1083
|
+
function readMessages(snapshot) {
|
|
1084
|
+
const messages = snapshot.messages.map((message) => structuredClone(message));
|
|
1085
|
+
if (snapshot.streamingMessage) {
|
|
1086
|
+
messages.push(structuredClone(snapshot.streamingMessage));
|
|
1087
|
+
}
|
|
1088
|
+
return messages;
|
|
1089
|
+
}
|
|
1090
|
+
function toSessionSummary(session, liveSession) {
|
|
1091
|
+
return {
|
|
1092
|
+
sessionId: session.sessionId,
|
|
1093
|
+
messageCount: session.messages.length,
|
|
1094
|
+
updatedAt: session.updatedAt,
|
|
1095
|
+
status: liveSession?.activeExecution ? "running" : "idle"
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
function toLiveSessionSummary(session) {
|
|
1099
|
+
const snapshot = session.stateManager.getSnapshot();
|
|
1100
|
+
return {
|
|
1101
|
+
sessionId: session.sessionId,
|
|
1102
|
+
messageCount: readMessages(snapshot).length,
|
|
1103
|
+
updatedAt: now(),
|
|
1104
|
+
status: session.activeExecution ? "running" : "idle"
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
function now() {
|
|
1108
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1109
|
+
}
|
|
1110
|
+
function isTerminalEvent(event) {
|
|
1111
|
+
switch (event.type) {
|
|
1112
|
+
case NcpEventType4.MessageAbort:
|
|
1113
|
+
case NcpEventType4.RunFinished:
|
|
1114
|
+
case NcpEventType4.RunError:
|
|
1115
|
+
return true;
|
|
1116
|
+
default:
|
|
1117
|
+
return false;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// src/agent/agent-backend/in-memory-agent-session-store.ts
|
|
1122
|
+
var InMemoryAgentSessionStore = class {
|
|
1123
|
+
sessions = /* @__PURE__ */ new Map();
|
|
1124
|
+
async getSession(sessionId) {
|
|
1125
|
+
const session = this.sessions.get(sessionId);
|
|
1126
|
+
return session ? structuredClone(session) : null;
|
|
1127
|
+
}
|
|
1128
|
+
async listSessions() {
|
|
1129
|
+
return [...this.sessions.values()].map((session) => structuredClone(session));
|
|
1130
|
+
}
|
|
1131
|
+
async saveSession(session) {
|
|
1132
|
+
this.sessions.set(session.sessionId, structuredClone(session));
|
|
1133
|
+
}
|
|
1134
|
+
async deleteSession(sessionId) {
|
|
1135
|
+
const session = this.sessions.get(sessionId);
|
|
1136
|
+
if (!session) {
|
|
1137
|
+
return null;
|
|
1138
|
+
}
|
|
1139
|
+
this.sessions.delete(sessionId);
|
|
1140
|
+
return structuredClone(session);
|
|
1141
|
+
}
|
|
517
1142
|
};
|
|
518
1143
|
export {
|
|
519
|
-
|
|
1144
|
+
AgentRunExecutor,
|
|
1145
|
+
DefaultNcpAgentBackend,
|
|
1146
|
+
DefaultNcpAgentConversationStateManager,
|
|
1147
|
+
EventPublisher,
|
|
1148
|
+
InMemoryAgentSessionStore,
|
|
1149
|
+
NcpErrorException,
|
|
1150
|
+
createAgentClientFromServer
|
|
520
1151
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/ncp-toolkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Toolkit implementations built on top of the NextClaw Communication Protocol.",
|
|
6
6
|
"type": "module",
|
|
@@ -15,18 +15,15 @@
|
|
|
15
15
|
"dist"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@nextclaw/ncp": "0.
|
|
18
|
+
"@nextclaw/ncp": "0.2.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@types/node": "^20.17.6",
|
|
22
|
-
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
23
|
-
"@typescript-eslint/parser": "^7.18.0",
|
|
24
|
-
"eslint": "^8.57.1",
|
|
25
|
-
"eslint-config-prettier": "^9.1.0",
|
|
26
22
|
"prettier": "^3.3.3",
|
|
27
23
|
"tsup": "^8.3.5",
|
|
28
24
|
"typescript": "^5.6.3",
|
|
29
|
-
"vitest": "^2.1.2"
|
|
25
|
+
"vitest": "^2.1.2",
|
|
26
|
+
"@nextclaw/ncp-agent-runtime": "0.1.1"
|
|
30
27
|
},
|
|
31
28
|
"scripts": {
|
|
32
29
|
"build": "tsup src/index.ts --format esm --dts --out-dir dist",
|