@nextclaw/ncp-toolkit 0.2.0 → 0.4.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/dist/index.d.ts +6 -5
- package/dist/index.js +249 -131
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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';
|
|
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, NcpSessionPatch, NcpErrorCode } from '@nextclaw/ncp';
|
|
2
2
|
|
|
3
3
|
declare class DefaultNcpAgentConversationStateManager implements NcpAgentConversationStateManager {
|
|
4
4
|
private messages;
|
|
@@ -38,14 +38,10 @@ declare class DefaultNcpAgentConversationStateManager implements NcpAgentConvers
|
|
|
38
38
|
private ensureStreamingMessage;
|
|
39
39
|
private resolveToolCallTargetMessage;
|
|
40
40
|
private updateMessageContainingToolCall;
|
|
41
|
-
private findToolInvocationPart;
|
|
42
|
-
private findToolNameByCallId;
|
|
43
|
-
private upsertToolInvocationPart;
|
|
44
41
|
private upsertMessage;
|
|
45
42
|
private replaceStreamingMessage;
|
|
46
43
|
private setError;
|
|
47
44
|
private clearActiveRun;
|
|
48
|
-
private clearToolCallTrackingByMessageId;
|
|
49
45
|
private notifyListeners;
|
|
50
46
|
}
|
|
51
47
|
|
|
@@ -68,12 +64,15 @@ declare class EventPublisher {
|
|
|
68
64
|
type RuntimeFactoryParams = {
|
|
69
65
|
sessionId: string;
|
|
70
66
|
stateManager: NcpAgentConversationStateManager;
|
|
67
|
+
sessionMetadata: Record<string, unknown>;
|
|
68
|
+
setSessionMetadata: (nextMetadata: Record<string, unknown>) => void;
|
|
71
69
|
};
|
|
72
70
|
type CreateRuntimeFn = (params: RuntimeFactoryParams) => NcpAgentRuntime;
|
|
73
71
|
type AgentSessionRecord = {
|
|
74
72
|
sessionId: string;
|
|
75
73
|
messages: NcpMessage[];
|
|
76
74
|
updatedAt: string;
|
|
75
|
+
metadata?: Record<string, unknown>;
|
|
77
76
|
};
|
|
78
77
|
type LiveSessionExecution = {
|
|
79
78
|
controller: AbortController;
|
|
@@ -86,6 +85,7 @@ type LiveSessionState = {
|
|
|
86
85
|
sessionId: string;
|
|
87
86
|
runtime: NcpAgentRuntime;
|
|
88
87
|
stateManager: NcpAgentConversationStateManager;
|
|
88
|
+
metadata: Record<string, unknown>;
|
|
89
89
|
activeExecution: LiveSessionExecution | null;
|
|
90
90
|
};
|
|
91
91
|
interface AgentSessionStore {
|
|
@@ -127,6 +127,7 @@ declare class DefaultNcpAgentBackend implements NcpAgentServerEndpoint, NcpSessi
|
|
|
127
127
|
listSessions(): Promise<NcpSessionSummary[]>;
|
|
128
128
|
listSessionMessages(sessionId: string): Promise<NcpMessage[]>;
|
|
129
129
|
getSession(sessionId: string): Promise<NcpSessionSummary | null>;
|
|
130
|
+
updateSession(sessionId: string, patch: NcpSessionPatch): Promise<NcpSessionSummary | null>;
|
|
130
131
|
deleteSession(sessionId: string): Promise<void>;
|
|
131
132
|
private ensureStarted;
|
|
132
133
|
private startSessionExecution;
|
package/dist/index.js
CHANGED
|
@@ -2,15 +2,22 @@
|
|
|
2
2
|
import {
|
|
3
3
|
NcpEventType
|
|
4
4
|
} from "@nextclaw/ncp";
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
|
|
6
|
+
// src/agent/agent-conversation-message-normalizer.ts
|
|
7
|
+
import { sanitizeAssistantReplyTags } from "@nextclaw/ncp";
|
|
8
|
+
function cloneConversationMessage(message) {
|
|
7
9
|
return {
|
|
8
10
|
...message,
|
|
9
11
|
parts: [...message.parts],
|
|
10
12
|
metadata: message.metadata ? { ...message.metadata } : void 0
|
|
11
13
|
};
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
+
}
|
|
15
|
+
function normalizeConversationMessage(message) {
|
|
16
|
+
return cloneConversationMessage(sanitizeAssistantReplyTags(message));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// src/agent/agent-conversation-state-manager.utils.ts
|
|
20
|
+
function buildRuntimeError(payload) {
|
|
14
21
|
const message = payload.error?.trim();
|
|
15
22
|
return {
|
|
16
23
|
code: "runtime-error",
|
|
@@ -22,7 +29,64 @@ var buildRuntimeError = (payload) => {
|
|
|
22
29
|
runId: payload.runId
|
|
23
30
|
}
|
|
24
31
|
};
|
|
25
|
-
}
|
|
32
|
+
}
|
|
33
|
+
function shouldPromoteStreamingMessageId(message, nextMessageId) {
|
|
34
|
+
if (!nextMessageId.trim()) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
if (message.id.startsWith("tool-")) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
return message.parts.some((part) => part.type === "tool-invocation");
|
|
41
|
+
}
|
|
42
|
+
function remapTrackedToolCallsToMessageId(toolCallMessageIdByCallId, fromMessageId, toMessageId) {
|
|
43
|
+
for (const [toolCallId, trackedMessageId] of toolCallMessageIdByCallId) {
|
|
44
|
+
if (trackedMessageId !== fromMessageId) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
toolCallMessageIdByCallId.set(toolCallId, toMessageId);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function clearToolCallTrackingByMessageId(toolCallMessageIdByCallId, toolCallArgsRawByCallId, messageId) {
|
|
51
|
+
for (const [toolCallId, trackedMessageId] of toolCallMessageIdByCallId) {
|
|
52
|
+
if (trackedMessageId !== messageId) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
toolCallMessageIdByCallId.delete(toolCallId);
|
|
56
|
+
toolCallArgsRawByCallId.delete(toolCallId);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function findToolInvocationPart(parts, toolCallId) {
|
|
60
|
+
for (let index = parts.length - 1; index >= 0; index -= 1) {
|
|
61
|
+
const part = parts[index];
|
|
62
|
+
if (part.type === "tool-invocation" && part.toolCallId === toolCallId) {
|
|
63
|
+
return part;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
function findToolNameByCallId(parts, toolCallId) {
|
|
69
|
+
const part = findToolInvocationPart(parts, toolCallId);
|
|
70
|
+
return part?.toolName ?? null;
|
|
71
|
+
}
|
|
72
|
+
function upsertToolInvocationPart(parts, toolPart) {
|
|
73
|
+
const nextParts = [...parts];
|
|
74
|
+
for (let index = nextParts.length - 1; index >= 0; index -= 1) {
|
|
75
|
+
const part = nextParts[index];
|
|
76
|
+
if (part.type === "tool-invocation" && part.toolCallId === toolPart.toolCallId) {
|
|
77
|
+
nextParts[index] = {
|
|
78
|
+
...part,
|
|
79
|
+
...toolPart
|
|
80
|
+
};
|
|
81
|
+
return nextParts;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
nextParts.push(toolPart);
|
|
85
|
+
return nextParts;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/agent/agent-conversation-state-manager.ts
|
|
89
|
+
var DEFAULT_ASSISTANT_ROLE = "assistant";
|
|
26
90
|
var DefaultNcpAgentConversationStateManager = class {
|
|
27
91
|
messages = [];
|
|
28
92
|
streamingMessage = null;
|
|
@@ -39,8 +103,8 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
39
103
|
return this.snapshotCache;
|
|
40
104
|
}
|
|
41
105
|
const snapshot = {
|
|
42
|
-
messages: this.messages.map((message) =>
|
|
43
|
-
streamingMessage: this.streamingMessage ?
|
|
106
|
+
messages: this.messages.map((message) => cloneConversationMessage(message)),
|
|
107
|
+
streamingMessage: this.streamingMessage ? cloneConversationMessage(this.streamingMessage) : null,
|
|
44
108
|
error: this.error ? { ...this.error, details: this.error.details ? { ...this.error.details } : void 0 } : null,
|
|
45
109
|
activeRun: this.activeRun ? { ...this.activeRun } : null
|
|
46
110
|
};
|
|
@@ -66,7 +130,7 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
66
130
|
this.notifyListeners();
|
|
67
131
|
}
|
|
68
132
|
hydrate(payload) {
|
|
69
|
-
this.messages = payload.messages.map((message) =>
|
|
133
|
+
this.messages = payload.messages.map((message) => normalizeConversationMessage(message));
|
|
70
134
|
this.streamingMessage = null;
|
|
71
135
|
this.error = null;
|
|
72
136
|
this.activeRun = payload.activeRun ? {
|
|
@@ -159,9 +223,17 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
159
223
|
});
|
|
160
224
|
this.replaceStreamingMessage(null);
|
|
161
225
|
if (targetMessageId) {
|
|
162
|
-
|
|
226
|
+
clearToolCallTrackingByMessageId(
|
|
227
|
+
this.toolCallMessageIdByCallId,
|
|
228
|
+
this.toolCallArgsRawByCallId,
|
|
229
|
+
targetMessageId
|
|
230
|
+
);
|
|
163
231
|
} else {
|
|
164
|
-
|
|
232
|
+
clearToolCallTrackingByMessageId(
|
|
233
|
+
this.toolCallMessageIdByCallId,
|
|
234
|
+
this.toolCallArgsRawByCallId,
|
|
235
|
+
streamingMessageId
|
|
236
|
+
);
|
|
165
237
|
}
|
|
166
238
|
}
|
|
167
239
|
}
|
|
@@ -238,7 +310,7 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
238
310
|
payload.messageId
|
|
239
311
|
);
|
|
240
312
|
this.toolCallArgsRawByCallId.set(payload.toolCallId, "");
|
|
241
|
-
const nextParts =
|
|
313
|
+
const nextParts = upsertToolInvocationPart(targetMessage.parts, {
|
|
242
314
|
type: "tool-invocation",
|
|
243
315
|
toolCallId: payload.toolCallId,
|
|
244
316
|
toolName: payload.toolName,
|
|
@@ -265,10 +337,10 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
265
337
|
handleMessageToolCallEnd(payload) {
|
|
266
338
|
const targetMessage = this.resolveToolCallTargetMessage(payload.sessionId, payload.toolCallId);
|
|
267
339
|
const args = this.toolCallArgsRawByCallId.get(payload.toolCallId) ?? "";
|
|
268
|
-
const nextParts =
|
|
340
|
+
const nextParts = upsertToolInvocationPart(targetMessage.parts, {
|
|
269
341
|
type: "tool-invocation",
|
|
270
342
|
toolCallId: payload.toolCallId,
|
|
271
|
-
toolName:
|
|
343
|
+
toolName: findToolNameByCallId(targetMessage.parts, payload.toolCallId) ?? "unknown",
|
|
272
344
|
state: "call",
|
|
273
345
|
args
|
|
274
346
|
});
|
|
@@ -288,11 +360,11 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
288
360
|
args: existingPart.args,
|
|
289
361
|
result: payload.content
|
|
290
362
|
};
|
|
291
|
-
return
|
|
363
|
+
return upsertToolInvocationPart(targetMessage.parts, mergedPart);
|
|
292
364
|
});
|
|
293
365
|
if (!updated) {
|
|
294
366
|
const fallbackMessage = this.resolveToolCallTargetMessage(payload.sessionId, payload.toolCallId);
|
|
295
|
-
const nextParts =
|
|
367
|
+
const nextParts = upsertToolInvocationPart(fallbackMessage.parts, {
|
|
296
368
|
type: "tool-invocation",
|
|
297
369
|
toolCallId: payload.toolCallId,
|
|
298
370
|
toolName: "unknown",
|
|
@@ -322,7 +394,11 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
322
394
|
};
|
|
323
395
|
this.upsertMessage(finalizedMessage);
|
|
324
396
|
this.replaceStreamingMessage(null);
|
|
325
|
-
|
|
397
|
+
clearToolCallTrackingByMessageId(
|
|
398
|
+
this.toolCallMessageIdByCallId,
|
|
399
|
+
this.toolCallArgsRawByCallId,
|
|
400
|
+
finalizedMessage.id
|
|
401
|
+
);
|
|
326
402
|
}
|
|
327
403
|
this.setError(null);
|
|
328
404
|
this.clearActiveRun();
|
|
@@ -335,7 +411,11 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
335
411
|
};
|
|
336
412
|
this.upsertMessage(failedMessage);
|
|
337
413
|
this.replaceStreamingMessage(null);
|
|
338
|
-
|
|
414
|
+
clearToolCallTrackingByMessageId(
|
|
415
|
+
this.toolCallMessageIdByCallId,
|
|
416
|
+
this.toolCallArgsRawByCallId,
|
|
417
|
+
failedMessage.id
|
|
418
|
+
);
|
|
339
419
|
}
|
|
340
420
|
this.setError(buildRuntimeError(payload));
|
|
341
421
|
this.clearActiveRun();
|
|
@@ -359,8 +439,8 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
359
439
|
}
|
|
360
440
|
applyToolCallArgs(sessionId, toolCallId, args, messageId) {
|
|
361
441
|
const targetMessage = this.resolveToolCallTargetMessage(sessionId, toolCallId, messageId);
|
|
362
|
-
const toolName =
|
|
363
|
-
const nextParts =
|
|
442
|
+
const toolName = findToolNameByCallId(targetMessage.parts, toolCallId) ?? "unknown";
|
|
443
|
+
const nextParts = upsertToolInvocationPart(targetMessage.parts, {
|
|
364
444
|
type: "tool-invocation",
|
|
365
445
|
toolCallId,
|
|
366
446
|
toolName,
|
|
@@ -387,7 +467,7 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
387
467
|
}
|
|
388
468
|
const messageIndex = this.messages.findIndex((message) => message.id === messageId);
|
|
389
469
|
if (messageIndex >= 0) {
|
|
390
|
-
const existingMessage =
|
|
470
|
+
const existingMessage = cloneConversationMessage(this.messages[messageIndex]);
|
|
391
471
|
const nextMessages = [...this.messages];
|
|
392
472
|
nextMessages.splice(messageIndex, 1);
|
|
393
473
|
this.messages = nextMessages;
|
|
@@ -400,6 +480,22 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
400
480
|
this.replaceStreamingMessage(nextStreamingMessage2);
|
|
401
481
|
return nextStreamingMessage2;
|
|
402
482
|
}
|
|
483
|
+
const existingStreamingMessage = this.streamingMessage;
|
|
484
|
+
if (existingStreamingMessage && existingStreamingMessage.id !== messageId && existingStreamingMessage.sessionId === sessionId && shouldPromoteStreamingMessageId(existingStreamingMessage, messageId)) {
|
|
485
|
+
const nextStreamingMessage2 = {
|
|
486
|
+
...existingStreamingMessage,
|
|
487
|
+
id: messageId,
|
|
488
|
+
sessionId,
|
|
489
|
+
status
|
|
490
|
+
};
|
|
491
|
+
remapTrackedToolCallsToMessageId(
|
|
492
|
+
this.toolCallMessageIdByCallId,
|
|
493
|
+
existingStreamingMessage.id,
|
|
494
|
+
messageId
|
|
495
|
+
);
|
|
496
|
+
this.replaceStreamingMessage(nextStreamingMessage2);
|
|
497
|
+
return nextStreamingMessage2;
|
|
498
|
+
}
|
|
403
499
|
const nextStreamingMessage = {
|
|
404
500
|
id: messageId,
|
|
405
501
|
sessionId,
|
|
@@ -418,7 +514,7 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
418
514
|
}
|
|
419
515
|
updateMessageContainingToolCall(toolCallId, updater) {
|
|
420
516
|
if (this.streamingMessage) {
|
|
421
|
-
const part =
|
|
517
|
+
const part = findToolInvocationPart(this.streamingMessage.parts, toolCallId);
|
|
422
518
|
if (part) {
|
|
423
519
|
const nextParts = updater(this.streamingMessage, part);
|
|
424
520
|
this.replaceStreamingMessage({ ...this.streamingMessage, parts: nextParts });
|
|
@@ -427,7 +523,7 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
427
523
|
}
|
|
428
524
|
for (let index = this.messages.length - 1; index >= 0; index -= 1) {
|
|
429
525
|
const candidateMessage = this.messages[index];
|
|
430
|
-
const part =
|
|
526
|
+
const part = findToolInvocationPart(candidateMessage.parts, toolCallId);
|
|
431
527
|
if (!part) {
|
|
432
528
|
continue;
|
|
433
529
|
}
|
|
@@ -442,36 +538,8 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
442
538
|
}
|
|
443
539
|
return false;
|
|
444
540
|
}
|
|
445
|
-
findToolInvocationPart(parts, toolCallId) {
|
|
446
|
-
for (let index = parts.length - 1; index >= 0; index -= 1) {
|
|
447
|
-
const part = parts[index];
|
|
448
|
-
if (part.type === "tool-invocation" && part.toolCallId === toolCallId) {
|
|
449
|
-
return part;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
return null;
|
|
453
|
-
}
|
|
454
|
-
findToolNameByCallId(parts, toolCallId) {
|
|
455
|
-
const part = this.findToolInvocationPart(parts, toolCallId);
|
|
456
|
-
return part?.toolName ?? null;
|
|
457
|
-
}
|
|
458
|
-
upsertToolInvocationPart(parts, toolPart) {
|
|
459
|
-
const nextParts = [...parts];
|
|
460
|
-
for (let index = nextParts.length - 1; index >= 0; index -= 1) {
|
|
461
|
-
const part = nextParts[index];
|
|
462
|
-
if (part.type === "tool-invocation" && part.toolCallId === toolPart.toolCallId) {
|
|
463
|
-
nextParts[index] = {
|
|
464
|
-
...part,
|
|
465
|
-
...toolPart
|
|
466
|
-
};
|
|
467
|
-
return nextParts;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
nextParts.push(toolPart);
|
|
471
|
-
return nextParts;
|
|
472
|
-
}
|
|
473
541
|
upsertMessage(message) {
|
|
474
|
-
const normalizedMessage =
|
|
542
|
+
const normalizedMessage = normalizeConversationMessage(message);
|
|
475
543
|
const messageIndex = this.messages.findIndex((item) => item.id === normalizedMessage.id);
|
|
476
544
|
if (messageIndex < 0) {
|
|
477
545
|
this.messages = [...this.messages, normalizedMessage];
|
|
@@ -487,7 +555,7 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
487
555
|
if (!nextStreamingMessage && !this.streamingMessage) {
|
|
488
556
|
return;
|
|
489
557
|
}
|
|
490
|
-
this.streamingMessage = nextStreamingMessage ?
|
|
558
|
+
this.streamingMessage = nextStreamingMessage ? normalizeConversationMessage(nextStreamingMessage) : null;
|
|
491
559
|
this.stateVersion += 1;
|
|
492
560
|
}
|
|
493
561
|
setError(nextError) {
|
|
@@ -505,15 +573,6 @@ var DefaultNcpAgentConversationStateManager = class {
|
|
|
505
573
|
this.activeRun = null;
|
|
506
574
|
this.stateVersion += 1;
|
|
507
575
|
}
|
|
508
|
-
clearToolCallTrackingByMessageId(messageId) {
|
|
509
|
-
for (const [toolCallId, trackedMessageId] of this.toolCallMessageIdByCallId) {
|
|
510
|
-
if (trackedMessageId !== messageId) {
|
|
511
|
-
continue;
|
|
512
|
-
}
|
|
513
|
-
this.toolCallMessageIdByCallId.delete(toolCallId);
|
|
514
|
-
this.toolCallArgsRawByCallId.delete(toolCallId);
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
576
|
notifyListeners() {
|
|
518
577
|
const snapshot = this.getSnapshot();
|
|
519
578
|
for (const listener of this.listeners) {
|
|
@@ -574,7 +633,7 @@ async function consume(events) {
|
|
|
574
633
|
|
|
575
634
|
// src/agent/agent-backend/agent-backend.ts
|
|
576
635
|
import {
|
|
577
|
-
NcpEventType as
|
|
636
|
+
NcpEventType as NcpEventType5
|
|
578
637
|
} from "@nextclaw/ncp";
|
|
579
638
|
|
|
580
639
|
// src/errors/ncp-error-exception.ts
|
|
@@ -596,9 +655,15 @@ var AgentLiveSessionRegistry = class {
|
|
|
596
655
|
this.createRuntime = createRuntime;
|
|
597
656
|
}
|
|
598
657
|
sessions = /* @__PURE__ */ new Map();
|
|
599
|
-
async ensureSession(sessionId) {
|
|
658
|
+
async ensureSession(sessionId, initialMetadata) {
|
|
600
659
|
const existing = this.sessions.get(sessionId);
|
|
601
660
|
if (existing) {
|
|
661
|
+
if (initialMetadata && Object.keys(initialMetadata).length > 0) {
|
|
662
|
+
existing.metadata = {
|
|
663
|
+
...existing.metadata,
|
|
664
|
+
...structuredClone(initialMetadata)
|
|
665
|
+
};
|
|
666
|
+
}
|
|
602
667
|
return existing;
|
|
603
668
|
}
|
|
604
669
|
const storedSession = await this.sessionStore.getSession(sessionId);
|
|
@@ -607,12 +672,27 @@ var AgentLiveSessionRegistry = class {
|
|
|
607
672
|
sessionId,
|
|
608
673
|
messages: cloneMessages(storedSession?.messages ?? [])
|
|
609
674
|
});
|
|
675
|
+
const sessionMetadata = {
|
|
676
|
+
...storedSession?.metadata ? structuredClone(storedSession.metadata) : {},
|
|
677
|
+
...initialMetadata ? structuredClone(initialMetadata) : {}
|
|
678
|
+
};
|
|
610
679
|
const session = {
|
|
611
680
|
sessionId,
|
|
612
681
|
stateManager,
|
|
613
|
-
|
|
682
|
+
metadata: sessionMetadata,
|
|
683
|
+
runtime: null,
|
|
614
684
|
activeExecution: null
|
|
615
685
|
};
|
|
686
|
+
session.runtime = this.createRuntime({
|
|
687
|
+
sessionId,
|
|
688
|
+
stateManager,
|
|
689
|
+
sessionMetadata,
|
|
690
|
+
setSessionMetadata: (nextMetadata) => {
|
|
691
|
+
session.metadata = {
|
|
692
|
+
...structuredClone(nextMetadata)
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
});
|
|
616
696
|
this.sessions.set(sessionId, session);
|
|
617
697
|
return session;
|
|
618
698
|
}
|
|
@@ -652,21 +732,19 @@ var AgentRunExecutor = class {
|
|
|
652
732
|
metadata: envelope.metadata
|
|
653
733
|
}
|
|
654
734
|
};
|
|
655
|
-
|
|
735
|
+
await session.stateManager.dispatch(messageSent);
|
|
736
|
+
await this.persistSession(envelope.sessionId);
|
|
737
|
+
yield structuredClone(messageSent);
|
|
656
738
|
try {
|
|
657
739
|
for await (const event of session.runtime.run(
|
|
658
740
|
{
|
|
659
741
|
sessionId: envelope.sessionId,
|
|
660
742
|
messages: [envelope.message],
|
|
661
|
-
correlationId: envelope.correlationId
|
|
743
|
+
correlationId: envelope.correlationId,
|
|
744
|
+
metadata: envelope.metadata
|
|
662
745
|
},
|
|
663
746
|
{ signal: controller.signal }
|
|
664
747
|
)) {
|
|
665
|
-
if (!messageSentPublished) {
|
|
666
|
-
messageSentPublished = true;
|
|
667
|
-
await this.persistSession(envelope.sessionId);
|
|
668
|
-
yield structuredClone(messageSent);
|
|
669
|
-
}
|
|
670
748
|
await this.persistSession(envelope.sessionId);
|
|
671
749
|
yield structuredClone(event);
|
|
672
750
|
}
|
|
@@ -753,6 +831,78 @@ function createAsyncQueue() {
|
|
|
753
831
|
};
|
|
754
832
|
}
|
|
755
833
|
|
|
834
|
+
// src/agent/agent-backend/agent-backend-session-utils.ts
|
|
835
|
+
import { NcpEventType as NcpEventType4 } from "@nextclaw/ncp";
|
|
836
|
+
function readMessages(snapshot) {
|
|
837
|
+
const messages = snapshot.messages.map((message) => structuredClone(message));
|
|
838
|
+
if (snapshot.streamingMessage) {
|
|
839
|
+
messages.push(structuredClone(snapshot.streamingMessage));
|
|
840
|
+
}
|
|
841
|
+
return messages;
|
|
842
|
+
}
|
|
843
|
+
function toSessionSummary(session, liveSession) {
|
|
844
|
+
return {
|
|
845
|
+
sessionId: session.sessionId,
|
|
846
|
+
messageCount: session.messages.length,
|
|
847
|
+
updatedAt: session.updatedAt,
|
|
848
|
+
status: liveSession?.activeExecution ? "running" : "idle",
|
|
849
|
+
...session.metadata ? {
|
|
850
|
+
metadata: structuredClone({
|
|
851
|
+
...session.metadata,
|
|
852
|
+
...liveSession?.metadata ? liveSession.metadata : {}
|
|
853
|
+
})
|
|
854
|
+
} : liveSession?.metadata ? { metadata: structuredClone(liveSession.metadata) } : {}
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
function toLiveSessionSummary(session) {
|
|
858
|
+
const snapshot = session.stateManager.getSnapshot();
|
|
859
|
+
return {
|
|
860
|
+
sessionId: session.sessionId,
|
|
861
|
+
messageCount: readMessages(snapshot).length,
|
|
862
|
+
updatedAt: now(),
|
|
863
|
+
status: session.activeExecution ? "running" : "idle",
|
|
864
|
+
...Object.keys(session.metadata).length > 0 ? { metadata: structuredClone(session.metadata) } : session.activeExecution?.requestEnvelope.metadata ? { metadata: structuredClone(session.activeExecution.requestEnvelope.metadata) } : {}
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
function now() {
|
|
868
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
869
|
+
}
|
|
870
|
+
function isTerminalEvent(event) {
|
|
871
|
+
switch (event.type) {
|
|
872
|
+
case NcpEventType4.MessageAbort:
|
|
873
|
+
case NcpEventType4.RunFinished:
|
|
874
|
+
case NcpEventType4.RunError:
|
|
875
|
+
return true;
|
|
876
|
+
default:
|
|
877
|
+
return false;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
// src/agent/agent-backend/agent-backend-session-persistence.ts
|
|
882
|
+
function buildUpdatedSessionRecord(params) {
|
|
883
|
+
const nextMetadata = params.patch.metadata === null ? {} : params.patch.metadata ? structuredClone(params.patch.metadata) : structuredClone(params.liveSession?.metadata ?? params.storedSession?.metadata ?? {});
|
|
884
|
+
if (params.liveSession) {
|
|
885
|
+
params.liveSession.metadata = structuredClone(nextMetadata);
|
|
886
|
+
}
|
|
887
|
+
return {
|
|
888
|
+
sessionId: params.sessionId,
|
|
889
|
+
messages: params.liveSession ? readMessages(params.liveSession.stateManager.getSnapshot()) : params.storedSession?.messages.map((message) => structuredClone(message)) ?? [],
|
|
890
|
+
updatedAt: params.updatedAt,
|
|
891
|
+
metadata: nextMetadata
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
function buildPersistedLiveSessionRecord(params) {
|
|
895
|
+
return {
|
|
896
|
+
sessionId: params.sessionId,
|
|
897
|
+
messages: readMessages(params.session.stateManager.getSnapshot()),
|
|
898
|
+
updatedAt: params.updatedAt,
|
|
899
|
+
metadata: {
|
|
900
|
+
...params.session.metadata ? structuredClone(params.session.metadata) : {},
|
|
901
|
+
...params.session.activeExecution?.requestEnvelope.metadata ? structuredClone(params.session.activeExecution.requestEnvelope.metadata) : {}
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
|
|
756
906
|
// src/agent/agent-backend/event-publisher.ts
|
|
757
907
|
var EventPublisher = class {
|
|
758
908
|
listeners = /* @__PURE__ */ new Set();
|
|
@@ -844,7 +994,7 @@ var DefaultNcpAgentBackend = class {
|
|
|
844
994
|
return;
|
|
845
995
|
}
|
|
846
996
|
this.started = true;
|
|
847
|
-
this.publisher.publish({ type:
|
|
997
|
+
this.publisher.publish({ type: NcpEventType5.EndpointReady });
|
|
848
998
|
}
|
|
849
999
|
async stop() {
|
|
850
1000
|
if (!this.started) {
|
|
@@ -865,13 +1015,13 @@ var DefaultNcpAgentBackend = class {
|
|
|
865
1015
|
async emit(event) {
|
|
866
1016
|
await this.ensureStarted();
|
|
867
1017
|
switch (event.type) {
|
|
868
|
-
case
|
|
1018
|
+
case NcpEventType5.MessageRequest:
|
|
869
1019
|
await this.handleRequest(event.payload);
|
|
870
1020
|
return;
|
|
871
|
-
case
|
|
1021
|
+
case NcpEventType5.MessageStreamRequest:
|
|
872
1022
|
await this.streamToSubscribers(event.payload);
|
|
873
1023
|
return;
|
|
874
|
-
case
|
|
1024
|
+
case NcpEventType5.MessageAbort:
|
|
875
1025
|
await this.handleAbort(event.payload);
|
|
876
1026
|
return;
|
|
877
1027
|
default:
|
|
@@ -883,7 +1033,10 @@ var DefaultNcpAgentBackend = class {
|
|
|
883
1033
|
}
|
|
884
1034
|
async *send(envelope, options) {
|
|
885
1035
|
await this.ensureStarted();
|
|
886
|
-
const session = await this.sessionRegistry.ensureSession(
|
|
1036
|
+
const session = await this.sessionRegistry.ensureSession(
|
|
1037
|
+
envelope.sessionId,
|
|
1038
|
+
envelope.metadata
|
|
1039
|
+
);
|
|
887
1040
|
const execution = this.startSessionExecution(session, envelope, options?.signal);
|
|
888
1041
|
try {
|
|
889
1042
|
for await (const event of this.executor.executeRun(session, envelope, execution.controller)) {
|
|
@@ -954,22 +1107,27 @@ var DefaultNcpAgentBackend = class {
|
|
|
954
1107
|
}
|
|
955
1108
|
async listSessionMessages(sessionId) {
|
|
956
1109
|
const liveSession = this.sessionRegistry.getSession(sessionId);
|
|
957
|
-
if (liveSession)
|
|
958
|
-
return readMessages(liveSession.stateManager.getSnapshot());
|
|
959
|
-
}
|
|
1110
|
+
if (liveSession) return readMessages(liveSession.stateManager.getSnapshot());
|
|
960
1111
|
const session = await this.sessionStore.getSession(sessionId);
|
|
961
1112
|
return session ? session.messages.map((message) => structuredClone(message)) : [];
|
|
962
1113
|
}
|
|
963
1114
|
async getSession(sessionId) {
|
|
964
1115
|
const liveSession = this.sessionRegistry.getSession(sessionId);
|
|
965
1116
|
const storedSession = await this.sessionStore.getSession(sessionId);
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1117
|
+
return storedSession ? toSessionSummary(storedSession, liveSession) : liveSession ? toLiveSessionSummary(liveSession) : null;
|
|
1118
|
+
}
|
|
1119
|
+
async updateSession(sessionId, patch) {
|
|
1120
|
+
const liveSession = this.sessionRegistry.getSession(sessionId);
|
|
1121
|
+
const storedSession = await this.sessionStore.getSession(sessionId);
|
|
1122
|
+
if (!liveSession && !storedSession) return null;
|
|
1123
|
+
await this.sessionStore.saveSession(buildUpdatedSessionRecord({
|
|
1124
|
+
sessionId,
|
|
1125
|
+
patch,
|
|
1126
|
+
liveSession,
|
|
1127
|
+
storedSession,
|
|
1128
|
+
updatedAt: now()
|
|
1129
|
+
}));
|
|
1130
|
+
return this.getSession(sessionId);
|
|
973
1131
|
}
|
|
974
1132
|
async deleteSession(sessionId) {
|
|
975
1133
|
const liveSession = this.sessionRegistry.deleteSession(sessionId);
|
|
@@ -1054,7 +1212,7 @@ var DefaultNcpAgentBackend = class {
|
|
|
1054
1212
|
}
|
|
1055
1213
|
async createAbortEvent(sessionId, messageId) {
|
|
1056
1214
|
const abortEvent = {
|
|
1057
|
-
type:
|
|
1215
|
+
type: NcpEventType5.MessageAbort,
|
|
1058
1216
|
payload: {
|
|
1059
1217
|
sessionId,
|
|
1060
1218
|
...messageId ? { messageId } : {}
|
|
@@ -1069,54 +1227,14 @@ var DefaultNcpAgentBackend = class {
|
|
|
1069
1227
|
}
|
|
1070
1228
|
async persistSession(sessionId) {
|
|
1071
1229
|
const session = this.sessionRegistry.getSession(sessionId);
|
|
1072
|
-
if (!session)
|
|
1073
|
-
|
|
1074
|
-
}
|
|
1075
|
-
const snapshot = session.stateManager.getSnapshot();
|
|
1076
|
-
await this.sessionStore.saveSession({
|
|
1230
|
+
if (!session) return;
|
|
1231
|
+
await this.sessionStore.saveSession(buildPersistedLiveSessionRecord({
|
|
1077
1232
|
sessionId,
|
|
1078
|
-
|
|
1233
|
+
session,
|
|
1079
1234
|
updatedAt: now()
|
|
1080
|
-
});
|
|
1235
|
+
}));
|
|
1081
1236
|
}
|
|
1082
1237
|
};
|
|
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
1238
|
|
|
1121
1239
|
// src/agent/agent-backend/in-memory-agent-session-store.ts
|
|
1122
1240
|
var InMemoryAgentSessionStore = class {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/ncp-toolkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Toolkit implementations built on top of the NextClaw Communication Protocol.",
|
|
6
6
|
"type": "module",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"dist"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@nextclaw/ncp": "0.
|
|
18
|
+
"@nextclaw/ncp": "0.3.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@types/node": "^20.17.6",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"tsup": "^8.3.5",
|
|
24
24
|
"typescript": "^5.6.3",
|
|
25
25
|
"vitest": "^2.1.2",
|
|
26
|
-
"@nextclaw/ncp-agent-runtime": "0.
|
|
26
|
+
"@nextclaw/ncp-agent-runtime": "0.2.0"
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
29
|
"build": "tsup src/index.ts --format esm --dts --out-dir dist",
|