@nextclaw/ncp-toolkit 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { NcpAgentConversationStateManager, NcpAgentConversationSnapshot, NcpAgentConversationHydrationParams, NcpEndpointEvent, NcpMessageSentPayload, NcpMessageAbortPayload, NcpTextStartPayload, NcpTextDeltaPayload, NcpTextEndPayload, NcpReasoningStartPayload, NcpReasoningDeltaPayload, NcpReasoningEndPayload, NcpToolCallStartPayload, NcpToolCallArgsPayload, NcpToolCallArgsDeltaPayload, NcpToolCallEndPayload, NcpToolCallResultPayload, NcpRunStartedPayload, NcpRunFinishedPayload, NcpRunErrorPayload, NcpRunMetadataPayload, NcpError, NcpAgentServerEndpoint, NcpAgentClientEndpoint, NcpEndpointSubscriber, NcpAgentRuntime, NcpRequestEnvelope, NcpMessage, NcpSessionApi, NcpAgentStreamProvider, NcpAgentRunApi, NcpEndpointManifest, NcpAgentRunSendOptions, NcpStreamRequestPayload, NcpAgentRunStreamOptions, NcpSessionSummary, NcpErrorCode } from '@nextclaw/ncp';
1
+ import { NcpAgentConversationStateManager, NcpAgentConversationSnapshot, NcpAgentConversationHydrationParams, NcpEndpointEvent, NcpMessageSentPayload, NcpMessageAbortPayload, NcpTextStartPayload, NcpTextDeltaPayload, NcpTextEndPayload, NcpReasoningStartPayload, NcpReasoningDeltaPayload, NcpReasoningEndPayload, NcpToolCallStartPayload, NcpToolCallArgsPayload, NcpToolCallArgsDeltaPayload, NcpToolCallEndPayload, NcpToolCallResultPayload, NcpRunStartedPayload, NcpRunFinishedPayload, NcpRunErrorPayload, NcpRunMetadataPayload, NcpError, NcpAgentServerEndpoint, NcpAgentClientEndpoint, NcpEndpointSubscriber, NcpAgentRuntime, NcpRequestEnvelope, NcpMessage, NcpSessionApi, NcpAgentStreamProvider, NcpAgentRunApi, NcpEndpointManifest, NcpAgentRunSendOptions, NcpStreamRequestPayload, NcpAgentRunStreamOptions, NcpSessionSummary, NcpSessionPatch, NcpErrorCode } from '@nextclaw/ncp';
2
2
 
3
3
  declare class DefaultNcpAgentConversationStateManager implements NcpAgentConversationStateManager {
4
4
  private messages;
@@ -38,14 +38,10 @@ declare class DefaultNcpAgentConversationStateManager implements NcpAgentConvers
38
38
  private ensureStreamingMessage;
39
39
  private resolveToolCallTargetMessage;
40
40
  private updateMessageContainingToolCall;
41
- private findToolInvocationPart;
42
- private findToolNameByCallId;
43
- private upsertToolInvocationPart;
44
41
  private upsertMessage;
45
42
  private replaceStreamingMessage;
46
43
  private setError;
47
44
  private clearActiveRun;
48
- private clearToolCallTrackingByMessageId;
49
45
  private notifyListeners;
50
46
  }
51
47
 
