@nextclaw/ncp-toolkit 0.5.0 → 0.5.1
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 +154 -147
- package/dist/index.js +1206 -1486
- package/package.json +2 -3
package/dist/index.js
CHANGED
|
@@ -1,1562 +1,1282 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
NcpEventType
|
|
4
|
-
} from "@nextclaw/ncp";
|
|
5
|
-
|
|
6
|
-
// src/agent/agent-conversation-message-normalizer.ts
|
|
7
|
-
import { sanitizeAssistantReplyTags } from "@nextclaw/ncp";
|
|
1
|
+
import { NcpEventType, isHiddenNcpMessage, sanitizeAssistantReplyTags } from "@nextclaw/ncp";
|
|
2
|
+
//#region src/agent/agent-conversation-message-normalizer.ts
|
|
8
3
|
function cloneConversationMessage(message) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
return {
|
|
5
|
+
...message,
|
|
6
|
+
parts: [...message.parts],
|
|
7
|
+
metadata: message.metadata ? { ...message.metadata } : void 0
|
|
8
|
+
};
|
|
14
9
|
}
|
|
15
10
|
function normalizeConversationMessage(message) {
|
|
16
|
-
|
|
11
|
+
return cloneConversationMessage(sanitizeAssistantReplyTags(message));
|
|
17
12
|
}
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/agent/agent-conversation-state-manager.utils.ts
|
|
20
15
|
function buildRuntimeError(payload) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
16
|
+
const message = payload.error?.trim();
|
|
17
|
+
return {
|
|
18
|
+
code: "runtime-error",
|
|
19
|
+
message: message && message.length > 0 ? message : "Agent run failed.",
|
|
20
|
+
details: {
|
|
21
|
+
sessionId: payload.sessionId,
|
|
22
|
+
messageId: payload.messageId,
|
|
23
|
+
threadId: payload.threadId,
|
|
24
|
+
runId: payload.runId
|
|
25
|
+
}
|
|
26
|
+
};
|
|
32
27
|
}
|
|
33
28
|
function shouldPromoteStreamingMessageId(message, nextMessageId) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (message.id.startsWith("tool-")) {
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
return message.parts.some((part) => part.type === "tool-invocation");
|
|
29
|
+
if (!nextMessageId.trim()) return false;
|
|
30
|
+
if (message.id.startsWith("tool-")) return true;
|
|
31
|
+
return message.parts.some((part) => part.type === "tool-invocation");
|
|
41
32
|
}
|
|
42
33
|
function remapTrackedToolCallsToMessageId(toolCallMessageIdByCallId, fromMessageId, toMessageId) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
toolCallMessageIdByCallId.set(toolCallId, toMessageId);
|
|
48
|
-
}
|
|
34
|
+
for (const [toolCallId, trackedMessageId] of toolCallMessageIdByCallId) {
|
|
35
|
+
if (trackedMessageId !== fromMessageId) continue;
|
|
36
|
+
toolCallMessageIdByCallId.set(toolCallId, toMessageId);
|
|
37
|
+
}
|
|
49
38
|
}
|
|
50
39
|
function clearToolCallTrackingByMessageId(toolCallMessageIdByCallId, toolCallArgsRawByCallId, messageId) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
toolCallArgsRawByCallId.delete(toolCallId);
|
|
57
|
-
}
|
|
40
|
+
for (const [toolCallId, trackedMessageId] of toolCallMessageIdByCallId) {
|
|
41
|
+
if (trackedMessageId !== messageId) continue;
|
|
42
|
+
toolCallMessageIdByCallId.delete(toolCallId);
|
|
43
|
+
toolCallArgsRawByCallId.delete(toolCallId);
|
|
44
|
+
}
|
|
58
45
|
}
|
|
59
46
|
function findToolInvocationPart(parts, toolCallId) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
return null;
|
|
47
|
+
for (let index = parts.length - 1; index >= 0; index -= 1) {
|
|
48
|
+
const part = parts[index];
|
|
49
|
+
if (part.type === "tool-invocation" && part.toolCallId === toolCallId) return part;
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
67
52
|
}
|
|
68
53
|
function findToolNameByCallId(parts, toolCallId) {
|
|
69
|
-
|
|
70
|
-
return part?.toolName ?? null;
|
|
54
|
+
return findToolInvocationPart(parts, toolCallId)?.toolName ?? null;
|
|
71
55
|
}
|
|
72
56
|
function upsertToolInvocationPart(parts, toolPart) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
57
|
+
const nextParts = [...parts];
|
|
58
|
+
for (let index = nextParts.length - 1; index >= 0; index -= 1) {
|
|
59
|
+
const part = nextParts[index];
|
|
60
|
+
if (part.type === "tool-invocation" && part.toolCallId === toolPart.toolCallId) {
|
|
61
|
+
nextParts[index] = {
|
|
62
|
+
...part,
|
|
63
|
+
...toolPart
|
|
64
|
+
};
|
|
65
|
+
return nextParts;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
nextParts.push(toolPart);
|
|
69
|
+
return nextParts;
|
|
86
70
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/agent/agent-conversation-state-manager.ts
|
|
73
|
+
const DEFAULT_ASSISTANT_ROLE = "assistant";
|
|
90
74
|
var DefaultNcpAgentConversationStateManager = class {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
};
|
|
555
|
-
updateMessageContainingToolCall = (toolCallId, updater) => {
|
|
556
|
-
if (this.streamingMessage) {
|
|
557
|
-
const part = findToolInvocationPart(
|
|
558
|
-
this.streamingMessage.parts,
|
|
559
|
-
toolCallId
|
|
560
|
-
);
|
|
561
|
-
if (part) {
|
|
562
|
-
const nextParts = updater(this.streamingMessage, part);
|
|
563
|
-
this.replaceStreamingMessage({
|
|
564
|
-
...this.streamingMessage,
|
|
565
|
-
parts: nextParts
|
|
566
|
-
});
|
|
567
|
-
return true;
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
for (let index = this.messages.length - 1; index >= 0; index -= 1) {
|
|
571
|
-
const candidateMessage = this.messages[index];
|
|
572
|
-
const part = findToolInvocationPart(candidateMessage.parts, toolCallId);
|
|
573
|
-
if (!part) {
|
|
574
|
-
continue;
|
|
575
|
-
}
|
|
576
|
-
const nextMessages = [...this.messages];
|
|
577
|
-
nextMessages[index] = {
|
|
578
|
-
...candidateMessage,
|
|
579
|
-
parts: updater(candidateMessage, part)
|
|
580
|
-
};
|
|
581
|
-
this.messages = nextMessages;
|
|
582
|
-
this.stateVersion += 1;
|
|
583
|
-
return true;
|
|
584
|
-
}
|
|
585
|
-
return false;
|
|
586
|
-
};
|
|
587
|
-
upsertMessage = (message) => {
|
|
588
|
-
const normalizedMessage = normalizeConversationMessage(message);
|
|
589
|
-
const messageIndex = this.messages.findIndex(
|
|
590
|
-
(item) => item.id === normalizedMessage.id
|
|
591
|
-
);
|
|
592
|
-
if (messageIndex < 0) {
|
|
593
|
-
this.messages = [...this.messages, normalizedMessage];
|
|
594
|
-
this.stateVersion += 1;
|
|
595
|
-
return;
|
|
596
|
-
}
|
|
597
|
-
const nextMessages = [...this.messages];
|
|
598
|
-
nextMessages[messageIndex] = normalizedMessage;
|
|
599
|
-
this.messages = nextMessages;
|
|
600
|
-
this.stateVersion += 1;
|
|
601
|
-
};
|
|
602
|
-
replaceStreamingMessage = (nextStreamingMessage) => {
|
|
603
|
-
if (!nextStreamingMessage && !this.streamingMessage) {
|
|
604
|
-
return;
|
|
605
|
-
}
|
|
606
|
-
this.streamingMessage = nextStreamingMessage ? normalizeConversationMessage(nextStreamingMessage) : null;
|
|
607
|
-
this.stateVersion += 1;
|
|
608
|
-
};
|
|
609
|
-
setError = (nextError) => {
|
|
610
|
-
const hasSameError = this.error?.code === nextError?.code && this.error?.message === nextError?.message && this.error?.details === nextError?.details && this.error?.cause === nextError?.cause;
|
|
611
|
-
if (hasSameError) {
|
|
612
|
-
return;
|
|
613
|
-
}
|
|
614
|
-
this.error = nextError ? {
|
|
615
|
-
...nextError,
|
|
616
|
-
details: nextError.details ? { ...nextError.details } : void 0
|
|
617
|
-
} : null;
|
|
618
|
-
this.stateVersion += 1;
|
|
619
|
-
};
|
|
620
|
-
clearActiveRun = () => {
|
|
621
|
-
if (!this.activeRun) return;
|
|
622
|
-
this.activeRun = null;
|
|
623
|
-
this.stateVersion += 1;
|
|
624
|
-
};
|
|
625
|
-
isSettledRunId = (runId) => {
|
|
626
|
-
return Boolean(runId?.trim()) && runId === this.lastSettledRunId;
|
|
627
|
-
};
|
|
628
|
-
markRunAsSettled = (runId) => {
|
|
629
|
-
this.lastSettledRunId = runId?.trim() || null;
|
|
630
|
-
};
|
|
631
|
-
settleStreamingMessage = (status) => {
|
|
632
|
-
if (!this.streamingMessage) {
|
|
633
|
-
return;
|
|
634
|
-
}
|
|
635
|
-
const settledMessage = {
|
|
636
|
-
...this.streamingMessage,
|
|
637
|
-
status
|
|
638
|
-
};
|
|
639
|
-
this.upsertMessage(settledMessage);
|
|
640
|
-
this.replaceStreamingMessage(null);
|
|
641
|
-
clearToolCallTrackingByMessageId(
|
|
642
|
-
this.toolCallMessageIdByCallId,
|
|
643
|
-
this.toolCallArgsRawByCallId,
|
|
644
|
-
settledMessage.id
|
|
645
|
-
);
|
|
646
|
-
};
|
|
647
|
-
notifyListeners = () => {
|
|
648
|
-
const snapshot = this.getSnapshot();
|
|
649
|
-
for (const listener of this.listeners) listener(snapshot);
|
|
650
|
-
};
|
|
75
|
+
messages = [];
|
|
76
|
+
streamingMessage = null;
|
|
77
|
+
error = null;
|
|
78
|
+
activeRun = null;
|
|
79
|
+
listeners = /* @__PURE__ */ new Set();
|
|
80
|
+
toolCallMessageIdByCallId = /* @__PURE__ */ new Map();
|
|
81
|
+
toolCallArgsRawByCallId = /* @__PURE__ */ new Map();
|
|
82
|
+
lastSettledRunId = null;
|
|
83
|
+
snapshotCache = null;
|
|
84
|
+
snapshotVersion = -1;
|
|
85
|
+
stateVersion = 0;
|
|
86
|
+
getSnapshot = () => {
|
|
87
|
+
if (this.snapshotCache && this.snapshotVersion === this.stateVersion) return this.snapshotCache;
|
|
88
|
+
const snapshot = {
|
|
89
|
+
messages: this.messages,
|
|
90
|
+
streamingMessage: this.streamingMessage,
|
|
91
|
+
error: this.error ? {
|
|
92
|
+
...this.error,
|
|
93
|
+
details: this.error.details ? { ...this.error.details } : void 0
|
|
94
|
+
} : null,
|
|
95
|
+
activeRun: this.activeRun ? { ...this.activeRun } : null
|
|
96
|
+
};
|
|
97
|
+
this.snapshotCache = snapshot;
|
|
98
|
+
this.snapshotVersion = this.stateVersion;
|
|
99
|
+
return snapshot;
|
|
100
|
+
};
|
|
101
|
+
subscribe = (listener) => {
|
|
102
|
+
this.listeners.add(listener);
|
|
103
|
+
return () => this.listeners.delete(listener);
|
|
104
|
+
};
|
|
105
|
+
reset = () => {
|
|
106
|
+
if (this.messages.length === 0 && !this.streamingMessage && !this.error && !this.activeRun && this.toolCallMessageIdByCallId.size === 0 && this.toolCallArgsRawByCallId.size === 0) return;
|
|
107
|
+
this.messages = [];
|
|
108
|
+
this.streamingMessage = null;
|
|
109
|
+
this.error = null;
|
|
110
|
+
this.activeRun = null;
|
|
111
|
+
this.toolCallMessageIdByCallId.clear();
|
|
112
|
+
this.toolCallArgsRawByCallId.clear();
|
|
113
|
+
this.lastSettledRunId = null;
|
|
114
|
+
this.stateVersion += 1;
|
|
115
|
+
this.notifyListeners();
|
|
116
|
+
};
|
|
117
|
+
hydrate = (payload) => {
|
|
118
|
+
this.messages = payload.messages.map((message) => normalizeConversationMessage(message));
|
|
119
|
+
this.streamingMessage = null;
|
|
120
|
+
this.error = null;
|
|
121
|
+
this.activeRun = payload.activeRun ? {
|
|
122
|
+
...payload.activeRun,
|
|
123
|
+
sessionId: payload.activeRun.sessionId ?? payload.sessionId,
|
|
124
|
+
abortDisabledReason: payload.activeRun.abortDisabledReason ?? null
|
|
125
|
+
} : null;
|
|
126
|
+
this.toolCallMessageIdByCallId.clear();
|
|
127
|
+
this.toolCallArgsRawByCallId.clear();
|
|
128
|
+
this.lastSettledRunId = null;
|
|
129
|
+
this.stateVersion += 1;
|
|
130
|
+
this.notifyListeners();
|
|
131
|
+
};
|
|
132
|
+
dispatch = (event) => this.dispatchBatch([event]);
|
|
133
|
+
dispatchBatch = async (events) => {
|
|
134
|
+
if (!events.length) return;
|
|
135
|
+
const versionBeforeDispatch = this.stateVersion;
|
|
136
|
+
events.forEach(this.applyEvent);
|
|
137
|
+
if (this.stateVersion !== versionBeforeDispatch) this.notifyListeners();
|
|
138
|
+
};
|
|
139
|
+
applyEvent = (event) => {
|
|
140
|
+
switch (event.type) {
|
|
141
|
+
case NcpEventType.MessageSent:
|
|
142
|
+
this.handleMessageSent(event.payload);
|
|
143
|
+
break;
|
|
144
|
+
case NcpEventType.MessageAbort:
|
|
145
|
+
this.handleMessageAbort(event.payload);
|
|
146
|
+
break;
|
|
147
|
+
case NcpEventType.MessageTextStart:
|
|
148
|
+
this.handleMessageTextStart(event.payload);
|
|
149
|
+
break;
|
|
150
|
+
case NcpEventType.MessageTextDelta:
|
|
151
|
+
this.handleMessageTextDelta(event.payload);
|
|
152
|
+
break;
|
|
153
|
+
case NcpEventType.MessageTextEnd:
|
|
154
|
+
this.handleMessageTextEnd(event.payload);
|
|
155
|
+
break;
|
|
156
|
+
case NcpEventType.MessageReasoningStart:
|
|
157
|
+
this.handleMessageReasoningStart(event.payload);
|
|
158
|
+
break;
|
|
159
|
+
case NcpEventType.MessageReasoningDelta:
|
|
160
|
+
this.handleMessageReasoningDelta(event.payload);
|
|
161
|
+
break;
|
|
162
|
+
case NcpEventType.MessageReasoningEnd:
|
|
163
|
+
this.handleMessageReasoningEnd(event.payload);
|
|
164
|
+
break;
|
|
165
|
+
case NcpEventType.MessageToolCallStart:
|
|
166
|
+
this.handleMessageToolCallStart(event.payload);
|
|
167
|
+
break;
|
|
168
|
+
case NcpEventType.MessageToolCallArgs:
|
|
169
|
+
this.handleMessageToolCallArgs(event.payload);
|
|
170
|
+
break;
|
|
171
|
+
case NcpEventType.MessageToolCallArgsDelta:
|
|
172
|
+
this.handleMessageToolCallArgsDelta(event.payload);
|
|
173
|
+
break;
|
|
174
|
+
case NcpEventType.MessageToolCallEnd:
|
|
175
|
+
this.handleMessageToolCallEnd(event.payload);
|
|
176
|
+
break;
|
|
177
|
+
case NcpEventType.MessageToolCallResult:
|
|
178
|
+
this.handleMessageToolCallResult(event.payload);
|
|
179
|
+
break;
|
|
180
|
+
case NcpEventType.RunStarted:
|
|
181
|
+
this.handleRunStarted(event.payload);
|
|
182
|
+
break;
|
|
183
|
+
case NcpEventType.RunFinished:
|
|
184
|
+
this.handleRunFinished(event.payload);
|
|
185
|
+
break;
|
|
186
|
+
case NcpEventType.RunError:
|
|
187
|
+
this.handleRunError(event.payload);
|
|
188
|
+
break;
|
|
189
|
+
case NcpEventType.RunMetadata:
|
|
190
|
+
this.handleRunMetadata(event.payload);
|
|
191
|
+
break;
|
|
192
|
+
case NcpEventType.EndpointError:
|
|
193
|
+
this.handleEndpointError(event.payload);
|
|
194
|
+
break;
|
|
195
|
+
default: break;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
handleMessageSent = (payload) => {
|
|
199
|
+
this.upsertMessage(payload.message);
|
|
200
|
+
this.setError(null);
|
|
201
|
+
};
|
|
202
|
+
handleMessageAbort = (payload) => {
|
|
203
|
+
const targetMessageId = payload.messageId?.trim();
|
|
204
|
+
this.clearActiveRun();
|
|
205
|
+
this.setError(null);
|
|
206
|
+
if (this.streamingMessage && (!targetMessageId || this.streamingMessage.id === targetMessageId)) {
|
|
207
|
+
const streamingMessageId = this.streamingMessage.id;
|
|
208
|
+
this.upsertMessage({
|
|
209
|
+
...this.streamingMessage,
|
|
210
|
+
status: "final"
|
|
211
|
+
});
|
|
212
|
+
this.replaceStreamingMessage(null);
|
|
213
|
+
if (targetMessageId) clearToolCallTrackingByMessageId(this.toolCallMessageIdByCallId, this.toolCallArgsRawByCallId, targetMessageId);
|
|
214
|
+
else clearToolCallTrackingByMessageId(this.toolCallMessageIdByCallId, this.toolCallArgsRawByCallId, streamingMessageId);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
handleMessageTextStart = (payload) => {
|
|
218
|
+
this.ensureStreamingMessage(payload.sessionId, payload.messageId, "streaming");
|
|
219
|
+
this.setError(null);
|
|
220
|
+
};
|
|
221
|
+
handleMessageTextDelta = (payload) => {
|
|
222
|
+
if (!payload.delta) return;
|
|
223
|
+
const targetMessage = this.ensureStreamingMessage(payload.sessionId, payload.messageId, "streaming");
|
|
224
|
+
const nextParts = [...targetMessage.parts];
|
|
225
|
+
const lastPart = nextParts[nextParts.length - 1];
|
|
226
|
+
if (lastPart?.type === "text") nextParts[nextParts.length - 1] = {
|
|
227
|
+
type: "text",
|
|
228
|
+
text: `${lastPart.text}${payload.delta}`
|
|
229
|
+
};
|
|
230
|
+
else nextParts.push({
|
|
231
|
+
type: "text",
|
|
232
|
+
text: payload.delta
|
|
233
|
+
});
|
|
234
|
+
this.replaceStreamingMessage({
|
|
235
|
+
...targetMessage,
|
|
236
|
+
parts: nextParts,
|
|
237
|
+
status: "streaming"
|
|
238
|
+
});
|
|
239
|
+
};
|
|
240
|
+
handleMessageTextEnd = (payload) => {
|
|
241
|
+
if (this.streamingMessage?.id !== payload.messageId) return;
|
|
242
|
+
if (this.streamingMessage.status !== "streaming") return;
|
|
243
|
+
this.replaceStreamingMessage({
|
|
244
|
+
...this.streamingMessage,
|
|
245
|
+
status: "pending"
|
|
246
|
+
});
|
|
247
|
+
};
|
|
248
|
+
handleMessageReasoningStart = (payload) => {
|
|
249
|
+
this.ensureStreamingMessage(payload.sessionId, payload.messageId, "streaming");
|
|
250
|
+
};
|
|
251
|
+
handleMessageReasoningDelta = (payload) => {
|
|
252
|
+
if (!payload.delta) return;
|
|
253
|
+
const targetMessage = this.ensureStreamingMessage(payload.sessionId, payload.messageId, "streaming");
|
|
254
|
+
const nextParts = [...targetMessage.parts];
|
|
255
|
+
const lastPart = nextParts[nextParts.length - 1];
|
|
256
|
+
if (lastPart?.type === "reasoning") nextParts[nextParts.length - 1] = {
|
|
257
|
+
type: "reasoning",
|
|
258
|
+
text: `${lastPart.text}${payload.delta}`
|
|
259
|
+
};
|
|
260
|
+
else nextParts.push({
|
|
261
|
+
type: "reasoning",
|
|
262
|
+
text: payload.delta
|
|
263
|
+
});
|
|
264
|
+
this.replaceStreamingMessage({
|
|
265
|
+
...targetMessage,
|
|
266
|
+
parts: nextParts,
|
|
267
|
+
status: "streaming"
|
|
268
|
+
});
|
|
269
|
+
};
|
|
270
|
+
handleMessageReasoningEnd = (payload) => {
|
|
271
|
+
if (this.streamingMessage?.id !== payload.messageId) return;
|
|
272
|
+
};
|
|
273
|
+
handleMessageToolCallStart = (payload) => {
|
|
274
|
+
const targetMessage = this.resolveToolCallTargetMessage(payload.sessionId, payload.toolCallId, payload.messageId);
|
|
275
|
+
this.toolCallArgsRawByCallId.set(payload.toolCallId, "");
|
|
276
|
+
const nextParts = upsertToolInvocationPart(targetMessage.parts, {
|
|
277
|
+
type: "tool-invocation",
|
|
278
|
+
toolCallId: payload.toolCallId,
|
|
279
|
+
toolName: payload.toolName,
|
|
280
|
+
state: "partial-call",
|
|
281
|
+
args: ""
|
|
282
|
+
});
|
|
283
|
+
this.replaceStreamingMessage({
|
|
284
|
+
...targetMessage,
|
|
285
|
+
parts: nextParts,
|
|
286
|
+
status: "streaming"
|
|
287
|
+
});
|
|
288
|
+
this.setError(null);
|
|
289
|
+
};
|
|
290
|
+
handleMessageToolCallArgs = (payload) => {
|
|
291
|
+
this.toolCallArgsRawByCallId.set(payload.toolCallId, payload.args);
|
|
292
|
+
this.applyToolCallArgs(payload.sessionId, payload.toolCallId, payload.args);
|
|
293
|
+
};
|
|
294
|
+
handleMessageToolCallArgsDelta = (payload) => {
|
|
295
|
+
const nextArgs = `${this.toolCallArgsRawByCallId.get(payload.toolCallId) ?? ""}${payload.delta}`;
|
|
296
|
+
this.toolCallArgsRawByCallId.set(payload.toolCallId, nextArgs);
|
|
297
|
+
this.applyToolCallArgs(payload.sessionId, payload.toolCallId, nextArgs, payload.messageId);
|
|
298
|
+
};
|
|
299
|
+
handleMessageToolCallEnd = (payload) => {
|
|
300
|
+
const targetMessage = this.resolveToolCallTargetMessage(payload.sessionId, payload.toolCallId);
|
|
301
|
+
const args = this.toolCallArgsRawByCallId.get(payload.toolCallId) ?? "";
|
|
302
|
+
const nextParts = upsertToolInvocationPart(targetMessage.parts, {
|
|
303
|
+
type: "tool-invocation",
|
|
304
|
+
toolCallId: payload.toolCallId,
|
|
305
|
+
toolName: findToolNameByCallId(targetMessage.parts, payload.toolCallId) ?? "unknown",
|
|
306
|
+
state: "call",
|
|
307
|
+
args
|
|
308
|
+
});
|
|
309
|
+
this.replaceStreamingMessage({
|
|
310
|
+
...targetMessage,
|
|
311
|
+
parts: nextParts,
|
|
312
|
+
status: "streaming"
|
|
313
|
+
});
|
|
314
|
+
};
|
|
315
|
+
handleMessageToolCallResult = (payload) => {
|
|
316
|
+
if (!this.updateMessageContainingToolCall(payload.toolCallId, (targetMessage, existingPart) => {
|
|
317
|
+
const mergedPart = {
|
|
318
|
+
type: "tool-invocation",
|
|
319
|
+
toolCallId: payload.toolCallId,
|
|
320
|
+
toolName: existingPart.toolName,
|
|
321
|
+
state: "result",
|
|
322
|
+
args: existingPart.args,
|
|
323
|
+
result: payload.content
|
|
324
|
+
};
|
|
325
|
+
return upsertToolInvocationPart(targetMessage.parts, mergedPart);
|
|
326
|
+
})) {
|
|
327
|
+
const fallbackMessage = this.resolveToolCallTargetMessage(payload.sessionId, payload.toolCallId);
|
|
328
|
+
const nextParts = upsertToolInvocationPart(fallbackMessage.parts, {
|
|
329
|
+
type: "tool-invocation",
|
|
330
|
+
toolCallId: payload.toolCallId,
|
|
331
|
+
toolName: "unknown",
|
|
332
|
+
state: "result",
|
|
333
|
+
result: payload.content
|
|
334
|
+
});
|
|
335
|
+
this.replaceStreamingMessage({
|
|
336
|
+
...fallbackMessage,
|
|
337
|
+
parts: nextParts,
|
|
338
|
+
status: "streaming"
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
handleRunStarted = (payload) => {
|
|
343
|
+
if (this.isSettledRunId(payload.runId)) return;
|
|
344
|
+
this.setError(null);
|
|
345
|
+
this.activeRun = {
|
|
346
|
+
runId: payload.runId ?? null,
|
|
347
|
+
sessionId: payload.sessionId
|
|
348
|
+
};
|
|
349
|
+
this.stateVersion += 1;
|
|
350
|
+
};
|
|
351
|
+
handleRunFinished = (payload) => {
|
|
352
|
+
this.markRunAsSettled(payload.runId ?? this.activeRun?.runId ?? null);
|
|
353
|
+
this.settleStreamingMessage("final");
|
|
354
|
+
this.setError(null);
|
|
355
|
+
this.clearActiveRun();
|
|
356
|
+
};
|
|
357
|
+
handleRunError = (payload) => {
|
|
358
|
+
this.markRunAsSettled(payload.runId ?? this.activeRun?.runId ?? null);
|
|
359
|
+
this.settleStreamingMessage("error");
|
|
360
|
+
this.setError(buildRuntimeError(payload));
|
|
361
|
+
this.clearActiveRun();
|
|
362
|
+
};
|
|
363
|
+
handleRunMetadata = (payload) => {
|
|
364
|
+
const m = payload.metadata;
|
|
365
|
+
if (m?.kind === "ready") {
|
|
366
|
+
const ready = m;
|
|
367
|
+
if (this.isSettledRunId(ready.runId)) return;
|
|
368
|
+
this.activeRun = {
|
|
369
|
+
runId: ready.runId ?? this.activeRun?.runId ?? null,
|
|
370
|
+
sessionId: ready.sessionId ?? this.activeRun?.sessionId,
|
|
371
|
+
abortDisabledReason: ready.supportsAbort === false ? ready.abortDisabledReason ?? "Unsupported" : null
|
|
372
|
+
};
|
|
373
|
+
this.stateVersion += 1;
|
|
374
|
+
} else if (m?.kind === "final") {
|
|
375
|
+
this.markRunAsSettled(payload.runId ?? this.activeRun?.runId ?? null);
|
|
376
|
+
this.clearActiveRun();
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
handleEndpointError = (payload) => {
|
|
380
|
+
if (payload.code === "abort-error") {
|
|
381
|
+
this.handleMessageAbort({
|
|
382
|
+
sessionId: this.activeRun?.sessionId ?? this.streamingMessage?.sessionId ?? "",
|
|
383
|
+
...this.streamingMessage?.id ? { messageId: this.streamingMessage.id } : {}
|
|
384
|
+
});
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
this.settleStreamingMessage("error");
|
|
388
|
+
this.clearActiveRun();
|
|
389
|
+
this.setError(payload);
|
|
390
|
+
};
|
|
391
|
+
applyToolCallArgs = (sessionId, toolCallId, args, messageId) => {
|
|
392
|
+
const targetMessage = this.resolveToolCallTargetMessage(sessionId, toolCallId, messageId);
|
|
393
|
+
const toolName = findToolNameByCallId(targetMessage.parts, toolCallId) ?? "unknown";
|
|
394
|
+
const nextParts = upsertToolInvocationPart(targetMessage.parts, {
|
|
395
|
+
type: "tool-invocation",
|
|
396
|
+
toolCallId,
|
|
397
|
+
toolName,
|
|
398
|
+
state: "partial-call",
|
|
399
|
+
args
|
|
400
|
+
});
|
|
401
|
+
this.replaceStreamingMessage({
|
|
402
|
+
...targetMessage,
|
|
403
|
+
parts: nextParts,
|
|
404
|
+
status: "streaming"
|
|
405
|
+
});
|
|
406
|
+
};
|
|
407
|
+
ensureStreamingMessage = (sessionId, messageId, status) => {
|
|
408
|
+
if (this.streamingMessage?.id === messageId) {
|
|
409
|
+
if (this.streamingMessage.status === status) return this.streamingMessage;
|
|
410
|
+
const nextStreamingMessage = {
|
|
411
|
+
...this.streamingMessage,
|
|
412
|
+
status
|
|
413
|
+
};
|
|
414
|
+
this.replaceStreamingMessage(nextStreamingMessage);
|
|
415
|
+
return nextStreamingMessage;
|
|
416
|
+
}
|
|
417
|
+
const messageIndex = this.messages.findIndex((message) => message.id === messageId);
|
|
418
|
+
if (messageIndex >= 0) {
|
|
419
|
+
const existingMessage = cloneConversationMessage(this.messages[messageIndex]);
|
|
420
|
+
const nextMessages = [...this.messages];
|
|
421
|
+
nextMessages.splice(messageIndex, 1);
|
|
422
|
+
this.messages = nextMessages;
|
|
423
|
+
this.stateVersion += 1;
|
|
424
|
+
const nextStreamingMessage = {
|
|
425
|
+
...existingMessage,
|
|
426
|
+
sessionId,
|
|
427
|
+
status
|
|
428
|
+
};
|
|
429
|
+
this.replaceStreamingMessage(nextStreamingMessage);
|
|
430
|
+
return nextStreamingMessage;
|
|
431
|
+
}
|
|
432
|
+
const existingStreamingMessage = this.streamingMessage;
|
|
433
|
+
if (existingStreamingMessage && existingStreamingMessage.id !== messageId && existingStreamingMessage.sessionId === sessionId && shouldPromoteStreamingMessageId(existingStreamingMessage, messageId)) {
|
|
434
|
+
const nextStreamingMessage = {
|
|
435
|
+
...existingStreamingMessage,
|
|
436
|
+
id: messageId,
|
|
437
|
+
sessionId,
|
|
438
|
+
status
|
|
439
|
+
};
|
|
440
|
+
remapTrackedToolCallsToMessageId(this.toolCallMessageIdByCallId, existingStreamingMessage.id, messageId);
|
|
441
|
+
this.replaceStreamingMessage(nextStreamingMessage);
|
|
442
|
+
return nextStreamingMessage;
|
|
443
|
+
}
|
|
444
|
+
const nextStreamingMessage = {
|
|
445
|
+
id: messageId,
|
|
446
|
+
sessionId,
|
|
447
|
+
role: DEFAULT_ASSISTANT_ROLE,
|
|
448
|
+
status,
|
|
449
|
+
parts: [],
|
|
450
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
451
|
+
};
|
|
452
|
+
this.replaceStreamingMessage(nextStreamingMessage);
|
|
453
|
+
return nextStreamingMessage;
|
|
454
|
+
};
|
|
455
|
+
resolveToolCallTargetMessage = (sessionId, toolCallId, messageId) => {
|
|
456
|
+
const preferredMessageId = messageId?.trim() || this.toolCallMessageIdByCallId.get(toolCallId) || this.streamingMessage?.id || `tool-${toolCallId}`;
|
|
457
|
+
this.toolCallMessageIdByCallId.set(toolCallId, preferredMessageId);
|
|
458
|
+
return this.ensureStreamingMessage(sessionId, preferredMessageId, "streaming");
|
|
459
|
+
};
|
|
460
|
+
updateMessageContainingToolCall = (toolCallId, updater) => {
|
|
461
|
+
if (this.streamingMessage) {
|
|
462
|
+
const part = findToolInvocationPart(this.streamingMessage.parts, toolCallId);
|
|
463
|
+
if (part) {
|
|
464
|
+
const nextParts = updater(this.streamingMessage, part);
|
|
465
|
+
this.replaceStreamingMessage({
|
|
466
|
+
...this.streamingMessage,
|
|
467
|
+
parts: nextParts
|
|
468
|
+
});
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
for (let index = this.messages.length - 1; index >= 0; index -= 1) {
|
|
473
|
+
const candidateMessage = this.messages[index];
|
|
474
|
+
const part = findToolInvocationPart(candidateMessage.parts, toolCallId);
|
|
475
|
+
if (!part) continue;
|
|
476
|
+
const nextMessages = [...this.messages];
|
|
477
|
+
nextMessages[index] = {
|
|
478
|
+
...candidateMessage,
|
|
479
|
+
parts: updater(candidateMessage, part)
|
|
480
|
+
};
|
|
481
|
+
this.messages = nextMessages;
|
|
482
|
+
this.stateVersion += 1;
|
|
483
|
+
return true;
|
|
484
|
+
}
|
|
485
|
+
return false;
|
|
486
|
+
};
|
|
487
|
+
upsertMessage = (message) => {
|
|
488
|
+
const normalizedMessage = normalizeConversationMessage(message);
|
|
489
|
+
const messageIndex = this.messages.findIndex((item) => item.id === normalizedMessage.id);
|
|
490
|
+
if (messageIndex < 0) {
|
|
491
|
+
this.messages = [...this.messages, normalizedMessage];
|
|
492
|
+
this.stateVersion += 1;
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
const nextMessages = [...this.messages];
|
|
496
|
+
nextMessages[messageIndex] = normalizedMessage;
|
|
497
|
+
this.messages = nextMessages;
|
|
498
|
+
this.stateVersion += 1;
|
|
499
|
+
};
|
|
500
|
+
replaceStreamingMessage = (nextStreamingMessage) => {
|
|
501
|
+
if (!nextStreamingMessage && !this.streamingMessage) return;
|
|
502
|
+
this.streamingMessage = nextStreamingMessage ? normalizeConversationMessage(nextStreamingMessage) : null;
|
|
503
|
+
this.stateVersion += 1;
|
|
504
|
+
};
|
|
505
|
+
setError = (nextError) => {
|
|
506
|
+
if (this.error?.code === nextError?.code && this.error?.message === nextError?.message && this.error?.details === nextError?.details && this.error?.cause === nextError?.cause) return;
|
|
507
|
+
this.error = nextError ? {
|
|
508
|
+
...nextError,
|
|
509
|
+
details: nextError.details ? { ...nextError.details } : void 0
|
|
510
|
+
} : null;
|
|
511
|
+
this.stateVersion += 1;
|
|
512
|
+
};
|
|
513
|
+
clearActiveRun = () => {
|
|
514
|
+
if (!this.activeRun) return;
|
|
515
|
+
this.activeRun = null;
|
|
516
|
+
this.stateVersion += 1;
|
|
517
|
+
};
|
|
518
|
+
isSettledRunId = (runId) => {
|
|
519
|
+
return Boolean(runId?.trim()) && runId === this.lastSettledRunId;
|
|
520
|
+
};
|
|
521
|
+
markRunAsSettled = (runId) => {
|
|
522
|
+
this.lastSettledRunId = runId?.trim() || null;
|
|
523
|
+
};
|
|
524
|
+
settleStreamingMessage = (status) => {
|
|
525
|
+
if (!this.streamingMessage) return;
|
|
526
|
+
const settledMessage = {
|
|
527
|
+
...this.streamingMessage,
|
|
528
|
+
status
|
|
529
|
+
};
|
|
530
|
+
this.upsertMessage(settledMessage);
|
|
531
|
+
this.replaceStreamingMessage(null);
|
|
532
|
+
clearToolCallTrackingByMessageId(this.toolCallMessageIdByCallId, this.toolCallArgsRawByCallId, settledMessage.id);
|
|
533
|
+
};
|
|
534
|
+
notifyListeners = () => {
|
|
535
|
+
const snapshot = this.getSnapshot();
|
|
536
|
+
for (const listener of this.listeners) listener(snapshot);
|
|
537
|
+
};
|
|
651
538
|
};
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
539
|
+
//#endregion
|
|
540
|
+
//#region src/agent/agent-client-from-server.ts
|
|
541
|
+
/**
|
|
542
|
+
* Creates an NcpAgentClientEndpoint that forwards to an in-process NcpAgentServerEndpoint.
|
|
543
|
+
* Use when the agent runs in-process and you need to pass a client endpoint to the HTTP server.
|
|
544
|
+
*/
|
|
657
545
|
function createAgentClientFromServer(server) {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
};
|
|
546
|
+
return {
|
|
547
|
+
get manifest() {
|
|
548
|
+
return server.manifest;
|
|
549
|
+
},
|
|
550
|
+
async start() {
|
|
551
|
+
await server.start();
|
|
552
|
+
},
|
|
553
|
+
async stop() {
|
|
554
|
+
await server.stop();
|
|
555
|
+
},
|
|
556
|
+
async emit(event) {
|
|
557
|
+
switch (event.type) {
|
|
558
|
+
case NcpEventType.MessageRequest:
|
|
559
|
+
await consume(server.send(event.payload));
|
|
560
|
+
return;
|
|
561
|
+
case NcpEventType.MessageStreamRequest:
|
|
562
|
+
await consume(server.stream(event.payload));
|
|
563
|
+
return;
|
|
564
|
+
case NcpEventType.MessageAbort:
|
|
565
|
+
await server.abort(event.payload);
|
|
566
|
+
return;
|
|
567
|
+
default: await server.emit(event);
|
|
568
|
+
}
|
|
569
|
+
},
|
|
570
|
+
subscribe(listener) {
|
|
571
|
+
return server.subscribe(listener);
|
|
572
|
+
},
|
|
573
|
+
async send(envelope) {
|
|
574
|
+
await consume(server.send(envelope));
|
|
575
|
+
},
|
|
576
|
+
async stream(payload) {
|
|
577
|
+
await consume(server.stream(payload));
|
|
578
|
+
},
|
|
579
|
+
async abort(payload) {
|
|
580
|
+
await server.abort(payload);
|
|
581
|
+
}
|
|
582
|
+
};
|
|
696
583
|
}
|
|
697
584
|
async function consume(events) {
|
|
698
|
-
|
|
699
|
-
void event;
|
|
700
|
-
}
|
|
585
|
+
for await (const event of events);
|
|
701
586
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
import {
|
|
705
|
-
NcpEventType as NcpEventType6
|
|
706
|
-
} from "@nextclaw/ncp";
|
|
707
|
-
|
|
708
|
-
// src/agent/agent-backend/event-publisher.ts
|
|
587
|
+
//#endregion
|
|
588
|
+
//#region src/agent/agent-backend/event-publisher.ts
|
|
709
589
|
var EventPublisher = class {
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
if (this.closed) {
|
|
742
|
-
return;
|
|
743
|
-
}
|
|
744
|
-
this.closed = true;
|
|
745
|
-
this.listeners.clear();
|
|
746
|
-
for (const listener of this.closeListeners) {
|
|
747
|
-
listener();
|
|
748
|
-
}
|
|
749
|
-
this.closeListeners.clear();
|
|
750
|
-
}
|
|
590
|
+
listeners = /* @__PURE__ */ new Set();
|
|
591
|
+
closeListeners = /* @__PURE__ */ new Set();
|
|
592
|
+
closed = false;
|
|
593
|
+
subscribe(listener) {
|
|
594
|
+
if (this.closed) return () => void 0;
|
|
595
|
+
this.listeners.add(listener);
|
|
596
|
+
return () => {
|
|
597
|
+
this.listeners.delete(listener);
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
onClose(listener) {
|
|
601
|
+
if (this.closed) {
|
|
602
|
+
listener();
|
|
603
|
+
return () => void 0;
|
|
604
|
+
}
|
|
605
|
+
this.closeListeners.add(listener);
|
|
606
|
+
return () => {
|
|
607
|
+
this.closeListeners.delete(listener);
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
publish(event) {
|
|
611
|
+
if (this.closed) return;
|
|
612
|
+
for (const listener of this.listeners) listener(structuredClone(event));
|
|
613
|
+
}
|
|
614
|
+
close() {
|
|
615
|
+
if (this.closed) return;
|
|
616
|
+
this.closed = true;
|
|
617
|
+
this.listeners.clear();
|
|
618
|
+
for (const listener of this.closeListeners) listener();
|
|
619
|
+
this.closeListeners.clear();
|
|
620
|
+
}
|
|
751
621
|
};
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
function readOptionalAgentId(value) {
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
const trimmed = value.trim().toLowerCase();
|
|
759
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
622
|
+
//#endregion
|
|
623
|
+
//#region src/agent/agent-backend/agent-live-session-registry.ts
|
|
624
|
+
function readOptionalAgentId$2(value) {
|
|
625
|
+
if (typeof value !== "string") return;
|
|
626
|
+
const trimmed = value.trim().toLowerCase();
|
|
627
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
760
628
|
}
|
|
761
|
-
function readAgentIdFromMetadata(metadata) {
|
|
762
|
-
|
|
629
|
+
function readAgentIdFromMetadata$1(metadata) {
|
|
630
|
+
return readOptionalAgentId$2(metadata?.agent_id) ?? readOptionalAgentId$2(metadata?.agentId);
|
|
763
631
|
}
|
|
764
632
|
var AgentLiveSessionRegistry = class {
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
return session;
|
|
827
|
-
};
|
|
828
|
-
clear = () => {
|
|
829
|
-
this.sessions.clear();
|
|
830
|
-
};
|
|
831
|
-
listSessions = () => {
|
|
832
|
-
return [...this.sessions.values()];
|
|
833
|
-
};
|
|
633
|
+
sessions = /* @__PURE__ */ new Map();
|
|
634
|
+
constructor(sessionStore, createRuntime) {
|
|
635
|
+
this.sessionStore = sessionStore;
|
|
636
|
+
this.createRuntime = createRuntime;
|
|
637
|
+
}
|
|
638
|
+
ensureSession = async (sessionId, initialMetadata) => {
|
|
639
|
+
const existing = this.sessions.get(sessionId);
|
|
640
|
+
if (existing) {
|
|
641
|
+
if (!existing.agentId) existing.agentId = readAgentIdFromMetadata$1(initialMetadata) ?? existing.agentId;
|
|
642
|
+
if (initialMetadata && Object.keys(initialMetadata).length > 0) existing.metadata = {
|
|
643
|
+
...existing.metadata,
|
|
644
|
+
...structuredClone(initialMetadata)
|
|
645
|
+
};
|
|
646
|
+
return existing;
|
|
647
|
+
}
|
|
648
|
+
const storedSession = await this.sessionStore.getSession(sessionId);
|
|
649
|
+
const stateManager = new DefaultNcpAgentConversationStateManager();
|
|
650
|
+
stateManager.hydrate({
|
|
651
|
+
sessionId,
|
|
652
|
+
messages: cloneMessages(storedSession?.messages ?? [])
|
|
653
|
+
});
|
|
654
|
+
const sessionMetadata = {
|
|
655
|
+
...storedSession?.metadata ? structuredClone(storedSession.metadata) : {},
|
|
656
|
+
...initialMetadata ? structuredClone(initialMetadata) : {}
|
|
657
|
+
};
|
|
658
|
+
const sessionAgentId = readOptionalAgentId$2(storedSession?.agentId) ?? readAgentIdFromMetadata$1(initialMetadata);
|
|
659
|
+
const session = {
|
|
660
|
+
sessionId,
|
|
661
|
+
...sessionAgentId ? { agentId: sessionAgentId } : {},
|
|
662
|
+
stateManager,
|
|
663
|
+
metadata: sessionMetadata,
|
|
664
|
+
runtime: null,
|
|
665
|
+
publisher: new EventPublisher(),
|
|
666
|
+
activeExecution: null
|
|
667
|
+
};
|
|
668
|
+
session.runtime = this.createRuntime({
|
|
669
|
+
sessionId,
|
|
670
|
+
...sessionAgentId ? { agentId: sessionAgentId } : {},
|
|
671
|
+
stateManager,
|
|
672
|
+
sessionMetadata,
|
|
673
|
+
setSessionMetadata: (nextMetadata) => {
|
|
674
|
+
session.metadata = { ...structuredClone(nextMetadata) };
|
|
675
|
+
}
|
|
676
|
+
});
|
|
677
|
+
this.sessions.set(sessionId, session);
|
|
678
|
+
return session;
|
|
679
|
+
};
|
|
680
|
+
getSession = (sessionId) => {
|
|
681
|
+
return this.sessions.get(sessionId) ?? null;
|
|
682
|
+
};
|
|
683
|
+
deleteSession = (sessionId) => {
|
|
684
|
+
const session = this.sessions.get(sessionId) ?? null;
|
|
685
|
+
if (session) this.sessions.delete(sessionId);
|
|
686
|
+
return session;
|
|
687
|
+
};
|
|
688
|
+
clear = () => {
|
|
689
|
+
this.sessions.clear();
|
|
690
|
+
};
|
|
691
|
+
listSessions = () => {
|
|
692
|
+
return [...this.sessions.values()];
|
|
693
|
+
};
|
|
834
694
|
};
|
|
835
695
|
function cloneMessages(messages) {
|
|
836
|
-
|
|
696
|
+
return messages.map((message) => structuredClone(message));
|
|
837
697
|
}
|
|
838
|
-
|
|
839
|
-
|
|
698
|
+
//#endregion
|
|
699
|
+
//#region src/errors/ncp-error-exception.ts
|
|
700
|
+
/**
|
|
701
|
+
* Throwable form of protocol error for exception-based control flows.
|
|
702
|
+
*/
|
|
840
703
|
var NcpErrorException = class extends Error {
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
704
|
+
code;
|
|
705
|
+
details;
|
|
706
|
+
constructor(code, message, details) {
|
|
707
|
+
super(message);
|
|
708
|
+
this.name = "NcpErrorException";
|
|
709
|
+
this.code = code;
|
|
710
|
+
this.details = details;
|
|
711
|
+
}
|
|
849
712
|
};
|
|
850
|
-
|
|
851
|
-
|
|
713
|
+
//#endregion
|
|
714
|
+
//#region src/agent/agent-backend/agent-backend-execution-utils.ts
|
|
852
715
|
function startAgentBackendSessionExecution(params) {
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
requestEnvelope: structuredClone(envelope),
|
|
870
|
-
abortHandled: false,
|
|
871
|
-
closed: false
|
|
872
|
-
};
|
|
873
|
-
session.activeExecution = execution;
|
|
874
|
-
onStatusChanged?.({ sessionKey: session.sessionId, status: "running" });
|
|
875
|
-
return execution;
|
|
716
|
+
const { session, envelope, signal, onStatusChanged } = params;
|
|
717
|
+
if (session.activeExecution && !session.activeExecution.closed) throw new NcpErrorException("runtime-error", `Session ${session.sessionId} already has an active execution.`, { sessionId: session.sessionId });
|
|
718
|
+
const controller = new AbortController();
|
|
719
|
+
if (signal) signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
720
|
+
const execution = {
|
|
721
|
+
controller,
|
|
722
|
+
requestEnvelope: structuredClone(envelope),
|
|
723
|
+
abortHandled: false,
|
|
724
|
+
closed: false
|
|
725
|
+
};
|
|
726
|
+
session.activeExecution = execution;
|
|
727
|
+
onStatusChanged?.({
|
|
728
|
+
sessionKey: session.sessionId,
|
|
729
|
+
status: "running"
|
|
730
|
+
});
|
|
731
|
+
return execution;
|
|
876
732
|
}
|
|
877
733
|
function finishAgentBackendSessionExecution(params) {
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
734
|
+
const { session, execution, onStatusChanged } = params;
|
|
735
|
+
if (session.activeExecution === execution) {
|
|
736
|
+
session.activeExecution = null;
|
|
737
|
+
onStatusChanged?.({
|
|
738
|
+
sessionKey: session.sessionId,
|
|
739
|
+
status: "idle"
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
closeAgentBackendSessionExecution(execution);
|
|
884
743
|
}
|
|
885
744
|
function closeAgentBackendSessionExecution(execution) {
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
}
|
|
889
|
-
execution.closed = true;
|
|
745
|
+
if (execution.closed) return;
|
|
746
|
+
execution.closed = true;
|
|
890
747
|
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
import {
|
|
894
|
-
isHiddenNcpMessage
|
|
895
|
-
} from "@nextclaw/ncp";
|
|
896
|
-
import { NcpEventType as NcpEventType3 } from "@nextclaw/ncp";
|
|
748
|
+
//#endregion
|
|
749
|
+
//#region src/agent/agent-backend/agent-run-executor.ts
|
|
897
750
|
var AgentRunExecutor = class {
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
type: NcpEventType3.RunError,
|
|
938
|
-
payload: {
|
|
939
|
-
sessionId: envelope.sessionId,
|
|
940
|
-
error: message
|
|
941
|
-
}
|
|
942
|
-
};
|
|
943
|
-
await session.stateManager.dispatch(runErrorEvent);
|
|
944
|
-
return runErrorEvent;
|
|
945
|
-
}
|
|
751
|
+
async *executeRun(session, envelope, controller) {
|
|
752
|
+
if (!isHiddenNcpMessage(envelope.message)) {
|
|
753
|
+
const messageSent = {
|
|
754
|
+
type: NcpEventType.MessageSent,
|
|
755
|
+
payload: {
|
|
756
|
+
sessionId: envelope.sessionId,
|
|
757
|
+
message: structuredClone(envelope.message),
|
|
758
|
+
metadata: envelope.metadata
|
|
759
|
+
}
|
|
760
|
+
};
|
|
761
|
+
await session.stateManager.dispatch(messageSent);
|
|
762
|
+
yield structuredClone(messageSent);
|
|
763
|
+
}
|
|
764
|
+
try {
|
|
765
|
+
for await (const event of session.runtime.run({
|
|
766
|
+
sessionId: envelope.sessionId,
|
|
767
|
+
messages: [envelope.message],
|
|
768
|
+
correlationId: envelope.correlationId,
|
|
769
|
+
metadata: envelope.metadata
|
|
770
|
+
}, { signal: controller.signal })) yield structuredClone(event);
|
|
771
|
+
} catch (error) {
|
|
772
|
+
if (!controller.signal.aborted) {
|
|
773
|
+
const runErrorEvent = await this.publishFailure(error, envelope, session);
|
|
774
|
+
yield structuredClone(runErrorEvent);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
async publishFailure(error, envelope, session) {
|
|
779
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
780
|
+
const runErrorEvent = {
|
|
781
|
+
type: NcpEventType.RunError,
|
|
782
|
+
payload: {
|
|
783
|
+
sessionId: envelope.sessionId,
|
|
784
|
+
error: message
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
await session.stateManager.dispatch(runErrorEvent);
|
|
788
|
+
return runErrorEvent;
|
|
789
|
+
}
|
|
946
790
|
};
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
import { NcpEventType as NcpEventType4 } from "@nextclaw/ncp";
|
|
950
|
-
|
|
951
|
-
// src/agent/agent-backend/async-queue.ts
|
|
791
|
+
//#endregion
|
|
792
|
+
//#region src/agent/agent-backend/async-queue.ts
|
|
952
793
|
function createAsyncQueue() {
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
},
|
|
1006
|
-
iterable
|
|
1007
|
-
};
|
|
794
|
+
const items = [];
|
|
795
|
+
let closed = false;
|
|
796
|
+
let pendingResolve = null;
|
|
797
|
+
return {
|
|
798
|
+
push(item) {
|
|
799
|
+
if (closed) return;
|
|
800
|
+
if (pendingResolve) {
|
|
801
|
+
const resolve = pendingResolve;
|
|
802
|
+
pendingResolve = null;
|
|
803
|
+
resolve({
|
|
804
|
+
value: item,
|
|
805
|
+
done: false
|
|
806
|
+
});
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
items.push(item);
|
|
810
|
+
},
|
|
811
|
+
close() {
|
|
812
|
+
if (closed) return;
|
|
813
|
+
closed = true;
|
|
814
|
+
if (pendingResolve) {
|
|
815
|
+
const resolve = pendingResolve;
|
|
816
|
+
pendingResolve = null;
|
|
817
|
+
resolve({
|
|
818
|
+
value: void 0,
|
|
819
|
+
done: true
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
},
|
|
823
|
+
iterable: { [Symbol.asyncIterator]() {
|
|
824
|
+
return { next: () => {
|
|
825
|
+
if (items.length > 0) return Promise.resolve({
|
|
826
|
+
value: items.shift(),
|
|
827
|
+
done: false
|
|
828
|
+
});
|
|
829
|
+
if (closed) return Promise.resolve({
|
|
830
|
+
value: void 0,
|
|
831
|
+
done: true
|
|
832
|
+
});
|
|
833
|
+
return new Promise((resolve) => {
|
|
834
|
+
pendingResolve = resolve;
|
|
835
|
+
});
|
|
836
|
+
} };
|
|
837
|
+
} }
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
//#endregion
|
|
841
|
+
//#region src/agent/agent-backend/agent-backend-session-realtime.ts
|
|
842
|
+
function readEventSessionId(event) {
|
|
843
|
+
const payload = "payload" in event ? event.payload : null;
|
|
844
|
+
if (!payload || typeof payload !== "object") return null;
|
|
845
|
+
return "sessionId" in payload && typeof payload.sessionId === "string" ? payload.sessionId : null;
|
|
1008
846
|
}
|
|
1009
|
-
|
|
1010
|
-
// src/agent/agent-backend/agent-backend-session-realtime.ts
|
|
1011
847
|
var AgentBackendSessionRealtime = class {
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
);
|
|
1088
|
-
return this.params.getSessionSummary(normalizedSessionId);
|
|
1089
|
-
};
|
|
1090
|
-
updateToolCallResult = async (sessionId, toolCallId, content) => {
|
|
1091
|
-
const normalizedSessionId = sessionId.trim();
|
|
1092
|
-
const normalizedToolCallId = toolCallId.trim();
|
|
1093
|
-
if (!normalizedSessionId || !normalizedToolCallId) {
|
|
1094
|
-
return null;
|
|
1095
|
-
}
|
|
1096
|
-
const liveSession = await this.params.sessionRegistry.ensureSession(normalizedSessionId);
|
|
1097
|
-
await this.publishSessionEvent(
|
|
1098
|
-
liveSession,
|
|
1099
|
-
{
|
|
1100
|
-
type: NcpEventType4.MessageToolCallResult,
|
|
1101
|
-
payload: {
|
|
1102
|
-
sessionId: normalizedSessionId,
|
|
1103
|
-
toolCallId: normalizedToolCallId,
|
|
1104
|
-
content
|
|
1105
|
-
}
|
|
1106
|
-
},
|
|
1107
|
-
{
|
|
1108
|
-
dispatchToStateManager: true
|
|
1109
|
-
}
|
|
1110
|
-
);
|
|
1111
|
-
return this.params.getSessionSummary(normalizedSessionId);
|
|
1112
|
-
};
|
|
848
|
+
constructor(params) {
|
|
849
|
+
this.params = params;
|
|
850
|
+
}
|
|
851
|
+
publishSessionEvent = async (session, event, options = {}) => {
|
|
852
|
+
if (options.dispatchToStateManager) await session.stateManager.dispatch(event);
|
|
853
|
+
this.params.publishEndpointEvent(event);
|
|
854
|
+
session.publisher.publish(event);
|
|
855
|
+
if (options.persistSession !== false) await this.params.persistSession(session.sessionId);
|
|
856
|
+
};
|
|
857
|
+
streamSessionEvents = (payloadOrParams, opts) => {
|
|
858
|
+
return (async function* (self) {
|
|
859
|
+
const payload = "payload" in payloadOrParams && "signal" in payloadOrParams ? payloadOrParams.payload : payloadOrParams;
|
|
860
|
+
const signal = "payload" in payloadOrParams && "signal" in payloadOrParams ? payloadOrParams.signal : opts?.signal ?? new AbortController().signal;
|
|
861
|
+
const queue = createAsyncQueue();
|
|
862
|
+
const unsubscribe = self.params.subscribeEndpointEvent((event) => {
|
|
863
|
+
if (readEventSessionId(event) !== payload.sessionId) return;
|
|
864
|
+
queue.push(event);
|
|
865
|
+
});
|
|
866
|
+
const stop = () => {
|
|
867
|
+
unsubscribe();
|
|
868
|
+
queue.close();
|
|
869
|
+
signal.removeEventListener("abort", stop);
|
|
870
|
+
};
|
|
871
|
+
const liveSession = self.params.sessionRegistry.getSession(payload.sessionId);
|
|
872
|
+
const unsubscribeClose = liveSession ? liveSession.publisher.onClose(() => {
|
|
873
|
+
queue.close();
|
|
874
|
+
}) : () => void 0;
|
|
875
|
+
signal.addEventListener("abort", stop, { once: true });
|
|
876
|
+
try {
|
|
877
|
+
for await (const event of queue.iterable) {
|
|
878
|
+
if (signal.aborted) break;
|
|
879
|
+
yield event;
|
|
880
|
+
}
|
|
881
|
+
} finally {
|
|
882
|
+
unsubscribeClose();
|
|
883
|
+
stop();
|
|
884
|
+
}
|
|
885
|
+
})(this);
|
|
886
|
+
};
|
|
887
|
+
appendMessage = async (sessionId, message) => {
|
|
888
|
+
const normalizedSessionId = sessionId.trim();
|
|
889
|
+
if (!normalizedSessionId) return null;
|
|
890
|
+
let liveSession = this.params.sessionRegistry.getSession(normalizedSessionId);
|
|
891
|
+
if (!liveSession) {
|
|
892
|
+
if (!await this.params.sessionStore.getSession(normalizedSessionId)) return null;
|
|
893
|
+
liveSession = await this.params.sessionRegistry.ensureSession(normalizedSessionId);
|
|
894
|
+
}
|
|
895
|
+
const nextMessage = {
|
|
896
|
+
...structuredClone(message),
|
|
897
|
+
sessionId: normalizedSessionId
|
|
898
|
+
};
|
|
899
|
+
await this.publishSessionEvent(liveSession, {
|
|
900
|
+
type: NcpEventType.MessageSent,
|
|
901
|
+
payload: {
|
|
902
|
+
sessionId: normalizedSessionId,
|
|
903
|
+
message: nextMessage
|
|
904
|
+
}
|
|
905
|
+
}, { dispatchToStateManager: true });
|
|
906
|
+
return this.params.getSessionSummary(normalizedSessionId);
|
|
907
|
+
};
|
|
908
|
+
updateToolCallResult = async (sessionId, toolCallId, content) => {
|
|
909
|
+
const normalizedSessionId = sessionId.trim();
|
|
910
|
+
const normalizedToolCallId = toolCallId.trim();
|
|
911
|
+
if (!normalizedSessionId || !normalizedToolCallId) return null;
|
|
912
|
+
const liveSession = await this.params.sessionRegistry.ensureSession(normalizedSessionId);
|
|
913
|
+
await this.publishSessionEvent(liveSession, {
|
|
914
|
+
type: NcpEventType.MessageToolCallResult,
|
|
915
|
+
payload: {
|
|
916
|
+
sessionId: normalizedSessionId,
|
|
917
|
+
toolCallId: normalizedToolCallId,
|
|
918
|
+
content
|
|
919
|
+
}
|
|
920
|
+
}, { dispatchToStateManager: true });
|
|
921
|
+
return this.params.getSessionSummary(normalizedSessionId);
|
|
922
|
+
};
|
|
1113
923
|
};
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
}
|
|
1122
|
-
const trimmed = value.trim().toLowerCase();
|
|
1123
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
924
|
+
//#endregion
|
|
925
|
+
//#region src/agent/agent-backend/agent-backend-session-utils.ts
|
|
926
|
+
const AUTO_SESSION_LABEL_MAX_LENGTH = 64;
|
|
927
|
+
function readOptionalAgentId$1(value) {
|
|
928
|
+
if (typeof value !== "string") return;
|
|
929
|
+
const trimmed = value.trim().toLowerCase();
|
|
930
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
1124
931
|
}
|
|
1125
932
|
function readMessages(snapshot) {
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
}
|
|
1130
|
-
return messages;
|
|
933
|
+
const messages = snapshot.messages.map((message) => structuredClone(message));
|
|
934
|
+
if (snapshot.streamingMessage) messages.push(structuredClone(snapshot.streamingMessage));
|
|
935
|
+
return messages;
|
|
1131
936
|
}
|
|
1132
937
|
function toSessionSummary(session, liveSession) {
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
938
|
+
const metadata = withAutoSessionLabel({
|
|
939
|
+
metadata: session.metadata ? structuredClone({
|
|
940
|
+
...session.metadata,
|
|
941
|
+
...liveSession?.metadata ? liveSession.metadata : {}
|
|
942
|
+
}) : liveSession?.metadata ? structuredClone(liveSession.metadata) : {},
|
|
943
|
+
messages: session.messages
|
|
944
|
+
});
|
|
945
|
+
return {
|
|
946
|
+
sessionId: session.sessionId,
|
|
947
|
+
...readOptionalAgentId$1(session.agentId) ? { agentId: readOptionalAgentId$1(session.agentId) } : {},
|
|
948
|
+
messageCount: session.messages.length,
|
|
949
|
+
updatedAt: session.updatedAt,
|
|
950
|
+
status: liveSession?.activeExecution ? "running" : "idle",
|
|
951
|
+
...Object.keys(metadata).length > 0 ? { metadata } : {}
|
|
952
|
+
};
|
|
1148
953
|
}
|
|
1149
954
|
function toLiveSessionSummary(session) {
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
};
|
|
955
|
+
const messages = readMessages(session.stateManager.getSnapshot());
|
|
956
|
+
const metadata = withAutoSessionLabel({
|
|
957
|
+
metadata: Object.keys(session.metadata).length > 0 ? structuredClone(session.metadata) : session.activeExecution?.requestEnvelope.metadata ? structuredClone(session.activeExecution.requestEnvelope.metadata) : {},
|
|
958
|
+
messages
|
|
959
|
+
});
|
|
960
|
+
return {
|
|
961
|
+
sessionId: session.sessionId,
|
|
962
|
+
...readOptionalAgentId$1(session.agentId) ? { agentId: readOptionalAgentId$1(session.agentId) } : {},
|
|
963
|
+
messageCount: messages.length,
|
|
964
|
+
updatedAt: now(),
|
|
965
|
+
status: session.activeExecution ? "running" : "idle",
|
|
966
|
+
...Object.keys(metadata).length > 0 ? { metadata } : {}
|
|
967
|
+
};
|
|
1164
968
|
}
|
|
1165
969
|
function now() {
|
|
1166
|
-
|
|
970
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1167
971
|
}
|
|
1168
972
|
function readOptionalString(value) {
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
const trimmed = value.trim();
|
|
1173
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
973
|
+
if (typeof value !== "string") return null;
|
|
974
|
+
const trimmed = value.trim();
|
|
975
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1174
976
|
}
|
|
1175
977
|
function truncateLabel(value) {
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
}
|
|
1180
|
-
return `${characters.slice(0, AUTO_SESSION_LABEL_MAX_LENGTH).join("")}\u2026`;
|
|
978
|
+
const characters = Array.from(value);
|
|
979
|
+
if (characters.length <= AUTO_SESSION_LABEL_MAX_LENGTH) return value;
|
|
980
|
+
return `${characters.slice(0, AUTO_SESSION_LABEL_MAX_LENGTH).join("")}…`;
|
|
1181
981
|
}
|
|
1182
982
|
function resolveAutoSessionLabelFromMessages(messages) {
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
return truncateLabel(text);
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
return null;
|
|
983
|
+
for (const message of messages) {
|
|
984
|
+
if (message.role !== "user") continue;
|
|
985
|
+
for (const part of message.parts) if (part.type === "text" || part.type === "rich-text") {
|
|
986
|
+
const text = readOptionalString(part.text);
|
|
987
|
+
if (text) return truncateLabel(text);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
return null;
|
|
1197
991
|
}
|
|
1198
992
|
function withAutoSessionLabel(params) {
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
}
|
|
1207
|
-
return {
|
|
1208
|
-
...params.metadata,
|
|
1209
|
-
label: nextLabel
|
|
1210
|
-
};
|
|
993
|
+
if (readOptionalString(params.metadata.label)) return params.metadata;
|
|
994
|
+
const nextLabel = resolveAutoSessionLabelFromMessages(params.messages);
|
|
995
|
+
if (!nextLabel) return params.metadata;
|
|
996
|
+
return {
|
|
997
|
+
...params.metadata,
|
|
998
|
+
label: nextLabel
|
|
999
|
+
};
|
|
1211
1000
|
}
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
function
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
const trimmed = value.trim().toLowerCase();
|
|
1219
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
1001
|
+
//#endregion
|
|
1002
|
+
//#region src/agent/agent-backend/agent-backend-session-persistence.ts
|
|
1003
|
+
function readOptionalAgentId(value) {
|
|
1004
|
+
if (typeof value !== "string") return;
|
|
1005
|
+
const trimmed = value.trim().toLowerCase();
|
|
1006
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
1220
1007
|
}
|
|
1221
|
-
function
|
|
1222
|
-
|
|
1008
|
+
function readAgentIdFromMetadata(metadata) {
|
|
1009
|
+
return readOptionalAgentId(metadata?.agent_id) ?? readOptionalAgentId(metadata?.agentId);
|
|
1223
1010
|
}
|
|
1224
1011
|
function resolvePersistedAgentId(params) {
|
|
1225
|
-
|
|
1012
|
+
return readOptionalAgentId(params.liveSession?.agentId) ?? readOptionalAgentId(params.storedSession?.agentId) ?? readAgentIdFromMetadata(params.liveSession?.metadata) ?? readAgentIdFromMetadata(params.storedSession?.metadata);
|
|
1226
1013
|
}
|
|
1227
1014
|
function buildUpdatedSessionRecord(params) {
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
messages: params.liveSession ? readMessages(params.liveSession.stateManager.getSnapshot()) : params.storedSession?.messages.map((message) => structuredClone(message)) ?? [],
|
|
1244
|
-
updatedAt: params.updatedAt,
|
|
1245
|
-
metadata: nextMetadata
|
|
1246
|
-
};
|
|
1015
|
+
const nextMetadata = params.patch.metadata === null ? {} : params.patch.metadata ? structuredClone(params.patch.metadata) : structuredClone(params.liveSession?.metadata ?? params.storedSession?.metadata ?? {});
|
|
1016
|
+
if (params.liveSession) params.liveSession.metadata = structuredClone(nextMetadata);
|
|
1017
|
+
return {
|
|
1018
|
+
sessionId: params.sessionId,
|
|
1019
|
+
...resolvePersistedAgentId({
|
|
1020
|
+
liveSession: params.liveSession,
|
|
1021
|
+
storedSession: params.storedSession
|
|
1022
|
+
}) ? { agentId: resolvePersistedAgentId({
|
|
1023
|
+
liveSession: params.liveSession,
|
|
1024
|
+
storedSession: params.storedSession
|
|
1025
|
+
}) } : {},
|
|
1026
|
+
messages: params.liveSession ? readMessages(params.liveSession.stateManager.getSnapshot()) : params.storedSession?.messages.map((message) => structuredClone(message)) ?? [],
|
|
1027
|
+
updatedAt: params.updatedAt,
|
|
1028
|
+
metadata: nextMetadata
|
|
1029
|
+
};
|
|
1247
1030
|
}
|
|
1248
1031
|
function buildPersistedLiveSessionRecord(params) {
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
metadata
|
|
1265
|
-
};
|
|
1032
|
+
const messages = readMessages(params.session.stateManager.getSnapshot());
|
|
1033
|
+
const metadata = withAutoSessionLabel({
|
|
1034
|
+
metadata: {
|
|
1035
|
+
...params.session.metadata ? structuredClone(params.session.metadata) : {},
|
|
1036
|
+
...params.session.activeExecution?.requestEnvelope.metadata ? structuredClone(params.session.activeExecution.requestEnvelope.metadata) : {}
|
|
1037
|
+
},
|
|
1038
|
+
messages
|
|
1039
|
+
});
|
|
1040
|
+
return {
|
|
1041
|
+
sessionId: params.sessionId,
|
|
1042
|
+
...readOptionalAgentId(params.session.agentId) ?? readAgentIdFromMetadata(params.session.metadata) ?? readAgentIdFromMetadata(params.session.activeExecution?.requestEnvelope.metadata) ? { agentId: readOptionalAgentId(params.session.agentId) ?? readAgentIdFromMetadata(params.session.metadata) ?? readAgentIdFromMetadata(params.session.activeExecution?.requestEnvelope.metadata) } : {},
|
|
1043
|
+
messages,
|
|
1044
|
+
updatedAt: params.updatedAt,
|
|
1045
|
+
metadata
|
|
1046
|
+
};
|
|
1266
1047
|
}
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1048
|
+
//#endregion
|
|
1049
|
+
//#region src/agent/agent-backend/agent-backend.ts
|
|
1050
|
+
const DEFAULT_SUPPORTED_PART_TYPES = [
|
|
1051
|
+
"text",
|
|
1052
|
+
"file",
|
|
1053
|
+
"source",
|
|
1054
|
+
"step-start",
|
|
1055
|
+
"reasoning",
|
|
1056
|
+
"tool-invocation",
|
|
1057
|
+
"card",
|
|
1058
|
+
"rich-text",
|
|
1059
|
+
"action",
|
|
1060
|
+
"extension"
|
|
1280
1061
|
];
|
|
1281
1062
|
var DefaultNcpAgentBackend = class {
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
await this.sessionStore.deleteSession(sessionId);
|
|
1476
|
-
};
|
|
1477
|
-
ensureStarted = async () => {
|
|
1478
|
-
if (!this.started) {
|
|
1479
|
-
await this.start();
|
|
1480
|
-
}
|
|
1481
|
-
};
|
|
1482
|
-
startSessionExecution = (session, envelope, signal) => startAgentBackendSessionExecution({
|
|
1483
|
-
session,
|
|
1484
|
-
envelope,
|
|
1485
|
-
signal,
|
|
1486
|
-
onStatusChanged: this.onSessionRunStatusChanged
|
|
1487
|
-
});
|
|
1488
|
-
finishSessionExecution = (session, execution) => finishAgentBackendSessionExecution({
|
|
1489
|
-
session,
|
|
1490
|
-
execution,
|
|
1491
|
-
onStatusChanged: this.onSessionRunStatusChanged
|
|
1492
|
-
});
|
|
1493
|
-
publishEndpointEvent = (event) => {
|
|
1494
|
-
this.publisher.publish(event);
|
|
1495
|
-
};
|
|
1496
|
-
handleAbort = async (payload) => {
|
|
1497
|
-
const session = this.sessionRegistry.getSession(payload.sessionId);
|
|
1498
|
-
const execution = session?.activeExecution;
|
|
1499
|
-
if (!session || !execution || execution.closed) {
|
|
1500
|
-
return;
|
|
1501
|
-
}
|
|
1502
|
-
execution.abortHandled = true;
|
|
1503
|
-
execution.controller.abort();
|
|
1504
|
-
const abortEvent = {
|
|
1505
|
-
type: NcpEventType6.MessageAbort,
|
|
1506
|
-
payload: {
|
|
1507
|
-
sessionId: payload.sessionId,
|
|
1508
|
-
...payload.messageId ? { messageId: payload.messageId } : {}
|
|
1509
|
-
}
|
|
1510
|
-
};
|
|
1511
|
-
await this.sessionRealtime.publishSessionEvent(session, abortEvent, {
|
|
1512
|
-
dispatchToStateManager: true
|
|
1513
|
-
});
|
|
1514
|
-
this.finishSessionExecution(session, execution);
|
|
1515
|
-
};
|
|
1516
|
-
persistSession = async (sessionId) => {
|
|
1517
|
-
const session = this.sessionRegistry.getSession(sessionId);
|
|
1518
|
-
if (!session) return;
|
|
1519
|
-
await this.sessionStore.saveSession(
|
|
1520
|
-
buildPersistedLiveSessionRecord({
|
|
1521
|
-
sessionId,
|
|
1522
|
-
session,
|
|
1523
|
-
updatedAt: now()
|
|
1524
|
-
})
|
|
1525
|
-
);
|
|
1526
|
-
};
|
|
1063
|
+
manifest;
|
|
1064
|
+
sessionStore;
|
|
1065
|
+
onSessionRunStatusChanged;
|
|
1066
|
+
sessionRegistry;
|
|
1067
|
+
executor;
|
|
1068
|
+
publisher;
|
|
1069
|
+
sessionRealtime;
|
|
1070
|
+
started = false;
|
|
1071
|
+
constructor(config) {
|
|
1072
|
+
this.sessionStore = config.sessionStore;
|
|
1073
|
+
this.onSessionRunStatusChanged = config.onSessionRunStatusChanged;
|
|
1074
|
+
this.sessionRegistry = new AgentLiveSessionRegistry(this.sessionStore, config.createRuntime);
|
|
1075
|
+
this.executor = new AgentRunExecutor();
|
|
1076
|
+
this.publisher = new EventPublisher();
|
|
1077
|
+
this.sessionRealtime = new AgentBackendSessionRealtime({
|
|
1078
|
+
sessionRegistry: this.sessionRegistry,
|
|
1079
|
+
sessionStore: this.sessionStore,
|
|
1080
|
+
publishEndpointEvent: (event) => this.publisher.publish(event),
|
|
1081
|
+
subscribeEndpointEvent: (listener) => this.publisher.subscribe(listener),
|
|
1082
|
+
persistSession: (sessionId) => this.persistSession(sessionId),
|
|
1083
|
+
getSessionSummary: (sessionId) => this.getSession(sessionId)
|
|
1084
|
+
});
|
|
1085
|
+
this.manifest = {
|
|
1086
|
+
endpointKind: "agent",
|
|
1087
|
+
endpointId: config.endpointId?.trim() || "ncp-agent-backend",
|
|
1088
|
+
version: config.version?.trim() || "0.1.0",
|
|
1089
|
+
supportsStreaming: true,
|
|
1090
|
+
supportsAbort: true,
|
|
1091
|
+
supportsProactiveMessages: false,
|
|
1092
|
+
supportsLiveSessionStream: true,
|
|
1093
|
+
supportedPartTypes: config.supportedPartTypes ?? DEFAULT_SUPPORTED_PART_TYPES,
|
|
1094
|
+
expectedLatency: config.expectedLatency ?? "seconds",
|
|
1095
|
+
metadata: config.metadata
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
start = async () => {
|
|
1099
|
+
if (this.started) return;
|
|
1100
|
+
this.started = true;
|
|
1101
|
+
this.publisher.publish({ type: NcpEventType.EndpointReady });
|
|
1102
|
+
};
|
|
1103
|
+
stop = async () => {
|
|
1104
|
+
if (!this.started) return;
|
|
1105
|
+
this.started = false;
|
|
1106
|
+
for (const session of this.sessionRegistry.listSessions()) {
|
|
1107
|
+
const execution = session.activeExecution;
|
|
1108
|
+
if (!execution) {
|
|
1109
|
+
session.publisher.close();
|
|
1110
|
+
continue;
|
|
1111
|
+
}
|
|
1112
|
+
execution.abortHandled = true;
|
|
1113
|
+
execution.controller.abort();
|
|
1114
|
+
this.finishSessionExecution(session, execution);
|
|
1115
|
+
session.publisher.close();
|
|
1116
|
+
}
|
|
1117
|
+
this.sessionRegistry.clear();
|
|
1118
|
+
};
|
|
1119
|
+
emit = async (event) => {
|
|
1120
|
+
await this.ensureStarted();
|
|
1121
|
+
switch (event.type) {
|
|
1122
|
+
case NcpEventType.MessageRequest:
|
|
1123
|
+
for await (const emittedEvent of this.send(event.payload));
|
|
1124
|
+
return;
|
|
1125
|
+
case NcpEventType.MessageStreamRequest:
|
|
1126
|
+
await this.ensureStarted();
|
|
1127
|
+
return;
|
|
1128
|
+
case NcpEventType.MessageAbort:
|
|
1129
|
+
await this.handleAbort(event.payload);
|
|
1130
|
+
return;
|
|
1131
|
+
default: this.publisher.publish(event);
|
|
1132
|
+
}
|
|
1133
|
+
};
|
|
1134
|
+
subscribe = (listener) => {
|
|
1135
|
+
return this.publisher.subscribe(listener);
|
|
1136
|
+
};
|
|
1137
|
+
send = (envelope, options) => {
|
|
1138
|
+
return (async function* (self) {
|
|
1139
|
+
await self.ensureStarted();
|
|
1140
|
+
const session = await self.sessionRegistry.ensureSession(envelope.sessionId, envelope.metadata);
|
|
1141
|
+
const execution = self.startSessionExecution(session, envelope, options?.signal);
|
|
1142
|
+
try {
|
|
1143
|
+
for await (const event of self.executor.executeRun(session, envelope, execution.controller)) {
|
|
1144
|
+
await self.sessionRealtime.publishSessionEvent(session, event);
|
|
1145
|
+
yield event;
|
|
1146
|
+
}
|
|
1147
|
+
if (execution.controller.signal.aborted && !execution.abortHandled) {
|
|
1148
|
+
const abortEvent = {
|
|
1149
|
+
type: NcpEventType.MessageAbort,
|
|
1150
|
+
payload: { sessionId: session.sessionId }
|
|
1151
|
+
};
|
|
1152
|
+
execution.abortHandled = true;
|
|
1153
|
+
await self.sessionRealtime.publishSessionEvent(session, abortEvent, { dispatchToStateManager: true });
|
|
1154
|
+
yield abortEvent;
|
|
1155
|
+
}
|
|
1156
|
+
} finally {
|
|
1157
|
+
self.finishSessionExecution(session, execution);
|
|
1158
|
+
await self.persistSession(session.sessionId);
|
|
1159
|
+
}
|
|
1160
|
+
})(this);
|
|
1161
|
+
};
|
|
1162
|
+
abort = async (payload) => {
|
|
1163
|
+
await this.handleAbort(payload);
|
|
1164
|
+
};
|
|
1165
|
+
stream = (payloadOrParams, opts) => this.sessionRealtime.streamSessionEvents(payloadOrParams, opts);
|
|
1166
|
+
listSessions = async () => {
|
|
1167
|
+
const summaries = (await this.sessionStore.listSessions()).map((session) => toSessionSummary(session, this.sessionRegistry.getSession(session.sessionId)));
|
|
1168
|
+
for (const liveSession of this.sessionRegistry.listSessions()) {
|
|
1169
|
+
if (summaries.some((session) => session.sessionId === liveSession.sessionId)) continue;
|
|
1170
|
+
summaries.push(toLiveSessionSummary(liveSession));
|
|
1171
|
+
}
|
|
1172
|
+
return summaries.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
1173
|
+
};
|
|
1174
|
+
listSessionMessages = async (sessionId) => {
|
|
1175
|
+
const liveSession = this.sessionRegistry.getSession(sessionId);
|
|
1176
|
+
if (liveSession) return readMessages(liveSession.stateManager.getSnapshot());
|
|
1177
|
+
const session = await this.sessionStore.getSession(sessionId);
|
|
1178
|
+
return session ? session.messages.map((message) => structuredClone(message)) : [];
|
|
1179
|
+
};
|
|
1180
|
+
getSession = async (sessionId) => {
|
|
1181
|
+
const liveSession = this.sessionRegistry.getSession(sessionId);
|
|
1182
|
+
const storedSession = await this.sessionStore.getSession(sessionId);
|
|
1183
|
+
return storedSession ? toSessionSummary(storedSession, liveSession) : liveSession ? toLiveSessionSummary(liveSession) : null;
|
|
1184
|
+
};
|
|
1185
|
+
appendMessage = async (sessionId, message) => {
|
|
1186
|
+
await this.ensureStarted();
|
|
1187
|
+
return this.sessionRealtime.appendMessage(sessionId, message);
|
|
1188
|
+
};
|
|
1189
|
+
updateToolCallResult = async (sessionId, toolCallId, content) => {
|
|
1190
|
+
await this.ensureStarted();
|
|
1191
|
+
return this.sessionRealtime.updateToolCallResult(sessionId, toolCallId, content);
|
|
1192
|
+
};
|
|
1193
|
+
updateSession = async (sessionId, patch) => {
|
|
1194
|
+
const liveSession = this.sessionRegistry.getSession(sessionId);
|
|
1195
|
+
const storedSession = await this.sessionStore.getSession(sessionId);
|
|
1196
|
+
if (!liveSession && !storedSession) return null;
|
|
1197
|
+
await this.sessionStore.replaceSession(buildUpdatedSessionRecord({
|
|
1198
|
+
sessionId,
|
|
1199
|
+
patch,
|
|
1200
|
+
liveSession,
|
|
1201
|
+
storedSession,
|
|
1202
|
+
updatedAt: now()
|
|
1203
|
+
}));
|
|
1204
|
+
return this.getSession(sessionId);
|
|
1205
|
+
};
|
|
1206
|
+
deleteSession = async (sessionId) => {
|
|
1207
|
+
const liveSession = this.sessionRegistry.deleteSession(sessionId);
|
|
1208
|
+
const execution = liveSession?.activeExecution;
|
|
1209
|
+
if (execution) {
|
|
1210
|
+
execution.abortHandled = true;
|
|
1211
|
+
execution.controller.abort();
|
|
1212
|
+
closeAgentBackendSessionExecution(execution);
|
|
1213
|
+
}
|
|
1214
|
+
liveSession?.publisher.close();
|
|
1215
|
+
await this.sessionStore.deleteSession(sessionId);
|
|
1216
|
+
};
|
|
1217
|
+
ensureStarted = async () => {
|
|
1218
|
+
if (!this.started) await this.start();
|
|
1219
|
+
};
|
|
1220
|
+
startSessionExecution = (session, envelope, signal) => startAgentBackendSessionExecution({
|
|
1221
|
+
session,
|
|
1222
|
+
envelope,
|
|
1223
|
+
signal,
|
|
1224
|
+
onStatusChanged: this.onSessionRunStatusChanged
|
|
1225
|
+
});
|
|
1226
|
+
finishSessionExecution = (session, execution) => finishAgentBackendSessionExecution({
|
|
1227
|
+
session,
|
|
1228
|
+
execution,
|
|
1229
|
+
onStatusChanged: this.onSessionRunStatusChanged
|
|
1230
|
+
});
|
|
1231
|
+
handleAbort = async (payload) => {
|
|
1232
|
+
const session = this.sessionRegistry.getSession(payload.sessionId);
|
|
1233
|
+
const execution = session?.activeExecution;
|
|
1234
|
+
if (!session || !execution || execution.closed) return;
|
|
1235
|
+
execution.abortHandled = true;
|
|
1236
|
+
execution.controller.abort();
|
|
1237
|
+
const abortEvent = {
|
|
1238
|
+
type: NcpEventType.MessageAbort,
|
|
1239
|
+
payload: {
|
|
1240
|
+
sessionId: payload.sessionId,
|
|
1241
|
+
...payload.messageId ? { messageId: payload.messageId } : {}
|
|
1242
|
+
}
|
|
1243
|
+
};
|
|
1244
|
+
await this.sessionRealtime.publishSessionEvent(session, abortEvent, { dispatchToStateManager: true });
|
|
1245
|
+
this.finishSessionExecution(session, execution);
|
|
1246
|
+
};
|
|
1247
|
+
persistSession = async (sessionId) => {
|
|
1248
|
+
const session = this.sessionRegistry.getSession(sessionId);
|
|
1249
|
+
if (!session) return;
|
|
1250
|
+
await this.sessionStore.saveSession(buildPersistedLiveSessionRecord({
|
|
1251
|
+
sessionId,
|
|
1252
|
+
session,
|
|
1253
|
+
updatedAt: now()
|
|
1254
|
+
}));
|
|
1255
|
+
};
|
|
1527
1256
|
};
|
|
1528
|
-
|
|
1529
|
-
|
|
1257
|
+
//#endregion
|
|
1258
|
+
//#region src/agent/agent-backend/in-memory-agent-session-store.ts
|
|
1530
1259
|
var InMemoryAgentSessionStore = class {
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
return structuredClone(session);
|
|
1552
|
-
};
|
|
1553
|
-
};
|
|
1554
|
-
export {
|
|
1555
|
-
AgentRunExecutor,
|
|
1556
|
-
DefaultNcpAgentBackend,
|
|
1557
|
-
DefaultNcpAgentConversationStateManager,
|
|
1558
|
-
EventPublisher,
|
|
1559
|
-
InMemoryAgentSessionStore,
|
|
1560
|
-
NcpErrorException,
|
|
1561
|
-
createAgentClientFromServer
|
|
1260
|
+
sessions = /* @__PURE__ */ new Map();
|
|
1261
|
+
getSession = async (sessionId) => {
|
|
1262
|
+
const session = this.sessions.get(sessionId);
|
|
1263
|
+
return session ? structuredClone(session) : null;
|
|
1264
|
+
};
|
|
1265
|
+
listSessions = async () => {
|
|
1266
|
+
return [...this.sessions.values()].map((session) => structuredClone(session));
|
|
1267
|
+
};
|
|
1268
|
+
saveSession = async (session) => {
|
|
1269
|
+
this.sessions.set(session.sessionId, structuredClone(session));
|
|
1270
|
+
};
|
|
1271
|
+
replaceSession = async (session) => {
|
|
1272
|
+
this.sessions.set(session.sessionId, structuredClone(session));
|
|
1273
|
+
};
|
|
1274
|
+
deleteSession = async (sessionId) => {
|
|
1275
|
+
const session = this.sessions.get(sessionId);
|
|
1276
|
+
if (!session) return null;
|
|
1277
|
+
this.sessions.delete(sessionId);
|
|
1278
|
+
return structuredClone(session);
|
|
1279
|
+
};
|
|
1562
1280
|
};
|
|
1281
|
+
//#endregion
|
|
1282
|
+
export { AgentRunExecutor, DefaultNcpAgentBackend, DefaultNcpAgentConversationStateManager, EventPublisher, InMemoryAgentSessionStore, NcpErrorException, createAgentClientFromServer };
|