@nextclaw/ncp-toolkit 0.5.0 → 0.5.2

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +154 -147
  2. package/dist/index.js +1206 -1486
  3. package/package.json +2 -3
package/dist/index.js CHANGED
@@ -1,1562 +1,1282 @@
1
- // src/agent/agent-conversation-state-manager.ts
2
- import {
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
- return {
10
- ...message,
11
- parts: [...message.parts],
12
- metadata: message.metadata ? { ...message.metadata } : void 0
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
- return cloneConversationMessage(sanitizeAssistantReplyTags(message));
11
+ return cloneConversationMessage(sanitizeAssistantReplyTags(message));
17
12
  }
18
-
19
- // src/agent/agent-conversation-state-manager.utils.ts
13
+ //#endregion
14
+ //#region src/agent/agent-conversation-state-manager.utils.ts
20
15
  function buildRuntimeError(payload) {
21
- const message = payload.error?.trim();
22
- return {
23
- code: "runtime-error",
24
- message: message && message.length > 0 ? message : "Agent run failed.",
25
- details: {
26
- sessionId: payload.sessionId,
27
- messageId: payload.messageId,
28
- threadId: payload.threadId,
29
- runId: payload.runId
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
- 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");
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
- for (const [toolCallId, trackedMessageId] of toolCallMessageIdByCallId) {
44
- if (trackedMessageId !== fromMessageId) {
45
- continue;
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
- for (const [toolCallId, trackedMessageId] of toolCallMessageIdByCallId) {
52
- if (trackedMessageId !== messageId) {
53
- continue;
54
- }
55
- toolCallMessageIdByCallId.delete(toolCallId);
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
- 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;
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
- const part = findToolInvocationPart(parts, toolCallId);
70
- return part?.toolName ?? null;
54
+ return findToolInvocationPart(parts, toolCallId)?.toolName ?? null;
71
55
  }
72
56
  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;
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
- // src/agent/agent-conversation-state-manager.ts
89
- var DEFAULT_ASSISTANT_ROLE = "assistant";
71
+ //#endregion
72
+ //#region src/agent/agent-conversation-state-manager.ts
73
+ const DEFAULT_ASSISTANT_ROLE = "assistant";
90
74
  var DefaultNcpAgentConversationStateManager = class {
91
- messages = [];
92
- streamingMessage = null;
93
- error = null;
94
- activeRun = null;
95
- listeners = /* @__PURE__ */ new Set();
96
- toolCallMessageIdByCallId = /* @__PURE__ */ new Map();
97
- toolCallArgsRawByCallId = /* @__PURE__ */ new Map();
98
- lastSettledRunId = null;
99
- snapshotCache = null;
100
- snapshotVersion = -1;
101
- stateVersion = 0;
102
- getSnapshot = () => {
103
- if (this.snapshotCache && this.snapshotVersion === this.stateVersion) {
104
- return this.snapshotCache;
105
- }
106
- const snapshot = {
107
- messages: this.messages,
108
- streamingMessage: this.streamingMessage,
109
- error: this.error ? {
110
- ...this.error,
111
- details: this.error.details ? { ...this.error.details } : void 0
112
- } : null,
113
- activeRun: this.activeRun ? { ...this.activeRun } : null
114
- };
115
- this.snapshotCache = snapshot;
116
- this.snapshotVersion = this.stateVersion;
117
- return snapshot;
118
- };
119
- subscribe = (listener) => {
120
- this.listeners.add(listener);
121
- return () => this.listeners.delete(listener);
122
- };
123
- reset = () => {
124
- if (this.messages.length === 0 && !this.streamingMessage && !this.error && !this.activeRun && this.toolCallMessageIdByCallId.size === 0 && this.toolCallArgsRawByCallId.size === 0) {
125
- return;
126
- }
127
- this.messages = [];
128
- this.streamingMessage = null;
129
- this.error = null;
130
- this.activeRun = null;
131
- this.toolCallMessageIdByCallId.clear();
132
- this.toolCallArgsRawByCallId.clear();
133
- this.lastSettledRunId = null;
134
- this.stateVersion += 1;
135
- this.notifyListeners();
136
- };
137
- hydrate = (payload) => {
138
- this.messages = payload.messages.map(
139
- (message) => normalizeConversationMessage(message)
140
- );
141
- this.streamingMessage = null;
142
- this.error = null;
143
- this.activeRun = payload.activeRun ? {
144
- ...payload.activeRun,
145
- sessionId: payload.activeRun.sessionId ?? payload.sessionId,
146
- abortDisabledReason: payload.activeRun.abortDisabledReason ?? null
147
- } : null;
148
- this.toolCallMessageIdByCallId.clear();
149
- this.toolCallArgsRawByCallId.clear();
150
- this.lastSettledRunId = null;
151
- this.stateVersion += 1;
152
- this.notifyListeners();
153
- };
154
- dispatch = (event) => this.dispatchBatch([event]);
155
- dispatchBatch = async (events) => {
156
- if (!events.length) return;
157
- const versionBeforeDispatch = this.stateVersion;
158
- events.forEach(this.applyEvent);
159
- if (this.stateVersion !== versionBeforeDispatch) this.notifyListeners();
160
- };
161
- applyEvent = (event) => {
162
- switch (event.type) {
163
- case NcpEventType.MessageSent:
164
- this.handleMessageSent(event.payload);
165
- break;
166
- case NcpEventType.MessageAbort:
167
- this.handleMessageAbort(event.payload);
168
- break;
169
- case NcpEventType.MessageTextStart:
170
- this.handleMessageTextStart(event.payload);
171
- break;
172
- case NcpEventType.MessageTextDelta:
173
- this.handleMessageTextDelta(event.payload);
174
- break;
175
- case NcpEventType.MessageTextEnd:
176
- this.handleMessageTextEnd(event.payload);
177
- break;
178
- case NcpEventType.MessageReasoningStart:
179
- this.handleMessageReasoningStart(event.payload);
180
- break;
181
- case NcpEventType.MessageReasoningDelta:
182
- this.handleMessageReasoningDelta(event.payload);
183
- break;
184
- case NcpEventType.MessageReasoningEnd:
185
- this.handleMessageReasoningEnd(event.payload);
186
- break;
187
- case NcpEventType.MessageToolCallStart:
188
- this.handleMessageToolCallStart(event.payload);
189
- break;
190
- case NcpEventType.MessageToolCallArgs:
191
- this.handleMessageToolCallArgs(event.payload);
192
- break;
193
- case NcpEventType.MessageToolCallArgsDelta:
194
- this.handleMessageToolCallArgsDelta(event.payload);
195
- break;
196
- case NcpEventType.MessageToolCallEnd:
197
- this.handleMessageToolCallEnd(event.payload);
198
- break;
199
- case NcpEventType.MessageToolCallResult:
200
- this.handleMessageToolCallResult(event.payload);
201
- break;
202
- case NcpEventType.RunStarted:
203
- this.handleRunStarted(event.payload);
204
- break;
205
- case NcpEventType.RunFinished:
206
- this.handleRunFinished(event.payload);
207
- break;
208
- case NcpEventType.RunError:
209
- this.handleRunError(event.payload);
210
- break;
211
- case NcpEventType.RunMetadata:
212
- this.handleRunMetadata(event.payload);
213
- break;
214
- case NcpEventType.EndpointError:
215
- this.handleEndpointError(event.payload);
216
- break;
217
- default:
218
- break;
219
- }
220
- };
221
- handleMessageSent = (payload) => {
222
- this.upsertMessage(payload.message);
223
- this.setError(null);
224
- };
225
- handleMessageAbort = (payload) => {
226
- const targetMessageId = payload.messageId?.trim();
227
- this.clearActiveRun();
228
- this.setError(null);
229
- if (this.streamingMessage && (!targetMessageId || this.streamingMessage.id === targetMessageId)) {
230
- const streamingMessageId = this.streamingMessage.id;
231
- this.upsertMessage({
232
- ...this.streamingMessage,
233
- status: "final"
234
- });
235
- this.replaceStreamingMessage(null);
236
- if (targetMessageId) {
237
- clearToolCallTrackingByMessageId(
238
- this.toolCallMessageIdByCallId,
239
- this.toolCallArgsRawByCallId,
240
- targetMessageId
241
- );
242
- } else {
243
- clearToolCallTrackingByMessageId(
244
- this.toolCallMessageIdByCallId,
245
- this.toolCallArgsRawByCallId,
246
- streamingMessageId
247
- );
248
- }
249
- }
250
- };
251
- handleMessageTextStart = (payload) => {
252
- this.ensureStreamingMessage(
253
- payload.sessionId,
254
- payload.messageId,
255
- "streaming"
256
- );
257
- this.setError(null);
258
- };
259
- handleMessageTextDelta = (payload) => {
260
- if (!payload.delta) {
261
- return;
262
- }
263
- const targetMessage = this.ensureStreamingMessage(
264
- payload.sessionId,
265
- payload.messageId,
266
- "streaming"
267
- );
268
- const nextParts = [...targetMessage.parts];
269
- const lastPart = nextParts[nextParts.length - 1];
270
- if (lastPart?.type === "text") {
271
- nextParts[nextParts.length - 1] = {
272
- type: "text",
273
- text: `${lastPart.text}${payload.delta}`
274
- };
275
- } else {
276
- nextParts.push({ type: "text", text: payload.delta });
277
- }
278
- this.replaceStreamingMessage({
279
- ...targetMessage,
280
- parts: nextParts,
281
- status: "streaming"
282
- });
283
- };
284
- handleMessageTextEnd = (payload) => {
285
- if (this.streamingMessage?.id !== payload.messageId) {
286
- return;
287
- }
288
- if (this.streamingMessage.status !== "streaming") {
289
- return;
290
- }
291
- this.replaceStreamingMessage({
292
- ...this.streamingMessage,
293
- status: "pending"
294
- });
295
- };
296
- handleMessageReasoningStart = (payload) => {
297
- this.ensureStreamingMessage(
298
- payload.sessionId,
299
- payload.messageId,
300
- "streaming"
301
- );
302
- };
303
- handleMessageReasoningDelta = (payload) => {
304
- if (!payload.delta) {
305
- return;
306
- }
307
- const targetMessage = this.ensureStreamingMessage(
308
- payload.sessionId,
309
- payload.messageId,
310
- "streaming"
311
- );
312
- const nextParts = [...targetMessage.parts];
313
- const lastPart = nextParts[nextParts.length - 1];
314
- if (lastPart?.type === "reasoning") {
315
- nextParts[nextParts.length - 1] = {
316
- type: "reasoning",
317
- text: `${lastPart.text}${payload.delta}`
318
- };
319
- } else {
320
- nextParts.push({ type: "reasoning", text: payload.delta });
321
- }
322
- this.replaceStreamingMessage({
323
- ...targetMessage,
324
- parts: nextParts,
325
- status: "streaming"
326
- });
327
- };
328
- handleMessageReasoningEnd = (payload) => {
329
- if (this.streamingMessage?.id !== payload.messageId) {
330
- return;
331
- }
332
- };
333
- handleMessageToolCallStart = (payload) => {
334
- const targetMessage = this.resolveToolCallTargetMessage(
335
- payload.sessionId,
336
- payload.toolCallId,
337
- payload.messageId
338
- );
339
- this.toolCallArgsRawByCallId.set(payload.toolCallId, "");
340
- const nextParts = upsertToolInvocationPart(targetMessage.parts, {
341
- type: "tool-invocation",
342
- toolCallId: payload.toolCallId,
343
- toolName: payload.toolName,
344
- state: "partial-call",
345
- args: ""
346
- });
347
- this.replaceStreamingMessage({
348
- ...targetMessage,
349
- parts: nextParts,
350
- status: "streaming"
351
- });
352
- this.setError(null);
353
- };
354
- handleMessageToolCallArgs = (payload) => {
355
- this.toolCallArgsRawByCallId.set(payload.toolCallId, payload.args);
356
- this.applyToolCallArgs(payload.sessionId, payload.toolCallId, payload.args);
357
- };
358
- handleMessageToolCallArgsDelta = (payload) => {
359
- const currentArgs = this.toolCallArgsRawByCallId.get(payload.toolCallId) ?? "";
360
- const nextArgs = `${currentArgs}${payload.delta}`;
361
- this.toolCallArgsRawByCallId.set(payload.toolCallId, nextArgs);
362
- this.applyToolCallArgs(
363
- payload.sessionId,
364
- payload.toolCallId,
365
- nextArgs,
366
- payload.messageId
367
- );
368
- };
369
- handleMessageToolCallEnd = (payload) => {
370
- const targetMessage = this.resolveToolCallTargetMessage(
371
- payload.sessionId,
372
- payload.toolCallId
373
- );
374
- const args = this.toolCallArgsRawByCallId.get(payload.toolCallId) ?? "";
375
- const nextParts = upsertToolInvocationPart(targetMessage.parts, {
376
- type: "tool-invocation",
377
- toolCallId: payload.toolCallId,
378
- toolName: findToolNameByCallId(targetMessage.parts, payload.toolCallId) ?? "unknown",
379
- state: "call",
380
- args
381
- });
382
- this.replaceStreamingMessage({
383
- ...targetMessage,
384
- parts: nextParts,
385
- status: "streaming"
386
- });
387
- };
388
- handleMessageToolCallResult = (payload) => {
389
- const updated = this.updateMessageContainingToolCall(
390
- payload.toolCallId,
391
- (targetMessage, existingPart) => {
392
- const mergedPart = {
393
- type: "tool-invocation",
394
- toolCallId: payload.toolCallId,
395
- toolName: existingPart.toolName,
396
- state: "result",
397
- args: existingPart.args,
398
- result: payload.content
399
- };
400
- return upsertToolInvocationPart(targetMessage.parts, mergedPart);
401
- }
402
- );
403
- if (!updated) {
404
- const fallbackMessage = this.resolveToolCallTargetMessage(
405
- payload.sessionId,
406
- payload.toolCallId
407
- );
408
- const nextParts = upsertToolInvocationPart(fallbackMessage.parts, {
409
- type: "tool-invocation",
410
- toolCallId: payload.toolCallId,
411
- toolName: "unknown",
412
- state: "result",
413
- result: payload.content
414
- });
415
- this.replaceStreamingMessage({
416
- ...fallbackMessage,
417
- parts: nextParts,
418
- status: "streaming"
419
- });
420
- }
421
- };
422
- handleRunStarted = (payload) => {
423
- if (this.isSettledRunId(payload.runId)) return;
424
- this.setError(null);
425
- this.activeRun = { runId: payload.runId ?? null, sessionId: payload.sessionId };
426
- this.stateVersion += 1;
427
- };
428
- handleRunFinished = (payload) => {
429
- this.markRunAsSettled(payload.runId ?? this.activeRun?.runId ?? null);
430
- this.settleStreamingMessage("final");
431
- this.setError(null);
432
- this.clearActiveRun();
433
- };
434
- handleRunError = (payload) => {
435
- this.markRunAsSettled(payload.runId ?? this.activeRun?.runId ?? null);
436
- this.settleStreamingMessage("error");
437
- this.setError(buildRuntimeError(payload));
438
- this.clearActiveRun();
439
- };
440
- handleRunMetadata = (payload) => {
441
- const m = payload.metadata;
442
- if (m?.kind === "ready") {
443
- const ready = m;
444
- if (this.isSettledRunId(ready.runId)) return;
445
- this.activeRun = {
446
- runId: ready.runId ?? this.activeRun?.runId ?? null,
447
- sessionId: ready.sessionId ?? this.activeRun?.sessionId,
448
- abortDisabledReason: ready.supportsAbort === false ? ready.abortDisabledReason ?? "Unsupported" : null
449
- };
450
- this.stateVersion += 1;
451
- } else if (m?.kind === "final") {
452
- this.markRunAsSettled(payload.runId ?? this.activeRun?.runId ?? null);
453
- this.clearActiveRun();
454
- }
455
- };
456
- handleEndpointError = (payload) => {
457
- if (payload.code === "abort-error") {
458
- this.handleMessageAbort({
459
- sessionId: this.activeRun?.sessionId ?? this.streamingMessage?.sessionId ?? "",
460
- ...this.streamingMessage?.id ? { messageId: this.streamingMessage.id } : {}
461
- });
462
- return;
463
- }
464
- this.settleStreamingMessage("error");
465
- this.clearActiveRun();
466
- this.setError(payload);
467
- };
468
- applyToolCallArgs = (sessionId, toolCallId, args, messageId) => {
469
- const targetMessage = this.resolveToolCallTargetMessage(
470
- sessionId,
471
- toolCallId,
472
- messageId
473
- );
474
- const toolName = findToolNameByCallId(targetMessage.parts, toolCallId) ?? "unknown";
475
- const nextParts = upsertToolInvocationPart(targetMessage.parts, {
476
- type: "tool-invocation",
477
- toolCallId,
478
- toolName,
479
- state: "partial-call",
480
- args
481
- });
482
- this.replaceStreamingMessage({
483
- ...targetMessage,
484
- parts: nextParts,
485
- status: "streaming"
486
- });
487
- };
488
- ensureStreamingMessage = (sessionId, messageId, status) => {
489
- if (this.streamingMessage?.id === messageId) {
490
- if (this.streamingMessage.status === status) {
491
- return this.streamingMessage;
492
- }
493
- const nextStreamingMessage2 = {
494
- ...this.streamingMessage,
495
- status
496
- };
497
- this.replaceStreamingMessage(nextStreamingMessage2);
498
- return nextStreamingMessage2;
499
- }
500
- const messageIndex = this.messages.findIndex(
501
- (message) => message.id === messageId
502
- );
503
- if (messageIndex >= 0) {
504
- const existingMessage = cloneConversationMessage(
505
- this.messages[messageIndex]
506
- );
507
- const nextMessages = [...this.messages];
508
- nextMessages.splice(messageIndex, 1);
509
- this.messages = nextMessages;
510
- this.stateVersion += 1;
511
- const nextStreamingMessage2 = {
512
- ...existingMessage,
513
- sessionId,
514
- status
515
- };
516
- this.replaceStreamingMessage(nextStreamingMessage2);
517
- return nextStreamingMessage2;
518
- }
519
- const existingStreamingMessage = this.streamingMessage;
520
- if (existingStreamingMessage && existingStreamingMessage.id !== messageId && existingStreamingMessage.sessionId === sessionId && shouldPromoteStreamingMessageId(existingStreamingMessage, messageId)) {
521
- const nextStreamingMessage2 = {
522
- ...existingStreamingMessage,
523
- id: messageId,
524
- sessionId,
525
- status
526
- };
527
- remapTrackedToolCallsToMessageId(
528
- this.toolCallMessageIdByCallId,
529
- existingStreamingMessage.id,
530
- messageId
531
- );
532
- this.replaceStreamingMessage(nextStreamingMessage2);
533
- return nextStreamingMessage2;
534
- }
535
- const nextStreamingMessage = {
536
- id: messageId,
537
- sessionId,
538
- role: DEFAULT_ASSISTANT_ROLE,
539
- status,
540
- parts: [],
541
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
542
- };
543
- this.replaceStreamingMessage(nextStreamingMessage);
544
- return nextStreamingMessage;
545
- };
546
- resolveToolCallTargetMessage = (sessionId, toolCallId, messageId) => {
547
- const preferredMessageId = messageId?.trim() || this.toolCallMessageIdByCallId.get(toolCallId) || this.streamingMessage?.id || `tool-${toolCallId}`;
548
- this.toolCallMessageIdByCallId.set(toolCallId, preferredMessageId);
549
- return this.ensureStreamingMessage(
550
- sessionId,
551
- preferredMessageId,
552
- "streaming"
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
- // src/agent/agent-client-from-server.ts
654
- import {
655
- NcpEventType as NcpEventType2
656
- } from "@nextclaw/ncp";
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
- return {
659
- get manifest() {
660
- return server.manifest;
661
- },
662
- async start() {
663
- await server.start();
664
- },
665
- async stop() {
666
- await server.stop();
667
- },
668
- async emit(event) {
669
- switch (event.type) {
670
- case NcpEventType2.MessageRequest:
671
- await consume(server.send(event.payload));
672
- return;
673
- case NcpEventType2.MessageStreamRequest:
674
- await consume(server.stream(event.payload));
675
- return;
676
- case NcpEventType2.MessageAbort:
677
- await server.abort(event.payload);
678
- return;
679
- default:
680
- await server.emit(event);
681
- }
682
- },
683
- subscribe(listener) {
684
- return server.subscribe(listener);
685
- },
686
- async send(envelope) {
687
- await consume(server.send(envelope));
688
- },
689
- async stream(payload) {
690
- await consume(server.stream(payload));
691
- },
692
- async abort(payload) {
693
- await server.abort(payload);
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
- for await (const event of events) {
699
- void event;
700
- }
585
+ for await (const event of events);
701
586
  }
702
-
703
- // src/agent/agent-backend/agent-backend.ts
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
- listeners = /* @__PURE__ */ new Set();
711
- closeListeners = /* @__PURE__ */ new Set();
712
- closed = false;
713
- subscribe(listener) {
714
- if (this.closed) {
715
- return () => void 0;
716
- }
717
- this.listeners.add(listener);
718
- return () => {
719
- this.listeners.delete(listener);
720
- };
721
- }
722
- onClose(listener) {
723
- if (this.closed) {
724
- listener();
725
- return () => void 0;
726
- }
727
- this.closeListeners.add(listener);
728
- return () => {
729
- this.closeListeners.delete(listener);
730
- };
731
- }
732
- publish(event) {
733
- if (this.closed) {
734
- return;
735
- }
736
- for (const listener of this.listeners) {
737
- listener(structuredClone(event));
738
- }
739
- }
740
- close() {
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
- // src/agent/agent-backend/agent-live-session-registry.ts
754
- function readOptionalAgentId(value) {
755
- if (typeof value !== "string") {
756
- return void 0;
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
- return readOptionalAgentId(metadata?.agent_id) ?? readOptionalAgentId(metadata?.agentId);
629
+ function readAgentIdFromMetadata$1(metadata) {
630
+ return readOptionalAgentId$2(metadata?.agent_id) ?? readOptionalAgentId$2(metadata?.agentId);
763
631
  }
764
632
  var AgentLiveSessionRegistry = class {
765
- constructor(sessionStore, createRuntime) {
766
- this.sessionStore = sessionStore;
767
- this.createRuntime = createRuntime;
768
- }
769
- sessions = /* @__PURE__ */ new Map();
770
- ensureSession = async (sessionId, initialMetadata) => {
771
- const existing = this.sessions.get(sessionId);
772
- if (existing) {
773
- if (!existing.agentId) {
774
- existing.agentId = readAgentIdFromMetadata(initialMetadata) ?? existing.agentId;
775
- }
776
- if (initialMetadata && Object.keys(initialMetadata).length > 0) {
777
- existing.metadata = {
778
- ...existing.metadata,
779
- ...structuredClone(initialMetadata)
780
- };
781
- }
782
- return existing;
783
- }
784
- const storedSession = await this.sessionStore.getSession(sessionId);
785
- const stateManager = new DefaultNcpAgentConversationStateManager();
786
- stateManager.hydrate({
787
- sessionId,
788
- messages: cloneMessages(storedSession?.messages ?? [])
789
- });
790
- const sessionMetadata = {
791
- ...storedSession?.metadata ? structuredClone(storedSession.metadata) : {},
792
- ...initialMetadata ? structuredClone(initialMetadata) : {}
793
- };
794
- const sessionAgentId = readOptionalAgentId(storedSession?.agentId) ?? readAgentIdFromMetadata(initialMetadata);
795
- const session = {
796
- sessionId,
797
- ...sessionAgentId ? { agentId: sessionAgentId } : {},
798
- stateManager,
799
- metadata: sessionMetadata,
800
- runtime: null,
801
- publisher: new EventPublisher(),
802
- activeExecution: null
803
- };
804
- session.runtime = this.createRuntime({
805
- sessionId,
806
- ...sessionAgentId ? { agentId: sessionAgentId } : {},
807
- stateManager,
808
- sessionMetadata,
809
- setSessionMetadata: (nextMetadata) => {
810
- session.metadata = {
811
- ...structuredClone(nextMetadata)
812
- };
813
- }
814
- });
815
- this.sessions.set(sessionId, session);
816
- return session;
817
- };
818
- getSession = (sessionId) => {
819
- return this.sessions.get(sessionId) ?? null;
820
- };
821
- deleteSession = (sessionId) => {
822
- const session = this.sessions.get(sessionId) ?? null;
823
- if (session) {
824
- this.sessions.delete(sessionId);
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
- return messages.map((message) => structuredClone(message));
696
+ return messages.map((message) => structuredClone(message));
837
697
  }
838
-
839
- // src/errors/ncp-error-exception.ts
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
- code;
842
- details;
843
- constructor(code, message, details) {
844
- super(message);
845
- this.name = "NcpErrorException";
846
- this.code = code;
847
- this.details = details;
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
- // src/agent/agent-backend/agent-backend-execution-utils.ts
713
+ //#endregion
714
+ //#region src/agent/agent-backend/agent-backend-execution-utils.ts
852
715
  function startAgentBackendSessionExecution(params) {
853
- const { session, envelope, signal, onStatusChanged } = params;
854
- if (session.activeExecution && !session.activeExecution.closed) {
855
- throw new NcpErrorException(
856
- "runtime-error",
857
- `Session ${session.sessionId} already has an active execution.`,
858
- { sessionId: session.sessionId }
859
- );
860
- }
861
- const controller = new AbortController();
862
- if (signal) {
863
- signal.addEventListener("abort", () => controller.abort(), {
864
- once: true
865
- });
866
- }
867
- const execution = {
868
- controller,
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
- const { session, execution, onStatusChanged } = params;
879
- if (session.activeExecution === execution) {
880
- session.activeExecution = null;
881
- onStatusChanged?.({ sessionKey: session.sessionId, status: "idle" });
882
- }
883
- closeAgentBackendSessionExecution(execution);
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
- if (execution.closed) {
887
- return;
888
- }
889
- execution.closed = true;
745
+ if (execution.closed) return;
746
+ execution.closed = true;
890
747
  }
891
-
892
- // src/agent/agent-backend/agent-run-executor.ts
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
- async *executeRun(session, envelope, controller) {
899
- if (!isHiddenNcpMessage(envelope.message)) {
900
- const messageSent = {
901
- type: NcpEventType3.MessageSent,
902
- payload: {
903
- sessionId: envelope.sessionId,
904
- message: structuredClone(envelope.message),
905
- metadata: envelope.metadata
906
- }
907
- };
908
- await session.stateManager.dispatch(messageSent);
909
- yield structuredClone(messageSent);
910
- }
911
- try {
912
- for await (const event of session.runtime.run(
913
- {
914
- sessionId: envelope.sessionId,
915
- messages: [envelope.message],
916
- correlationId: envelope.correlationId,
917
- metadata: envelope.metadata
918
- },
919
- { signal: controller.signal }
920
- )) {
921
- yield structuredClone(event);
922
- }
923
- } catch (error) {
924
- if (!controller.signal.aborted) {
925
- const runErrorEvent = await this.publishFailure(
926
- error,
927
- envelope,
928
- session
929
- );
930
- yield structuredClone(runErrorEvent);
931
- }
932
- }
933
- }
934
- async publishFailure(error, envelope, session) {
935
- const message = error instanceof Error ? error.message : String(error);
936
- const runErrorEvent = {
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
- // src/agent/agent-backend/agent-backend-session-realtime.ts
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
- const items = [];
954
- let closed = false;
955
- let pendingResolve = null;
956
- const iterable = {
957
- [Symbol.asyncIterator]() {
958
- return {
959
- next: () => {
960
- if (items.length > 0) {
961
- return Promise.resolve({
962
- value: items.shift(),
963
- done: false
964
- });
965
- }
966
- if (closed) {
967
- return Promise.resolve({
968
- value: void 0,
969
- done: true
970
- });
971
- }
972
- return new Promise((resolve) => {
973
- pendingResolve = resolve;
974
- });
975
- }
976
- };
977
- }
978
- };
979
- return {
980
- push(item) {
981
- if (closed) {
982
- return;
983
- }
984
- if (pendingResolve) {
985
- const resolve = pendingResolve;
986
- pendingResolve = null;
987
- resolve({ value: item, done: false });
988
- return;
989
- }
990
- items.push(item);
991
- },
992
- close() {
993
- if (closed) {
994
- return;
995
- }
996
- closed = true;
997
- if (pendingResolve) {
998
- const resolve = pendingResolve;
999
- pendingResolve = null;
1000
- resolve({
1001
- value: void 0,
1002
- done: true
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
- constructor(params) {
1013
- this.params = params;
1014
- }
1015
- publishSessionEvent = async (session, event, options = {}) => {
1016
- if (options.dispatchToStateManager) {
1017
- await session.stateManager.dispatch(event);
1018
- }
1019
- this.params.publishEndpointEvent(event);
1020
- session.publisher.publish(event);
1021
- if (options.persistSession !== false) {
1022
- await this.params.persistSession(session.sessionId);
1023
- }
1024
- };
1025
- streamSessionEvents = (payloadOrParams, opts) => {
1026
- return (async function* (self) {
1027
- const payload = "payload" in payloadOrParams && "signal" in payloadOrParams ? payloadOrParams.payload : payloadOrParams;
1028
- const signal = "payload" in payloadOrParams && "signal" in payloadOrParams ? payloadOrParams.signal : opts?.signal ?? new AbortController().signal;
1029
- const session = await self.params.sessionRegistry.ensureSession(
1030
- payload.sessionId
1031
- );
1032
- const queue = createAsyncQueue();
1033
- const unsubscribe = session.publisher.subscribe((event) => {
1034
- queue.push(event);
1035
- });
1036
- const unsubscribeClose = session.publisher.onClose(() => {
1037
- queue.close();
1038
- });
1039
- const stop = () => {
1040
- unsubscribe();
1041
- unsubscribeClose();
1042
- queue.close();
1043
- signal.removeEventListener("abort", stop);
1044
- };
1045
- signal.addEventListener("abort", stop, { once: true });
1046
- try {
1047
- for await (const event of queue.iterable) {
1048
- if (signal.aborted) {
1049
- break;
1050
- }
1051
- yield event;
1052
- }
1053
- } finally {
1054
- stop();
1055
- }
1056
- })(this);
1057
- };
1058
- appendMessage = async (sessionId, message) => {
1059
- const normalizedSessionId = sessionId.trim();
1060
- if (!normalizedSessionId) {
1061
- return null;
1062
- }
1063
- let liveSession = this.params.sessionRegistry.getSession(normalizedSessionId);
1064
- if (!liveSession) {
1065
- const storedSession = await this.params.sessionStore.getSession(normalizedSessionId);
1066
- if (!storedSession) {
1067
- return null;
1068
- }
1069
- liveSession = await this.params.sessionRegistry.ensureSession(normalizedSessionId);
1070
- }
1071
- const nextMessage = {
1072
- ...structuredClone(message),
1073
- sessionId: normalizedSessionId
1074
- };
1075
- await this.publishSessionEvent(
1076
- liveSession,
1077
- {
1078
- type: NcpEventType4.MessageSent,
1079
- payload: {
1080
- sessionId: normalizedSessionId,
1081
- message: nextMessage
1082
- }
1083
- },
1084
- {
1085
- dispatchToStateManager: true
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
- // src/agent/agent-backend/agent-backend-session-utils.ts
1116
- import { NcpEventType as NcpEventType5 } from "@nextclaw/ncp";
1117
- var AUTO_SESSION_LABEL_MAX_LENGTH = 64;
1118
- function readOptionalAgentId2(value) {
1119
- if (typeof value !== "string") {
1120
- return void 0;
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
- const messages = snapshot.messages.map((message) => structuredClone(message));
1127
- if (snapshot.streamingMessage) {
1128
- messages.push(structuredClone(snapshot.streamingMessage));
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
- const metadata = withAutoSessionLabel({
1134
- metadata: session.metadata ? structuredClone({
1135
- ...session.metadata,
1136
- ...liveSession?.metadata ? liveSession.metadata : {}
1137
- }) : liveSession?.metadata ? structuredClone(liveSession.metadata) : {},
1138
- messages: session.messages
1139
- });
1140
- return {
1141
- sessionId: session.sessionId,
1142
- ...readOptionalAgentId2(session.agentId) ? { agentId: readOptionalAgentId2(session.agentId) } : {},
1143
- messageCount: session.messages.length,
1144
- updatedAt: session.updatedAt,
1145
- status: liveSession?.activeExecution ? "running" : "idle",
1146
- ...Object.keys(metadata).length > 0 ? { metadata } : {}
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
- const snapshot = session.stateManager.getSnapshot();
1151
- const messages = readMessages(snapshot);
1152
- const metadata = withAutoSessionLabel({
1153
- metadata: Object.keys(session.metadata).length > 0 ? structuredClone(session.metadata) : session.activeExecution?.requestEnvelope.metadata ? structuredClone(session.activeExecution.requestEnvelope.metadata) : {},
1154
- messages
1155
- });
1156
- return {
1157
- sessionId: session.sessionId,
1158
- ...readOptionalAgentId2(session.agentId) ? { agentId: readOptionalAgentId2(session.agentId) } : {},
1159
- messageCount: messages.length,
1160
- updatedAt: now(),
1161
- status: session.activeExecution ? "running" : "idle",
1162
- ...Object.keys(metadata).length > 0 ? { metadata } : {}
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
- return (/* @__PURE__ */ new Date()).toISOString();
970
+ return (/* @__PURE__ */ new Date()).toISOString();
1167
971
  }
1168
972
  function readOptionalString(value) {
1169
- if (typeof value !== "string") {
1170
- return null;
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
- const characters = Array.from(value);
1177
- if (characters.length <= AUTO_SESSION_LABEL_MAX_LENGTH) {
1178
- return value;
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
- for (const message of messages) {
1184
- if (message.role !== "user") {
1185
- continue;
1186
- }
1187
- for (const part of message.parts) {
1188
- if (part.type === "text" || part.type === "rich-text") {
1189
- const text = readOptionalString(part.text);
1190
- if (text) {
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
- const existingLabel = readOptionalString(params.metadata.label);
1200
- if (existingLabel) {
1201
- return params.metadata;
1202
- }
1203
- const nextLabel = resolveAutoSessionLabelFromMessages(params.messages);
1204
- if (!nextLabel) {
1205
- return params.metadata;
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
- // src/agent/agent-backend/agent-backend-session-persistence.ts
1214
- function readOptionalAgentId3(value) {
1215
- if (typeof value !== "string") {
1216
- return void 0;
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 readAgentIdFromMetadata2(metadata) {
1222
- return readOptionalAgentId3(metadata?.agent_id) ?? readOptionalAgentId3(metadata?.agentId);
1008
+ function readAgentIdFromMetadata(metadata) {
1009
+ return readOptionalAgentId(metadata?.agent_id) ?? readOptionalAgentId(metadata?.agentId);
1223
1010
  }
1224
1011
  function resolvePersistedAgentId(params) {
1225
- return readOptionalAgentId3(params.liveSession?.agentId) ?? readOptionalAgentId3(params.storedSession?.agentId) ?? readAgentIdFromMetadata2(params.liveSession?.metadata) ?? readAgentIdFromMetadata2(params.storedSession?.metadata);
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
- const nextMetadata = params.patch.metadata === null ? {} : params.patch.metadata ? structuredClone(params.patch.metadata) : structuredClone(params.liveSession?.metadata ?? params.storedSession?.metadata ?? {});
1229
- if (params.liveSession) {
1230
- params.liveSession.metadata = structuredClone(nextMetadata);
1231
- }
1232
- return {
1233
- sessionId: params.sessionId,
1234
- ...resolvePersistedAgentId({
1235
- liveSession: params.liveSession,
1236
- storedSession: params.storedSession
1237
- }) ? {
1238
- agentId: resolvePersistedAgentId({
1239
- liveSession: params.liveSession,
1240
- storedSession: params.storedSession
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
- const messages = readMessages(params.session.stateManager.getSnapshot());
1250
- const metadata = withAutoSessionLabel({
1251
- metadata: {
1252
- ...params.session.metadata ? structuredClone(params.session.metadata) : {},
1253
- ...params.session.activeExecution?.requestEnvelope.metadata ? structuredClone(params.session.activeExecution.requestEnvelope.metadata) : {}
1254
- },
1255
- messages
1256
- });
1257
- return {
1258
- sessionId: params.sessionId,
1259
- ...readOptionalAgentId3(params.session.agentId) ?? readAgentIdFromMetadata2(params.session.metadata) ?? readAgentIdFromMetadata2(params.session.activeExecution?.requestEnvelope.metadata) ? {
1260
- agentId: readOptionalAgentId3(params.session.agentId) ?? readAgentIdFromMetadata2(params.session.metadata) ?? readAgentIdFromMetadata2(params.session.activeExecution?.requestEnvelope.metadata)
1261
- } : {},
1262
- messages,
1263
- updatedAt: params.updatedAt,
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
- // src/agent/agent-backend/agent-backend.ts
1269
- var DEFAULT_SUPPORTED_PART_TYPES = [
1270
- "text",
1271
- "file",
1272
- "source",
1273
- "step-start",
1274
- "reasoning",
1275
- "tool-invocation",
1276
- "card",
1277
- "rich-text",
1278
- "action",
1279
- "extension"
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
- manifest;
1283
- sessionStore;
1284
- onSessionRunStatusChanged;
1285
- sessionRegistry;
1286
- executor;
1287
- publisher;
1288
- sessionRealtime;
1289
- started = false;
1290
- constructor(config) {
1291
- this.sessionStore = config.sessionStore;
1292
- this.onSessionRunStatusChanged = config.onSessionRunStatusChanged;
1293
- this.sessionRegistry = new AgentLiveSessionRegistry(
1294
- this.sessionStore,
1295
- config.createRuntime
1296
- );
1297
- this.executor = new AgentRunExecutor();
1298
- this.publisher = new EventPublisher();
1299
- this.sessionRealtime = new AgentBackendSessionRealtime({
1300
- sessionRegistry: this.sessionRegistry,
1301
- sessionStore: this.sessionStore,
1302
- publishEndpointEvent: (event) => this.publishEndpointEvent(event),
1303
- persistSession: (sessionId) => this.persistSession(sessionId),
1304
- getSessionSummary: (sessionId) => this.getSession(sessionId)
1305
- });
1306
- this.manifest = {
1307
- endpointKind: "agent",
1308
- endpointId: config.endpointId?.trim() || "ncp-agent-backend",
1309
- version: config.version?.trim() || "0.1.0",
1310
- supportsStreaming: true,
1311
- supportsAbort: true,
1312
- supportsProactiveMessages: false,
1313
- supportsLiveSessionStream: true,
1314
- supportedPartTypes: config.supportedPartTypes ?? DEFAULT_SUPPORTED_PART_TYPES,
1315
- expectedLatency: config.expectedLatency ?? "seconds",
1316
- metadata: config.metadata
1317
- };
1318
- }
1319
- start = async () => {
1320
- if (this.started) {
1321
- return;
1322
- }
1323
- this.started = true;
1324
- this.publisher.publish({ type: NcpEventType6.EndpointReady });
1325
- };
1326
- stop = async () => {
1327
- if (!this.started) {
1328
- return;
1329
- }
1330
- this.started = false;
1331
- for (const session of this.sessionRegistry.listSessions()) {
1332
- const execution = session.activeExecution;
1333
- if (!execution) {
1334
- session.publisher.close();
1335
- continue;
1336
- }
1337
- execution.abortHandled = true;
1338
- execution.controller.abort();
1339
- this.finishSessionExecution(session, execution);
1340
- session.publisher.close();
1341
- }
1342
- this.sessionRegistry.clear();
1343
- };
1344
- emit = async (event) => {
1345
- await this.ensureStarted();
1346
- switch (event.type) {
1347
- case NcpEventType6.MessageRequest:
1348
- for await (const emittedEvent of this.send(event.payload)) {
1349
- void emittedEvent;
1350
- }
1351
- return;
1352
- case NcpEventType6.MessageStreamRequest:
1353
- await this.ensureStarted();
1354
- return;
1355
- case NcpEventType6.MessageAbort:
1356
- await this.handleAbort(event.payload);
1357
- return;
1358
- default:
1359
- this.publisher.publish(event);
1360
- }
1361
- };
1362
- subscribe = (listener) => {
1363
- return this.publisher.subscribe(listener);
1364
- };
1365
- send = (envelope, options) => {
1366
- return (async function* (self) {
1367
- await self.ensureStarted();
1368
- const session = await self.sessionRegistry.ensureSession(
1369
- envelope.sessionId,
1370
- envelope.metadata
1371
- );
1372
- const execution = self.startSessionExecution(
1373
- session,
1374
- envelope,
1375
- options?.signal
1376
- );
1377
- try {
1378
- for await (const event of self.executor.executeRun(
1379
- session,
1380
- envelope,
1381
- execution.controller
1382
- )) {
1383
- await self.sessionRealtime.publishSessionEvent(session, event);
1384
- yield event;
1385
- }
1386
- if (execution.controller.signal.aborted && !execution.abortHandled) {
1387
- const abortEvent = {
1388
- type: NcpEventType6.MessageAbort,
1389
- payload: {
1390
- sessionId: session.sessionId
1391
- }
1392
- };
1393
- execution.abortHandled = true;
1394
- await self.sessionRealtime.publishSessionEvent(session, abortEvent, {
1395
- dispatchToStateManager: true
1396
- });
1397
- yield abortEvent;
1398
- }
1399
- } finally {
1400
- self.finishSessionExecution(session, execution);
1401
- await self.persistSession(session.sessionId);
1402
- }
1403
- })(this);
1404
- };
1405
- abort = async (payload) => {
1406
- await this.handleAbort(payload);
1407
- };
1408
- stream = (payloadOrParams, opts) => this.sessionRealtime.streamSessionEvents(payloadOrParams, opts);
1409
- listSessions = async () => {
1410
- const storedSessions = await this.sessionStore.listSessions();
1411
- const summaries = storedSessions.map(
1412
- (session) => toSessionSummary(
1413
- session,
1414
- this.sessionRegistry.getSession(session.sessionId)
1415
- )
1416
- );
1417
- for (const liveSession of this.sessionRegistry.listSessions()) {
1418
- if (summaries.some((session) => session.sessionId === liveSession.sessionId)) {
1419
- continue;
1420
- }
1421
- summaries.push(toLiveSessionSummary(liveSession));
1422
- }
1423
- return summaries.sort(
1424
- (left, right) => right.updatedAt.localeCompare(left.updatedAt)
1425
- );
1426
- };
1427
- listSessionMessages = async (sessionId) => {
1428
- const liveSession = this.sessionRegistry.getSession(sessionId);
1429
- if (liveSession)
1430
- return readMessages(liveSession.stateManager.getSnapshot());
1431
- const session = await this.sessionStore.getSession(sessionId);
1432
- return session ? session.messages.map((message) => structuredClone(message)) : [];
1433
- };
1434
- getSession = async (sessionId) => {
1435
- const liveSession = this.sessionRegistry.getSession(sessionId);
1436
- const storedSession = await this.sessionStore.getSession(sessionId);
1437
- return storedSession ? toSessionSummary(storedSession, liveSession) : liveSession ? toLiveSessionSummary(liveSession) : null;
1438
- };
1439
- appendMessage = async (sessionId, message) => {
1440
- await this.ensureStarted();
1441
- return this.sessionRealtime.appendMessage(sessionId, message);
1442
- };
1443
- updateToolCallResult = async (sessionId, toolCallId, content) => {
1444
- await this.ensureStarted();
1445
- return this.sessionRealtime.updateToolCallResult(
1446
- sessionId,
1447
- toolCallId,
1448
- content
1449
- );
1450
- };
1451
- updateSession = async (sessionId, patch) => {
1452
- const liveSession = this.sessionRegistry.getSession(sessionId);
1453
- const storedSession = await this.sessionStore.getSession(sessionId);
1454
- if (!liveSession && !storedSession) return null;
1455
- await this.sessionStore.replaceSession(
1456
- buildUpdatedSessionRecord({
1457
- sessionId,
1458
- patch,
1459
- liveSession,
1460
- storedSession,
1461
- updatedAt: now()
1462
- })
1463
- );
1464
- return this.getSession(sessionId);
1465
- };
1466
- deleteSession = async (sessionId) => {
1467
- const liveSession = this.sessionRegistry.deleteSession(sessionId);
1468
- const execution = liveSession?.activeExecution;
1469
- if (execution) {
1470
- execution.abortHandled = true;
1471
- execution.controller.abort();
1472
- closeAgentBackendSessionExecution(execution);
1473
- }
1474
- liveSession?.publisher.close();
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
- // src/agent/agent-backend/in-memory-agent-session-store.ts
1257
+ //#endregion
1258
+ //#region src/agent/agent-backend/in-memory-agent-session-store.ts
1530
1259
  var InMemoryAgentSessionStore = class {
1531
- sessions = /* @__PURE__ */ new Map();
1532
- getSession = async (sessionId) => {
1533
- const session = this.sessions.get(sessionId);
1534
- return session ? structuredClone(session) : null;
1535
- };
1536
- listSessions = async () => {
1537
- return [...this.sessions.values()].map((session) => structuredClone(session));
1538
- };
1539
- saveSession = async (session) => {
1540
- this.sessions.set(session.sessionId, structuredClone(session));
1541
- };
1542
- replaceSession = async (session) => {
1543
- this.sessions.set(session.sessionId, structuredClone(session));
1544
- };
1545
- deleteSession = async (sessionId) => {
1546
- const session = this.sessions.get(sessionId);
1547
- if (!session) {
1548
- return null;
1549
- }
1550
- this.sessions.delete(sessionId);
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 };