@nextclaw/kernel 0.6.14 → 0.6.16

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.js CHANGED
@@ -5,8 +5,8 @@ import { APP_NAME, AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, CLEAR_THINKING_TOK
5
5
  import { NCP_AI_EXECUTION_METADATA_KEY, NcpEventType, createUnavailableNcpAiExecutionMetadata, normalizeAssistantText, readNcpAiExecutionMetadata, sanitizeAssistantReplyTags } from "@nextclaw/ncp";
6
6
  import { LocalAssetStore, buildAssetContentPath, buildNcpUserContent, buildOpenAiFunctionTool, ncpMessageToOpenAiMessages, validateToolArgs } from "@nextclaw/ncp-agent-runtime";
7
7
  import { createHash, createHmac, randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
8
- import { CHAT_INLINE_TOKENS_METADATA_KEY, CHAT_SESSION_MATERIALIZATION_METADATA_KEY, CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND, CHAT_WORKSPACE_FILE_TOKEN_KIND, EventBus, Ingress, PANEL_APP_INLINE_HOST_CONTRACT, eventKeys, ingressKeys, isRuntimeDefaultModelValue, normalizeRuntimeModelSelectionMode, readInlineContentHeight } from "@nextclaw/shared";
9
- import { catchError, from, lastValueFrom, tap } from "rxjs";
8
+ import { CHAT_INLINE_TOKENS_METADATA_KEY, CHAT_INLINE_TOKENS_SCHEMA_VERSION, CHAT_SESSION_MATERIALIZATION_METADATA_KEY, CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND, CHAT_WORKSPACE_FILE_TOKEN_KIND, EventBus, Ingress, PANEL_APP_INLINE_HOST_CONTRACT, eventKeys, ingressKeys, isRuntimeDefaultModelValue, normalizeRuntimeModelSelectionMode, readInlineContentHeight } from "@nextclaw/shared";
9
+ import { catchError, filter, from, lastValueFrom, tap } from "rxjs";
10
10
  import { appendFileSync, chmodSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
11
11
  import { basename, delimiter, dirname, extname, isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
12
12
  import { execFileSync, spawn } from "node:child_process";
@@ -692,7 +692,11 @@ function createUnavailableAiExecutionMetadataEvent(params) {
692
692
  };
693
693
  }
694
694
  //#endregion
695
- //#region src/managers/agent-run-request.manager.ts
695
+ //#region src/utils/agent-run-request.utils.ts
696
+ function readOptionalString$11(value) {
697
+ if (typeof value !== "string") return;
698
+ return value.trim() || void 0;
699
+ }
696
700
  function toAgentRunRequest(envelope) {
697
701
  const metadata = envelope.metadata ?? {};
698
702
  const peerId = readOptionalString$11(envelope.peerId);
@@ -730,26 +734,21 @@ function toAgentRunRequest(envelope) {
730
734
  const messageSessionId = readOptionalString$11(sourceMessage.sessionId);
731
735
  const sessionId = envelopeSessionId ?? messageSessionId;
732
736
  if (sessionId && peerId) throw new Error("agent-run.send cannot accept both sessionId and peerId.");
733
- const message = {
734
- ...sourceMessage,
735
- sessionId: sessionId ?? "",
736
- id: sourceMessage.id ?? `user-message-${randomUUID()}`,
737
- role: sourceMessage.role ?? "user",
738
- status: sourceMessage.status ?? "final",
739
- timestamp: sourceMessage.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
740
- parts: structuredClone(sourceMessage.parts)
741
- };
742
737
  return {
743
738
  ...requestMetadata,
744
739
  sessionId,
745
740
  peerId,
746
- message
741
+ message: {
742
+ ...sourceMessage,
743
+ sessionId: sessionId ?? "",
744
+ id: sourceMessage.id ?? `user-message-${randomUUID()}`,
745
+ role: sourceMessage.role ?? "user",
746
+ status: sourceMessage.status ?? "final",
747
+ timestamp: sourceMessage.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
748
+ parts: structuredClone(sourceMessage.parts)
749
+ }
747
750
  };
748
751
  }
749
- function readOptionalString$11(value) {
750
- if (typeof value !== "string") return;
751
- return value.trim() || void 0;
752
- }
753
752
  function createCompletedAssistantMessageEvent(params) {
754
753
  return {
755
754
  occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -761,6 +760,17 @@ function createCompletedAssistantMessageEvent(params) {
761
760
  }
762
761
  };
763
762
  }
763
+ function createMessageSentEvent(params) {
764
+ return {
765
+ occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
766
+ type: NcpEventType.MessageSent,
767
+ payload: {
768
+ sessionId: params.sessionId,
769
+ message: params.message,
770
+ correlationId: params.correlationId
771
+ }
772
+ };
773
+ }
764
774
  function createSyntheticRunErrorEvent(params) {
765
775
  const { correlationId, error, runId, sessionId, startedAt } = params;
766
776
  const endedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -807,19 +817,16 @@ function readMessageTask(message) {
807
817
  return message.parts.flatMap((part) => (part.type === "text" || part.type === "rich-text" || part.type === "reasoning") && part.text.trim() ? [part.text.trim()] : [])[0] ?? "Session";
808
818
  }
809
819
  function resolveRunSpec(params) {
810
- const { agentManager, configManager, request, runId, session } = params;
811
- const modelSource = request.model !== void 0 ? "request" : session.model !== void 0 ? "session" : "default";
812
- const model = request.model ?? session.model ?? configManager.getDefaultModel();
813
- const agentId = request.agentId ?? session.agentId ?? agentManager.getDefaultAgentId();
820
+ const { defaultAgentId, model, modelMaxTokens, request, runId, session } = params;
814
821
  return {
815
- modelSource,
822
+ modelSource: request.model !== void 0 ? "request" : session.model !== void 0 ? "session" : "default",
816
823
  spec: {
817
824
  runId,
818
825
  runtimeId: session.agentRuntimeId,
819
- agentId,
826
+ agentId: request.agentId ?? session.agentId ?? defaultAgentId,
820
827
  model,
821
828
  requestedModel: request.model ?? null,
822
- maxTokens: request.maxTokens ?? configManager.getModelMaxTokens(model),
829
+ maxTokens: request.maxTokens ?? modelMaxTokens,
823
830
  thinkingEffort: request.thinkingEffort ?? session.thinkingEffort ?? null,
824
831
  correlationId: request.correlationId
825
832
  }
@@ -850,8 +857,11 @@ function attachRunSpecMetadata(params) {
850
857
  metadata
851
858
  };
852
859
  }
860
+ //#endregion
861
+ //#region src/managers/agent-run-request.manager.ts
853
862
  var AgentRunRequestManager = class {
854
863
  cleanups = [];
864
+ observedSessionRuns = /* @__PURE__ */ new Set();
855
865
  started = false;
856
866
  constructor(agentRuntimeManager, agentManager, configManager, contextProviderManager, eventBus, ingress, sessionManager, sessionRunManager, toolProviderManager) {
857
867
  this.agentRuntimeManager = agentRuntimeManager;
@@ -871,8 +881,18 @@ var AgentRunRequestManager = class {
871
881
  };
872
882
  dispose = () => {
873
883
  while (this.cleanups.length > 0) this.cleanups.pop()?.();
884
+ this.observedSessionRuns.clear();
874
885
  this.started = false;
875
886
  };
887
+ listQueuedInputs = (sessionId) => {
888
+ return this.sessionRunManager.getSessionRun(sessionId)?.listQueuedRequests().map(this.toQueuedInput) ?? [];
889
+ };
890
+ removeQueuedInput = (sessionId, queuedInputId) => {
891
+ const removed = this.sessionRunManager.getSessionRun(sessionId)?.removeQueuedRequest(queuedInputId) ?? null;
892
+ if (!removed) return null;
893
+ this.publishRunQueueUpdated(sessionId);
894
+ return this.toQueuedInput(removed);
895
+ };
876
896
  handleSendRequest = async (envelope) => {
877
897
  if (!envelope.payload) throw new Error("Invalid agent run send request.");
878
898
  return toRunHandle(await this.send(toAgentRunRequest(envelope.payload)));
@@ -899,41 +919,46 @@ var AgentRunRequestManager = class {
899
919
  };
900
920
  send = async (request) => {
901
921
  const session = await this.getOrCreateSessionForRequest(request);
902
- const sessionRun = this.sessionRunManager.getSessionRun(session.sessionId) ?? await this.sessionRunManager.createSessionRun(session.sessionId);
922
+ const sessionRun = await this.sessionRunManager.getOrCreateSessionRun(session.sessionId);
923
+ this.observeSessionRun(sessionRun);
903
924
  const baseMessage = {
904
925
  ...request.message,
905
926
  sessionId: session.sessionId
906
927
  };
907
- let stopPublishingRunStatus = () => void 0;
908
- stopPublishingRunStatus = sessionRun.onStatusChange((status) => {
909
- this.eventBus.emit(eventKeys.sessionRunStatus, {
910
- sessionKey: sessionRun.sessionId,
911
- status
912
- }, {
913
- emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
914
- source: "agent-run-request"
915
- });
916
- if (status === "idle") stopPublishingRunStatus();
917
- });
918
- this.cleanups.push(stopPublishingRunStatus);
928
+ const queuedRequest = sessionRun.enqueueRequest({
929
+ ...request,
930
+ sessionId: session.sessionId,
931
+ message: baseMessage
932
+ }, session);
933
+ const activeRequest = sessionRun.beginNextRun();
934
+ this.publishRunQueueUpdated(session.sessionId);
935
+ if (activeRequest?.id === queuedRequest.id) await this.startQueuedRun(sessionRun, activeRequest);
936
+ else if (activeRequest) this.startQueuedRun(sessionRun, activeRequest).catch(() => void 0);
937
+ return {
938
+ sessionId: session.sessionId,
939
+ userMessageId: queuedRequest.request.message.id,
940
+ runId: activeRequest?.id === queuedRequest.id ? activeRequest.runId : null,
941
+ correlationId: request.correlationId
942
+ };
943
+ };
944
+ startQueuedRun = async (sessionRun, activeRequest) => {
945
+ const { request, session } = activeRequest;
919
946
  const requestRunStartedAt = (/* @__PURE__ */ new Date()).toISOString();
920
- const activeRun = (() => {
921
- try {
922
- return sessionRun.beginRun();
923
- } catch (error) {
924
- stopPublishingRunStatus();
925
- throw error;
926
- }
927
- })();
947
+ const model = request.model ?? session.model ?? this.configManager.getDefaultModel();
928
948
  const { modelSource, spec } = resolveRunSpec({
929
- agentManager: this.agentManager,
930
- configManager: this.configManager,
949
+ defaultAgentId: this.agentManager.getDefaultAgentId(),
950
+ model,
951
+ modelMaxTokens: this.configManager.getModelMaxTokens(model),
931
952
  request,
932
- runId: activeRun.runId,
953
+ runId: activeRequest.runId,
933
954
  session
934
955
  });
935
956
  const message = attachRunSpecMetadata({
936
- message: baseMessage,
957
+ message: {
958
+ ...request.message,
959
+ sessionId: session.sessionId,
960
+ timestamp: requestRunStartedAt
961
+ },
937
962
  modelSource,
938
963
  request,
939
964
  session,
@@ -945,32 +970,44 @@ var AgentRunRequestManager = class {
945
970
  sessionId: session.sessionId,
946
971
  message
947
972
  };
948
- sessionRun.inbox.enqueue(message);
949
- const runtime = this.agentRuntimeManager.getOrCreate({
950
- agentRuntimeId: session.agentRuntimeId,
951
- session,
952
- sessionRun
973
+ const messageSentEvent = createMessageSentEvent({
974
+ sessionId: session.sessionId,
975
+ message,
976
+ correlationId: request.correlationId
953
977
  });
954
- const contextBlocks = await this.contextProviderManager.buildContext(providerRequest);
955
- const tools = await this.toolProviderManager.buildTools(providerRequest);
956
- this.startRuntimeRun({
957
- options: {
958
- contextBlocks,
978
+ await sessionRun.applyEvents([messageSentEvent]);
979
+ this.publishNcpEvent(messageSentEvent);
980
+ try {
981
+ const runtime = this.agentRuntimeManager.getOrCreate({
982
+ agentRuntimeId: session.agentRuntimeId,
959
983
  session,
984
+ sessionRun
985
+ });
986
+ const contextBlocks = await this.contextProviderManager.buildContext(providerRequest);
987
+ const tools = await this.toolProviderManager.buildTools(providerRequest);
988
+ sessionRun.inbox.enqueue(message);
989
+ this.startRuntimeRun({
990
+ options: {
991
+ contextBlocks,
992
+ session,
993
+ sessionRun,
994
+ signal: activeRequest.signal,
995
+ tools
996
+ },
997
+ requestRunStartedAt,
998
+ runtime,
999
+ spec
1000
+ });
1001
+ } catch (error) {
1002
+ await this.publishRunStartupFailure({
1003
+ error,
1004
+ requestRunStartedAt,
960
1005
  sessionRun,
961
- signal: activeRun.signal,
962
- tools
963
- },
964
- requestRunStartedAt,
965
- runtime,
966
- spec
967
- });
968
- return {
969
- sessionId: session.sessionId,
970
- userMessageId: message.id,
971
- runId: spec.runId,
972
- correlationId: request.correlationId
973
- };
1006
+ spec
1007
+ });
1008
+ this.startNextQueuedRun(sessionRun);
1009
+ throw error;
1010
+ }
974
1011
  };
975
1012
  startRuntimeRun = (params) => {
976
1013
  const { options, requestRunStartedAt, runtime, spec } = params;
@@ -979,7 +1016,7 @@ var AgentRunRequestManager = class {
979
1016
  let executionMetadataSeen = false;
980
1017
  let runtimeFailed = false;
981
1018
  let runStartedAt = requestRunStartedAt;
982
- lastValueFrom(from(runtime.run(spec, options)).pipe(tap((event) => {
1019
+ lastValueFrom(from(runtime.run(spec, options)).pipe(filter((event) => event.type !== NcpEventType.MessageSent), tap((event) => {
983
1020
  const eventsToPublish = [];
984
1021
  if (event.type === NcpEventType.RunError) runtimeFailed = true;
985
1022
  if (hasAiExecutionMetadata(event)) executionMetadataSeen = true;
@@ -996,10 +1033,7 @@ var AgentRunRequestManager = class {
996
1033
  messageCompletedSeen = true;
997
1034
  }
998
1035
  eventsToPublish.push(event);
999
- for (const eventToPublish of eventsToPublish) this.eventBus.emit(eventKeys.ncpEvent, eventToPublish, {
1000
- emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
1001
- source: "agent-run-request"
1002
- });
1036
+ eventsToPublish.forEach(this.publishNcpEvent);
1003
1037
  }), catchError(async (error) => {
1004
1038
  runtimeFailed = true;
1005
1039
  if (!executionMetadataSeen) {
@@ -1008,10 +1042,7 @@ var AgentRunRequestManager = class {
1008
1042
  sessionId: session.sessionId
1009
1043
  });
1010
1044
  await sessionRun.applyEvents([metadataEvent]);
1011
- this.eventBus.emit(eventKeys.ncpEvent, metadataEvent, {
1012
- emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
1013
- source: "agent-run-request"
1014
- });
1045
+ this.publishNcpEvent(metadataEvent);
1015
1046
  }
1016
1047
  const event = createSyntheticRunErrorEvent({
1017
1048
  error,
@@ -1021,19 +1052,75 @@ var AgentRunRequestManager = class {
1021
1052
  startedAt: runStartedAt
1022
1053
  });
1023
1054
  await sessionRun.applyEvents([event]);
1024
- this.eventBus.emit(eventKeys.ncpEvent, event, {
1025
- emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
1026
- source: "agent-run-request"
1027
- });
1028
- })), { defaultValue: void 0 }).finally(() => {
1029
- if (!runtimeFailed) return;
1030
- this.agentRuntimeManager.disposeRuntime({
1055
+ this.publishNcpEvent(event);
1056
+ })), { defaultValue: void 0 }).finally(async () => {
1057
+ if (runtimeFailed) await this.agentRuntimeManager.disposeRuntime({
1031
1058
  agentRuntimeId: session.agentRuntimeId,
1032
1059
  session,
1033
1060
  sessionRun
1034
1061
  }).catch(() => void 0);
1062
+ this.startNextQueuedRun(sessionRun);
1063
+ });
1064
+ };
1065
+ publishRunStartupFailure = async (params) => {
1066
+ const { error, requestRunStartedAt, sessionRun, spec } = params;
1067
+ const metadataEvent = createUnavailableAiExecutionMetadataEvent({
1068
+ spec,
1069
+ sessionId: sessionRun.sessionId
1070
+ });
1071
+ const errorEvent = createSyntheticRunErrorEvent({
1072
+ error,
1073
+ runId: spec.runId,
1074
+ sessionId: sessionRun.sessionId,
1075
+ correlationId: spec.correlationId,
1076
+ startedAt: requestRunStartedAt
1035
1077
  });
1078
+ await sessionRun.applyEvents([metadataEvent, errorEvent]);
1079
+ this.publishNcpEvent(metadataEvent);
1080
+ this.publishNcpEvent(errorEvent);
1036
1081
  };
1082
+ startNextQueuedRun = (sessionRun) => {
1083
+ const nextRequest = sessionRun.beginNextRun();
1084
+ if (!nextRequest) return;
1085
+ this.publishRunQueueUpdated(sessionRun.sessionId);
1086
+ this.startQueuedRun(sessionRun, nextRequest).catch(() => void 0);
1087
+ };
1088
+ observeSessionRun = (sessionRun) => {
1089
+ if (this.observedSessionRuns.has(sessionRun)) return;
1090
+ this.observedSessionRuns.add(sessionRun);
1091
+ const stop = sessionRun.onStatusChange((status) => {
1092
+ this.eventBus.emit(eventKeys.sessionRunStatus, {
1093
+ sessionKey: sessionRun.sessionId,
1094
+ status
1095
+ }, {
1096
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
1097
+ source: "agent-run-request"
1098
+ });
1099
+ });
1100
+ this.cleanups.push(() => {
1101
+ stop();
1102
+ this.observedSessionRuns.delete(sessionRun);
1103
+ });
1104
+ };
1105
+ publishRunQueueUpdated = (sessionId) => {
1106
+ this.eventBus.emit(eventKeys.sessionRunQueueUpdated, { sessionKey: sessionId }, {
1107
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
1108
+ source: "agent-run-request"
1109
+ });
1110
+ };
1111
+ publishNcpEvent = (event) => {
1112
+ this.eventBus.emit(eventKeys.ncpEvent, event, {
1113
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
1114
+ source: "agent-run-request"
1115
+ });
1116
+ };
1117
+ toQueuedInput = (queuedRequest) => ({
1118
+ id: queuedRequest.id,
1119
+ sessionId: queuedRequest.request.message.sessionId,
1120
+ enqueuedAt: queuedRequest.enqueuedAt,
1121
+ message: structuredClone(queuedRequest.request.message),
1122
+ metadata: structuredClone(queuedRequest.request.metadata ?? {})
1123
+ });
1037
1124
  getOrCreateSessionForRequest = async (request) => {
1038
1125
  const sessionMaterialization = readSessionMaterialization(request.metadata ?? {});
1039
1126
  if (sessionMaterialization && (request.sessionId || request.peerId)) throw new Error("session_materialization requires a new session request.");
@@ -7344,6 +7431,7 @@ var SessionRun = class {
7344
7431
  inbox = new MessageInbox();
7345
7432
  sessionId;
7346
7433
  statusListeners = /* @__PURE__ */ new Set();
7434
+ queuedRequests = [];
7347
7435
  activeRunId = null;
7348
7436
  activeRunController = null;
7349
7437
  constructor(seed, stateManager = new DefaultNcpAgentConversationStateManager()) {
@@ -7369,32 +7457,52 @@ var SessionRun = class {
7369
7457
  this.statusListeners.delete(listener);
7370
7458
  };
7371
7459
  };
7372
- beginRun = () => {
7373
- if (this.activeRunId) throw new Error(`Session ${this.sessionId} already has an active run.`);
7374
- const wasRunning = this.isRunning();
7375
- const runId = `agent-run-${randomUUID()}`;
7460
+ enqueueRequest = (request, session) => {
7461
+ const wasBusy = this.isBusy();
7462
+ const queuedRequest = {
7463
+ id: `queued-input-${randomUUID()}`,
7464
+ runId: `agent-run-${randomUUID()}`,
7465
+ enqueuedAt: (/* @__PURE__ */ new Date()).toISOString(),
7466
+ request: structuredClone(request),
7467
+ session: structuredClone(session)
7468
+ };
7469
+ this.queuedRequests.push(queuedRequest);
7470
+ this.emitStatusChangeIfNeeded(wasBusy);
7471
+ return structuredClone(queuedRequest);
7472
+ };
7473
+ listQueuedRequests = () => structuredClone(this.queuedRequests);
7474
+ removeQueuedRequest = (queuedRequestId) => {
7475
+ const index = this.queuedRequests.findIndex(({ id }) => id === queuedRequestId);
7476
+ if (index < 0) return null;
7477
+ const wasBusy = this.isBusy();
7478
+ const [removed] = this.queuedRequests.splice(index, 1);
7479
+ this.emitStatusChangeIfNeeded(wasBusy);
7480
+ return removed ? structuredClone(removed) : null;
7481
+ };
7482
+ beginNextRun = () => {
7483
+ if (this.activeRunId) return null;
7484
+ const wasBusy = this.isBusy();
7485
+ const queuedRequest = this.queuedRequests.shift();
7486
+ if (!queuedRequest) return null;
7376
7487
  const controller = new AbortController();
7377
- this.activeRunId = runId;
7488
+ this.activeRunId = queuedRequest.runId;
7378
7489
  this.activeRunController = controller;
7379
- this.emitStatusChangeIfNeeded(wasRunning);
7490
+ this.emitStatusChangeIfNeeded(wasBusy);
7380
7491
  return {
7381
- runId,
7492
+ ...structuredClone(queuedRequest),
7382
7493
  signal: controller.signal
7383
7494
  };
7384
7495
  };
7385
7496
  abortRun = (runId, reason) => {
7386
7497
  if (!this.activeRunId || !this.activeRunController) return false;
7387
7498
  if (runId && this.activeRunId !== runId) return false;
7388
- const wasRunning = this.isRunning();
7389
7499
  this.activeRunController.abort(reason);
7390
- this.activeRunController = null;
7391
- this.activeRunId = null;
7392
- this.emitStatusChangeIfNeeded(wasRunning);
7393
7500
  return true;
7394
7501
  };
7395
7502
  isRunning = () => this.activeRunId !== null;
7503
+ isBusy = () => this.isRunning() || this.queuedRequests.length > 0;
7396
7504
  dispose = () => {
7397
- const wasRunning = this.isRunning();
7505
+ const wasBusy = this.isBusy();
7398
7506
  this.activeRunController?.abort({
7399
7507
  code: "abort-error",
7400
7508
  message: "Session run owner was disposed; the current run was cancelled.",
@@ -7402,33 +7510,46 @@ var SessionRun = class {
7402
7510
  });
7403
7511
  this.activeRunController = null;
7404
7512
  this.activeRunId = null;
7405
- this.emitStatusChangeIfNeeded(wasRunning);
7513
+ this.queuedRequests.length = 0;
7514
+ this.emitStatusChangeIfNeeded(wasBusy);
7406
7515
  this.statusListeners.clear();
7407
7516
  };
7408
7517
  applyRunEvents = (events) => {
7409
- const wasRunning = this.isRunning();
7518
+ const wasBusy = this.isBusy();
7410
7519
  for (const event of events) {
7411
7520
  if (event.type === NcpEventType.RunStarted && event.payload.runId) this.activeRunId = event.payload.runId;
7412
- if ((event.type === NcpEventType.RunFinished || event.type === NcpEventType.RunError) && (!event.payload.runId || event.payload.runId === this.activeRunId)) {
7521
+ if ((event.type === NcpEventType.MessageAbort || event.type === NcpEventType.RunFinished || event.type === NcpEventType.RunError) && (!event.payload.runId || event.payload.runId === this.activeRunId)) {
7413
7522
  this.activeRunId = null;
7414
7523
  this.activeRunController = null;
7415
7524
  }
7416
7525
  }
7417
- this.emitStatusChangeIfNeeded(wasRunning);
7526
+ this.emitStatusChangeIfNeeded(wasBusy);
7418
7527
  };
7419
- emitStatusChangeIfNeeded = (wasRunning) => {
7420
- const running = this.isRunning();
7421
- if (running === wasRunning) return;
7422
- for (const listener of [...this.statusListeners]) listener(running ? "running" : "idle");
7528
+ emitStatusChangeIfNeeded = (wasBusy) => {
7529
+ const busy = this.isBusy();
7530
+ if (busy === wasBusy) return;
7531
+ for (const listener of [...this.statusListeners]) listener(busy ? "running" : "idle");
7423
7532
  };
7424
7533
  };
7425
7534
  var SessionRunManager = class {
7426
7535
  runs = /* @__PURE__ */ new Map();
7536
+ pendingCreations = /* @__PURE__ */ new Map();
7427
7537
  constructor(sessionManager) {
7428
7538
  this.sessionManager = sessionManager;
7429
7539
  }
7430
7540
  getSessionRun = (sessionId) => this.runs.get(sessionId) ?? null;
7431
- isSessionRunning = (sessionId) => this.runs.get(sessionId.trim())?.isRunning() ?? false;
7541
+ isSessionRunning = (sessionId) => this.runs.get(sessionId.trim())?.isBusy() ?? false;
7542
+ getOrCreateSessionRun = async (sessionId) => {
7543
+ const existing = this.getSessionRun(sessionId);
7544
+ if (existing) return existing;
7545
+ const pending = this.pendingCreations.get(sessionId);
7546
+ if (pending) return await pending;
7547
+ const creation = this.createSessionRun(sessionId).finally(() => {
7548
+ this.pendingCreations.delete(sessionId);
7549
+ });
7550
+ this.pendingCreations.set(sessionId, creation);
7551
+ return await creation;
7552
+ };
7432
7553
  createSessionRun = async (sessionId) => {
7433
7554
  if (this.runs.has(sessionId)) throw new Error(`Session run already exists: ${sessionId}`);
7434
7555
  const run = new SessionRun({
@@ -7447,6 +7568,7 @@ var SessionRunManager = class {
7447
7568
  dispose = () => {
7448
7569
  for (const run of this.runs.values()) run.dispose();
7449
7570
  this.runs.clear();
7571
+ this.pendingCreations.clear();
7450
7572
  };
7451
7573
  };
7452
7574
  //#endregion
@@ -9494,7 +9616,6 @@ var NcpAgentRuntimeWrapper = class {
9494
9616
  const messages = sessionRun.inbox.drain();
9495
9617
  let executionMetadataSeen = false;
9496
9618
  try {
9497
- for (const event of this.toMessageSentEvents(messages, spec, sessionRun.sessionId)) yield await this.applyEvent(sessionRun, event);
9498
9619
  const input = {
9499
9620
  sessionId: sessionRun.sessionId,
9500
9621
  runId: spec.runId,
@@ -9545,15 +9666,6 @@ var NcpAgentRuntimeWrapper = class {
9545
9666
  });
9546
9667
  return this.runtime;
9547
9668
  };
9548
- toMessageSentEvents = (messages, spec, sessionId) => messages.map((message) => ({
9549
- occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
9550
- type: NcpEventType.MessageSent,
9551
- payload: {
9552
- sessionId,
9553
- message,
9554
- correlationId: spec.correlationId
9555
- }
9556
- }));
9557
9669
  buildMetadata = (session, spec) => ({
9558
9670
  ...session.metadata,
9559
9671
  agentId: spec.agentId,
@@ -10386,21 +10498,21 @@ function readString$2(value) {
10386
10498
  return typeof value === "string" && value.trim() ? value.trim() : null;
10387
10499
  }
10388
10500
  function readWorkspaceReferences(metadata) {
10389
- const rawTokens = metadata?.[CHAT_INLINE_TOKENS_METADATA_KEY];
10390
- if (!Array.isArray(rawTokens)) return [];
10501
+ const raw = metadata?.[CHAT_INLINE_TOKENS_METADATA_KEY];
10502
+ const rawTokens = Array.isArray(raw) ? raw : isRecord$2(raw) && raw.schemaVersion === CHAT_INLINE_TOKENS_SCHEMA_VERSION && Array.isArray(raw.items) ? raw.items : null;
10503
+ if (!rawTokens) return [];
10391
10504
  const references = [];
10392
10505
  const seen = /* @__PURE__ */ new Set();
10393
10506
  for (const rawToken of rawTokens) {
10394
10507
  if (!isRecord$2(rawToken)) continue;
10395
- const token = rawToken;
10396
- const kind = token.kind === CHAT_WORKSPACE_FILE_TOKEN_KIND ? CHAT_WORKSPACE_FILE_TOKEN_KIND : token.kind === CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND ? CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND : null;
10397
- const key = readString$2(token.key);
10508
+ const kind = rawToken.kind === CHAT_WORKSPACE_FILE_TOKEN_KIND ? CHAT_WORKSPACE_FILE_TOKEN_KIND : rawToken.kind === CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND ? CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND : null;
10509
+ const key = readString$2(rawToken.key);
10398
10510
  if (!kind || !key || seen.has(`${kind}:${key}`)) continue;
10399
10511
  seen.add(`${kind}:${key}`);
10400
10512
  references.push({
10401
10513
  kind,
10402
10514
  key,
10403
- label: readString$2(token.label) ?? key
10515
+ label: readString$2(rawToken.label) ?? key
10404
10516
  });
10405
10517
  }
10406
10518
  return references;