@@ -68,6 +64,8 @@ declare class EventPublisher {
68
64
  type RuntimeFactoryParams = {
69
65
  sessionId: string;
70
66
  stateManager: NcpAgentConversationStateManager;
67
+ sessionMetadata: Record<string, unknown>;
68
+ setSessionMetadata: (nextMetadata: Record<string, unknown>) => void;
71
69
  };
72
70
  type CreateRuntimeFn = (params: RuntimeFactoryParams) => NcpAgentRuntime;
73
71
  type AgentSessionRecord = {
@@ -87,6 +85,7 @@ type LiveSessionState = {
87
85
  sessionId: string;
88
86
  runtime: NcpAgentRuntime;
89
87
  stateManager: NcpAgentConversationStateManager;
88
+ metadata: Record<string, unknown>;
90
89
  activeExecution: LiveSessionExecution | null;
91
90
  };
92
91
  interface AgentSessionStore {
@@ -128,6 +127,7 @@ declare class DefaultNcpAgentBackend implements NcpAgentServerEndpoint, NcpSessi
128
127
  listSessions(): Promise<NcpSessionSummary[]>;
129
128
  listSessionMessages(sessionId: string): Promise<NcpMessage[]>;
130
129
  getSession(sessionId: string): Promise<NcpSessionSummary | null>;
130
+ updateSession(sessionId: string, patch: NcpSessionPatch): Promise<NcpSessionSummary | null>;
131
131
  deleteSession(sessionId: string): Promise<void>;
132
132
  private ensureStarted;
133
133
  private startSessionExecution;
package/dist/index.js CHANGED
@@ -16,9 +16,8 @@ function normalizeConversationMessage(message) {
16
16
  return cloneConversationMessage(sanitizeAssistantReplyTags(message));
17
17
  }
18
18
 
19
- // src/agent/agent-conversation-state-manager.ts
20
- var DEFAULT_ASSISTANT_ROLE = "assistant";
21
- var buildRuntimeError = (payload) => {
19
+ // src/agent/agent-conversation-state-manager.utils.ts
20
+ function buildRuntimeError(payload) {
22
21
  const message = payload.error?.trim();
23
22
  return {
24
23
  code: "runtime-error",
@@ -30,7 +29,64 @@ var buildRuntimeError = (payload) => {
30
29
  runId: payload.runId
31
30
  }
32
31
  };
33
- };
32
+ }
33
+ function shouldPromoteStreamingMessageId(message, nextMessageId) {
34
+ if (!nextMessageId.trim()) {
35
+ return false;
36
+ }
37
+ if (message.id.startsWith("tool-")) {
38
+ return true;
39
+ }
40
+ return message.parts.some((part) => part.type === "tool-invocation");
41
+ }
42
+ function remapTrackedToolCallsToMessageId(toolCallMessageIdByCallId, fromMessageId, toMessageId) {
43
+ for (const [toolCallId, trackedMessageId] of toolCallMessageIdByCallId) {
44
+ if (trackedMessageId !== fromMessageId) {
45
+ continue;
46
+ }
47
+ toolCallMessageIdByCallId.set(toolCallId, toMessageId);
48
+ }
49
+ }
50
+ function clearToolCallTrackingByMessageId(toolCallMessageIdByCallId, toolCallArgsRawByCallId, messageId) {
51
+ for (const [toolCallId, trackedMessageId] of toolCallMessageIdByCallId) {
52
+ if (trackedMessageId !== messageId) {
53
+ continue;
54
+ }
55
+ toolCallMessageIdByCallId.delete(toolCallId);
56
+ toolCallArgsRawByCallId.delete(toolCallId);
57
+ }
58
+ }
59
+ function findToolInvocationPart(parts, toolCallId) {
60
+ for (let index = parts.length - 1; index >= 0; index -= 1) {
61
+ const part = parts[index];
62
+ if (part.type === "tool-invocation" && part.toolCallId === toolCallId) {
63
+ return part;
64
+ }
65
+ }
66
+ return null;
67
+ }
68
+ function findToolNameByCallId(parts, toolCallId) {
69
+ const part = findToolInvocationPart(parts, toolCallId);
70
+ return part?.toolName ?? null;
71
+ }
72
+ function upsertToolInvocationPart(parts, toolPart) {
73
+ const nextParts = [...parts];
74
+ for (let index = nextParts.length - 1; index >= 0; index -= 1) {
75
+ const part = nextParts[index];
76
+ if (part.type === "tool-invocation" && part.toolCallId === toolPart.toolCallId) {
77
+ nextParts[index] = {
78
+ ...part,
79
+ ...toolPart
80
+ };
81
+ return nextParts;
82
+ }
83
+ }
84
+ nextParts.push(toolPart);
85
+ return nextParts;
86
+ }
87
+
88
+ // src/agent/agent-conversation-state-manager.ts
89
+ var DEFAULT_ASSISTANT_ROLE = "assistant";
34
90
  var DefaultNcpAgentConversationStateManager = class {
35
91
  messages = [];
36
92
  streamingMessage = null;
@@ -167,9 +223,17 @@ var DefaultNcpAgentConversationStateManager = class {
167
223
  });
168
224
  this.replaceStreamingMessage(null);
169
225
  if (targetMessageId) {
170
- this.clearToolCallTrackingByMessageId(targetMessageId);
226
+ clearToolCallTrackingByMessageId(
227
+ this.toolCallMessageIdByCallId,
228
+ this.toolCallArgsRawByCallId,
229
+ targetMessageId
230
+ );
171
231
  } else {
172
- this.clearToolCallTrackingByMessageId(streamingMessageId);
232
+ clearToolCallTrackingByMessageId(
233
+ this.toolCallMessageIdByCallId,
234
+ this.toolCallArgsRawByCallId,
235
+ streamingMessageId
236
+ );
173
237
  }
174
238
  }
175
239
  }
@@ -246,7 +310,7 @@ var DefaultNcpAgentConversationStateManager = class {
246
310
  payload.messageId
247
311
  );
248
312
  this.toolCallArgsRawByCallId.set(payload.toolCallId, "");
249
- const nextParts = this.upsertToolInvocationPart(targetMessage.parts, {
313
+ const nextParts = upsertToolInvocationPart(targetMessage.parts, {
250
314
  type: "tool-invocation",
251
315
  toolCallId: payload.toolCallId,
252
316
  toolName: payload.toolName,
@@ -273,10 +337,10 @@ var DefaultNcpAgentConversationStateManager = class {
273
337
  handleMessageToolCallEnd(payload) {
274
338
  const targetMessage = this.resolveToolCallTargetMessage(payload.sessionId, payload.toolCallId);
275
339
  const args = this.toolCallArgsRawByCallId.get(payload.toolCallId) ?? "";
276
- const nextParts = this.upsertToolInvocationPart(targetMessage.parts, {
340
+ const nextParts = upsertToolInvocationPart(targetMessage.parts, {
277
341
  type: "tool-invocation",
278
342
  toolCallId: payload.toolCallId,
279
- toolName: this.findToolNameByCallId(targetMessage.parts, payload.toolCallId) ?? "unknown",
343
+ toolName: findToolNameByCallId(targetMessage.parts, payload.toolCallId) ?? "unknown",
280
344
  state: "call",
281
345
  args
282
346
  });
@@ -296,11 +360,11 @@ var DefaultNcpAgentConversationStateManager = class {
296
360
  args: existingPart.args,
297
361
  result: payload.content
298
362
  };
299
- return this.upsertToolInvocationPart(targetMessage.parts, mergedPart);
363
+ return upsertToolInvocationPart(targetMessage.parts, mergedPart);
300
364
  });
301
365
  if (!updated) {
302
366
  const fallbackMessage = this.resolveToolCallTargetMessage(payload.sessionId, payload.toolCallId);
303
- const nextParts = this.upsertToolInvocationPart(fallbackMessage.parts, {
367
+ const nextParts = upsertToolInvocationPart(fallbackMessage.parts, {
304
368
  type: "tool-invocation",
305
369
  toolCallId: payload.toolCallId,
306
370
  toolName: "unknown",
@@ -330,7 +394,11 @@ var DefaultNcpAgentConversationStateManager = class {
330
394
  };
331
395
  this.upsertMessage(finalizedMessage);
332
396
  this.replaceStreamingMessage(null);
333
- this.clearToolCallTrackingByMessageId(finalizedMessage.id);
397
+ clearToolCallTrackingByMessageId(
398
+ this.toolCallMessageIdByCallId,
399
+ this.toolCallArgsRawByCallId,
400
+ finalizedMessage.id
401
+ );
334
402
  }
335
403
  this.setError(null);
336
404
  this.clearActiveRun();
@@ -343,7 +411,11 @@ var DefaultNcpAgentConversationStateManager = class {
343
411
  };
344
412
  this.upsertMessage(failedMessage);
345
413
  this.replaceStreamingMessage(null);
346
- this.clearToolCallTrackingByMessageId(failedMessage.id);
414
+ clearToolCallTrackingByMessageId(
415
+ this.toolCallMessageIdByCallId,
416
+ this.toolCallArgsRawByCallId,
417
+ failedMessage.id
418
+ );
347
419
  }
348
420
  this.setError(buildRuntimeError(payload));
349
421
  this.clearActiveRun();
@@ -367,8 +439,8 @@ var DefaultNcpAgentConversationStateManager = class {
367
439
  }
368
440
  applyToolCallArgs(sessionId, toolCallId, args, messageId) {
369
441
  const targetMessage = this.resolveToolCallTargetMessage(sessionId, toolCallId, messageId);
370
- const toolName = this.findToolNameByCallId(targetMessage.parts, toolCallId) ?? "unknown";
371
- const nextParts = this.upsertToolInvocationPart(targetMessage.parts, {
442
+ const toolName = findToolNameByCallId(targetMessage.parts, toolCallId) ?? "unknown";
443
+ const nextParts = upsertToolInvocationPart(targetMessage.parts, {
372
444
  type: "tool-invocation",
373
445
  toolCallId,
374
446
  toolName,
@@ -408,6 +480,22 @@ var DefaultNcpAgentConversationStateManager = class {
408
480
  this.replaceStreamingMessage(nextStreamingMessage2);
409
481
  return nextStreamingMessage2;
410
482
  }
483
+ const existingStreamingMessage = this.streamingMessage;
484
+ if (existingStreamingMessage && existingStreamingMessage.id !== messageId && existingStreamingMessage.sessionId === sessionId && shouldPromoteStreamingMessageId(existingStreamingMessage, messageId)) {
485
+ const nextStreamingMessage2 = {
486
+ ...existingStreamingMessage,
487
+ id: messageId,
488
+ sessionId,
489
+ status
490
+ };
491
+ remapTrackedToolCallsToMessageId(
492
+ this.toolCallMessageIdByCallId,
493
+ existingStreamingMessage.id,
494
+ messageId
495
+ );
496
+ this.replaceStreamingMessage(nextStreamingMessage2);
497
+ return nextStreamingMessage2;
498
+ }
411
499
  const nextStreamingMessage = {
412
500
  id: messageId,
413
501
  sessionId,
@@ -426,7 +514,7 @@ var DefaultNcpAgentConversationStateManager = class {
426
514
  }
427
515
  updateMessageContainingToolCall(toolCallId, updater) {
428
516
  if (this.streamingMessage) {
429
- const part = this.findToolInvocationPart(this.streamingMessage.parts, toolCallId);
517
+ const part = findToolInvocationPart(this.streamingMessage.parts, toolCallId);
430
518
  if (part) {
431
519
  const nextParts = updater(this.streamingMessage, part);
432
520
  this.replaceStreamingMessage({ ...this.streamingMessage, parts: nextParts });
@@ -435,7 +523,7 @@ var DefaultNcpAgentConversationStateManager = class {
435
523
  }
436
524
  for (let index = this.messages.length - 1; index >= 0; index -= 1) {
437
525
  const candidateMessage = this.messages[index];
438
- const part = this.findToolInvocationPart(candidateMessage.parts, toolCallId);
526
+ const part = findToolInvocationPart(candidateMessage.parts, toolCallId);
439
527
  if (!part) {
440
528
  continue;
441
529
  }
@@ -450,34 +538,6 @@ var DefaultNcpAgentConversationStateManager = class {
450
538
  }
451
539
  return false;
452
540
  }
453
- findToolInvocationPart(parts, toolCallId) {
454
- for (let index = parts.length - 1; index >= 0; index -= 1) {
455
- const part = parts[index];
456
- if (part.type === "tool-invocation" && part.toolCallId === toolCallId) {
457
- return part;
458
- }
459
- }
460
- return null;
461
- }
462
- findToolNameByCallId(parts, toolCallId) {
463
- const part = this.findToolInvocationPart(parts, toolCallId);
464
- return part?.toolName ?? null;
465
- }
466
- upsertToolInvocationPart(parts, toolPart) {
467
- const nextParts = [...parts];
468
- for (let index = nextParts.length - 1; index >= 0; index -= 1) {
469
- const part = nextParts[index];
470
- if (part.type === "tool-invocation" && part.toolCallId === toolPart.toolCallId) {
471
- nextParts[index] = {
472
- ...part,
473
- ...toolPart
474
- };
475
- return nextParts;
476
- }
477
- }
478
- nextParts.push(toolPart);
479
- return nextParts;
480
- }
481
541
  upsertMessage(message) {
482
542
  const normalizedMessage = normalizeConversationMessage(message);
483
543
  const messageIndex = this.messages.findIndex((item) => item.id === normalizedMessage.id);
@@ -513,15 +573,6 @@ var DefaultNcpAgentConversationStateManager = class {
513
573
  this.activeRun = null;
514
574
  this.stateVersion += 1;
515
575
  }
516
- clearToolCallTrackingByMessageId(messageId) {
517
- for (const [toolCallId, trackedMessageId] of this.toolCallMessageIdByCallId) {
518
- if (trackedMessageId !== messageId) {
519
- continue;
520
- }
521
- this.toolCallMessageIdByCallId.delete(toolCallId);
522
- this.toolCallArgsRawByCallId.delete(toolCallId);
523
- }
524
- }
525
576
  notifyListeners() {
526
577
  const snapshot = this.getSnapshot();
527
578
  for (const listener of this.listeners) {
@@ -582,7 +633,7 @@ async function consume(events) {
582
633
 
583
634
  // src/agent/agent-backend/agent-backend.ts
584
635
  import {
585
- NcpEventType as NcpEventType4
636
+ NcpEventType as NcpEventType5
586
637
  } from "@nextclaw/ncp";
587
638
 
588
639
  // src/errors/ncp-error-exception.ts
@@ -604,9 +655,15 @@ var AgentLiveSessionRegistry = class {
604
655
  this.createRuntime = createRuntime;
605
656
  }
606
657
  sessions = /* @__PURE__ */ new Map();
607
- async ensureSession(sessionId) {
658
+ async ensureSession(sessionId, initialMetadata) {
608
659
  const existing = this.sessions.get(sessionId);
609
660
  if (existing) {
661
+ if (initialMetadata && Object.keys(initialMetadata).length > 0) {
662
+ existing.metadata = {
663
+ ...existing.metadata,
664
+ ...structuredClone(initialMetadata)
665
+ };
666
+ }
610
667
  return existing;
611
668
  }
612
669
  const storedSession = await this.sessionStore.getSession(sessionId);
@@ -615,12 +672,27 @@ var AgentLiveSessionRegistry = class {
615
672
  sessionId,
616
673
  messages: cloneMessages(storedSession?.messages ?? [])
617
674
  });
675
+ const sessionMetadata = {
676
+ ...storedSession?.metadata ? structuredClone(storedSession.metadata) : {},
677
+ ...initialMetadata ? structuredClone(initialMetadata) : {}
678
+ };
618
679
  const session = {
619
680
  sessionId,
620
681
  stateManager,
621
- runtime: this.createRuntime({ sessionId, stateManager }),
682
+ metadata: sessionMetadata,
683
+ runtime: null,
622
684
  activeExecution: null
623
685
  };
686
+ session.runtime = this.createRuntime({
687
+ sessionId,
688
+ stateManager,
689
+ sessionMetadata,
690
+ setSessionMetadata: (nextMetadata) => {
691
+ session.metadata = {
692
+ ...structuredClone(nextMetadata)
693
+ };
694
+ }
695
+ });
624
696
  this.sessions.set(sessionId, session);
625
697
  return session;
626
698
  }
@@ -759,6 +831,78 @@ function createAsyncQueue() {
759
831
  };
760
832
  }
761
833
 
834
+ // src/agent/agent-backend/agent-backend-session-utils.ts
835
+ import { NcpEventType as NcpEventType4 } from "@nextclaw/ncp";
836
+ function readMessages(snapshot) {
837
+ const messages = snapshot.messages.map((message) => structuredClone(message));
838
+ if (snapshot.streamingMessage) {
839
+ messages.push(structuredClone(snapshot.streamingMessage));
840
+ }
841
+ return messages;
842
+ }
843
+ function toSessionSummary(session, liveSession) {
844
+ return {
845
+ sessionId: session.sessionId,
846
+ messageCount: session.messages.length,
847
+ updatedAt: session.updatedAt,
848
+ status: liveSession?.activeExecution ? "running" : "idle",
849
+ ...session.metadata ? {
850
+ metadata: structuredClone({
851
+ ...session.metadata,
852
+ ...liveSession?.metadata ? liveSession.metadata : {}
853
+ })
854
+ } : liveSession?.metadata ? { metadata: structuredClone(liveSession.metadata) } : {}
855
+ };
856
+ }
857
+ function toLiveSessionSummary(session) {
858
+ const snapshot = session.stateManager.getSnapshot();
859
+ return {
860
+ sessionId: session.sessionId,
861
+ messageCount: readMessages(snapshot).length,
862
+ updatedAt: now(),
863
+ status: session.activeExecution ? "running" : "idle",
864
+ ...Object.keys(session.metadata).length > 0 ? { metadata: structuredClone(session.metadata) } : session.activeExecution?.requestEnvelope.metadata ? { metadata: structuredClone(session.activeExecution.requestEnvelope.metadata) } : {}
865
+ };
866
+ }
867
+ function now() {
868
+ return (/* @__PURE__ */ new Date()).toISOString();
869
+ }
870
+ function isTerminalEvent(event) {
871
+ switch (event.type) {
872
+ case NcpEventType4.MessageAbort:
873
+ case NcpEventType4.RunFinished:
874
+ case NcpEventType4.RunError:
875
+ return true;
876
+ default:
877
+ return false;
878
+ }
879
+ }
880
+
881
+ // src/agent/agent-backend/agent-backend-session-persistence.ts
882
+ function buildUpdatedSessionRecord(params) {
883
+ const nextMetadata = params.patch.metadata === null ? {} : params.patch.metadata ? structuredClone(params.patch.metadata) : structuredClone(params.liveSession?.metadata ?? params.storedSession?.metadata ?? {});
884
+ if (params.liveSession) {
885
+ params.liveSession.metadata = structuredClone(nextMetadata);
886
+ }
887
+ return {
888
+ sessionId: params.sessionId,
889
+ messages: params.liveSession ? readMessages(params.liveSession.stateManager.getSnapshot()) : params.storedSession?.messages.map((message) => structuredClone(message)) ?? [],
890
+ updatedAt: params.updatedAt,
891
+ metadata: nextMetadata
892
+ };
893
+ }
894
+ function buildPersistedLiveSessionRecord(params) {
895
+ return {
896
+ sessionId: params.sessionId,
897
+ messages: readMessages(params.session.stateManager.getSnapshot()),
898
+ updatedAt: params.updatedAt,
899
+ metadata: {
900
+ ...params.session.metadata ? structuredClone(params.session.metadata) : {},
901
+ ...params.session.activeExecution?.requestEnvelope.metadata ? structuredClone(params.session.activeExecution.requestEnvelope.metadata) : {}
902
+ }
903
+ };
904
+ }
905
+
762
906
  // src/agent/agent-backend/event-publisher.ts
763
907
  var EventPublisher = class {
764
908
  listeners = /* @__PURE__ */ new Set();
@@ -850,7 +994,7 @@ var DefaultNcpAgentBackend = class {
850
994
  return;
851
995
  }
852
996
  this.started = true;
853
- this.publisher.publish({ type: NcpEventType4.EndpointReady });
997
+ this.publisher.publish({ type: NcpEventType5.EndpointReady });
854
998
  }
855
999
  async stop() {
856
1000
  if (!this.started) {
@@ -871,13 +1015,13 @@ var DefaultNcpAgentBackend = class {
871
1015
  async emit(event) {
872
1016
  await this.ensureStarted();
873
1017
  switch (event.type) {
874
- case NcpEventType4.MessageRequest:
1018
+ case NcpEventType5.MessageRequest:
875
1019
  await this.handleRequest(event.payload);
876
1020
  return;
877
- case NcpEventType4.MessageStreamRequest:
1021
+ case NcpEventType5.MessageStreamRequest:
878
1022
  await this.streamToSubscribers(event.payload);
879
1023
  return;
880
- case NcpEventType4.MessageAbort:
1024
+ case NcpEventType5.MessageAbort:
881
1025
  await this.handleAbort(event.payload);
882
1026
  return;
883
1027
  default:
@@ -889,7 +1033,10 @@ var DefaultNcpAgentBackend = class {
889
1033
  }
890
1034
  async *send(envelope, options) {
891
1035
  await this.ensureStarted();
892
- const session = await this.sessionRegistry.ensureSession(envelope.sessionId);
1036
+ const session = await this.sessionRegistry.ensureSession(
1037
+ envelope.sessionId,
1038
+ envelope.metadata
1039
+ );
893
1040
  const execution = this.startSessionExecution(session, envelope, options?.signal);
894
1041
  try {
895
1042
  for await (const event of this.executor.executeRun(session, envelope, execution.controller)) {
@@ -960,22 +1107,27 @@ var DefaultNcpAgentBackend = class {
960
1107
  }
961
1108
  async listSessionMessages(sessionId) {
962
1109
  const liveSession = this.sessionRegistry.getSession(sessionId);
963
- if (liveSession) {
964
- return readMessages(liveSession.stateManager.getSnapshot());
965
- }
1110
+ if (liveSession) return readMessages(liveSession.stateManager.getSnapshot());
966
1111
  const session = await this.sessionStore.getSession(sessionId);
967
1112
  return session ? session.messages.map((message) => structuredClone(message)) : [];
968
1113
  }
969
1114
  async getSession(sessionId) {
970
1115
  const liveSession = this.sessionRegistry.getSession(sessionId);
971
1116
  const storedSession = await this.sessionStore.getSession(sessionId);
972
- if (storedSession) {
973
- return toSessionSummary(storedSession, liveSession);
974
- }
975
- if (liveSession) {
976
- return toLiveSessionSummary(liveSession);
977
- }
978
- return null;
1117
+ return storedSession ? toSessionSummary(storedSession, liveSession) : liveSession ? toLiveSessionSummary(liveSession) : null;
1118
+ }
1119
+ async updateSession(sessionId, patch) {
1120
+ const liveSession = this.sessionRegistry.getSession(sessionId);
1121
+ const storedSession = await this.sessionStore.getSession(sessionId);
1122
+ if (!liveSession && !storedSession) return null;
1123
+ await this.sessionStore.saveSession(buildUpdatedSessionRecord({
1124
+ sessionId,
1125
+ patch,
1126
+ liveSession,
1127
+ storedSession,
1128
+ updatedAt: now()
1129
+ }));
1130
+ return this.getSession(sessionId);
979
1131
  }
980
1132
  async deleteSession(sessionId) {
981
1133
  const liveSession = this.sessionRegistry.deleteSession(sessionId);
@@ -1060,7 +1212,7 @@ var DefaultNcpAgentBackend = class {
1060
1212
  }
1061
1213
  async createAbortEvent(sessionId, messageId) {
1062
1214
  const abortEvent = {
1063
- type: NcpEventType4.MessageAbort,
1215
+ type: NcpEventType5.MessageAbort,
1064
1216
  payload: {
1065
1217
  sessionId,
1066
1218
  ...messageId ? { messageId } : {}
@@ -1075,57 +1227,14 @@ var DefaultNcpAgentBackend = class {
1075
1227
  }
1076
1228
  async persistSession(sessionId) {
1077
1229
  const session = this.sessionRegistry.getSession(sessionId);
1078
- if (!session) {
1079
- return;
1080
- }
1081
- const snapshot = session.stateManager.getSnapshot();
1082
- await this.sessionStore.saveSession({
1230
+ if (!session) return;
1231
+ await this.sessionStore.saveSession(buildPersistedLiveSessionRecord({
1083
1232
  sessionId,
1084
- messages: readMessages(snapshot),
1085
- updatedAt: now(),
1086
- metadata: session.activeExecution?.requestEnvelope.metadata
1087
- });
1233
+ session,
1234
+ updatedAt: now()
1235
+ }));
1088
1236
  }
1089
1237
  };
1090
- function readMessages(snapshot) {
1091
- const messages = snapshot.messages.map((message) => structuredClone(message));
1092
- if (snapshot.streamingMessage) {
1093
- messages.push(structuredClone(snapshot.streamingMessage));
1094
- }
1095
- return messages;
1096
- }
1097
- function toSessionSummary(session, liveSession) {
1098
- return {
1099
- sessionId: session.sessionId,
1100
- messageCount: session.messages.length,
1101
- updatedAt: session.updatedAt,
1102
- status: liveSession?.activeExecution ? "running" : "idle",
1103
- ...session.metadata ? { metadata: structuredClone(session.metadata) } : {}
1104
- };
1105
- }
1106
- function toLiveSessionSummary(session) {
1107
- const snapshot = session.stateManager.getSnapshot();
1108
- return {
1109
- sessionId: session.sessionId,
1110
- messageCount: readMessages(snapshot).length,
1111
- updatedAt: now(),
1112
- status: session.activeExecution ? "running" : "idle",
1113
- ...session.activeExecution?.requestEnvelope.metadata ? { metadata: structuredClone(session.activeExecution.requestEnvelope.metadata) } : {}
1114
- };
1115
- }
1116
- function now() {
1117
- return (/* @__PURE__ */ new Date()).toISOString();
1118
- }
1119
- function isTerminalEvent(event) {
1120
- switch (event.type) {
1121
- case NcpEventType4.MessageAbort:
1122
- case NcpEventType4.RunFinished:
1123
- case NcpEventType4.RunError:
1124
- return true;
1125
- default:
1126
- return false;
1127
- }
1128
- }
1129
1238
 
1130
1239
  // src/agent/agent-backend/in-memory-agent-session-store.ts
1131
1240
  var InMemoryAgentSessionStore = class {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/ncp-toolkit",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "description": "Toolkit implementations built on top of the NextClaw Communication Protocol.",
6
6
  "type": "module",