@nextclaw/kernel 0.1.14 → 0.1.15-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3,21 +3,22 @@ import { createRequire } from "node:module";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import { EventBus, Ingress, eventKeys, ingressKeys } from "@nextclaw/shared";
5
5
  import { NcpEventType, isHiddenNcpMessage, readAssistantReasoningNormalizationMode, readAssistantReasoningNormalizationModeFromMetadata, sanitizeAssistantReplyTags } from "@nextclaw/ncp";
6
- import { DefaultNcpAgentRuntime, LocalAssetStore, buildAssetContentPath, buildNcpUserContent, buildOpenAiFunctionTool } from "@nextclaw/ncp-agent-runtime";
7
- import fs, { appendFileSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
8
- import path, { basename, delimiter, dirname, join, relative, resolve } from "node:path";
9
- import { AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, CONTEXT_COMPACTION_METADATA_KEY, ChannelManager, ChannelManager as ChannelManager$1, CommandRegistry, ConfigSchema, ContextBuilder, ContextCompactionService, ContextWindowBudgetService, CronService, CronTool, DEFAULT_SESSION_SEARCH_LIMIT, EditFileTool, ExecTool, ExtensionToolAdapter, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, LiteLLMProvider, MAX_SESSION_SEARCH_LIMIT, MemoryGetTool, MemorySearchTool, MessageBus, MessageTool, ProviderRegistry, ReadFileTool, RequestedSkillsMetadataReader, SessionManager, SessionSearchManager, SkillsLoader, Tool, ToolRegistry, WebFetchTool, WebSearchTool, WriteFileTool, buildCompressingCompactionCheckpoint, buildConfigSchema, buildContextWindowSnapshot, buildMinimalSystemExecutionPrompt, buildReloadPlan, buildSessionRequestToolResult, buildToolCatalogEntries, createAssistantStreamDeltaControlMessage, createAssistantStreamResetControlMessage, createCompletedSessionRequest, createFailedSessionRequest, createRunningSessionRequest, createToolExecutionContext, createTypingStopControlMessage, diffConfigPaths, ensureDir, expandHome, findEffectiveAgentProfile, getAppLogger, getConfigPath, getDataDir, getDataPath, getSessionsPath, getWorkspacePath, loadConfig, normalizeInlineSecretRefs, parseAgentScopedSessionKey, parseThinkingLevel, readCompressedContextCompactionCheckpoint, readOptionalString, readParentSessionId, readSessionProjectRoot, redactConfigObject, resolveConfigSecrets, resolveDefaultAgentProfileId, resolveProviderRuntime, resolveSessionWorkspacePath, resolveThinkingLevel, saveConfig, summarizeSessionRequestTask, toDisposable } from "@nextclaw/core";
10
- import { discoverPluginStatusReport, getPackageManifestExtensions, getPluginChannelBindings, getPluginUiMetadataFromRegistry, loadOpenClawPlugins, loadOpenClawPluginsProgressively, loadPluginManifest, mergePluginConfigView, toPluginConfigView } from "@nextclaw/openclaw-compat";
6
+ import { DefaultNcpAgentRuntime, LocalAssetStore, buildAssetContentPath, buildNcpUserContent, buildOpenAiFunctionTool, ncpMessageToOpenAiMessages } from "@nextclaw/ncp-agent-runtime";
7
+ import { appendFileSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
8
+ import { basename, delimiter, dirname, join, relative, resolve } from "node:path";
9
+ import { AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, CONTEXT_COMPACTION_METADATA_KEY, ChannelManager, ChannelManager as ChannelManager$1, CommandRegistry, ConfigSchema, ContextBuilder, ContextCompactionService, ContextWindowBudgetService, CronService, CronTool, DEFAULT_SESSION_SEARCH_LIMIT, EditFileTool, ExecTool, ExtensionToolAdapter, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, LiteLLMProvider, MAX_SESSION_SEARCH_LIMIT, MemoryGetTool, MemorySearchTool, MessageBus, MessageTool, ProviderRegistry, ReadFileTool, RequestedSkillsMetadataReader, SessionManager, SessionSearchManager, SkillsLoader, Tool, ToolRegistry, WebFetchTool, WebSearchTool, WriteFileTool, buildCompressingCompactionCheckpoint, buildConfigSchema, buildContextWindowSnapshot, buildMinimalSystemExecutionPrompt, buildReloadPlan, buildSessionRequestToolResult, buildToolCatalogEntries, createAssistantStreamDeltaControlMessage, createAssistantStreamResetControlMessage, createCompletedSessionRequest, createFailedSessionRequest, createRunningSessionRequest, createToolExecutionContext, createTypingStopControlMessage, diffConfigPaths, ensureDir, expandHome, findEffectiveAgentProfile, getConfigPath, getDataDir, getDataPath, getSessionsPath, getWorkspacePath, loadConfig, mergeExtensionConfigView, modelSupportsVision, normalizeInlineSecretRefs, parseAgentScopedSessionKey, parseThinkingLevel, readCompressedContextCompactionCheckpoint, readOptionalString, readParentSessionId, readSessionProjectRoot, redactConfigObject, resolveConfigSecrets, resolveDefaultAgentProfileId, resolveProviderRuntime, resolveSessionWorkspacePath, resolveThinkingLevel, saveConfig, summarizeSessionRequestTask, toDisposable, toExtensionConfigView } from "@nextclaw/core";
11
10
  import { spawn } from "node:child_process";
12
11
  import { access, appendFile, mkdir, readFile, readdir, rename, rm, unlink, writeFile } from "node:fs/promises";
13
12
  import { fileURLToPath } from "node:url";
14
- import { BUILTIN_PROVIDER_PLUGINS, builtinProviderIds } from "@nextclaw/runtime";
13
+ import { BUILTIN_PROVIDER_PLUGINS } from "@nextclaw/runtime";
15
14
  import { McpRegistryService, McpServerLifecycleManager } from "@nextclaw/mcp";
16
15
  import { McpNcpToolRegistryAdapter } from "@nextclaw/ncp-mcp";
17
16
  import { DefaultNcpAgentConversationStateManager } from "@nextclaw/ncp-toolkit";
18
17
  import { parse } from "yaml";
18
+ import { catchError, from, lastValueFrom, tap } from "rxjs";
19
19
  import { HttpRuntimeConfigResolver, HttpRuntimeNcpAgentRuntime } from "@nextclaw/nextclaw-ncp-runtime-http-client";
20
20
  import { StdioRuntimeConfigResolver, StdioRuntimeNcpAgentRuntime, probeStdioRuntime } from "@nextclaw/nextclaw-ncp-runtime-stdio-client";
21
+ import { DefaultNcpAgentRuntime as DefaultNcpAgentRuntime$1 } from "@nextclaw/ncp-agent-runtime-next";
21
22
  //#region src/managers/agent.manager.ts
22
23
  var AgentManager = class {
23
24
  listAgents = () => {
@@ -50,54 +51,6 @@ var AgentManager = class {
50
51
  };
51
52
  //#endregion
52
53
  //#region src/utils/session-run.utils.ts
53
- var AsyncEventQueue = class {
54
- items = [];
55
- waiters = [];
56
- closed = false;
57
- push = (item) => {
58
- const waiter = this.waiters.shift();
59
- if (waiter) {
60
- waiter({
61
- value: item,
62
- done: false
63
- });
64
- return;
65
- }
66
- this.items.push(item);
67
- };
68
- close = () => {
69
- if (this.closed) return;
70
- this.closed = true;
71
- while (this.waiters.length > 0) this.waiters.shift()?.({
72
- value: void 0,
73
- done: true
74
- });
75
- };
76
- iterate = () => {
77
- const nextItem = this.next;
78
- return (async function* iterateQueuedEvents() {
79
- while (true) {
80
- const next = await nextItem();
81
- if (next.done) return;
82
- yield next.value;
83
- }
84
- })();
85
- };
86
- next = async () => {
87
- const item = this.items.shift();
88
- if (item) return {
89
- value: item,
90
- done: false
91
- };
92
- if (this.closed) return {
93
- value: void 0,
94
- done: true
95
- };
96
- return new Promise((resolve) => {
97
- this.waiters.push(resolve);
98
- });
99
- };
100
- };
101
54
  function readString$8(value) {
102
55
  return (typeof value === "string" ? value.trim() : "") || void 0;
103
56
  }
@@ -470,7 +423,7 @@ var LlmUsageManager = class {
470
423
  };
471
424
  //#endregion
472
425
  //#region src/utils/ncp-message-bridge.utils.ts
473
- function normalizeString(value) {
426
+ function normalizeString$1(value) {
474
427
  if (typeof value !== "string") return null;
475
428
  const trimmed = value.trim();
476
429
  return trimmed.length > 0 ? trimmed : null;
@@ -567,80 +520,6 @@ function toLegacyMessages(messages, options = {}) {
567
520
  return legacyMessages;
568
521
  }
569
522
  //#endregion
570
- //#region src/features/native-runtime/utils/nextclaw-ncp-session-preferences.utils.ts
571
- function normalizeOptionalString(value) {
572
- if (typeof value !== "string") return;
573
- const trimmed = value.trim();
574
- return trimmed.length > 0 ? trimmed : void 0;
575
- }
576
- function readMetadataModel(metadata) {
577
- const candidates = [
578
- metadata.model,
579
- metadata.llm_model,
580
- metadata.agent_model,
581
- metadata.session_model
582
- ];
583
- for (const candidate of candidates) {
584
- const normalized = normalizeOptionalString(candidate);
585
- if (normalized) return normalized;
586
- }
587
- return null;
588
- }
589
- function readMetadataThinking(metadata) {
590
- const candidates = [
591
- metadata.thinking,
592
- metadata.thinking_level,
593
- metadata.thinkingLevel,
594
- metadata.thinking_effort,
595
- metadata.thinkingEffort
596
- ];
597
- for (const candidate of candidates) {
598
- if (typeof candidate !== "string") continue;
599
- const normalized = candidate.trim().toLowerCase();
600
- if (!normalized) continue;
601
- if (normalized === "clear" || normalized === "reset" || normalized === "off!") return "__clear__";
602
- const level = parseThinkingLevel(normalized);
603
- if (level) return level;
604
- }
605
- return null;
606
- }
607
- function resolveEffectiveModel(params) {
608
- const { fallbackModel, requestMetadata } = params;
609
- const metadata = structuredClone(params.sessionMetadata);
610
- if (requestMetadata.clear_model === true || requestMetadata.reset_model === true) delete metadata.preferred_model;
611
- const inboundModel = readMetadataModel(requestMetadata);
612
- if (inboundModel) metadata.preferred_model = inboundModel;
613
- return {
614
- metadata,
615
- model: normalizeOptionalString(metadata.preferred_model) ?? fallbackModel
616
- };
617
- }
618
- function syncSessionThinkingPreference(params) {
619
- const { requestMetadata } = params;
620
- const metadata = structuredClone(params.sessionMetadata);
621
- if (requestMetadata.clear_thinking === true || requestMetadata.reset_thinking === true) delete metadata.preferred_thinking;
622
- const inboundThinking = readMetadataThinking(requestMetadata);
623
- if (inboundThinking === "__clear__") {
624
- delete metadata.preferred_thinking;
625
- return metadata;
626
- }
627
- if (inboundThinking) metadata.preferred_thinking = inboundThinking;
628
- return metadata;
629
- }
630
- function resolveSessionChannelContext(params) {
631
- const { requestMetadata } = params;
632
- const metadata = structuredClone(params.sessionMetadata);
633
- const channel = normalizeOptionalString(requestMetadata.channel) ?? normalizeOptionalString(metadata.last_channel) ?? "ui";
634
- const chatId = normalizeOptionalString(requestMetadata.chatId) ?? normalizeOptionalString(requestMetadata.chat_id) ?? normalizeOptionalString(metadata.last_to) ?? "web-ui";
635
- metadata.last_channel = channel;
636
- metadata.last_to = chatId;
637
- return {
638
- channel,
639
- chatId,
640
- metadata
641
- };
642
- }
643
- //#endregion
644
523
  //#region src/features/native-runtime/utils/nextclaw-ncp-current-turn.utils.ts
645
524
  function isTextLikePart(part) {
646
525
  return part.type === "text" || part.type === "rich-text";
@@ -833,17 +712,17 @@ function projectNcpMessagesWithContextCompaction(params) {
833
712
  }, ...sessionMessages.slice(checkpointIndex + 1).filter((message) => !readCompactionSummary(message)).map((message) => structuredClone(message))];
834
713
  }
835
714
  //#endregion
836
- //#region src/features/context-compaction/managers/context-compaction.manager.ts
715
+ //#region src/features/context-compaction/services/context-compaction-preflight.service.ts
837
716
  const SUMMARY_MAX_TOKENS = 4e3;
838
717
  const SUMMARY_SOURCE_MAX_CHARS = 12e4;
839
718
  function readRequestedAgentId$1(metadata) {
840
- return normalizeString(metadata.agent_id)?.toLowerCase() ?? normalizeString(metadata.agentId)?.toLowerCase() ?? null;
719
+ return normalizeString$1(metadata.agent_id)?.toLowerCase() ?? normalizeString$1(metadata.agentId)?.toLowerCase() ?? null;
841
720
  }
842
721
  function resolveCompactionProfile(params) {
843
722
  const { config, requestMetadata, storedAgentId } = params;
844
723
  const { defaults } = config.agents;
845
724
  const defaultAgentId = resolveDefaultAgentProfileId(config);
846
- const profile = findEffectiveAgentProfile(config, normalizeString(storedAgentId)?.toLowerCase() ?? readRequestedAgentId$1(requestMetadata) ?? defaultAgentId) ?? findEffectiveAgentProfile(config, defaultAgentId);
725
+ const profile = findEffectiveAgentProfile(config, normalizeString$1(storedAgentId)?.toLowerCase() ?? readRequestedAgentId$1(requestMetadata) ?? defaultAgentId) ?? findEffectiveAgentProfile(config, defaultAgentId);
847
726
  return {
848
727
  contextTokens: profile?.contextTokens ?? defaults.contextTokens,
849
728
  model: profile?.model ?? defaults.model,
@@ -898,20 +777,20 @@ function buildContextWindowSnapshotForMessages(contextWindowBudgetService, param
898
777
  totalContextTokens: contextTokens
899
778
  });
900
779
  }
901
- var ContextWindowPreviewManager = class {
780
+ var ContextCompactionPreflightService = class {
781
+ compactionService = new ContextCompactionService();
902
782
  contextWindowBudgetService = new ContextWindowBudgetService();
903
783
  constructor(options) {
904
784
  this.options = options;
905
785
  }
906
786
  preview = (params) => {
907
787
  const { requestMetadata, sessionId, sessionMessages, storedAgentId, storedMetadata } = params;
908
- const metadata = storedMetadata;
909
788
  const profile = resolveCompactionProfile({
910
789
  config: this.options.configManager.loadConfig(),
911
790
  requestMetadata,
912
791
  storedAgentId
913
792
  });
914
- const existingCheckpoint = readCompressedContextCompactionCheckpoint(metadata[CONTEXT_COMPACTION_METADATA_KEY]);
793
+ const existingCheckpoint = readCompressedContextCompactionCheckpoint(storedMetadata[CONTEXT_COMPACTION_METADATA_KEY]);
915
794
  return buildContextWindowSnapshotForMessages(this.contextWindowBudgetService, {
916
795
  checkpoint: existingCheckpoint,
917
796
  contextTokens: profile.contextTokens,
@@ -920,39 +799,6 @@ var ContextWindowPreviewManager = class {
920
799
  sessionMessages
921
800
  });
922
801
  };
923
- };
924
- var ContextCompactionManager = class {
925
- compactionService = new ContextCompactionService();
926
- contextWindowBudgetService = new ContextWindowBudgetService();
927
- constructor(options) {
928
- this.options = options;
929
- }
930
- runLivePreflight = async function* (params) {
931
- const sessionRunManager = this.options.sessionRunManager;
932
- const { input, session } = params;
933
- const beginResult = this.begin({
934
- inputMessages: input.messages,
935
- requestMetadata: input.metadata ?? {},
936
- sessionId: input.sessionId,
937
- sessionMessages: session.stateManager.getSnapshot().messages,
938
- ...session.agentId ? { storedAgentId: session.agentId } : {},
939
- storedMetadata: input.metadata ?? {}
940
- });
941
- yield* this.applyLivePreflightResult({
942
- input,
943
- result: beginResult,
944
- session,
945
- sessionRunManager
946
- });
947
- if (!beginResult.pendingCompaction) return;
948
- const finishResult = await this.finish(beginResult.pendingCompaction);
949
- yield* this.applyLivePreflightResult({
950
- input,
951
- result: finishResult,
952
- session,
953
- sessionRunManager
954
- });
955
- };
956
802
  begin = (params) => {
957
803
  const { inputMessages, requestMetadata, sessionId, sessionMessages, storedAgentId, storedMetadata } = params;
958
804
  const profile = resolveCompactionProfile({
@@ -1003,7 +849,6 @@ var ContextCompactionManager = class {
1003
849
  pendingCompaction: plan && checkpoint ? {
1004
850
  checkpoint,
1005
851
  contextTokens,
1006
- inputMessageIds: inputMessages.map((message) => message.id),
1007
852
  model: profile.model,
1008
853
  plan,
1009
854
  reservedContextTokens,
@@ -1075,6 +920,61 @@ var ContextCompactionManager = class {
1075
920
  if (!summary) throw new Error("context compaction summary is empty");
1076
921
  return summary;
1077
922
  };
923
+ };
924
+ //#endregion
925
+ //#region src/features/context-compaction/managers/context-compaction.manager.ts
926
+ var ContextWindowPreviewManager = class {
927
+ preflightService;
928
+ constructor(options) {
929
+ this.options = options;
930
+ this.preflightService = new ContextCompactionPreflightService({ configManager: options.configManager });
931
+ }
932
+ preview = (params) => {
933
+ const { requestMetadata, sessionId, sessionMessages, storedAgentId, storedMetadata } = params;
934
+ return this.preflightService.preview({
935
+ requestMetadata,
936
+ sessionId,
937
+ sessionMessages,
938
+ storedAgentId,
939
+ storedMetadata
940
+ });
941
+ };
942
+ };
943
+ var ContextCompactionManager = class {
944
+ preflightService;
945
+ constructor(options) {
946
+ this.options = options;
947
+ this.preflightService = new ContextCompactionPreflightService({
948
+ configManager: options.configManager,
949
+ providerManager: options.providerManager
950
+ });
951
+ }
952
+ runLivePreflight = async function* (params) {
953
+ const sessionRunManager = this.options.sessionRunManager;
954
+ const { input, session } = params;
955
+ const beginResult = this.preflightService.begin({
956
+ inputMessages: input.messages,
957
+ requestMetadata: input.metadata ?? {},
958
+ sessionId: input.sessionId,
959
+ sessionMessages: session.stateManager.getSnapshot().messages,
960
+ ...session.agentId ? { storedAgentId: session.agentId } : {},
961
+ storedMetadata: input.metadata ?? {}
962
+ });
963
+ yield* this.applyLivePreflightResult({
964
+ input,
965
+ result: beginResult,
966
+ session,
967
+ sessionRunManager
968
+ });
969
+ if (!beginResult.pendingCompaction) return;
970
+ const finishResult = await this.preflightService.finish(beginResult.pendingCompaction);
971
+ yield* this.applyLivePreflightResult({
972
+ input,
973
+ result: finishResult,
974
+ session,
975
+ sessionRunManager
976
+ });
977
+ };
1078
978
  applyLivePreflightResult = async function* (params) {
1079
979
  const { input, result, session, sessionRunManager } = params;
1080
980
  if (Object.keys(result.metadataPatch).length > 0) await sessionRunManager.updateSessionMetadata(session.sessionId, result.metadataPatch);
@@ -1107,6 +1007,46 @@ var ContextCompactionManager = class {
1107
1007
  };
1108
1008
  };
1109
1009
  //#endregion
1010
+ //#region src/features/context-compaction/utils/context-window-event.utils.ts
1011
+ function readContextWindowEventSessionId(event) {
1012
+ const payload = "payload" in event ? event.payload : null;
1013
+ if (!payload || typeof payload !== "object") return null;
1014
+ return "sessionId" in payload && typeof payload.sessionId === "string" ? payload.sessionId.trim() || null : null;
1015
+ }
1016
+ function isContextWindowSnapshot(value) {
1017
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1018
+ }
1019
+ function createContextWindowSignature(value) {
1020
+ return JSON.stringify(value);
1021
+ }
1022
+ function shouldRefreshContextWindowDuringStream(event) {
1023
+ switch (event.type) {
1024
+ case NcpEventType.MessageTextStart:
1025
+ case NcpEventType.MessageTextDelta:
1026
+ case NcpEventType.MessageTextEnd:
1027
+ case NcpEventType.MessageReasoningStart:
1028
+ case NcpEventType.MessageReasoningDelta:
1029
+ case NcpEventType.MessageReasoningEnd:
1030
+ case NcpEventType.MessageToolCallStart:
1031
+ case NcpEventType.MessageToolCallArgs:
1032
+ case NcpEventType.MessageToolCallArgsDelta:
1033
+ case NcpEventType.MessageToolCallEnd:
1034
+ case NcpEventType.MessageToolCallResult: return true;
1035
+ default: return false;
1036
+ }
1037
+ }
1038
+ function shouldRefreshContextWindowImmediately(event) {
1039
+ switch (event.type) {
1040
+ case NcpEventType.MessageSent:
1041
+ case NcpEventType.MessageCompleted:
1042
+ case NcpEventType.MessageFailed:
1043
+ case NcpEventType.MessageAbort:
1044
+ case NcpEventType.RunFinished:
1045
+ case NcpEventType.RunError: return true;
1046
+ default: return false;
1047
+ }
1048
+ }
1049
+ //#endregion
1110
1050
  //#region src/managers/tool.manager.ts
1111
1051
  function toToolParams(args) {
1112
1052
  if (isRecord$7(args)) return args;
@@ -1229,36 +1169,94 @@ function resolveAgentHandoffDepth(metadata) {
1229
1169
  return Math.trunc(rawDepth);
1230
1170
  }
1231
1171
  //#endregion
1232
- //#region src/features/native-runtime/services/nextclaw-ncp-context-builder.service.ts
1233
- const TIME_HINT_TRIGGER_PATTERNS = [/\b(now|right now|current time|what time|today|tonight|tomorrow|yesterday|this morning|this afternoon|this evening|date)\b/i, /(现在|此刻|当前时间|现在几点|几点了|今天|今晚|今早|今晨|明天|昨天|日期)/];
1234
- function isRecord$6(value) {
1235
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1236
- }
1237
- function mergeInputMetadata(input) {
1238
- const messageMetadata = input.messages.slice().reverse().find((message) => isRecord$6(message.metadata))?.metadata;
1239
- return {
1240
- ...isRecord$6(messageMetadata) ? structuredClone(messageMetadata) : {},
1241
- ...isRecord$6(input.metadata) ? structuredClone(input.metadata) : {}
1242
- };
1243
- }
1244
- const REQUESTED_SKILLS_METADATA_READER = new RequestedSkillsMetadataReader();
1245
- function resolveRequestedToolNames(metadata) {
1246
- const rawValue = metadata.requested_tools ?? metadata.requestedTools;
1247
- if (!Array.isArray(rawValue)) return [];
1248
- return Array.from(new Set(rawValue.map((item) => normalizeString(item)).filter((item) => Boolean(item))));
1172
+ //#region src/features/native-runtime/utils/nextclaw-ncp-session-preferences.utils.ts
1173
+ function normalizeOptionalString(value) {
1174
+ if (typeof value !== "string") return;
1175
+ const trimmed = value.trim();
1176
+ return trimmed.length > 0 ? trimmed : void 0;
1249
1177
  }
1250
- function readRequestedAgentId(metadata) {
1251
- return normalizeString(metadata.agent_id)?.toLowerCase() ?? normalizeString(metadata.agentId)?.toLowerCase() ?? null;
1178
+ function readMetadataModel(metadata) {
1179
+ const candidates = [
1180
+ metadata.model,
1181
+ metadata.llm_model,
1182
+ metadata.agent_model,
1183
+ metadata.session_model
1184
+ ];
1185
+ for (const candidate of candidates) {
1186
+ const normalized = normalizeOptionalString(candidate);
1187
+ if (normalized) return normalized;
1188
+ }
1189
+ return null;
1252
1190
  }
1253
- function resolveAgentProfile(params) {
1254
- const { config, requestMetadata, storedAgentId } = params;
1255
- const { agents: { defaults }, search: searchConfig, tools: { restrictToWorkspace, exec: { timeout: execTimeoutSeconds } } } = config;
1256
- const defaultAgentId = resolveDefaultAgentProfileId(config);
1257
- const profile = findEffectiveAgentProfile(config, normalizeString(storedAgentId)?.toLowerCase() ?? readRequestedAgentId(requestMetadata) ?? defaultAgentId) ?? findEffectiveAgentProfile(config, defaultAgentId);
1258
- if (!profile) throw new Error(`default agent profile not found: ${defaultAgentId}`);
1259
- const contextTokens = profile.contextTokens ?? defaults.contextTokens;
1260
- return {
1261
- agentId: profile.id,
1191
+ function readMetadataThinking(metadata) {
1192
+ const candidates = [
1193
+ metadata.thinking,
1194
+ metadata.thinking_level,
1195
+ metadata.thinkingLevel,
1196
+ metadata.thinking_effort,
1197
+ metadata.thinkingEffort
1198
+ ];
1199
+ for (const candidate of candidates) {
1200
+ if (typeof candidate !== "string") continue;
1201
+ const normalized = candidate.trim().toLowerCase();
1202
+ if (!normalized) continue;
1203
+ if (normalized === "clear" || normalized === "reset" || normalized === "off!") return "__clear__";
1204
+ const level = parseThinkingLevel(normalized);
1205
+ if (level) return level;
1206
+ }
1207
+ return null;
1208
+ }
1209
+ function resolveEffectiveModel(params) {
1210
+ const { fallbackModel, requestMetadata } = params;
1211
+ const metadata = structuredClone(params.sessionMetadata);
1212
+ if (requestMetadata.clear_model === true || requestMetadata.reset_model === true) delete metadata.preferred_model;
1213
+ const inboundModel = readMetadataModel(requestMetadata);
1214
+ if (inboundModel) metadata.preferred_model = inboundModel;
1215
+ return {
1216
+ metadata,
1217
+ model: normalizeOptionalString(metadata.preferred_model) ?? fallbackModel
1218
+ };
1219
+ }
1220
+ function syncSessionThinkingPreference(params) {
1221
+ const { requestMetadata } = params;
1222
+ const metadata = structuredClone(params.sessionMetadata);
1223
+ if (requestMetadata.clear_thinking === true || requestMetadata.reset_thinking === true) delete metadata.preferred_thinking;
1224
+ const inboundThinking = readMetadataThinking(requestMetadata);
1225
+ if (inboundThinking === "__clear__") {
1226
+ delete metadata.preferred_thinking;
1227
+ return metadata;
1228
+ }
1229
+ if (inboundThinking) metadata.preferred_thinking = inboundThinking;
1230
+ return metadata;
1231
+ }
1232
+ function resolveSessionChannelContext(params) {
1233
+ const { requestMetadata } = params;
1234
+ const metadata = structuredClone(params.sessionMetadata);
1235
+ const channel = normalizeOptionalString(requestMetadata.channel) ?? normalizeOptionalString(metadata.last_channel) ?? "ui";
1236
+ const chatId = normalizeOptionalString(requestMetadata.chatId) ?? normalizeOptionalString(requestMetadata.chat_id) ?? normalizeOptionalString(metadata.last_to) ?? "web-ui";
1237
+ metadata.last_channel = channel;
1238
+ metadata.last_to = chatId;
1239
+ return {
1240
+ channel,
1241
+ chatId,
1242
+ metadata
1243
+ };
1244
+ }
1245
+ //#endregion
1246
+ //#region src/features/native-runtime/utils/nextclaw-ncp-run-context.utils.ts
1247
+ const REQUESTED_SKILLS_METADATA_READER = new RequestedSkillsMetadataReader();
1248
+ function readRequestedAgentId(metadata) {
1249
+ return normalizeOptionalString(metadata.agent_id)?.toLowerCase() ?? normalizeOptionalString(metadata.agentId)?.toLowerCase() ?? null;
1250
+ }
1251
+ function resolveAgentProfile(params) {
1252
+ const { config, requestMetadata, storedAgentId } = params;
1253
+ const { agents: { defaults }, search: searchConfig, tools: { restrictToWorkspace, exec: { timeout: execTimeoutSeconds } } } = config;
1254
+ const defaultAgentId = resolveDefaultAgentProfileId(config);
1255
+ const profile = findEffectiveAgentProfile(config, normalizeOptionalString(storedAgentId)?.toLowerCase() ?? readRequestedAgentId(requestMetadata) ?? defaultAgentId) ?? findEffectiveAgentProfile(config, defaultAgentId);
1256
+ if (!profile) throw new Error(`default agent profile not found: ${defaultAgentId}`);
1257
+ const contextTokens = profile.contextTokens ?? defaults.contextTokens;
1258
+ return {
1259
+ agentId: profile.id,
1262
1260
  workspace: getWorkspacePath(profile.workspace ?? defaults.workspace),
1263
1261
  model: profile.model ?? defaults.model,
1264
1262
  contextTokens,
@@ -1271,6 +1269,105 @@ function resolveAgentProfile(params) {
1271
1269
  execTimeoutSeconds
1272
1270
  };
1273
1271
  }
1272
+ function resolveRequestedToolNames(metadata) {
1273
+ const rawValue = metadata.requested_tools ?? metadata.requestedTools;
1274
+ if (!Array.isArray(rawValue)) return [];
1275
+ return Array.from(new Set(rawValue.map((item) => normalizeOptionalString(item)).filter((item) => Boolean(item))));
1276
+ }
1277
+ function mergeRunMetadata(params) {
1278
+ const { requestMetadata, sessionMetadata } = params;
1279
+ return {
1280
+ ...sessionMetadata ? structuredClone(sessionMetadata) : {},
1281
+ ...requestMetadata ? structuredClone(requestMetadata) : {}
1282
+ };
1283
+ }
1284
+ function buildSessionOrchestrationSection() {
1285
+ return [
1286
+ "## Session Orchestration",
1287
+ "- Before passing a non-default `runtime` to `sessions_spawn` or agent creation/update flows, inspect the installed runtime kinds with `nextclaw agents runtimes --json`.",
1288
+ "- `sessions_spawn` is the unified session-creation tool. Omit `scope` or use `scope=\"standalone\"` for a regular session, and use `scope=\"child\"` when the new session should be a child session of the current flow.",
1289
+ "- `sessions_spawn` only creates the session by default. Add top-level `notify: \"none\" | \"final_reply\"` when the new session should start working immediately.",
1290
+ "- When `sessions_spawn.scope=\"child\"` and `sessions_spawn.notify=\"final_reply\"`, the new child session starts right away and this session automatically continues after that child reaches its final reply.",
1291
+ "- Use `sessions_spawn` without `notify` when the user wants a separate thread created now but does not need it to start working yet.",
1292
+ "- Use `sessions_request` to send one task to an existing session, including a session that was just created by `sessions_spawn` or a previously created child session.",
1293
+ "- `sessions_request.target` must be an object shaped like `{ \"session_id\": \"<target-session-id>\" }`. Do not pass a bare string.",
1294
+ "- Prefer `notify=\"final_reply\"` when the current session should continue after the target session produces its final reply. Use `notify=\"none\"` when you only want the target session to run independently."
1295
+ ].join("\n");
1296
+ }
1297
+ function resolveNextclawNcpRunContext(params) {
1298
+ const { configManager, requestMetadata: inputRequestMetadata, sessionId, sessionMetadata: inputSessionMetadata, storedAgentId } = params;
1299
+ const config = configManager.loadConfig();
1300
+ const requestMetadata = mergeRunMetadata({
1301
+ sessionMetadata: inputSessionMetadata,
1302
+ requestMetadata: inputRequestMetadata
1303
+ });
1304
+ const profile = resolveAgentProfile({
1305
+ config,
1306
+ storedAgentId,
1307
+ requestMetadata
1308
+ });
1309
+ const { metadata: modelMetadata, model: effectiveModel } = resolveEffectiveModel({
1310
+ sessionMetadata: requestMetadata,
1311
+ requestMetadata,
1312
+ fallbackModel: profile.model
1313
+ });
1314
+ const effectiveWorkspace = resolveSessionWorkspacePath({
1315
+ sessionMetadata: modelMetadata,
1316
+ workspace: profile.workspace
1317
+ });
1318
+ const channelContext = resolveSessionChannelContext({
1319
+ sessionMetadata: modelMetadata,
1320
+ requestMetadata
1321
+ });
1322
+ const sessionMetadata = channelContext.metadata;
1323
+ const requestedSkills = REQUESTED_SKILLS_METADATA_READER.readSelection(requestMetadata);
1324
+ const runtimeThinking = resolveThinkingLevel({
1325
+ config,
1326
+ agentId: profile.agentId,
1327
+ model: effectiveModel,
1328
+ sessionThinkingLevel: parseThinkingLevel(sessionMetadata.preferred_thinking) ?? null
1329
+ });
1330
+ return {
1331
+ channel: channelContext.channel,
1332
+ chatId: channelContext.chatId,
1333
+ config,
1334
+ effectiveModel,
1335
+ effectiveWorkspace,
1336
+ profile,
1337
+ requestMetadata,
1338
+ requestedSkills,
1339
+ requestedToolNames: resolveRequestedToolNames(requestMetadata),
1340
+ runtimeThinking,
1341
+ sessionKey: sessionId,
1342
+ sessionMetadata,
1343
+ toolRunContext: {
1344
+ sessionId,
1345
+ channel: channelContext.channel,
1346
+ chatId: channelContext.chatId,
1347
+ agentId: profile.agentId,
1348
+ config,
1349
+ execTimeoutSeconds: profile.execTimeoutSeconds,
1350
+ handoffDepth: resolveAgentHandoffDepth(requestMetadata),
1351
+ metadata: requestMetadata,
1352
+ restrictToWorkspace: profile.restrictToWorkspace,
1353
+ searchConfig: profile.searchConfig,
1354
+ workspace: effectiveWorkspace
1355
+ }
1356
+ };
1357
+ }
1358
+ //#endregion
1359
+ //#region src/features/native-runtime/services/nextclaw-ncp-context-builder.service.ts
1360
+ const TIME_HINT_TRIGGER_PATTERNS = [/\b(now|right now|current time|what time|today|tonight|tomorrow|yesterday|this morning|this afternoon|this evening|date)\b/i, /(现在|此刻|当前时间|现在几点|几点了|今天|今晚|今早|今晨|明天|昨天|日期)/];
1361
+ function isRecord$6(value) {
1362
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1363
+ }
1364
+ function mergeInputMetadata(input) {
1365
+ const messageMetadata = input.messages.slice().reverse().find((message) => isRecord$6(message.metadata))?.metadata;
1366
+ return {
1367
+ ...isRecord$6(messageMetadata) ? structuredClone(messageMetadata) : {},
1368
+ ...isRecord$6(input.metadata) ? structuredClone(input.metadata) : {}
1369
+ };
1370
+ }
1274
1371
  function shouldAppendTimeHint(content) {
1275
1372
  const normalized = content.trim();
1276
1373
  if (!normalized) return false;
@@ -1295,19 +1392,6 @@ function prependRequestedSkills(content, requestedSkillSelectors) {
1295
1392
  if (requestedSkillSelectors.length === 0) return content;
1296
1393
  return `[Requested skills for this turn: ${requestedSkillSelectors.join(", ")}]\n\n${content}`;
1297
1394
  }
1298
- function buildSessionOrchestrationSection() {
1299
- return [
1300
- "## Session Orchestration",
1301
- "- Before passing a non-default `runtime` to `sessions_spawn` or agent creation/update flows, inspect the installed runtime kinds with `nextclaw agents runtimes --json`.",
1302
- "- `sessions_spawn` is the unified session-creation tool. Omit `scope` or use `scope=\"standalone\"` for a regular session, and use `scope=\"child\"` when the new session should be a child session of the current flow.",
1303
- "- `sessions_spawn` only creates the session by default. Add top-level `notify: \"none\" | \"final_reply\"` when the new session should start working immediately.",
1304
- "- When `sessions_spawn.scope=\"child\"` and `sessions_spawn.notify=\"final_reply\"`, the new child session starts right away and this session automatically continues after that child reaches its final reply.",
1305
- "- Use `sessions_spawn` without `notify` when the user wants a separate thread created now but does not need it to start working yet.",
1306
- "- Use `sessions_request` to send one task to an existing session, including a session that was just created by `sessions_spawn` or a previously created child session.",
1307
- "- `sessions_request.target` must be an object shaped like `{ \"session_id\": \"<target-session-id>\" }`. Do not pass a bare string.",
1308
- "- Prefer `notify=\"final_reply\"` when the current session should continue after the target session produces its final reply. Use `notify=\"none\"` when you only want the target session to run independently."
1309
- ].join("\n");
1310
- }
1311
1395
  function buildRequestedOpenAiTools(toolDefinitions, requestedToolNames) {
1312
1396
  const tools = toolDefinitions.map(buildOpenAiFunctionTool);
1313
1397
  if (tools.length === 0) return;
@@ -1332,68 +1416,25 @@ var NextclawNcpContextBuilder = class {
1332
1416
  };
1333
1417
  };
1334
1418
  prepareRunContext = (input) => {
1335
- const config = this.options.configManager.loadConfig();
1336
1419
  const requestMetadata = mergeInputMetadata(input);
1337
- const sessionMetadata = structuredClone(requestMetadata);
1338
- const profile = resolveAgentProfile({
1339
- config,
1340
- storedAgentId: this.options.agentId,
1341
- requestMetadata
1342
- });
1343
- let { model: effectiveModel } = resolveEffectiveModel({
1344
- sessionMetadata,
1420
+ const resolved = resolveNextclawNcpRunContext({
1421
+ configManager: this.options.configManager,
1422
+ sessionId: input.sessionId,
1345
1423
  requestMetadata,
1346
- fallbackModel: profile.model
1347
- });
1348
- const effectiveWorkspace = resolveSessionWorkspacePath({
1349
- sessionMetadata,
1350
- workspace: profile.workspace
1351
- });
1352
- const { channel, chatId } = resolveSessionChannelContext({
1353
- sessionMetadata,
1354
- requestMetadata
1424
+ sessionMetadata: requestMetadata,
1425
+ storedAgentId: this.options.agentId
1355
1426
  });
1356
- const requestedSkills = REQUESTED_SKILLS_METADATA_READER.readSelection(requestMetadata);
1357
1427
  const currentTurn = buildCurrentTurnState({
1358
1428
  input,
1359
- currentModel: effectiveModel,
1360
- formatPrompt: ({ text, timestamp }) => appendTimeHintForPrompt(prependRequestedSkills(text, requestedSkills.selectors), timestamp),
1429
+ currentModel: resolved.effectiveModel,
1430
+ formatPrompt: ({ text, timestamp }) => appendTimeHintForPrompt(prependRequestedSkills(text, resolved.requestedSkills.selectors), timestamp),
1361
1431
  assetStore: this.options.assetStore
1362
1432
  });
1363
- effectiveModel = currentTurn.effectiveModel;
1364
- const runtimeThinking = resolveThinkingLevel({
1365
- config,
1366
- agentId: profile.agentId,
1367
- model: effectiveModel,
1368
- sessionThinkingLevel: parseThinkingLevel(sessionMetadata.preferred_thinking) ?? null
1369
- });
1370
- this.options.toolRegistry.prepareForRun({
1371
- sessionId: input.sessionId,
1372
- channel,
1373
- chatId,
1374
- agentId: profile.agentId,
1375
- config,
1376
- execTimeoutSeconds: profile.execTimeoutSeconds,
1377
- handoffDepth: resolveAgentHandoffDepth(requestMetadata),
1378
- metadata: requestMetadata,
1379
- restrictToWorkspace: profile.restrictToWorkspace,
1380
- searchConfig: profile.searchConfig,
1381
- workspace: effectiveWorkspace
1382
- });
1433
+ this.options.toolRegistry.prepareForRun(resolved.toolRunContext);
1383
1434
  return {
1384
- channel,
1385
- chatId,
1386
- config,
1435
+ ...resolved,
1387
1436
  currentTurn,
1388
- effectiveModel,
1389
- effectiveWorkspace,
1390
- profile,
1391
- requestMetadata,
1392
- requestedSkills,
1393
- requestedToolNames: resolveRequestedToolNames(requestMetadata),
1394
- runtimeThinking,
1395
- sessionMetadata,
1396
- sessionKey: input.sessionId
1437
+ effectiveModel: currentTurn.effectiveModel
1397
1438
  };
1398
1439
  };
1399
1440
  buildModelMessages = (input, _options, runContext) => {
@@ -1564,13 +1605,13 @@ var NativeAgentRuntimeFactory = class {
1564
1605
  };
1565
1606
  //#endregion
1566
1607
  //#region src/features/native-runtime/tools/ncp-asset.tools.ts
1567
- function readOptionalString$6(value) {
1608
+ function readOptionalString$5(value) {
1568
1609
  if (typeof value !== "string") return null;
1569
1610
  const trimmed = value.trim();
1570
1611
  return trimmed.length > 0 ? trimmed : null;
1571
1612
  }
1572
1613
  function readOptionalBase64Bytes(value) {
1573
- const base64 = readOptionalString$6(value);
1614
+ const base64 = readOptionalString$5(value);
1574
1615
  if (!base64) return null;
1575
1616
  try {
1576
1617
  return Buffer.from(base64, "base64");
@@ -1621,18 +1662,18 @@ var AssetPutTool = class {
1621
1662
  this.contentBasePath = contentBasePath;
1622
1663
  }
1623
1664
  validateArgs = (args) => {
1624
- const path = readOptionalString$6(args.path);
1625
- const bytesBase64 = readOptionalString$6(args.bytesBase64);
1626
- const fileName = readOptionalString$6(args.fileName);
1665
+ const path = readOptionalString$5(args.path);
1666
+ const bytesBase64 = readOptionalString$5(args.bytesBase64);
1667
+ const fileName = readOptionalString$5(args.fileName);
1627
1668
  if (path && bytesBase64) return ["Provide either path, or bytesBase64 + fileName, not both."];
1628
1669
  if (path) return [];
1629
1670
  if (bytesBase64) return fileName ? [] : ["fileName is required when using bytesBase64."];
1630
1671
  return ["Provide either path, or bytesBase64 + fileName."];
1631
1672
  };
1632
1673
  execute = async (args) => {
1633
- const path = readOptionalString$6(args?.path);
1634
- const fileName = readOptionalString$6(args?.fileName);
1635
- const mimeType = readOptionalString$6(args?.mimeType);
1674
+ const path = readOptionalString$5(args?.path);
1675
+ const fileName = readOptionalString$5(args?.fileName);
1676
+ const mimeType = readOptionalString$5(args?.mimeType);
1636
1677
  const bytes = readOptionalBase64Bytes(args?.bytesBase64);
1637
1678
  if (path) return {
1638
1679
  ok: true,
@@ -1675,8 +1716,8 @@ var AssetExportTool = class {
1675
1716
  this.assetStore = assetStore;
1676
1717
  }
1677
1718
  execute = async (args) => {
1678
- const assetUri = readOptionalString$6(args?.assetUri);
1679
- const targetPath = readOptionalString$6(args?.targetPath);
1719
+ const assetUri = readOptionalString$5(args?.assetUri);
1720
+ const targetPath = readOptionalString$5(args?.targetPath);
1680
1721
  if (!assetUri || !targetPath) throw new Error("asset_export requires assetUri and targetPath.");
1681
1722
  return {
1682
1723
  ok: true,
@@ -1702,7 +1743,7 @@ var AssetStatTool = class {
1702
1743
  this.contentBasePath = contentBasePath;
1703
1744
  }
1704
1745
  execute = async (args) => {
1705
- const assetUri = readOptionalString$6(args?.assetUri);
1746
+ const assetUri = readOptionalString$5(args?.assetUri);
1706
1747
  if (!assetUri) throw new Error("asset_stat requires assetUri.");
1707
1748
  const record = await this.assetStore.statRecord(assetUri);
1708
1749
  if (!record) return {
@@ -1938,8 +1979,10 @@ var AgentRunRequestManager = class {
1938
1979
  };
1939
1980
  };
1940
1981
  //#endregion
1941
- //#region src/features/runtime-registry/services/agent-runtime-registry.service.ts
1982
+ //#region src/configs/agent-runtime.config.ts
1942
1983
  const DEFAULT_AGENT_RUNTIME_ENTRY_ID = "native";
1984
+ //#endregion
1985
+ //#region src/features/runtime-registry/services/agent-runtime-registry.service.ts
1943
1986
  function normalizeIdentifier(value) {
1944
1987
  if (typeof value !== "string") return null;
1945
1988
  const normalized = value.trim().toLowerCase();
@@ -2232,6 +2275,11 @@ var ConfigManager = class {
2232
2275
  return this.currentConfig;
2233
2276
  }
2234
2277
  loadConfig = () => resolveConfigSecrets(loadConfig(this.configPath), { configPath: this.configPath });
2278
+ getDefaultModel = () => this.loadConfig().agents.defaults.model;
2279
+ getModelMaxTokens = (model) => {
2280
+ const value = this.loadConfig().agents.defaults.models[model]?.params?.max_tokens;
2281
+ return typeof value === "number" ? Math.trunc(value) : void 0;
2282
+ };
2235
2283
  installRuntimeHooks = (hooks) => {
2236
2284
  this.hooks = {
2237
2285
  ...this.hooks,
@@ -2246,14 +2294,6 @@ var ConfigManager = class {
2246
2294
  if (!changedPaths.length) return;
2247
2295
  this.currentConfig = nextConfig;
2248
2296
  const plan = buildReloadPlan(changedPaths);
2249
- let reloadPluginsResult = void 0;
2250
- if (plan.reloadPlugins) {
2251
- reloadPluginsResult = await this.reloadPlugins({
2252
- config: nextConfig,
2253
- changedPaths
2254
- });
2255
- console.log("Config reload: plugins reloaded.");
2256
- }
2257
2297
  if (plan.reloadMcp) {
2258
2298
  await this.reloadMcp({
2259
2299
  config: nextConfig,
@@ -2268,7 +2308,7 @@ var ConfigManager = class {
2268
2308
  });
2269
2309
  console.log("Config reload: companion setting applied.");
2270
2310
  }
2271
- if (plan.restartChannels || reloadPluginsResult?.restartChannels) {
2311
+ if (plan.restartChannels) {
2272
2312
  await this.rebuildChannels(nextConfig, { start: true });
2273
2313
  console.log("Config reload: channels restarted.");
2274
2314
  }
@@ -2363,9 +2403,6 @@ var ConfigManager = class {
2363
2403
  this.providerReloadTask = null;
2364
2404
  }
2365
2405
  };
2366
- reloadPlugins = async (params) => {
2367
- return await this.hooks.reloadPlugins?.(params);
2368
- };
2369
2406
  reloadMcp = async (params) => {
2370
2407
  await this.hooks.reloadMcp?.(params);
2371
2408
  };
@@ -2572,8 +2609,15 @@ var ExtensionLifecycleService = class {
2572
2609
  //#region src/features/extension-runtime/services/extension-manifest-discovery.service.ts
2573
2610
  const EXTENSION_MANIFEST_FILE = "nextclaw.extension.json";
2574
2611
  const BUILTIN_EXTENSION_PACKAGES = [
2612
+ "@nextclaw/channel-extension-dingtalk",
2613
+ "@nextclaw/channel-extension-discord",
2614
+ "@nextclaw/channel-extension-email",
2575
2615
  "@nextclaw/channel-extension-feishu",
2576
2616
  "@nextclaw/channel-extension-qq",
2617
+ "@nextclaw/channel-extension-slack",
2618
+ "@nextclaw/channel-extension-telegram",
2619
+ "@nextclaw/channel-extension-wecom",
2620
+ "@nextclaw/channel-extension-whatsapp",
2577
2621
  "@nextclaw/channel-extension-weixin"
2578
2622
  ];
2579
2623
  const runtimeRequire = createRequire(import.meta.url);
@@ -2612,7 +2656,7 @@ function findExtensionManifestRoot(startPath) {
2612
2656
  current = parent;
2613
2657
  }
2614
2658
  }
2615
- function resolveDevFirstPartyExtensionDir(explicitDir = process.env.NEXTCLAW_DEV_FIRST_PARTY_PLUGIN_DIR, moduleDir = dirname(fileURLToPath(import.meta.url))) {
2659
+ function resolveDevFirstPartyExtensionDir(explicitDir = process.env.NEXTCLAW_DEV_FIRST_PARTY_EXTENSION_DIR, moduleDir = dirname(fileURLToPath(import.meta.url))) {
2616
2660
  const configured = explicitDir?.trim();
2617
2661
  if (configured) return configured;
2618
2662
  return [
@@ -2660,8 +2704,7 @@ function resolveExtensionManifestRoots(params) {
2660
2704
  join(getDataPath(), "extensions"),
2661
2705
  join(params.workspace, ".nextclaw", "extensions"),
2662
2706
  ...devExtensionsDir ? [devExtensionsDir] : [],
2663
- ...resolveBuiltinExtensionManifestRoots(),
2664
- ...params.config.plugins.load?.paths ?? []
2707
+ ...resolveBuiltinExtensionManifestRoots()
2665
2708
  ]);
2666
2709
  }
2667
2710
  var ExtensionManifestDiscoveryService = class {
@@ -2781,11 +2824,35 @@ function toInboundMessage(value) {
2781
2824
  metadata: readRecord$1(payload.metadata)
2782
2825
  };
2783
2826
  }
2827
+ function normalizeChannelConfigResult(value) {
2828
+ return readRecord$1(readRecord$1(value).channelConfig);
2829
+ }
2830
+ function normalizeAuthLoginResult(value) {
2831
+ const record = readRecord$1(value);
2832
+ return {
2833
+ channelConfig: normalizeChannelConfigResult(record),
2834
+ accountId: typeof record.accountId === "string" ? record.accountId : null,
2835
+ notes: Array.isArray(record.notes) ? record.notes.filter((note) => typeof note === "string") : []
2836
+ };
2837
+ }
2838
+ function normalizeAuthPollResult(value) {
2839
+ if (!value) return null;
2840
+ const record = readRecord$1(value);
2841
+ return {
2842
+ channel: readRequiredString$2(record.channel, "channel"),
2843
+ status: record.status,
2844
+ message: readString$5(record.message),
2845
+ nextPollMs: readOptionalNumber(record.nextPollMs),
2846
+ accountId: typeof record.accountId === "string" ? record.accountId : null,
2847
+ notes: Array.isArray(record.notes) ? record.notes.filter((note) => typeof note === "string") : [],
2848
+ channelConfig: normalizeChannelConfigResult(record)
2849
+ };
2850
+ }
2784
2851
  var ExtensionChannelClient = class {
2785
2852
  constructor(params) {
2786
2853
  this.params = params;
2787
2854
  }
2788
- login = async ({ accountId, baseUrl, verbose }) => await this.params.request({
2855
+ login = async ({ accountId, baseUrl, verbose }) => normalizeAuthLoginResult(await this.params.request({
2789
2856
  extensionId: this.params.extensionId,
2790
2857
  kind: "channel.auth.login",
2791
2858
  payload: {
@@ -2794,7 +2861,7 @@ var ExtensionChannelClient = class {
2794
2861
  baseUrl,
2795
2862
  verbose
2796
2863
  }
2797
- });
2864
+ }));
2798
2865
  start = async (params) => await this.params.request({
2799
2866
  extensionId: this.params.extensionId,
2800
2867
  kind: "channel.auth.start",
@@ -2805,14 +2872,24 @@ var ExtensionChannelClient = class {
2805
2872
  domain: params.domain
2806
2873
  }
2807
2874
  });
2808
- poll = async ({ sessionId }) => await this.params.request({
2875
+ connect = async (params) => normalizeAuthPollResult(await this.params.request({
2876
+ extensionId: this.params.extensionId,
2877
+ kind: "channel.auth.connect",
2878
+ payload: {
2879
+ channelId: this.params.channelId,
2880
+ accountId: params.accountId,
2881
+ domain: params.domain,
2882
+ fields: params.fields
2883
+ }
2884
+ }));
2885
+ poll = async ({ sessionId }) => normalizeAuthPollResult(await this.params.request({
2809
2886
  extensionId: this.params.extensionId,
2810
2887
  kind: "channel.auth.poll",
2811
2888
  payload: {
2812
2889
  channelId: this.params.channelId,
2813
2890
  sessionId
2814
2891
  }
2815
- });
2892
+ }));
2816
2893
  sendText = async ({ to, text, accountId, replyTo, media, metadata }) => await this.params.request({
2817
2894
  extensionId: this.params.extensionId,
2818
2895
  kind: "channel.outbound.sendText",
@@ -2838,6 +2915,8 @@ var ExtensionRuntimeService = class {
2838
2915
  registerIngressHandlers = () => {
2839
2916
  this.options.ingress.addHandler(ingressKeys.extension.channelConfigGet, this.handleChannelConfigGet);
2840
2917
  this.options.ingress.addHandler(ingressKeys.extension.channelMessageSubmit, this.handleChannelMessageSubmit);
2918
+ this.options.ingress.addHandler(ingressKeys.extension.channelCommandList, this.handleChannelCommandList);
2919
+ this.options.ingress.addHandler(ingressKeys.extension.channelCommandExecute, this.handleChannelCommandExecute);
2841
2920
  this.options.ingress.addHandler(ingressKeys.extension.response, this.handleExtensionResponse);
2842
2921
  };
2843
2922
  loadChannelContributions = async (params) => {
@@ -2876,7 +2955,7 @@ var ExtensionRuntimeService = class {
2876
2955
  if (!channelId) continue;
2877
2956
  const configUiHints = this.readConfigUiHints(channel.configUiHints);
2878
2957
  channelBindings.push({
2879
- pluginId: manifest.id,
2958
+ extensionId: manifest.id,
2880
2959
  channelId,
2881
2960
  channel: {
2882
2961
  id: channelId,
@@ -2915,6 +2994,49 @@ var ExtensionRuntimeService = class {
2915
2994
  await this.options.messageBus.publishInbound(toInboundMessage(envelope.payload));
2916
2995
  return { accepted: true };
2917
2996
  };
2997
+ handleChannelCommandList = (_envelope, context) => {
2998
+ this.assertAuthorized(context);
2999
+ return { commands: new CommandRegistry(this.options.getConfig(), this.options.sessionManager).listSlashCommands() };
3000
+ };
3001
+ handleChannelCommandExecute = async (envelope, context) => {
3002
+ this.assertAuthorized(context);
3003
+ const payload = readRecord$1(envelope.payload);
3004
+ const channel = readRequiredString$2(payload.channelId, "channelId");
3005
+ const chatId = readRequiredString$2(payload.conversationId, "conversationId");
3006
+ const senderId = readRequiredString$2(payload.senderId, "senderId");
3007
+ const metadata = readRecord$1(payload.metadata);
3008
+ const config = this.options.getConfig();
3009
+ const registry = new CommandRegistry(config, this.options.sessionManager);
3010
+ const route = new AgentRouteResolver(config).resolveInbound({
3011
+ message: {
3012
+ channel,
3013
+ chatId,
3014
+ senderId,
3015
+ content: readString$5(payload.rawText) ?? readString$5(payload.commandName) ?? "",
3016
+ timestamp: /* @__PURE__ */ new Date(),
3017
+ attachments: [],
3018
+ metadata
3019
+ },
3020
+ forcedAgentId: readString$5(metadata.target_agent_id),
3021
+ sessionKeyOverride: readString$5(metadata.session_key_override)
3022
+ });
3023
+ const rawText = readString$5(payload.rawText);
3024
+ if (rawText) return await registry.executeText(rawText, {
3025
+ channel,
3026
+ chatId,
3027
+ senderId,
3028
+ sessionKey: route.sessionKey
3029
+ }) ?? {
3030
+ content: "",
3031
+ ephemeral: true
3032
+ };
3033
+ return await registry.execute(readRequiredString$2(payload.commandName, "commandName"), readRecord$1(payload.args), {
3034
+ channel,
3035
+ chatId,
3036
+ senderId,
3037
+ sessionKey: route.sessionKey
3038
+ });
3039
+ };
2918
3040
  handleExtensionResponse = (envelope, context) => {
2919
3041
  this.assertAuthorized(context);
2920
3042
  const payload = readRecord$1(envelope.payload);
@@ -2975,451 +3097,64 @@ var ExtensionRuntimeService = class {
2975
3097
  };
2976
3098
  };
2977
3099
  //#endregion
2978
- //#region src/features/extension-development-source/utils/first-party-plugin-load-paths.utils.ts
2979
- const readJsonFile = (filePath) => {
2980
- try {
2981
- const raw = fs.readFileSync(filePath, "utf-8");
2982
- const parsed = JSON.parse(raw);
2983
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
2984
- } catch {
2985
- return null;
2986
- }
2987
- };
2988
- const readString$4 = (value) => {
2989
- if (typeof value !== "string") return;
2990
- return value.trim() || void 0;
2991
- };
2992
- const toPluginIdFromPackageName = (packageName) => {
2993
- const trimmed = packageName.trim();
2994
- if (!trimmed) return "";
2995
- return trimmed.includes("/") ? trimmed.split("/").pop() ?? trimmed : trimmed;
2996
- };
2997
- const normalizeInstallRecordPath = (value) => {
2998
- const filePath = readString$4(value);
2999
- return filePath ? path.resolve(filePath) : void 0;
3000
- };
3001
- const resolveDevFirstPartyPluginDir = (explicitDir, moduleDir = path.dirname(fileURLToPath(import.meta.url))) => {
3002
- const configured = explicitDir?.trim();
3003
- if (configured) return configured;
3004
- return [
3005
- path.resolve(moduleDir, "../../../extensions"),
3006
- path.resolve(moduleDir, "../../../../extensions"),
3007
- path.resolve(moduleDir, "../../../../../extensions")
3008
- ].find((candidate) => fs.existsSync(candidate));
3009
- };
3010
- const hasOpenClawExtensions = (pkg) => {
3011
- const openclaw = pkg.openclaw;
3012
- if (!openclaw || typeof openclaw !== "object" || Array.isArray(openclaw)) return false;
3013
- const extensions = openclaw.extensions;
3014
- return Array.isArray(extensions) && extensions.some((entry) => typeof entry === "string" && entry.trim().length > 0);
3015
- };
3016
- const hasOpenClawDevelopmentExtensions = (pkg) => {
3017
- const openclaw = pkg.openclaw;
3018
- if (!openclaw || typeof openclaw !== "object" || Array.isArray(openclaw)) return false;
3019
- const development = openclaw.development;
3020
- if (!development || typeof development !== "object" || Array.isArray(development)) return false;
3021
- const extensions = development.extensions;
3022
- return Array.isArray(extensions) && extensions.some((entry) => typeof entry === "string" && entry.trim().length > 0);
3023
- };
3024
- const normalizePackageSpec = (spec) => {
3025
- const trimmed = spec.trim();
3026
- if (!trimmed) return;
3027
- if (trimmed.startsWith("@")) {
3028
- const slashIndex = trimmed.indexOf("/");
3029
- if (slashIndex < 0) return;
3030
- const secondAtIndex = trimmed.indexOf("@", slashIndex + 1);
3031
- return secondAtIndex < 0 ? trimmed : trimmed.slice(0, secondAtIndex);
3032
- }
3033
- const versionIndex = trimmed.indexOf("@");
3034
- return versionIndex < 0 ? trimmed : trimmed.slice(0, versionIndex);
3035
- };
3036
- const readWorkspacePluginPackages = (workspaceExtensionsDir) => {
3037
- if (!workspaceExtensionsDir.trim() || !fs.existsSync(workspaceExtensionsDir)) return [];
3038
- const entries = fs.readdirSync(workspaceExtensionsDir, { withFileTypes: true });
3039
- const packages = [];
3040
- for (const entry of entries) {
3041
- if (!entry.isDirectory()) continue;
3042
- const packageDir = path.join(workspaceExtensionsDir, entry.name);
3043
- const pkg = readJsonFile(path.join(packageDir, "package.json"));
3044
- if (!pkg || !hasOpenClawExtensions(pkg)) continue;
3045
- const packageName = readString$4(pkg.name);
3046
- if (!packageName?.startsWith("@nextclaw/")) continue;
3047
- packages.push({
3048
- pluginId: toPluginIdFromPackageName(packageName),
3049
- packageName,
3050
- dir: packageDir,
3051
- supportsDevelopmentSource: hasOpenClawDevelopmentExtensions(pkg)
3052
- });
3053
- }
3054
- return packages;
3055
- };
3056
- const readInstalledFirstPartyPluginMatches = (workspaceExtensionsDir) => {
3057
- const workspacePackages = readWorkspacePluginPackages(workspaceExtensionsDir);
3058
- if (workspacePackages.length === 0) return [];
3059
- const globalExtensionsDir = path.join(getDataPath(), "extensions");
3060
- if (!fs.existsSync(globalExtensionsDir)) return [];
3061
- const workspacePackageByName = new Map(workspacePackages.map((entry) => [entry.packageName, entry]));
3062
- const entries = fs.readdirSync(globalExtensionsDir, { withFileTypes: true });
3063
- const matches = [];
3064
- for (const entry of entries) {
3065
- if (!entry.isDirectory()) continue;
3066
- const packageDir = path.join(globalExtensionsDir, entry.name);
3067
- const packageName = readString$4(readJsonFile(path.join(packageDir, "package.json"))?.name);
3068
- if (!packageName) continue;
3069
- const workspacePackage = workspacePackageByName.get(packageName);
3070
- if (!workspacePackage) continue;
3071
- matches.push({
3072
- packageName,
3073
- workspaceDir: workspacePackage.dir,
3074
- installPath: packageDir
3075
- });
3076
- }
3077
- return matches;
3078
- };
3079
- const mergeLoadPaths$1 = (existingLoadPaths, devLoadPaths) => {
3080
- const mergedLoadPaths = [...devLoadPaths];
3081
- for (const entry of existingLoadPaths) if (!mergedLoadPaths.includes(entry)) mergedLoadPaths.push(entry);
3082
- return mergedLoadPaths;
3083
- };
3084
- const findWorkspacePackageForInstallRecord = (installRecord, workspacePackages, packageByName) => {
3085
- const packageName = normalizePackageSpec(readString$4(installRecord.spec) ?? "");
3086
- if (packageName) {
3087
- const matchedByPackageName = packageByName.get(packageName);
3088
- if (matchedByPackageName) return matchedByPackageName;
3089
- }
3090
- const installPathCandidates = new Set([installRecord.sourcePath, installRecord.installPath].map(normalizeInstallRecordPath).filter((entry) => Boolean(entry)));
3091
- if (installPathCandidates.size === 0) return;
3092
- return workspacePackages.find((workspacePackage) => installPathCandidates.has(path.resolve(workspacePackage.dir)));
3093
- };
3094
- const resolveDevFirstPartyPluginLoadPaths = (config, workspaceExtensionsDir) => {
3095
- const rootDir = resolveDevFirstPartyPluginDir(workspaceExtensionsDir);
3096
- if (!rootDir) return [];
3097
- const workspacePackages = readWorkspacePluginPackages(rootDir);
3098
- if (workspacePackages.length === 0) return [];
3099
- const installedPluginMatches = readInstalledFirstPartyPluginMatches(rootDir);
3100
- const packageDirByName = new Map(workspacePackages.map((entry) => [entry.packageName, entry.dir]));
3101
- const packageByName = new Map(workspacePackages.map((entry) => [entry.packageName, entry]));
3102
- const loadPaths = [];
3103
- const installs = config.plugins.installs ?? {};
3104
- for (const installRecord of Object.values(installs)) {
3105
- const matchedWorkspacePackage = findWorkspacePackageForInstallRecord(installRecord, workspacePackages, packageByName);
3106
- const packageDir = matchedWorkspacePackage ? matchedWorkspacePackage.dir : packageDirByName.get(normalizePackageSpec(readString$4(installRecord.spec) ?? "") ?? "");
3107
- if (!packageDir || loadPaths.includes(packageDir)) continue;
3108
- loadPaths.push(packageDir);
3109
- }
3110
- for (const installedPlugin of installedPluginMatches) {
3111
- if (loadPaths.includes(installedPlugin.workspaceDir)) continue;
3112
- loadPaths.push(installedPlugin.workspaceDir);
3113
- }
3114
- return loadPaths;
3115
- };
3116
- const resolveDevFirstPartyPluginInstallRoots = (config, workspaceExtensionsDir) => {
3117
- const rootDir = resolveDevFirstPartyPluginDir(workspaceExtensionsDir);
3118
- if (!rootDir) return [];
3119
- const workspacePackages = readWorkspacePluginPackages(rootDir);
3120
- if (workspacePackages.length === 0) return [];
3121
- const packageNames = new Set(workspacePackages.map((entry) => entry.packageName));
3122
- const packageByName = new Map(workspacePackages.map((entry) => [entry.packageName, entry]));
3123
- const installRoots = [];
3124
- const installedPluginMatches = readInstalledFirstPartyPluginMatches(rootDir);
3125
- for (const installedPlugin of installedPluginMatches) {
3126
- if (installRoots.includes(installedPlugin.installPath)) continue;
3127
- installRoots.push(installedPlugin.installPath);
3128
- }
3129
- for (const installRecord of Object.values(config.plugins.installs ?? {})) {
3130
- const workspacePackage = findWorkspacePackageForInstallRecord(installRecord, workspacePackages, packageByName);
3131
- const packageName = normalizePackageSpec(readString$4(installRecord.spec) ?? "");
3132
- if (!workspacePackage && (!packageName || !packageNames.has(packageName))) continue;
3133
- const installPath = readString$4(installRecord.installPath);
3134
- if (!installPath || installRoots.includes(installPath)) continue;
3135
- if (workspacePackage && path.resolve(installPath) === path.resolve(workspacePackage.dir)) continue;
3136
- installRoots.push(installPath);
3137
- }
3138
- return installRoots;
3139
- };
3140
- const applyDevFirstPartyPluginLoadPaths = (config, workspaceExtensionsDir) => {
3141
- const rootDir = resolveDevFirstPartyPluginDir(workspaceExtensionsDir);
3142
- if (!rootDir) return config;
3143
- if (readWorkspacePluginPackages(rootDir).length === 0) return config;
3144
- const devLoadPaths = resolveDevFirstPartyPluginLoadPaths(config, rootDir);
3145
- if (devLoadPaths.length === 0) return config;
3146
- const mergedLoadPaths = mergeLoadPaths$1(Array.isArray(config.plugins.load?.paths) ? config.plugins.load.paths.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [], devLoadPaths);
3100
+ //#region src/managers/extension.manager.ts
3101
+ function createEmptyExtensionRegistry() {
3147
3102
  return {
3148
- ...config,
3149
- plugins: {
3150
- ...config.plugins,
3151
- load: {
3152
- ...config.plugins.load,
3153
- paths: mergedLoadPaths
3154
- }
3155
- }
3103
+ tools: [],
3104
+ channels: [],
3105
+ diagnostics: []
3156
3106
  };
3157
- };
3158
- //#endregion
3159
- //#region src/features/extension-development-source/utils/dev-plugin-overrides.utils.ts
3160
- const DEV_PLUGIN_OVERRIDES_ENV = "NEXTCLAW_DEV_PLUGIN_OVERRIDES";
3161
- function isRecord$4(value) {
3162
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3163
- }
3164
- function readOptionalString$5(value) {
3165
- if (typeof value !== "string") return;
3166
- return value.trim() || void 0;
3167
3107
  }
3168
- function readPackageManifest(pluginPath) {
3169
- const packageJsonPath = path.join(pluginPath, "package.json");
3170
- if (!fs.existsSync(packageJsonPath)) return null;
3171
- try {
3172
- return JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
3173
- } catch {
3174
- return null;
3175
- }
3108
+ function buildSortedBindingSignatures(bindings) {
3109
+ return bindings.map((binding) => `${binding.extensionId}:${binding.channelId}`).sort();
3176
3110
  }
3177
- function assertOverridePluginReadable(override) {
3178
- if (!fs.existsSync(override.pluginPath)) throw new Error(`[dev-plugin-override] plugin path does not exist for "${override.pluginId}": ${override.pluginPath}`);
3179
- const packageManifest = readPackageManifest(override.pluginPath);
3180
- if (!packageManifest) throw new Error(`[dev-plugin-override] package.json is missing or invalid for "${override.pluginId}": ${override.pluginPath}`);
3181
- const pluginManifest = loadPluginManifest(override.pluginPath);
3182
- if (!pluginManifest.ok) throw new Error(`[dev-plugin-override] ${pluginManifest.error} for "${override.pluginId}": ${override.pluginPath}`);
3183
- if (pluginManifest.manifest.id !== override.pluginId) throw new Error(`[dev-plugin-override] plugin id mismatch: expected "${override.pluginId}" but found "${pluginManifest.manifest.id}" at ${override.pluginPath}`);
3184
- if (getPackageManifestExtensions(packageManifest, override.source).length === 0) {
3185
- const missingEntry = override.source === "development" ? "openclaw.development.extensions" : "openclaw.extensions";
3186
- throw new Error(`[dev-plugin-override] ${missingEntry} is missing for "${override.pluginId}" at ${override.pluginPath}`);
3187
- }
3188
- }
3189
- function readOverrideRecord(value, index) {
3190
- if (!isRecord$4(value)) throw new Error(`[dev-plugin-override] override[${index}] must be an object`);
3191
- const pluginId = readOptionalString$5(value.pluginId);
3192
- const pluginPath = readOptionalString$5(value.pluginPath);
3193
- const source = value.source === "development" ? "development" : "production";
3194
- if (!pluginId || !pluginPath) throw new Error(`[dev-plugin-override] override[${index}] requires pluginId and pluginPath`);
3195
- const normalized = {
3196
- pluginId,
3197
- pluginPath: path.resolve(pluginPath),
3198
- source
3199
- };
3200
- assertOverridePluginReadable(normalized);
3201
- return normalized;
3202
- }
3203
- function resolveDevPluginOverrides(rawEnv = process.env[DEV_PLUGIN_OVERRIDES_ENV]) {
3204
- if (typeof rawEnv !== "string" || rawEnv.trim().length === 0) return [];
3205
- let parsed;
3206
- try {
3207
- parsed = JSON.parse(rawEnv);
3208
- } catch (error) {
3209
- throw new Error(`[dev-plugin-override] failed to parse ${DEV_PLUGIN_OVERRIDES_ENV}: ${error instanceof Error ? error.message : String(error)}`);
3210
- }
3211
- if (!Array.isArray(parsed)) throw new Error(`[dev-plugin-override] ${DEV_PLUGIN_OVERRIDES_ENV} must be a JSON array`);
3212
- const seenPluginIds = /* @__PURE__ */ new Set();
3213
- const overrides = parsed.map((entry, index) => readOverrideRecord(entry, index));
3214
- for (const entry of overrides) {
3215
- if (seenPluginIds.has(entry.pluginId)) throw new Error(`[dev-plugin-override] duplicate plugin override for "${entry.pluginId}"`);
3216
- seenPluginIds.add(entry.pluginId);
3217
- }
3218
- return overrides;
3219
- }
3220
- function mergeLoadPaths(existingLoadPaths, overrideLoadPaths) {
3221
- const merged = [...overrideLoadPaths];
3222
- for (const entry of existingLoadPaths) if (!merged.includes(entry)) merged.push(entry);
3223
- return merged;
3224
- }
3225
- function applyExplicitDevPluginOverrides(config, overrides) {
3226
- if (overrides.length === 0) return config;
3227
- const nextEntries = { ...config.plugins.entries ?? {} };
3228
- for (const override of overrides) nextEntries[override.pluginId] = {
3229
- ...nextEntries[override.pluginId] ?? {},
3230
- source: override.source
3231
- };
3232
- const existingLoadPaths = Array.isArray(config.plugins.load?.paths) ? config.plugins.load.paths.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [];
3233
- return {
3234
- ...config,
3235
- plugins: {
3236
- ...config.plugins,
3237
- entries: nextEntries,
3238
- load: {
3239
- ...config.plugins.load,
3240
- paths: mergeLoadPaths(existingLoadPaths, overrides.map((entry) => entry.pluginPath))
3241
- }
3242
- }
3243
- };
3111
+ function buildSortedExtensionChannelIds(channels) {
3112
+ return channels.map((registration) => registration.channel.id).filter((id) => typeof id === "string" && id.trim().length > 0).sort();
3244
3113
  }
3245
- function resolveDevPluginOverrideInstallRoots(config, overrides) {
3246
- const installRoots = [];
3247
- for (const override of overrides) {
3248
- const installRecord = config.plugins.installs?.[override.pluginId];
3249
- const installPath = readOptionalString$5(installRecord?.installPath);
3250
- if (!installPath || installRoots.includes(installPath)) continue;
3251
- installRoots.push(installPath);
3252
- }
3253
- return installRoots;
3254
- }
3255
- function resolveDevPluginLoadingContext(config, workspaceExtensionsDir, rawOverridesEnv = process.env[DEV_PLUGIN_OVERRIDES_ENV]) {
3256
- const configWithFirstPartyOverrides = applyDevFirstPartyPluginLoadPaths(config, workspaceExtensionsDir);
3257
- const overrides = resolveDevPluginOverrides(rawOverridesEnv);
3258
- return {
3259
- configWithDevPluginOverrides: applyExplicitDevPluginOverrides(configWithFirstPartyOverrides, overrides),
3260
- excludedRoots: [...resolveDevFirstPartyPluginInstallRoots(config, workspaceExtensionsDir), ...resolveDevPluginOverrideInstallRoots(config, overrides)].filter((entry, index, list) => list.indexOf(entry) === index),
3261
- overrides
3262
- };
3263
- }
3264
- //#endregion
3265
- //#region src/services/extension-plugin-registry.service.ts
3266
- const RESERVED_TOOL_NAMES = [
3267
- "read_file",
3268
- "write_file",
3269
- "edit_file",
3270
- "list_dir",
3271
- "exec",
3272
- "web_search",
3273
- "web_fetch",
3274
- "message",
3275
- "spawn",
3276
- "sessions_list",
3277
- "sessions_history",
3278
- "memory_search",
3279
- "memory_get",
3280
- "subagents",
3281
- "gateway",
3282
- "cron"
3283
- ];
3284
- function buildReservedPluginLoadOptions() {
3285
- return {
3286
- reservedToolNames: [...RESERVED_TOOL_NAMES],
3287
- reservedChannelIds: [],
3288
- reservedProviderIds: builtinProviderIds()
3289
- };
3290
- }
3291
- function createPluginLogger() {
3292
- return getAppLogger("extension.manager.plugin_registry");
3293
- }
3294
- var ExtensionPluginRegistryService = class {
3295
- discoverEnabledPluginCount = (params) => {
3296
- const { configWithDevPluginOverrides } = this.resolveLoadingContext(params.config);
3297
- return discoverPluginStatusReport({
3298
- config: configWithDevPluginOverrides,
3299
- workspaceDir: params.workspace
3300
- }).plugins.filter((plugin) => plugin.enabled).length;
3301
- };
3302
- load = (params) => {
3303
- const { configWithDevPluginOverrides, excludedRoots } = this.resolveLoadingContext(params.config);
3304
- return loadOpenClawPlugins({
3305
- config: configWithDevPluginOverrides,
3306
- workspaceDir: params.workspace,
3307
- excludeRoots: excludedRoots,
3308
- ...buildReservedPluginLoadOptions(),
3309
- logger: createPluginLogger()
3310
- });
3311
- };
3312
- loadProgressively = async (params) => {
3313
- const { configWithDevPluginOverrides, excludedRoots } = this.resolveLoadingContext(params.config);
3314
- return await loadOpenClawPluginsProgressively({
3315
- config: configWithDevPluginOverrides,
3316
- workspaceDir: params.workspace,
3317
- excludeRoots: excludedRoots,
3318
- ...buildReservedPluginLoadOptions(),
3319
- onPluginProcessed: params.onPluginProcessed,
3320
- logger: createPluginLogger()
3321
- });
3322
- };
3323
- resolveLoadingContext = (config) => {
3324
- return resolveDevPluginLoadingContext(config, resolveDevFirstPartyPluginDir(process.env.NEXTCLAW_DEV_FIRST_PARTY_PLUGIN_DIR));
3325
- };
3326
- };
3327
- //#endregion
3328
- //#region src/managers/extension.manager.ts
3329
- function createEmptyPluginRegistry() {
3330
- return {
3331
- plugins: [],
3332
- tools: [],
3333
- channels: [],
3334
- providers: [],
3335
- diagnostics: [],
3336
- resolvedTools: []
3337
- };
3338
- }
3339
- function buildSortedBindingSignatures(bindings) {
3340
- return bindings.map((binding) => `${binding.pluginId}:${binding.channelId}`).sort();
3341
- }
3342
- function buildSortedExtensionChannelIds(channels) {
3343
- return channels.map((registration) => registration.channel.id).filter((id) => typeof id === "string" && id.trim().length > 0).sort();
3344
- }
3345
- function areSortedStringListsEqual(left, right) {
3346
- return left.length === right.length && left.every((value, index) => value === right[index]);
3114
+ function areSortedStringListsEqual(left, right) {
3115
+ return left.length === right.length && left.every((value, index) => value === right[index]);
3347
3116
  }
3348
3117
  function readConfigPathSegment(path, prefix) {
3349
3118
  if (!path.startsWith(prefix)) return null;
3350
3119
  const [segment] = path.slice(prefix.length).split(".");
3351
3120
  return segment?.trim() ? segment.trim() : null;
3352
3121
  }
3122
+ function buildSnapshot(params) {
3123
+ return {
3124
+ extensionRegistry: {
3125
+ ...createEmptyExtensionRegistry(),
3126
+ channels: params.channelBindings.map((binding) => ({
3127
+ extensionId: binding.extensionId,
3128
+ channel: binding.channel,
3129
+ source: "extension-manifest"
3130
+ }))
3131
+ },
3132
+ channelBindings: params.channelBindings,
3133
+ uiMetadata: params.uiMetadata
3134
+ };
3135
+ }
3353
3136
  function shouldRestartChannelsForExtensionReload(params) {
3354
3137
  const { changedPaths, currentSnapshot, nextSnapshot } = params;
3355
3138
  if (!areSortedStringListsEqual(buildSortedBindingSignatures(currentSnapshot.channelBindings), buildSortedBindingSignatures(nextSnapshot.channelBindings))) return true;
3356
3139
  if (!areSortedStringListsEqual(buildSortedExtensionChannelIds(currentSnapshot.extensionRegistry.channels), buildSortedExtensionChannelIds(nextSnapshot.extensionRegistry.channels))) return true;
3357
- const channelPluginIds = /* @__PURE__ */ new Set();
3358
3140
  const channelIds = /* @__PURE__ */ new Set();
3359
- for (const binding of [...currentSnapshot.channelBindings, ...nextSnapshot.channelBindings]) {
3360
- channelPluginIds.add(binding.pluginId);
3361
- channelIds.add(binding.channelId);
3362
- }
3141
+ for (const binding of [...currentSnapshot.channelBindings, ...nextSnapshot.channelBindings]) channelIds.add(binding.channelId);
3363
3142
  for (const path of changedPaths) {
3364
- const pluginId = readConfigPathSegment(path, "plugins.entries.");
3365
- if (pluginId && channelPluginIds.has(pluginId)) return true;
3366
3143
  const channelId = readConfigPathSegment(path, "channels.");
3367
3144
  if (channelId && channelIds.has(channelId)) return true;
3145
+ if (path.startsWith("extensions.")) return true;
3368
3146
  }
3369
3147
  return false;
3370
3148
  }
3371
- function toExtensionRegistry(pluginRegistry) {
3372
- return {
3373
- tools: pluginRegistry.tools.map((tool) => ({
3374
- extensionId: tool.pluginId,
3375
- factory: tool.factory,
3376
- names: tool.names,
3377
- optional: tool.optional,
3378
- source: tool.source
3379
- })),
3380
- channels: pluginRegistry.channels.map((channel) => ({
3381
- extensionId: channel.pluginId,
3382
- channel: channel.channel,
3383
- source: channel.source
3384
- })),
3385
- diagnostics: pluginRegistry.diagnostics.map((diag) => ({
3386
- level: diag.level,
3387
- message: diag.message,
3388
- extensionId: diag.pluginId,
3389
- source: diag.source
3390
- }))
3391
- };
3392
- }
3393
- function toManifestChannelRegistrations(channelBindings) {
3394
- return channelBindings.map((binding) => ({
3395
- extensionId: binding.pluginId,
3396
- channel: binding.channel,
3397
- source: "extension-manifest"
3398
- }));
3399
- }
3400
- function buildExtensionSnapshot(registry, contributions) {
3401
- const extensionChannelIds = new Set(contributions.channelBindings.map((binding) => binding.channelId));
3402
- const extensionPluginIds = new Set(contributions.uiMetadata.map((metadata) => metadata.id));
3403
- const registryExtensionRegistry = toExtensionRegistry(registry);
3404
- return {
3405
- extensionRegistry: {
3406
- ...registryExtensionRegistry,
3407
- channels: [...registryExtensionRegistry.channels.filter((registration) => {
3408
- const channelId = registration.channel.id?.trim();
3409
- return !channelId || !extensionChannelIds.has(channelId);
3410
- }), ...toManifestChannelRegistrations(contributions.channelBindings)]
3411
- },
3412
- channelBindings: [...getPluginChannelBindings(registry).filter((binding) => !extensionChannelIds.has(binding.channelId)), ...contributions.channelBindings],
3413
- uiMetadata: [...getPluginUiMetadataFromRegistry(registry).filter((metadata) => !extensionPluginIds.has(metadata.id)), ...contributions.uiMetadata]
3414
- };
3149
+ function countExtensionIds(bindings, metadata) {
3150
+ return new Set([...bindings.map((binding) => binding.extensionId), ...metadata.map((item) => item.id)]).size;
3415
3151
  }
3416
3152
  var ExtensionManager = class {
3417
- snapshot = buildExtensionSnapshot(createEmptyPluginRegistry(), {
3153
+ snapshot = buildSnapshot({
3418
3154
  channelBindings: [],
3419
3155
  uiMetadata: []
3420
3156
  });
3421
3157
  runtime;
3422
- pluginRegistry = new ExtensionPluginRegistryService();
3423
3158
  constructor(options) {
3424
3159
  this.options = options;
3425
3160
  this.runtime = new ExtensionRuntimeService({
@@ -3427,38 +3162,32 @@ var ExtensionManager = class {
3427
3162
  getConfig: options.configManager.loadConfig,
3428
3163
  getWorkspace: this.getWorkspace,
3429
3164
  ingress: options.ingress,
3430
- messageBus: options.messageBus
3165
+ messageBus: options.messageBus,
3166
+ sessionManager: options.sessionManager
3431
3167
  });
3432
3168
  }
3433
- load = async (params) => {
3434
- const { changedPaths = [], onLoadStart, onPluginProcessed } = params;
3169
+ load = async (params = {}) => {
3170
+ const { changedPaths = [], onLoadStart, onExtensionProcessed } = params;
3435
3171
  const config = params.config ?? this.options.configManager.loadConfig();
3436
3172
  const workspace = getWorkspacePath(config.agents.defaults.workspace);
3437
- const totalPluginCount = this.pluginRegistry.discoverEnabledPluginCount({
3438
- config,
3439
- workspace
3440
- });
3441
- onLoadStart?.({ totalPluginCount });
3442
- let loadedPluginCount = 0;
3443
- const registry = await this.pluginRegistry.loadProgressively({
3444
- config,
3445
- workspace,
3446
- onPluginProcessed: (progress) => {
3447
- loadedPluginCount = progress.loadedPluginCount;
3448
- onPluginProcessed?.(progress);
3449
- }
3450
- });
3451
3173
  const contributions = await this.runtime.loadChannelContributions({
3452
3174
  config,
3453
3175
  workspace
3454
3176
  });
3177
+ const totalExtensionCount = countExtensionIds(contributions.channelBindings, contributions.uiMetadata);
3178
+ onLoadStart?.({ totalExtensionCount });
3179
+ for (const [index, binding] of contributions.channelBindings.entries()) onExtensionProcessed?.({
3180
+ extensionId: binding.extensionId,
3181
+ loadedExtensionCount: index + 1,
3182
+ totalExtensionCount
3183
+ });
3455
3184
  const currentSnapshot = this.snapshot;
3456
- const nextSnapshot = buildExtensionSnapshot(registry, contributions);
3185
+ const nextSnapshot = buildSnapshot(contributions);
3457
3186
  this.snapshot = nextSnapshot;
3458
3187
  return {
3459
- diagnostics: registry.diagnostics,
3460
- totalPluginCount,
3461
- loadedPluginCount: loadedPluginCount || totalPluginCount,
3188
+ diagnostics: [],
3189
+ totalExtensionCount,
3190
+ loadedExtensionCount: totalExtensionCount,
3462
3191
  shouldRestartChannels: shouldRestartChannelsForExtensionReload({
3463
3192
  changedPaths,
3464
3193
  currentSnapshot,
@@ -3479,11 +3208,49 @@ var ExtensionManager = class {
3479
3208
  getExtensionRegistry = () => this.snapshot.extensionRegistry;
3480
3209
  getChannelBindings = () => this.snapshot.channelBindings;
3481
3210
  getUiMetadata = () => this.snapshot.uiMetadata;
3482
- toConfigView = (config) => toPluginConfigView(config, this.snapshot.channelBindings);
3483
- mergeConfigView = (current, nextConfigView) => mergePluginConfigView(current, nextConfigView, this.snapshot.channelBindings);
3211
+ toConfigView = (config) => toExtensionConfigView(config);
3212
+ mergeConfigView = (current, nextConfigView) => mergeExtensionConfigView(current, nextConfigView);
3484
3213
  getWorkspace = () => getWorkspacePath(this.options.configManager.loadConfig().agents.defaults.workspace);
3485
3214
  };
3486
3215
  //#endregion
3216
+ //#region src/utils/model-message-vision.utils.ts
3217
+ const IMAGE_OMITTED_TEXT = "[Image omitted: the selected model is not configured for vision input.]";
3218
+ function isRecord$4(value) {
3219
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3220
+ }
3221
+ function isImageContentPart(value) {
3222
+ if (!isRecord$4(value)) return false;
3223
+ const type = value.type;
3224
+ return type === "image_url" || type === "input_image";
3225
+ }
3226
+ function normalizeContentWithoutVision(content) {
3227
+ if (!Array.isArray(content)) return content;
3228
+ let sawImage = false;
3229
+ const parts = content.map((part) => {
3230
+ if (!isImageContentPart(part)) return part;
3231
+ sawImage = true;
3232
+ return {
3233
+ type: "text",
3234
+ text: IMAGE_OMITTED_TEXT
3235
+ };
3236
+ });
3237
+ if (!sawImage) return content;
3238
+ const textParts = parts.filter((part) => isRecord$4(part) && part.type === "text" && typeof part.text === "string").map((part) => part.text.trim()).filter(Boolean);
3239
+ if (textParts.length === parts.length) return textParts.join("\n\n");
3240
+ return parts;
3241
+ }
3242
+ function normalizeModelMessagesForVisionSupport(params) {
3243
+ if (params.supportsVision) return params.messages;
3244
+ return params.messages.map((message) => {
3245
+ if (!Object.prototype.hasOwnProperty.call(message, "content")) return message;
3246
+ const content = normalizeContentWithoutVision(message.content);
3247
+ return content === message.content ? message : {
3248
+ ...message,
3249
+ content
3250
+ };
3251
+ });
3252
+ }
3253
+ //#endregion
3487
3254
  //#region src/managers/llm-provider.manager.ts
3488
3255
  const normalizedModel = (value) => {
3489
3256
  if (typeof value !== "string") return null;
@@ -3529,10 +3296,19 @@ var LlmProviderManager = class {
3529
3296
  return this.getOrCreateProvider(route);
3530
3297
  };
3531
3298
  chat = async (params) => {
3532
- return this.get(params.model ?? null).chat(params);
3299
+ const route = this.resolveRoute(params.model ?? null);
3300
+ return (route ? this.getOrCreateProvider(route) : this.missingProvider).chat({
3301
+ ...params,
3302
+ messages: this.prepareMessagesForProvider(route, params.messages)
3303
+ });
3533
3304
  };
3534
3305
  chatStream = async function* (params) {
3535
- for await (const event of this.get(params.model ?? null).chatStream(params)) yield event;
3306
+ const route = this.resolveRoute(params.model ?? null);
3307
+ const provider = route ? this.getOrCreateProvider(route) : this.missingProvider;
3308
+ for await (const event of provider.chatStream({
3309
+ ...params,
3310
+ messages: this.prepareMessagesForProvider(route, params.messages)
3311
+ })) yield event;
3536
3312
  };
3537
3313
  testConnection = async (input) => {
3538
3314
  await this.createProvider({
@@ -3545,7 +3321,7 @@ var LlmProviderManager = class {
3545
3321
  extraHeaders: input.extraHeaders ?? null,
3546
3322
  wireApi: input.wireApi ?? "auto",
3547
3323
  models: [],
3548
- modelThinking: {}
3324
+ modelConfig: {}
3549
3325
  },
3550
3326
  apiBase: input.apiBase ?? null,
3551
3327
  model: input.defaultModel
@@ -3663,6 +3439,16 @@ var LlmProviderManager = class {
3663
3439
  wireApi: route.provider?.wireApi ?? null
3664
3440
  });
3665
3441
  };
3442
+ prepareMessagesForProvider = (route, messages) => {
3443
+ return normalizeModelMessagesForVisionSupport({
3444
+ messages,
3445
+ supportsVision: modelSupportsVision({
3446
+ model: route?.model,
3447
+ providerName: route?.providerName,
3448
+ modelConfig: route?.provider?.modelConfig
3449
+ })
3450
+ });
3451
+ };
3666
3452
  buildCacheKey = (route) => {
3667
3453
  const routeProvider = route.provider;
3668
3454
  return [
@@ -3923,7 +3709,7 @@ var NcpSessionManager = class {
3923
3709
  this.runningSessionIds.clear();
3924
3710
  };
3925
3711
  createSession = async (params) => {
3926
- const { agentId: requestedAgentId, metadataOverrides, model, parentSessionId: rawParentSessionId, projectRoot, requestId: rawRequestId, runtime, sessionType: requestedSessionType, sourceSessionId, sourceSessionMetadata, task, thinkingLevel, title: requestedTitle } = params;
3712
+ const { agentId: requestedAgentId, metadataOverrides, model, parentSessionId: rawParentSessionId, projectRoot, requestId: rawRequestId, runtime, sessionId: requestedSessionId, sessionType: requestedSessionType, sourceSessionId, sourceSessionMetadata, task, thinkingLevel, title: requestedTitle } = params;
3927
3713
  const sourceRecord = sourceSessionId ? await this.getSessionRecord(sourceSessionId) : null;
3928
3714
  const metadata = cloneInheritedMetadata(sourceSessionMetadata);
3929
3715
  const title = readOptionalString$4(requestedTitle) ?? summarizeTask(task);
@@ -3948,7 +3734,7 @@ var NcpSessionManager = class {
3948
3734
  const now = (/* @__PURE__ */ new Date()).toISOString();
3949
3735
  const nextMetadata = mergeMetadataOverrides(metadata, metadataOverrides);
3950
3736
  const agentId = readOptionalString$4(requestedAgentId) ?? readOptionalString$4(sourceRecord?.agentId) ?? BUILTIN_MAIN_AGENT_ID;
3951
- const sessionId = buildSessionId();
3737
+ const sessionId = readOptionalString$4(requestedSessionId) ?? buildSessionId();
3952
3738
  const record = {
3953
3739
  sessionId,
3954
3740
  ...agentId ? { agentId } : {},
@@ -4094,12 +3880,12 @@ function parseSkillFrontmatter(raw) {
4094
3880
  const summaryI18n = readLocalizedTextMap(parsed, "summaryi18n", "summary_i18n");
4095
3881
  const descriptionI18n = readLocalizedTextMap(parsed, "descriptioni18n", "description_i18n");
4096
3882
  return {
4097
- name: readString$3(parsed, "name"),
4098
- summary: readString$3(parsed, "summary"),
4099
- summaryI18n: mergeLocalizedTextMap(summaryI18n, { zh: readString$3(parsed, "summaryzh", "summary_zh") }),
4100
- description: readString$3(parsed, "description"),
4101
- descriptionI18n: mergeLocalizedTextMap(descriptionI18n, { zh: readString$3(parsed, "descriptionzh", "description_zh") }),
4102
- author: readString$3(parsed, "author"),
3883
+ name: readString$4(parsed, "name"),
3884
+ summary: readString$4(parsed, "summary"),
3885
+ summaryI18n: mergeLocalizedTextMap(summaryI18n, { zh: readString$4(parsed, "summaryzh", "summary_zh") }),
3886
+ description: readString$4(parsed, "description"),
3887
+ descriptionI18n: mergeLocalizedTextMap(descriptionI18n, { zh: readString$4(parsed, "descriptionzh", "description_zh") }),
3888
+ author: readString$4(parsed, "author"),
4103
3889
  tags: readTags(parsed)
4104
3890
  };
4105
3891
  }
@@ -4126,7 +3912,7 @@ function parseYamlFrontmatter(raw) {
4126
3912
  throw new Error(`Invalid SKILL.md frontmatter: ${message}`);
4127
3913
  }
4128
3914
  }
4129
- function readString$3(record, ...names) {
3915
+ function readString$4(record, ...names) {
4130
3916
  const value = readValue(record, names);
4131
3917
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
4132
3918
  }
@@ -4279,14 +4065,9 @@ const RUNTIME_METADATA_KEYS = [
4279
4065
  "session_type",
4280
4066
  "runtime_type"
4281
4067
  ];
4282
- function isDurableSessionEvent(event) {
4068
+ function isDurableSessionEvent$1(event) {
4283
4069
  return event.type !== NcpEventType.ContextWindowUpdated;
4284
4070
  }
4285
- function readEventSessionId$1(event) {
4286
- const payload = "payload" in event ? event.payload : null;
4287
- if (!payload || typeof payload !== "object") return null;
4288
- return "sessionId" in payload && typeof payload.sessionId === "string" ? payload.sessionId : null;
4289
- }
4290
4071
  function pickRuntimeMetadataPatch(current, next) {
4291
4072
  const patch = {};
4292
4073
  for (const key of RUNTIME_METADATA_KEYS) {
@@ -4341,29 +4122,6 @@ var SessionRunManager = class {
4341
4122
  status: "idle"
4342
4123
  });
4343
4124
  };
4344
- streamSessionEvents = (payload, options) => {
4345
- this.assertNotDisposed();
4346
- const signal = options?.signal ?? new AbortController().signal;
4347
- const queue = new AsyncEventQueue();
4348
- const stop = () => queue.close();
4349
- const unsubscribe = this.eventBus.on(eventKeys.ncpEvent, (event) => {
4350
- if (readEventSessionId$1(event) === payload.sessionId) queue.push(event);
4351
- });
4352
- signal.addEventListener("abort", stop, { once: true });
4353
- if (signal.aborted) stop();
4354
- return (async function* () {
4355
- try {
4356
- for await (const event of queue.iterate()) {
4357
- if (signal.aborted) break;
4358
- yield event;
4359
- }
4360
- } finally {
4361
- unsubscribe();
4362
- signal.removeEventListener("abort", stop);
4363
- queue.close();
4364
- }
4365
- })();
4366
- };
4367
4125
  updateToolCallResult = async ({ result, sessionId, toolCallId }) => {
4368
4126
  this.assertNotDisposed();
4369
4127
  const normalizedSessionId = sessionId.trim();
@@ -4442,7 +4200,7 @@ var SessionRunManager = class {
4442
4200
  });
4443
4201
  };
4444
4202
  persistLiveSessionEvent = async (session, event) => {
4445
- if (!isDurableSessionEvent(event)) return;
4203
+ if (!isDurableSessionEvent$1(event)) return;
4446
4204
  await this.ncpSessionManager.appendSessionEvent({
4447
4205
  sessionId: session.sessionId,
4448
4206
  event
@@ -5018,14 +4776,15 @@ function waitForAgentRuntimeSessionReply(input) {
5018
4776
  let acceptedMessageId = null;
5019
4777
  const completedMessagesById = /* @__PURE__ */ new Map();
5020
4778
  let unsubscribe = () => void 0;
4779
+ const acceptMessageId = (messageId) => {
4780
+ acceptedMessageId = messageId;
4781
+ input.onAccepted(messageId);
4782
+ };
5021
4783
  const promise = new Promise((resolve, reject) => {
5022
4784
  unsubscribe = input.eventBus.on(eventKeys.ncpEvent, (event) => {
5023
4785
  switch (event.type) {
5024
4786
  case NcpEventType.MessageAccepted:
5025
- if (event.payload.correlationId === input.requestId) {
5026
- acceptedMessageId = event.payload.messageId;
5027
- input.onAccepted(event.payload.messageId);
5028
- }
4787
+ if (event.payload.correlationId === input.requestId) acceptMessageId(event.payload.messageId);
5029
4788
  return;
5030
4789
  case NcpEventType.MessageCompleted:
5031
4790
  if (event.payload.correlationId === input.requestId || event.payload.message.id === acceptedMessageId) {
@@ -5037,8 +4796,7 @@ function waitForAgentRuntimeSessionReply(input) {
5037
4796
  return;
5038
4797
  case NcpEventType.RunFinished:
5039
4798
  if ((event.payload.correlationId === input.requestId || event.payload.messageId === acceptedMessageId) && event.payload.messageId) {
5040
- acceptedMessageId = event.payload.messageId;
5041
- input.onAccepted(event.payload.messageId);
4799
+ acceptMessageId(event.payload.messageId);
5042
4800
  const completedMessage = completedMessagesById.get(event.payload.messageId);
5043
4801
  if (completedMessage) {
5044
4802
  unsubscribe();
@@ -5112,132 +4870,854 @@ function createAgentRuntimeSessionRequestDispatcher(options) {
5112
4870
  } };
5113
4871
  }
5114
4872
  //#endregion
5115
- //#region src/features/narp-runtime/services/builtin-narp-runtime-provider.service.ts
5116
- const NARP_API_MODE_HEADER = "x-nextclaw-narp-api-mode";
5117
- const NARP_HTTP_RUNTIME_KIND = "narp-http";
5118
- const NARP_STDIO_RUNTIME_KIND = "narp-stdio";
5119
- const BUILTIN_PROVIDER_REGISTRY = new ProviderRegistry(BUILTIN_PROVIDER_PLUGINS);
5120
- function readRecord(value) {
5121
- if (!value || typeof value !== "object" || Array.isArray(value)) return;
5122
- return value;
5123
- }
5124
- function readString$2(value) {
5125
- if (typeof value !== "string") return;
5126
- return value.trim() || void 0;
5127
- }
5128
- function resolveEntryIcon(entry) {
5129
- return entry.icon ?? null;
5130
- }
5131
- function resolveRequestedModel(params) {
5132
- const { configuredModel, defaultModel, input, sessionMetadata } = params;
5133
- const runMetadata = readRecord(input.metadata);
5134
- return readString$2(runMetadata?.preferred_model) ?? readString$2(runMetadata?.preferredModel) ?? readString$2(runMetadata?.model) ?? readString$2(sessionMetadata.preferred_model) ?? readString$2(sessionMetadata.preferredModel) ?? readString$2(sessionMetadata.model) ?? configuredModel ?? defaultModel;
4873
+ //#region src/features/agent-run/managers/agent-run-context-compaction.manager.ts
4874
+ var AgentRunContextCompactionManager = class {
4875
+ preflightService;
4876
+ constructor(configManager, providerManager, sessionRepository) {
4877
+ this.sessionRepository = sessionRepository;
4878
+ this.preflightService = new ContextCompactionPreflightService({
4879
+ configManager,
4880
+ providerManager
4881
+ });
4882
+ }
4883
+ runPreflight = async (input) => {
4884
+ const beginResult = this.preflightService.begin({
4885
+ inputMessages: [],
4886
+ requestMetadata: input.metadata,
4887
+ sessionId: input.sessionId,
4888
+ sessionMessages: input.messages,
4889
+ storedAgentId: input.agentId,
4890
+ storedMetadata: input.metadata
4891
+ });
4892
+ const events = await this.toEvents(input.sessionId, beginResult);
4893
+ if (!beginResult.pendingCompaction) return events;
4894
+ const finishResult = await this.preflightService.finish(beginResult.pendingCompaction);
4895
+ return [...events, ...await this.toEvents(input.sessionId, finishResult)];
4896
+ };
4897
+ toEvents = async (sessionId, result) => {
4898
+ if (Object.keys(result.metadataPatch).length > 0) await this.sessionRepository.patchSessionMetadata(sessionId, result.metadataPatch);
4899
+ if (!result.timelineMessage) return [];
4900
+ return [{
4901
+ type: NcpEventType.MessageSent,
4902
+ payload: {
4903
+ sessionId,
4904
+ message: result.timelineMessage
4905
+ }
4906
+ }];
4907
+ };
4908
+ };
4909
+ //#endregion
4910
+ //#region src/features/agent-run/managers/agent-run-request.manager.ts
4911
+ function toAgentRunRequest(envelope) {
4912
+ const metadata = envelope.metadata ?? {};
4913
+ const requestMetadata = {
4914
+ agentRuntimeId: metadata.agentRuntimeId,
4915
+ agentId: metadata.agentId,
4916
+ projectRoot: metadata.projectRoot,
4917
+ channel: metadata.channel,
4918
+ correlationId: envelope.correlationId,
4919
+ metadata: structuredClone(metadata),
4920
+ model: metadata.model,
4921
+ maxTokens: metadata.maxTokens,
4922
+ thinkingEffort: metadata.thinkingEffort
4923
+ };
4924
+ if (Array.isArray(envelope.content)) return {
4925
+ ...requestMetadata,
4926
+ sessionId: envelope.sessionId,
4927
+ message: {
4928
+ sessionId: envelope.sessionId ?? "",
4929
+ id: `user-message-${randomUUID()}`,
4930
+ role: "user",
4931
+ status: "final",
4932
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4933
+ parts: structuredClone(envelope.content)
4934
+ }
4935
+ };
4936
+ const sourceMessage = envelope.message;
4937
+ if (!sourceMessage) throw new Error("Invalid agent run send request.");
4938
+ const messageSessionId = sourceMessage.sessionId;
4939
+ const message = {
4940
+ ...sourceMessage,
4941
+ sessionId: messageSessionId ?? envelope.sessionId ?? "",
4942
+ id: sourceMessage.id ?? `user-message-${randomUUID()}`,
4943
+ role: sourceMessage.role ?? "user",
4944
+ status: sourceMessage.status ?? "final",
4945
+ timestamp: sourceMessage.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
4946
+ parts: structuredClone(sourceMessage.parts)
4947
+ };
4948
+ return {
4949
+ ...requestMetadata,
4950
+ sessionId: envelope.sessionId ?? messageSessionId,
4951
+ message
4952
+ };
5135
4953
  }
5136
- function resolveProviderApiMode(resolution) {
5137
- const wireApi = resolution.provider?.wireApi?.trim().toLowerCase();
5138
- if (wireApi === "responses") return "codex_responses";
5139
- if (wireApi === "chat") return "chat_completions";
5140
- const providerName = resolution.providerName?.trim().toLowerCase();
5141
- const apiBase = resolution.apiBase?.trim().toLowerCase() ?? "";
5142
- if (providerName === "anthropic" || apiBase.includes("anthropic.com")) return "anthropic_messages";
5143
- return "chat_completions";
4954
+ function toSessionMessageRequest(payload) {
4955
+ return {
4956
+ sessionId: payload.sessionId,
4957
+ message: {
4958
+ ...payload.message,
4959
+ sessionId: payload.sessionId
4960
+ },
4961
+ correlationId: payload.requestId
4962
+ };
5144
4963
  }
5145
- function buildProviderRoute(params) {
5146
- const model = resolveRequestedModel(params);
5147
- if (!model) return;
5148
- const resolution = resolveProviderRuntime(params.config, model);
5149
- if (!resolution.provider) return;
5150
- const apiBase = resolution.apiBase ?? (resolution.providerName ? BUILTIN_PROVIDER_REGISTRY.findProviderByName(resolution.providerName)?.defaultApiBase ?? null : null);
4964
+ function toRunHandle(accepted) {
5151
4965
  return {
5152
- model: resolution.providerLocalModel,
5153
- apiKey: resolution.apiKey,
5154
- apiBase,
5155
- headers: {
5156
- ...resolution.provider.extraHeaders ?? {},
5157
- [NARP_API_MODE_HEADER]: resolveProviderApiMode(resolution)
5158
- }
4966
+ sessionId: accepted.sessionId,
4967
+ userMessageId: accepted.userMessageId,
4968
+ assistantMessageId: null,
4969
+ runId: accepted.runId,
4970
+ correlationId: accepted.correlationId
5159
4971
  };
5160
4972
  }
5161
- var BuiltinHttpRuntimeSessionTypeService = class {
5162
- pendingDescribeByMode = /* @__PURE__ */ new Map();
5163
- constructor(entry, defaultModel) {
5164
- this.entry = entry;
5165
- this.defaultModel = defaultModel;
4973
+ function findCompletedAssistantMessage(messages, messageId) {
4974
+ return [...messages].reverse().find((message) => message.role === "assistant" && message.status === "final" && (!messageId || message.id === messageId)) ?? null;
4975
+ }
4976
+ var AgentRunRequestManager$1 = class {
4977
+ cleanups = [];
4978
+ started = false;
4979
+ constructor(agentRuntimeManager, configManager, contextProviderManager, eventBus, ingress, sessionRepository, sessionRunManager, toolProviderManager) {
4980
+ this.agentRuntimeManager = agentRuntimeManager;
4981
+ this.configManager = configManager;
4982
+ this.contextProviderManager = contextProviderManager;
4983
+ this.eventBus = eventBus;
4984
+ this.ingress = ingress;
4985
+ this.sessionRepository = sessionRepository;
4986
+ this.sessionRunManager = sessionRunManager;
4987
+ this.toolProviderManager = toolProviderManager;
5166
4988
  }
5167
- describe = async (describeParams) => {
5168
- const describeMode = describeParams?.describeMode === "probe" ? "probe" : "observation";
5169
- const pending = this.pendingDescribeByMode.get(describeMode);
5170
- if (pending) return await pending;
5171
- const nextDescribe = this.describeInternal();
5172
- this.pendingDescribeByMode.set(describeMode, nextDescribe);
5173
- try {
5174
- return await nextDescribe;
5175
- } finally {
5176
- this.pendingDescribeByMode.delete(describeMode);
5177
- }
4989
+ start = () => {
4990
+ if (this.started) return;
4991
+ this.started = true;
4992
+ this.cleanups.push(this.ingress.addHandler(ingressKeys.agentRun.send, this.handleSendRequest), this.ingress.addHandler(ingressKeys.agentRun.abort, this.handleAbortRequest), this.ingress.addHandler(ingressKeys.agentRun.sessionMessageRequest, this.handleSessionMessageRequest));
5178
4993
  };
5179
- describeInternal = async () => {
5180
- const config = new HttpRuntimeConfigResolver(this.entry.config ?? {}).resolve({ defaultModel: this.defaultModel });
5181
- if (!config.baseUrl) return {
5182
- icon: resolveEntryIcon(this.entry),
5183
- ready: false,
5184
- reason: "base_url_missing",
5185
- reasonMessage: "Configure the runtime entry baseUrl before starting an HTTP runtime session.",
5186
- recommendedModel: config.recommendedModel ?? null,
5187
- cta: {
5188
- kind: "settings",
5189
- label: "Configure HTTP Runtime"
5190
- }
4994
+ dispose = () => {
4995
+ while (this.cleanups.length > 0) this.cleanups.pop()?.();
4996
+ this.started = false;
4997
+ };
4998
+ handleSendRequest = async (envelope) => {
4999
+ if (!envelope.payload) throw new Error("Invalid agent run send request.");
5000
+ return toRunHandle(await this.send(toAgentRunRequest(envelope.payload)));
5001
+ };
5002
+ handleAbortRequest = async (envelope) => {
5003
+ if (!envelope.payload?.sessionId) throw new Error("Invalid agent run abort request.");
5004
+ await this.abort({ sessionId: envelope.payload.sessionId });
5005
+ };
5006
+ handleSessionMessageRequest = async (envelope) => {
5007
+ if (!envelope.payload) throw new Error("Invalid agent run session message request.");
5008
+ return toRunHandle(await this.send(toSessionMessageRequest(envelope.payload)));
5009
+ };
5010
+ send = async (request) => {
5011
+ const session = await this.sessionRepository.getOrCreateSession({
5012
+ sessionId: request.sessionId,
5013
+ agentId: request.agentId,
5014
+ agentRuntimeId: request.agentRuntimeId,
5015
+ channel: request.channel,
5016
+ metadata: request.metadata,
5017
+ model: request.model,
5018
+ projectRoot: request.projectRoot,
5019
+ task: readMessageTask(request.message),
5020
+ thinkingEffort: request.thinkingEffort
5021
+ });
5022
+ const sessionRun = this.sessionRunManager.getSessionRun(session.sessionId) ?? await this.sessionRunManager.createSessionRun(session.sessionId);
5023
+ const message = {
5024
+ ...request.message,
5025
+ sessionId: session.sessionId
5191
5026
  };
5192
- if (!((config.capabilityProbe ?? true) && Boolean(config.healthcheckUrl))) return {
5193
- icon: resolveEntryIcon(this.entry),
5194
- ready: true,
5195
- reason: null,
5196
- reasonMessage: null,
5197
- recommendedModel: config.recommendedModel ?? null,
5198
- ...config.supportedModels ? { supportedModels: config.supportedModels } : {},
5199
- cta: null
5027
+ const providerRequest = {
5028
+ ...request,
5029
+ sessionId: session.sessionId,
5030
+ message
5200
5031
  };
5201
- const controller = new AbortController();
5202
- const timeoutMs = config.healthcheckTimeoutMs ?? 3e3;
5203
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
5204
- try {
5205
- const response = await fetch(config.healthcheckUrl ?? "", {
5206
- method: "GET",
5207
- headers: config.headers,
5208
- signal: controller.signal
5032
+ sessionRun.inbox.enqueue(message);
5033
+ const activeRun = sessionRun.beginRun();
5034
+ const model = request.model ?? session.model ?? this.configManager.getDefaultModel();
5035
+ const agentId = request.agentId ?? session.agentId ?? resolveDefaultAgentProfileId(this.configManager.loadConfig());
5036
+ const spec = {
5037
+ runId: activeRun.runId,
5038
+ agentId,
5039
+ model,
5040
+ maxTokens: request.maxTokens ?? this.configManager.getModelMaxTokens(model),
5041
+ thinkingEffort: request.thinkingEffort ?? session.thinkingEffort ?? null,
5042
+ correlationId: request.correlationId
5043
+ };
5044
+ const runtime = this.agentRuntimeManager.getOrCreate({
5045
+ agentRuntimeId: session.agentRuntimeId,
5046
+ session,
5047
+ sessionRun
5048
+ });
5049
+ const contextBlocks = await this.contextProviderManager.buildContext(providerRequest);
5050
+ const tools = await this.toolProviderManager.buildTools(providerRequest);
5051
+ let messageCompletedSeen = false;
5052
+ lastValueFrom(from(runtime.run(spec, {
5053
+ contextBlocks,
5054
+ sessionRun,
5055
+ signal: activeRun.signal,
5056
+ tools
5057
+ })).pipe(tap((event) => {
5058
+ const eventsToPublish = [];
5059
+ if (event.type === NcpEventType.MessageCompleted) messageCompletedSeen = true;
5060
+ if (event.type === NcpEventType.RunFinished && !messageCompletedSeen) {
5061
+ const message = findCompletedAssistantMessage(sessionRun.getSnapshot().messages, event.payload.messageId);
5062
+ if (!message) throw new Error(`Run finished without a final assistant message for session "${session.sessionId}".`);
5063
+ eventsToPublish.push({
5064
+ type: NcpEventType.MessageCompleted,
5065
+ payload: {
5066
+ sessionId: event.payload.sessionId ?? session.sessionId,
5067
+ message,
5068
+ correlationId: event.payload.correlationId
5069
+ }
5070
+ });
5071
+ messageCompletedSeen = true;
5072
+ }
5073
+ eventsToPublish.push(event);
5074
+ for (const eventToPublish of eventsToPublish) this.eventBus.emit(eventKeys.ncpEvent, eventToPublish, {
5075
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
5076
+ source: "agent-run-request"
5209
5077
  });
5210
- if (!response.ok) return {
5211
- icon: resolveEntryIcon(this.entry),
5212
- ready: false,
5213
- reason: "healthcheck_failed",
5214
- reasonMessage: `HTTP runtime healthcheck returned HTTP ${response.status}.`,
5215
- recommendedModel: config.recommendedModel ?? null,
5216
- ...config.supportedModels ? { supportedModels: config.supportedModels } : {},
5217
- cta: null
5218
- };
5219
- return {
5220
- icon: resolveEntryIcon(this.entry),
5221
- ready: true,
5222
- reason: null,
5223
- reasonMessage: null,
5224
- recommendedModel: config.recommendedModel ?? null,
5225
- ...config.supportedModels ? { supportedModels: config.supportedModels } : {},
5226
- cta: null
5227
- };
5228
- } catch (error) {
5229
- return {
5230
- icon: resolveEntryIcon(this.entry),
5231
- ready: false,
5232
- reason: "healthcheck_unreachable",
5233
- reasonMessage: error instanceof Error ? error.message : "Failed to reach the configured HTTP runtime healthcheck.",
5234
- recommendedModel: config.recommendedModel ?? null,
5235
- ...config.supportedModels ? { supportedModels: config.supportedModels } : {},
5236
- cta: null
5078
+ }), catchError(async (error) => {
5079
+ const event = {
5080
+ type: NcpEventType.RunError,
5081
+ payload: {
5082
+ error: error instanceof Error ? error.message : String(error),
5083
+ runId: spec.runId,
5084
+ sessionId: session.sessionId,
5085
+ correlationId: spec.correlationId
5086
+ }
5237
5087
  };
5238
- } finally {
5239
- clearTimeout(timeout);
5240
- }
5088
+ await sessionRun.applyEvents([event]);
5089
+ this.eventBus.emit(eventKeys.ncpEvent, event, {
5090
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
5091
+ source: "agent-run-request"
5092
+ });
5093
+ })), { defaultValue: void 0 });
5094
+ return {
5095
+ sessionId: session.sessionId,
5096
+ userMessageId: message.id,
5097
+ runId: spec.runId,
5098
+ correlationId: request.correlationId
5099
+ };
5100
+ };
5101
+ abort = async (request) => {
5102
+ this.sessionRunManager.getSessionRun(request.sessionId)?.abortRun(request.runId);
5103
+ };
5104
+ };
5105
+ //#endregion
5106
+ //#region src/features/agent-run/managers/agent-runtime.manager.ts
5107
+ var AgentRuntimeManager$1 = class {
5108
+ providers = /* @__PURE__ */ new Map();
5109
+ entries = /* @__PURE__ */ new Map();
5110
+ globalRuntimes = /* @__PURE__ */ new Map();
5111
+ sessionRuntimes = /* @__PURE__ */ new Map();
5112
+ register = (registration) => {
5113
+ const kind = this.normalizeId(registration.kind);
5114
+ if (this.providers.has(kind)) throw new Error(`Agent runtime provider is already registered: ${kind}`);
5115
+ const normalizedRegistration = {
5116
+ ...registration,
5117
+ kind
5118
+ };
5119
+ this.providers.set(kind, normalizedRegistration);
5120
+ return async () => {
5121
+ if (this.providers.get(kind) !== normalizedRegistration) return;
5122
+ this.providers.delete(kind);
5123
+ await this.disposeAllRuntimes();
5124
+ };
5125
+ };
5126
+ applyEntries = (entries) => {
5127
+ this.entries.clear();
5128
+ for (const entry of entries) this.entries.set(this.normalizeId(entry.id), {
5129
+ ...entry,
5130
+ id: this.normalizeId(entry.id),
5131
+ type: this.normalizeId(entry.type)
5132
+ });
5133
+ };
5134
+ getOrCreate = (params) => {
5135
+ const { agentRuntimeId, session, sessionRun } = params;
5136
+ const entry = this.getEntry(agentRuntimeId);
5137
+ const provider = this.getProvider(entry.type);
5138
+ const reuseScope = this.resolveReuseScope(entry, provider);
5139
+ const cacheKey = reuseScope === "global" ? entry.id : `${entry.id}:${session.sessionId}`;
5140
+ const cache = reuseScope === "global" ? this.globalRuntimes : this.sessionRuntimes;
5141
+ const existing = cache.get(cacheKey);
5142
+ if (existing) return existing;
5143
+ const agentRuntime = provider.createRuntime({
5144
+ entry,
5145
+ session,
5146
+ sessionRun
5147
+ });
5148
+ cache.set(cacheKey, agentRuntime);
5149
+ return agentRuntime;
5150
+ };
5151
+ listSessionTypes = async (_params) => {
5152
+ const entries = [...this.entries.values()].filter((entry) => entry.enabled !== false);
5153
+ const options = await Promise.all(entries.map(async (entry) => {
5154
+ const provider = this.providers.get(entry.type);
5155
+ const descriptor = provider?.describeSessionTypeForEntry ? await provider.describeSessionTypeForEntry({
5156
+ entry,
5157
+ describeParams: _params
5158
+ }) : null;
5159
+ return {
5160
+ value: entry.id,
5161
+ label: entry.label,
5162
+ icon: descriptor?.icon ?? entry.icon ?? null,
5163
+ ready: provider ? descriptor?.ready ?? true : false,
5164
+ reason: provider ? descriptor?.reason ?? null : "runtime_provider_unavailable",
5165
+ reasonMessage: provider ? descriptor?.reasonMessage ?? null : `Runtime provider unavailable for type "${entry.type}".`,
5166
+ recommendedModel: descriptor?.recommendedModel ?? null,
5167
+ cta: descriptor?.cta ?? null,
5168
+ ...descriptor?.supportedModels ? { supportedModels: descriptor.supportedModels } : {}
5169
+ };
5170
+ }));
5171
+ return {
5172
+ defaultType: this.entries.has("native") ? DEFAULT_AGENT_RUNTIME_ENTRY_ID : entries[0]?.id ?? "native",
5173
+ options
5174
+ };
5175
+ };
5176
+ dispose = async () => {
5177
+ await this.disposeAllRuntimes();
5178
+ this.providers.clear();
5179
+ this.entries.clear();
5180
+ };
5181
+ normalizeId = (agentRuntimeId) => {
5182
+ const normalizedId = agentRuntimeId.trim();
5183
+ if (!normalizedId) throw new Error("Agent runtime id is required.");
5184
+ return normalizedId;
5185
+ };
5186
+ getEntry = (agentRuntimeId) => {
5187
+ const normalizedId = this.normalizeId(agentRuntimeId);
5188
+ const entry = this.entries.get(normalizedId);
5189
+ if (!entry || entry.enabled === false) throw new Error(`Agent runtime entry is not registered: ${normalizedId}`);
5190
+ return entry;
5191
+ };
5192
+ getProvider = (kind) => {
5193
+ const provider = this.providers.get(this.normalizeId(kind));
5194
+ if (!provider) throw new Error(`Agent runtime provider is not registered: ${kind}`);
5195
+ return provider;
5196
+ };
5197
+ resolveReuseScope = (entry, provider) => {
5198
+ return entry.config?.reuseScope === "session" || entry.config?.reuseScope === "global" ? entry.config.reuseScope : provider.defaultReuseScope;
5199
+ };
5200
+ disposeAllRuntimes = async () => {
5201
+ for (const runtime of [...this.globalRuntimes.values(), ...this.sessionRuntimes.values()]) await runtime.dispose?.();
5202
+ this.globalRuntimes.clear();
5203
+ this.sessionRuntimes.clear();
5204
+ };
5205
+ };
5206
+ //#endregion
5207
+ //#region src/features/agent-run/managers/context-provider.manager.ts
5208
+ var ContextProviderManager = class {
5209
+ providers = /* @__PURE__ */ new Set();
5210
+ register = (provider) => {
5211
+ this.providers.add(provider);
5212
+ return () => {
5213
+ this.providers.delete(provider);
5214
+ };
5215
+ };
5216
+ buildContext = async (request) => {
5217
+ const blocks = [];
5218
+ for (const provider of [...this.providers]) blocks.push(...await provider.provide(request));
5219
+ return blocks;
5220
+ };
5221
+ dispose = () => {
5222
+ this.providers.clear();
5223
+ };
5224
+ };
5225
+ //#endregion
5226
+ //#region src/features/agent-run/managers/session-run.manager.ts
5227
+ var MessageInbox = class {
5228
+ messages = [];
5229
+ enqueue = (message) => {
5230
+ this.messages.push(message);
5231
+ };
5232
+ drain = () => {
5233
+ return this.messages.splice(0, this.messages.length);
5234
+ };
5235
+ isEmpty = () => this.messages.length === 0;
5236
+ };
5237
+ function isConversationStateEvent(event) {
5238
+ return event.type !== NcpEventType.ContextWindowUpdated;
5239
+ }
5240
+ var SessionRun = class {
5241
+ inbox = new MessageInbox();
5242
+ sessionId;
5243
+ activeRunId = null;
5244
+ activeRunController = null;
5245
+ constructor(seed, stateManager = new DefaultNcpAgentConversationStateManager()) {
5246
+ this.stateManager = stateManager;
5247
+ this.sessionId = seed.sessionId;
5248
+ this.stateManager.hydrate({
5249
+ sessionId: seed.sessionId,
5250
+ messages: seed.messages
5251
+ });
5252
+ }
5253
+ getSnapshot = () => {
5254
+ const snapshot = this.stateManager.getSnapshot();
5255
+ return { messages: snapshot.streamingMessage ? [...snapshot.messages, snapshot.streamingMessage] : snapshot.messages };
5256
+ };
5257
+ applyEvents = async (events) => {
5258
+ this.applyRunEvents(events);
5259
+ const conversationEvents = events.filter(isConversationStateEvent);
5260
+ if (conversationEvents.length > 0) await this.stateManager.dispatchBatch(conversationEvents);
5261
+ };
5262
+ beginRun = () => {
5263
+ if (this.activeRunId) throw new Error(`Session ${this.sessionId} already has an active run.`);
5264
+ const runId = `agent-run-${randomUUID()}`;
5265
+ const controller = new AbortController();
5266
+ this.activeRunId = runId;
5267
+ this.activeRunController = controller;
5268
+ return {
5269
+ runId,
5270
+ signal: controller.signal
5271
+ };
5272
+ };
5273
+ abortRun = (runId) => {
5274
+ if (!this.activeRunId || !this.activeRunController) return false;
5275
+ if (runId && this.activeRunId !== runId) return false;
5276
+ this.activeRunController.abort();
5277
+ return true;
5278
+ };
5279
+ dispose = () => {
5280
+ this.activeRunController?.abort();
5281
+ this.activeRunController = null;
5282
+ this.activeRunId = null;
5283
+ };
5284
+ applyRunEvents = (events) => {
5285
+ for (const event of events) {
5286
+ if (event.type === NcpEventType.RunStarted && event.payload.runId) this.activeRunId = event.payload.runId;
5287
+ if (event.type === NcpEventType.MessageAbort || (event.type === NcpEventType.RunFinished || event.type === NcpEventType.RunError) && (!event.payload.runId || event.payload.runId === this.activeRunId)) {
5288
+ this.activeRunId = null;
5289
+ this.activeRunController = null;
5290
+ }
5291
+ }
5292
+ };
5293
+ };
5294
+ var SessionRunManager$1 = class {
5295
+ runs = /* @__PURE__ */ new Map();
5296
+ constructor(sessionRepository) {
5297
+ this.sessionRepository = sessionRepository;
5298
+ }
5299
+ getSessionRun = (sessionId) => this.runs.get(sessionId) ?? null;
5300
+ createSessionRun = async (sessionId) => {
5301
+ if (this.runs.has(sessionId)) throw new Error(`Session run already exists: ${sessionId}`);
5302
+ const run = new SessionRun({
5303
+ messages: await this.sessionRepository.listSessionMessages(sessionId),
5304
+ sessionId
5305
+ });
5306
+ this.runs.set(sessionId, run);
5307
+ return run;
5308
+ };
5309
+ deleteSessionRun = (sessionId) => {
5310
+ const run = this.runs.get(sessionId);
5311
+ if (!run) return false;
5312
+ run.dispose();
5313
+ return this.runs.delete(sessionId);
5314
+ };
5315
+ dispose = () => {
5316
+ for (const run of this.runs.values()) run.dispose();
5317
+ this.runs.clear();
5318
+ };
5319
+ };
5320
+ //#endregion
5321
+ //#region src/features/agent-run/managers/tool-provider.manager.ts
5322
+ var ToolProviderManager = class {
5323
+ providers = /* @__PURE__ */ new Set();
5324
+ register = (provider) => {
5325
+ this.providers.add(provider);
5326
+ return () => {
5327
+ this.providers.delete(provider);
5328
+ };
5329
+ };
5330
+ buildTools = async (request) => {
5331
+ const tools = [];
5332
+ for (const provider of [...this.providers]) tools.push(...await provider.provide(request));
5333
+ return tools;
5334
+ };
5335
+ dispose = () => {
5336
+ this.providers.clear();
5337
+ };
5338
+ };
5339
+ //#endregion
5340
+ //#region src/features/agent-run/repositories/session.repository.ts
5341
+ function readString$3(value) {
5342
+ return (typeof value === "string" ? value.trim() : "") || void 0;
5343
+ }
5344
+ function readEventSessionId$1(event) {
5345
+ return "payload" in event && "sessionId" in event.payload ? readString$3(event.payload.sessionId) : void 0;
5346
+ }
5347
+ function isDurableSessionEvent(event) {
5348
+ return event.type !== NcpEventType.ContextWindowUpdated;
5349
+ }
5350
+ function readThinkingEffort(metadata) {
5351
+ return readString$3(metadata?.thinkingEffort) ?? null;
5352
+ }
5353
+ function readProjectRoot(metadata) {
5354
+ return readString$3(metadata?.project_root) ?? readString$3(metadata?.projectRoot);
5355
+ }
5356
+ var SessionRepository = class {
5357
+ cleanups = [];
5358
+ started = false;
5359
+ constructor(eventBus, ncpSessionManager) {
5360
+ this.eventBus = eventBus;
5361
+ this.ncpSessionManager = ncpSessionManager;
5362
+ }
5363
+ start = () => {
5364
+ if (this.started) return;
5365
+ this.started = true;
5366
+ this.cleanups.push(this.eventBus.on(eventKeys.ncpEvent, async (event) => {
5367
+ const sessionId = readEventSessionId$1(event);
5368
+ if (!sessionId || !isDurableSessionEvent(event)) return;
5369
+ await this.appendSessionEvent(sessionId, event);
5370
+ }));
5371
+ };
5372
+ dispose = () => {
5373
+ while (this.cleanups.length > 0) this.cleanups.pop()?.();
5374
+ this.started = false;
5375
+ };
5376
+ getSession = async (sessionId) => {
5377
+ const record = await this.ncpSessionManager.getSessionRecord(sessionId);
5378
+ if (!record) throw new Error(`Session not found: ${sessionId}`);
5379
+ const agentRuntimeId = readString$3(record.metadata?.agentRuntimeId) ?? "native";
5380
+ const model = readString$3(record.metadata?.model);
5381
+ return {
5382
+ sessionId: record.sessionId,
5383
+ agentId: record.agentId,
5384
+ agentRuntimeId,
5385
+ metadata: structuredClone(record.metadata ?? {}),
5386
+ model,
5387
+ projectRoot: readProjectRoot(record.metadata),
5388
+ thinkingEffort: readThinkingEffort(record.metadata)
5389
+ };
5390
+ };
5391
+ createSession = async (params) => {
5392
+ const { agentId, agentRuntimeId: requestedAgentRuntimeId, channel, metadata, model, projectRoot, sessionId, task, thinkingEffort } = params;
5393
+ const agentRuntimeId = requestedAgentRuntimeId ?? "native";
5394
+ const created = await this.ncpSessionManager.createSession({
5395
+ sourceSessionMetadata: {},
5396
+ sessionId,
5397
+ task: task ?? "Session",
5398
+ agentId,
5399
+ metadataOverrides: {
5400
+ ...structuredClone(metadata ?? {}),
5401
+ agentRuntimeId,
5402
+ channel
5403
+ },
5404
+ model,
5405
+ projectRoot,
5406
+ runtime: agentRuntimeId,
5407
+ sessionType: agentRuntimeId,
5408
+ thinkingLevel: thinkingEffort ?? void 0
5409
+ });
5410
+ return {
5411
+ sessionId: created.sessionId,
5412
+ agentId,
5413
+ agentRuntimeId,
5414
+ metadata: structuredClone(created.metadata ?? {}),
5415
+ model,
5416
+ projectRoot,
5417
+ thinkingEffort: thinkingEffort ?? null
5418
+ };
5419
+ };
5420
+ getOrCreateSession = async (params) => {
5421
+ if (!params.sessionId) return await this.createSession(params);
5422
+ if (await this.ncpSessionManager.getSessionRecord(params.sessionId)) return await this.getSession(params.sessionId);
5423
+ return await this.createSession(params);
5424
+ };
5425
+ listSessionMessages = async (sessionId) => {
5426
+ return await this.ncpSessionManager.listSessionMessages(sessionId);
5427
+ };
5428
+ patchSessionMetadata = async (sessionId, patch) => {
5429
+ if (!await this.ncpSessionManager.updateSessionMetadata(sessionId, patch)) throw new Error(`Session metadata was not updated: ${sessionId}`);
5430
+ };
5431
+ appendSessionEvent = async (sessionId, event) => {
5432
+ await this.ncpSessionManager.appendSessionEvent({
5433
+ event,
5434
+ sessionId
5435
+ });
5436
+ };
5437
+ };
5438
+ //#endregion
5439
+ //#region src/features/agent-run/services/agent-run-message-projector.service.ts
5440
+ var AgentRunMessageProjector = class {
5441
+ project = (params) => projectNcpMessagesWithContextCompaction({
5442
+ sessionId: params.sessionId,
5443
+ sessionMessages: params.messages
5444
+ });
5445
+ };
5446
+ //#endregion
5447
+ //#region src/features/agent-run/services/agent-run-model-input-budgeter.service.ts
5448
+ var AgentRunModelInputBudgeter = class {
5449
+ inputBudgetPruner = new InputBudgetPruner();
5450
+ constructor(configManager) {
5451
+ this.configManager = configManager;
5452
+ }
5453
+ prune = async (params) => {
5454
+ const config = this.configManager.loadConfig();
5455
+ const defaultAgentId = resolveDefaultAgentProfileId(config);
5456
+ const profile = findEffectiveAgentProfile(config, params.spec.agentId) ?? findEffectiveAgentProfile(config, defaultAgentId);
5457
+ const contextTokens = profile?.contextTokens ?? config.agents.defaults.contextTokens;
5458
+ const reservedContextTokens = ContextWindowBudgetService.resolveReservedContextTokens({
5459
+ contextTokens,
5460
+ configuredReservedContextTokens: profile?.reservedContextTokens ?? config.agents.defaults.reservedContextTokens
5461
+ });
5462
+ const pruned = this.inputBudgetPruner.prune({
5463
+ messages: params.messages.map((message) => ({ ...message })),
5464
+ contextTokens,
5465
+ reserveTokensFloor: reservedContextTokens,
5466
+ softThresholdTokens: 0
5467
+ });
5468
+ return {
5469
+ ...pruned,
5470
+ messages: pruned.messages
5471
+ };
5472
+ };
5473
+ };
5474
+ //#endregion
5475
+ //#region src/features/agent-run/services/agent-run-model-input-builder.service.ts
5476
+ var AgentRunModelInputBuilder = class {
5477
+ constructor(messageProjector, modelInputBudgeter, assetStore = null) {
5478
+ this.messageProjector = messageProjector;
5479
+ this.modelInputBudgeter = modelInputBudgeter;
5480
+ this.assetStore = assetStore;
5481
+ }
5482
+ build = async (request) => {
5483
+ const contextContent = request.contextBlocks.map((block) => block.trim()).filter(Boolean).join("\n\n");
5484
+ const contextMessages = contextContent ? [{
5485
+ role: "system",
5486
+ content: contextContent
5487
+ }] : [];
5488
+ const conversationMessages = this.messageProjector.project({
5489
+ sessionId: request.sessionId,
5490
+ messages: request.messages
5491
+ }).flatMap((message) => ncpMessageToOpenAiMessages(message, { assetStore: this.assetStore }));
5492
+ const pruned = await this.modelInputBudgeter.prune({
5493
+ spec: request.spec,
5494
+ messages: [...contextMessages, ...conversationMessages]
5495
+ });
5496
+ const tools = request.tools.map((tool) => buildOpenAiFunctionTool({
5497
+ name: tool.name,
5498
+ description: tool.description,
5499
+ parameters: tool.parameters
5500
+ }));
5501
+ return {
5502
+ messages: pruned.messages,
5503
+ tools: tools.length > 0 ? tools : void 0,
5504
+ model: request.spec.model,
5505
+ thinkingLevel: request.spec.thinkingEffort ?? null,
5506
+ max_tokens: request.spec.maxTokens
5507
+ };
5508
+ };
5509
+ };
5510
+ //#endregion
5511
+ //#region src/features/agent-run/services/ncp-agent-runtime-wrapper.service.ts
5512
+ var NcpAgentRuntimeWrapper = class {
5513
+ stateManager = new DefaultNcpAgentConversationStateManager();
5514
+ runtime = null;
5515
+ currentTools = [];
5516
+ constructor(params) {
5517
+ this.params = params;
5518
+ }
5519
+ run = async function* (spec, options) {
5520
+ const { sessionRun, tools } = options;
5521
+ this.currentTools = tools.map(this.toOpenAiTool);
5522
+ const messages = sessionRun.inbox.drain();
5523
+ for (const event of this.toMessageSentEvents(messages, spec, sessionRun.sessionId)) yield await this.applyEvent(sessionRun, event);
5524
+ const input = {
5525
+ sessionId: sessionRun.sessionId,
5526
+ runId: spec.runId,
5527
+ messages,
5528
+ correlationId: spec.correlationId,
5529
+ metadata: this.buildMetadata(spec)
5530
+ };
5531
+ for await (const event of this.getRuntime().run(input, { signal: options.signal })) yield await this.applyEvent(sessionRun, event);
5532
+ this.currentTools = [];
5533
+ };
5534
+ dispose = async () => {
5535
+ if (this.runtime && "dispose" in this.runtime && typeof this.runtime.dispose === "function") await this.runtime.dispose();
5536
+ this.runtime = null;
5537
+ this.currentTools = [];
5538
+ };
5539
+ getRuntime = () => {
5540
+ if (!this.runtime) this.runtime = this.params.createRuntime({
5541
+ resolveTools: () => this.currentTools,
5542
+ stateManager: this.stateManager
5543
+ });
5544
+ return this.runtime;
5545
+ };
5546
+ toMessageSentEvents = (messages, spec, sessionId) => messages.map((message) => ({
5547
+ type: NcpEventType.MessageSent,
5548
+ payload: {
5549
+ sessionId,
5550
+ message,
5551
+ correlationId: spec.correlationId
5552
+ }
5553
+ }));
5554
+ buildMetadata = (spec) => ({
5555
+ ...this.params.session.metadata,
5556
+ agentId: spec.agentId,
5557
+ agentRuntimeId: this.params.session.agentRuntimeId,
5558
+ maxTokens: spec.maxTokens,
5559
+ model: spec.model,
5560
+ preferred_model: spec.model,
5561
+ thinkingEffort: spec.thinkingEffort
5562
+ });
5563
+ toOpenAiTool = (tool) => buildOpenAiFunctionTool({
5564
+ name: tool.name,
5565
+ description: tool.description,
5566
+ parameters: tool.parameters
5567
+ });
5568
+ applyEvent = async (sessionRun, event) => {
5569
+ await sessionRun.applyEvents([event]);
5570
+ return event;
5571
+ };
5572
+ };
5573
+ //#endregion
5574
+ //#region src/features/agent-run/utils/agent-run-request-metadata.utils.ts
5575
+ function normalizeString(value) {
5576
+ return value?.trim() || void 0;
5577
+ }
5578
+ function buildAgentRunRequestMetadata(params) {
5579
+ const { request, session } = params;
5580
+ const agentId = normalizeString(request.agentId ?? session?.agentId);
5581
+ const projectRoot = normalizeString(request.projectRoot ?? session?.projectRoot);
5582
+ const channel = normalizeString(request.channel);
5583
+ const model = normalizeString(request.model ?? session?.model);
5584
+ return {
5585
+ ...structuredClone(session?.metadata ?? {}),
5586
+ agentId,
5587
+ projectRoot,
5588
+ project_root: projectRoot,
5589
+ channel,
5590
+ model,
5591
+ thinkingEffort: request.thinkingEffort ?? session?.thinkingEffort
5592
+ };
5593
+ }
5594
+ //#endregion
5595
+ //#region src/features/narp-runtime/services/builtin-narp-runtime-provider.service.ts
5596
+ const NARP_API_MODE_HEADER = "x-nextclaw-narp-api-mode";
5597
+ const NARP_HTTP_RUNTIME_KIND = "narp-http";
5598
+ const NARP_STDIO_RUNTIME_KIND = "narp-stdio";
5599
+ const BUILTIN_PROVIDER_REGISTRY = new ProviderRegistry(BUILTIN_PROVIDER_PLUGINS);
5600
+ function readRecord(value) {
5601
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
5602
+ return value;
5603
+ }
5604
+ function readString$2(value) {
5605
+ if (typeof value !== "string") return;
5606
+ return value.trim() || void 0;
5607
+ }
5608
+ function resolveEntryIcon(entry) {
5609
+ return entry.icon ?? null;
5610
+ }
5611
+ function resolveRequestedModel(params) {
5612
+ const { configuredModel, defaultModel, input, sessionMetadata } = params;
5613
+ const runMetadata = readRecord(input.metadata);
5614
+ return readString$2(runMetadata?.preferred_model) ?? readString$2(runMetadata?.preferredModel) ?? readString$2(runMetadata?.model) ?? readString$2(sessionMetadata.preferred_model) ?? readString$2(sessionMetadata.preferredModel) ?? readString$2(sessionMetadata.model) ?? configuredModel ?? defaultModel;
5615
+ }
5616
+ function resolveProviderApiMode(resolution) {
5617
+ const wireApi = resolution.provider?.wireApi?.trim().toLowerCase();
5618
+ if (wireApi === "responses") return "codex_responses";
5619
+ if (wireApi === "chat") return "chat_completions";
5620
+ const providerName = resolution.providerName?.trim().toLowerCase();
5621
+ const apiBase = resolution.apiBase?.trim().toLowerCase() ?? "";
5622
+ if (providerName === "anthropic" || apiBase.includes("anthropic.com")) return "anthropic_messages";
5623
+ return "chat_completions";
5624
+ }
5625
+ function buildProviderRoute(params) {
5626
+ const model = resolveRequestedModel(params);
5627
+ if (!model) return;
5628
+ const resolution = resolveProviderRuntime(params.config, model);
5629
+ if (!resolution.provider) return;
5630
+ const apiBase = resolution.apiBase ?? (resolution.providerName ? BUILTIN_PROVIDER_REGISTRY.findProviderByName(resolution.providerName)?.defaultApiBase ?? null : null);
5631
+ return {
5632
+ model: resolution.providerLocalModel,
5633
+ apiKey: resolution.apiKey,
5634
+ apiBase,
5635
+ headers: {
5636
+ ...resolution.provider.extraHeaders ?? {},
5637
+ [NARP_API_MODE_HEADER]: resolveProviderApiMode(resolution)
5638
+ }
5639
+ };
5640
+ }
5641
+ var BuiltinHttpRuntimeSessionTypeService = class {
5642
+ pendingDescribeByMode = /* @__PURE__ */ new Map();
5643
+ constructor(entry, defaultModel) {
5644
+ this.entry = entry;
5645
+ this.defaultModel = defaultModel;
5646
+ }
5647
+ describe = async (describeParams) => {
5648
+ const describeMode = describeParams?.describeMode === "probe" ? "probe" : "observation";
5649
+ const pending = this.pendingDescribeByMode.get(describeMode);
5650
+ if (pending) return await pending;
5651
+ const nextDescribe = this.describeInternal();
5652
+ this.pendingDescribeByMode.set(describeMode, nextDescribe);
5653
+ try {
5654
+ return await nextDescribe;
5655
+ } finally {
5656
+ this.pendingDescribeByMode.delete(describeMode);
5657
+ }
5658
+ };
5659
+ describeInternal = async () => {
5660
+ const config = new HttpRuntimeConfigResolver(this.entry.config ?? {}).resolve({ defaultModel: this.defaultModel });
5661
+ if (!config.baseUrl) return {
5662
+ icon: resolveEntryIcon(this.entry),
5663
+ ready: false,
5664
+ reason: "base_url_missing",
5665
+ reasonMessage: "Configure the runtime entry baseUrl before starting an HTTP runtime session.",
5666
+ recommendedModel: config.recommendedModel ?? null,
5667
+ cta: {
5668
+ kind: "settings",
5669
+ label: "Configure HTTP Runtime"
5670
+ }
5671
+ };
5672
+ if (!((config.capabilityProbe ?? true) && Boolean(config.healthcheckUrl))) return {
5673
+ icon: resolveEntryIcon(this.entry),
5674
+ ready: true,
5675
+ reason: null,
5676
+ reasonMessage: null,
5677
+ recommendedModel: config.recommendedModel ?? null,
5678
+ ...config.supportedModels ? { supportedModels: config.supportedModels } : {},
5679
+ cta: null
5680
+ };
5681
+ const controller = new AbortController();
5682
+ const timeoutMs = config.healthcheckTimeoutMs ?? 3e3;
5683
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
5684
+ try {
5685
+ const response = await fetch(config.healthcheckUrl ?? "", {
5686
+ method: "GET",
5687
+ headers: config.headers,
5688
+ signal: controller.signal
5689
+ });
5690
+ if (!response.ok) return {
5691
+ icon: resolveEntryIcon(this.entry),
5692
+ ready: false,
5693
+ reason: "healthcheck_failed",
5694
+ reasonMessage: `HTTP runtime healthcheck returned HTTP ${response.status}.`,
5695
+ recommendedModel: config.recommendedModel ?? null,
5696
+ ...config.supportedModels ? { supportedModels: config.supportedModels } : {},
5697
+ cta: null
5698
+ };
5699
+ return {
5700
+ icon: resolveEntryIcon(this.entry),
5701
+ ready: true,
5702
+ reason: null,
5703
+ reasonMessage: null,
5704
+ recommendedModel: config.recommendedModel ?? null,
5705
+ ...config.supportedModels ? { supportedModels: config.supportedModels } : {},
5706
+ cta: null
5707
+ };
5708
+ } catch (error) {
5709
+ return {
5710
+ icon: resolveEntryIcon(this.entry),
5711
+ ready: false,
5712
+ reason: "healthcheck_unreachable",
5713
+ reasonMessage: error instanceof Error ? error.message : "Failed to reach the configured HTTP runtime healthcheck.",
5714
+ recommendedModel: config.recommendedModel ?? null,
5715
+ ...config.supportedModels ? { supportedModels: config.supportedModels } : {},
5716
+ cta: null
5717
+ };
5718
+ } finally {
5719
+ clearTimeout(timeout);
5720
+ }
5241
5721
  };
5242
5722
  };
5243
5723
  var BuiltinStdioRuntimeSessionTypeService = class {
@@ -5356,6 +5836,7 @@ var BuiltinNarpRuntimeProviderService = class {
5356
5836
  ...resolvedConfig.basePath ? { basePath: resolvedConfig.basePath } : {},
5357
5837
  ...resolvedConfig.endpointId ? { endpointId: resolvedConfig.endpointId } : {},
5358
5838
  ...resolvedConfig.headers ? { headers: resolvedConfig.headers } : {},
5839
+ resolveTools: runtimeParams.resolveTools,
5359
5840
  stateManager: runtimeParams.stateManager,
5360
5841
  resolveProviderRoute: (input) => buildProviderRoute({
5361
5842
  config: this.configManager.loadConfig(),
@@ -5371,6 +5852,7 @@ var BuiltinNarpRuntimeProviderService = class {
5371
5852
  return new StdioRuntimeNcpAgentRuntime({
5372
5853
  ...new StdioRuntimeConfigResolver(config).resolve(),
5373
5854
  sessionId: runtimeParams.sessionId,
5855
+ resolveTools: runtimeParams.resolveTools,
5374
5856
  stateManager: runtimeParams.stateManager,
5375
5857
  resolveProviderRoute: (input) => buildProviderRoute({
5376
5858
  config: this.configManager.loadConfig(),
@@ -5383,36 +5865,457 @@ var BuiltinNarpRuntimeProviderService = class {
5383
5865
  };
5384
5866
  };
5385
5867
  //#endregion
5386
- //#region src/contributions/agent-runtime/index.ts
5387
- var AgentRuntimeContribution = class {
5388
- cleanups = [];
5868
+ //#region src/contributions/kernel-branch/contributions/agent-run-runtime/index.ts
5869
+ var AgentRunRuntimeContribution = class {
5870
+ cleanups = [];
5871
+ modelInputBuilder;
5872
+ constructor(kernel, branch) {
5873
+ this.kernel = kernel;
5874
+ this.branch = branch;
5875
+ this.modelInputBuilder = new AgentRunModelInputBuilder(new AgentRunMessageProjector(), new AgentRunModelInputBudgeter(kernel.configManager), kernel.assetStore);
5876
+ }
5877
+ start = () => {
5878
+ if (this.cleanups.length > 0) return;
5879
+ const { entries } = resolveAgentRuntimeEntries({ config: this.kernel.configManager.loadConfig() });
5880
+ this.branch.agentRuntimeManager.applyEntries(entries);
5881
+ this.cleanups.push(this.registerNativeRuntime());
5882
+ for (const provider of new BuiltinNarpRuntimeProviderService(this.kernel.configManager).createProviders()) this.cleanups.push(this.registerNarpRuntime(provider));
5883
+ };
5884
+ dispose = async () => {
5885
+ while (this.cleanups.length > 0) await this.cleanups.pop()?.();
5886
+ };
5887
+ registerNativeRuntime = () => this.branch.agentRuntimeManager.register({
5888
+ kind: DEFAULT_AGENT_RUNTIME_ENTRY_ID,
5889
+ label: "Native",
5890
+ defaultReuseScope: "global",
5891
+ createRuntime: () => new DefaultNcpAgentRuntime$1({
5892
+ llmApi: new ProviderManagerNcpLLMApi(this.kernel.llmProviders),
5893
+ modelInputBuilder: this.modelInputBuilder,
5894
+ runPreflight: async ({ spec, sessionRun }) => {
5895
+ const session = await this.branch.sessionRepository.getSession(sessionRun.sessionId);
5896
+ return await this.branch.contextCompactionManager.runPreflight({
5897
+ agentId: spec.agentId,
5898
+ messages: sessionRun.getSnapshot().messages,
5899
+ metadata: session.metadata,
5900
+ sessionId: sessionRun.sessionId
5901
+ });
5902
+ }
5903
+ })
5904
+ });
5905
+ registerNarpRuntime = (provider) => this.branch.agentRuntimeManager.register({
5906
+ kind: provider.kind,
5907
+ label: provider.label,
5908
+ defaultReuseScope: provider.kind === "narp-stdio" ? "session" : "session",
5909
+ describeSessionTypeForEntry: provider.describeSessionTypeForEntry,
5910
+ createRuntime: ({ entry, session }) => {
5911
+ if (!provider.createRuntimeForEntry) throw new Error(`Agent runtime provider does not support entries: ${provider.kind}`);
5912
+ return new NcpAgentRuntimeWrapper({
5913
+ session,
5914
+ createRuntime: ({ resolveTools, stateManager }) => provider.createRuntimeForEntry({
5915
+ entry,
5916
+ runtimeParams: {
5917
+ sessionId: session.sessionId,
5918
+ ...session.agentId ? { agentId: session.agentId } : {},
5919
+ resolveAssetContentPath: (assetUri) => this.kernel.assetStore.resolveContentPath(assetUri),
5920
+ resolveTools,
5921
+ sessionMetadata: session.metadata,
5922
+ stateManager
5923
+ }
5924
+ })
5925
+ });
5926
+ }
5927
+ });
5928
+ };
5929
+ //#endregion
5930
+ //#region src/contributions/kernel-branch/contributions/context-window/index.ts
5931
+ const STREAM_REFRESH_DELAY_MS$1 = 1500;
5932
+ function formatBackgroundError$1(error) {
5933
+ if (error instanceof Error) return error.stack ?? error.message;
5934
+ return String(error);
5935
+ }
5936
+ var ContextWindowContribution = class {
5937
+ contextWindowPreview;
5938
+ lastPublishedSignatureBySession = /* @__PURE__ */ new Map();
5939
+ pendingTimers = /* @__PURE__ */ new Map();
5940
+ stopped = true;
5941
+ unsubscribeNcpEvent = null;
5942
+ constructor(kernel, branch) {
5943
+ this.kernel = kernel;
5944
+ this.branch = branch;
5945
+ this.contextWindowPreview = new ContextCompactionPreflightService({ configManager: kernel.configManager });
5946
+ }
5947
+ start = () => {
5948
+ if (this.unsubscribeNcpEvent) return;
5949
+ this.stopped = false;
5950
+ this.unsubscribeNcpEvent = this.kernel.eventBus.on(eventKeys.ncpEvent, this.handleNcpEvent);
5951
+ };
5952
+ dispose = () => {
5953
+ this.unsubscribeNcpEvent?.();
5954
+ this.unsubscribeNcpEvent = null;
5955
+ this.stopped = true;
5956
+ for (const timer of this.pendingTimers.values()) clearTimeout(timer);
5957
+ this.pendingTimers.clear();
5958
+ this.lastPublishedSignatureBySession.clear();
5959
+ };
5960
+ handleNcpEvent = (event) => {
5961
+ const sessionId = readContextWindowEventSessionId(event);
5962
+ if (!sessionId || !this.branch.sessionRunManager.getSessionRun(sessionId)) return;
5963
+ if (event.type === NcpEventType.ContextWindowUpdated) {
5964
+ this.rememberPublishedContextWindow(sessionId, event.payload.contextWindow);
5965
+ return;
5966
+ }
5967
+ if (shouldRefreshContextWindowImmediately(event)) {
5968
+ this.refreshNow(sessionId);
5969
+ return;
5970
+ }
5971
+ if (shouldRefreshContextWindowDuringStream(event)) this.refreshSoon(sessionId);
5972
+ };
5973
+ refreshSoon = (sessionId) => {
5974
+ if (this.pendingTimers.has(sessionId)) return;
5975
+ const timer = setTimeout(() => {
5976
+ this.pendingTimers.delete(sessionId);
5977
+ this.refreshNow(sessionId);
5978
+ }, STREAM_REFRESH_DELAY_MS$1);
5979
+ this.pendingTimers.set(sessionId, timer);
5980
+ };
5981
+ refreshNow = (sessionId) => {
5982
+ const timer = this.pendingTimers.get(sessionId);
5983
+ if (timer) {
5984
+ clearTimeout(timer);
5985
+ this.pendingTimers.delete(sessionId);
5986
+ }
5987
+ this.publishContextWindow(sessionId).catch((error) => {
5988
+ console.error(`[kernel-branch-context-window] failed to refresh ${sessionId}: ${formatBackgroundError$1(error)}`);
5989
+ });
5990
+ };
5991
+ publishContextWindow = async (sessionId) => {
5992
+ if (this.stopped) return;
5993
+ const sessionRun = this.branch.sessionRunManager.getSessionRun(sessionId);
5994
+ if (!sessionRun) return;
5995
+ const session = await this.branch.sessionRepository.getSession(sessionId);
5996
+ const contextWindow = this.contextWindowPreview.preview({
5997
+ requestMetadata: session.metadata,
5998
+ sessionId,
5999
+ sessionMessages: sessionRun.getSnapshot().messages,
6000
+ storedAgentId: session.agentId,
6001
+ storedMetadata: session.metadata
6002
+ });
6003
+ if (!isContextWindowSnapshot(contextWindow) || this.stopped) return;
6004
+ const signature = createContextWindowSignature(contextWindow);
6005
+ if (this.lastPublishedSignatureBySession.get(sessionId) === signature) return;
6006
+ this.lastPublishedSignatureBySession.set(sessionId, signature);
6007
+ this.kernel.eventBus.emit(eventKeys.ncpEvent, {
6008
+ type: NcpEventType.ContextWindowUpdated,
6009
+ payload: {
6010
+ contextWindow,
6011
+ sessionId
6012
+ }
6013
+ }, {
6014
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
6015
+ source: "kernel-branch-context-window"
6016
+ });
6017
+ };
6018
+ rememberPublishedContextWindow = (sessionId, contextWindow) => {
6019
+ this.lastPublishedSignatureBySession.set(sessionId, createContextWindowSignature(contextWindow));
6020
+ };
6021
+ };
6022
+ //#endregion
6023
+ //#region src/contributions/kernel-branch/contributions/context-provider/providers/kernel-context.provider.ts
6024
+ var KernelContextProvider = class {
6025
+ constructor(kernel, branch) {
6026
+ this.kernel = kernel;
6027
+ this.branch = branch;
6028
+ }
6029
+ provide = async (request) => {
6030
+ const session = request.sessionId ? await this.branch.sessionRepository.getSession(request.sessionId) : null;
6031
+ const sessionId = session?.sessionId ?? request.sessionId ?? request.message.sessionId ?? "";
6032
+ const requestMetadata = buildAgentRunRequestMetadata({
6033
+ request,
6034
+ session
6035
+ });
6036
+ const runContext = resolveNextclawNcpRunContext({
6037
+ configManager: this.kernel.configManager,
6038
+ sessionId,
6039
+ requestMetadata,
6040
+ sessionMetadata: session?.metadata ?? requestMetadata,
6041
+ storedAgentId: request.agentId ?? session?.agentId
6042
+ });
6043
+ const toolRegistry = this.kernel.toolManager.createRuntimeRegistry({ updateToolCallResult: async () => void 0 });
6044
+ toolRegistry.prepareForRun(runContext.toolRunContext);
6045
+ return [this.buildSystemContextBlock({
6046
+ availableTools: buildToolCatalogEntries(toolRegistry.getToolDefinitions()),
6047
+ runContext
6048
+ })];
6049
+ };
6050
+ buildSystemContextBlock = (params) => {
6051
+ const { availableTools, runContext } = params;
6052
+ const lines = [
6053
+ new ContextBuilder(runContext.effectiveWorkspace, runContext.config.agents.context, {
6054
+ hostWorkspace: runContext.profile.workspace,
6055
+ sessionProjectRoot: readSessionProjectRoot(runContext.sessionMetadata)
6056
+ }).buildSystemPrompt(void 0, runContext.sessionKey, availableTools, [buildSessionOrchestrationSection(), buildMinimalSystemExecutionPrompt(runContext.effectiveModel)]),
6057
+ "## Current Session",
6058
+ `Channel: ${runContext.channel}`,
6059
+ `Chat ID: ${runContext.chatId}`,
6060
+ `Session: ${runContext.sessionKey}`
6061
+ ];
6062
+ if (runContext.runtimeThinking) lines.push(`Thinking policy: ${runContext.runtimeThinking}`);
6063
+ return lines.join("\n");
6064
+ };
6065
+ };
6066
+ //#endregion
6067
+ //#region src/contributions/kernel-branch/contributions/context-provider/index.ts
6068
+ var ContextProviderContribution = class {
6069
+ unregister = null;
6070
+ constructor(kernel, branch) {
6071
+ this.kernel = kernel;
6072
+ this.branch = branch;
6073
+ }
6074
+ start = () => {
6075
+ if (this.unregister) return;
6076
+ this.unregister = this.branch.contextProviderManager.register(new KernelContextProvider(this.kernel, this.branch));
6077
+ };
6078
+ dispose = () => {
6079
+ this.unregister?.();
6080
+ this.unregister = null;
6081
+ };
6082
+ };
6083
+ //#endregion
6084
+ //#region src/contributions/kernel-branch/contributions/tool-provider/providers/kernel-tool.provider.ts
6085
+ var KernelToolProvider = class {
6086
+ constructor(kernel, branch) {
6087
+ this.kernel = kernel;
6088
+ this.branch = branch;
6089
+ }
6090
+ provide = async (request) => {
6091
+ const session = request.sessionId ? await this.branch.sessionRepository.getSession(request.sessionId) : null;
6092
+ const sessionId = session?.sessionId ?? request.sessionId ?? request.message.sessionId ?? "";
6093
+ const requestMetadata = buildAgentRunRequestMetadata({
6094
+ request,
6095
+ session
6096
+ });
6097
+ const runContext = resolveNextclawNcpRunContext({
6098
+ configManager: this.kernel.configManager,
6099
+ sessionId,
6100
+ requestMetadata,
6101
+ sessionMetadata: session?.metadata ?? requestMetadata,
6102
+ storedAgentId: request.agentId ?? session?.agentId
6103
+ });
6104
+ const toolRegistry = this.kernel.toolManager.createRuntimeRegistry({ updateToolCallResult: async ({ result, sessionId: targetSessionId, toolCallId }) => {
6105
+ this.kernel.eventBus.emit(eventKeys.ncpEvent, {
6106
+ type: NcpEventType.MessageToolCallResult,
6107
+ payload: {
6108
+ sessionId: targetSessionId,
6109
+ toolCallId,
6110
+ content: result
6111
+ }
6112
+ }, {
6113
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
6114
+ source: "agent-run-tool-provider"
6115
+ });
6116
+ } });
6117
+ toolRegistry.prepareForRun(runContext.toolRunContext);
6118
+ return toolRegistry.listTools();
6119
+ };
6120
+ };
6121
+ //#endregion
6122
+ //#region src/contributions/kernel-branch/contributions/tool-provider/index.ts
6123
+ var ToolProviderContribution = class {
6124
+ unregister = null;
6125
+ constructor(kernel, branch) {
6126
+ this.kernel = kernel;
6127
+ this.branch = branch;
6128
+ }
6129
+ start = () => {
6130
+ if (this.unregister) return;
6131
+ this.unregister = this.branch.toolProviderManager.register(new KernelToolProvider(this.kernel, this.branch));
6132
+ };
6133
+ dispose = () => {
6134
+ this.unregister?.();
6135
+ this.unregister = null;
6136
+ };
6137
+ };
6138
+ //#endregion
6139
+ //#region src/contributions/kernel-branch/index.ts
6140
+ var KernelBranch = class {
6141
+ agentRuntimeManager = new AgentRuntimeManager$1();
6142
+ contextCompactionManager;
6143
+ contextProviderManager = new ContextProviderManager();
6144
+ sessionRepository;
6145
+ sessionRunManager;
6146
+ toolProviderManager = new ToolProviderManager();
6147
+ agentRunRequestManager;
6148
+ contributions;
6149
+ started = false;
6150
+ constructor(kernel) {
6151
+ this.kernel = kernel;
6152
+ this.sessionRepository = new SessionRepository(kernel.eventBus, kernel.ncpSessionManager);
6153
+ this.contextCompactionManager = new AgentRunContextCompactionManager(kernel.configManager, kernel.llmProviders, this.sessionRepository);
6154
+ this.sessionRunManager = new SessionRunManager$1(this.sessionRepository);
6155
+ this.agentRunRequestManager = new AgentRunRequestManager$1(this.agentRuntimeManager, kernel.configManager, this.contextProviderManager, kernel.eventBus, kernel.ingress, this.sessionRepository, this.sessionRunManager, this.toolProviderManager);
6156
+ this.contributions = [
6157
+ new ToolProviderContribution(kernel, this),
6158
+ new ContextProviderContribution(kernel, this),
6159
+ new ContextWindowContribution(kernel, this),
6160
+ new AgentRunRuntimeContribution(kernel, this)
6161
+ ];
6162
+ }
6163
+ start = () => {
6164
+ if (this.started) return;
6165
+ this.started = true;
6166
+ this.sessionRepository.start();
6167
+ for (const contribution of this.contributions) contribution.start();
6168
+ this.agentRunRequestManager.start();
6169
+ };
6170
+ listSessionTypes = (params) => this.agentRuntimeManager.listSessionTypes(params);
6171
+ dispose = async () => {
6172
+ if (!this.started) return;
6173
+ for (const contribution of [...this.contributions].reverse()) await contribution.dispose?.();
6174
+ this.agentRunRequestManager.dispose();
6175
+ this.toolProviderManager.dispose();
6176
+ this.contextProviderManager.dispose();
6177
+ await this.agentRuntimeManager.dispose();
6178
+ this.sessionRunManager.dispose();
6179
+ this.sessionRepository.dispose();
6180
+ this.started = false;
6181
+ };
6182
+ };
6183
+ //#endregion
6184
+ //#region src/contributions/agent-runtime/index.ts
6185
+ var AgentRuntimeContribution = class {
6186
+ cleanups = [];
6187
+ constructor(kernel) {
6188
+ this.kernel = kernel;
6189
+ }
6190
+ start = () => {
6191
+ if (this.cleanups.length > 0) return;
6192
+ const nativeRuntimeFactory = new NativeAgentRuntimeFactory({
6193
+ providerManager: this.kernel.llmProviders,
6194
+ configManager: this.kernel.configManager,
6195
+ llmUsage: this.kernel.llmUsage,
6196
+ assetStore: this.kernel.assetStore,
6197
+ updateToolCallResult: this.kernel.sessionRunManager.updateToolCallResult,
6198
+ toolManager: this.kernel.toolManager
6199
+ });
6200
+ const builtinNarpRuntimeProviders = new BuiltinNarpRuntimeProviderService(this.kernel.configManager).createProviders();
6201
+ const nativeRegistration = this.kernel.agentRuntimeManager.registerRuntimeProvider({
6202
+ kind: "native",
6203
+ label: "Native",
6204
+ createRuntime: nativeRuntimeFactory.create
6205
+ });
6206
+ this.cleanups = [() => nativeRegistration.dispose(), ...builtinNarpRuntimeProviders.map((provider) => {
6207
+ const registration = this.kernel.agentRuntimeManager.registerRuntimeProvider(provider);
6208
+ return () => registration.dispose();
6209
+ })];
6210
+ };
6211
+ dispose = () => {
6212
+ for (const cleanup of this.cleanups) cleanup();
6213
+ this.cleanups = [];
6214
+ };
6215
+ };
6216
+ //#endregion
6217
+ //#region src/contributions/session-context-window/index.ts
6218
+ const STREAM_REFRESH_DELAY_MS = 1500;
6219
+ function formatBackgroundError(error) {
6220
+ if (error instanceof Error) return error.stack ?? error.message;
6221
+ return String(error);
6222
+ }
6223
+ var SessionContextWindowContribution = class {
6224
+ unsubscribeNcpEvent = null;
6225
+ stopped = true;
6226
+ pendingTimers = /* @__PURE__ */ new Map();
6227
+ lastPublishedSignatureBySession = /* @__PURE__ */ new Map();
6228
+ constructor(kernel) {
6229
+ this.kernel = kernel;
6230
+ }
6231
+ start = () => {
6232
+ if (this.unsubscribeNcpEvent) return;
6233
+ this.stopped = false;
6234
+ this.unsubscribeNcpEvent = this.kernel.eventBus.on(eventKeys.ncpEvent, this.handleNcpEvent);
6235
+ };
6236
+ dispose = () => {
6237
+ this.unsubscribeNcpEvent?.();
6238
+ this.unsubscribeNcpEvent = null;
6239
+ this.stopped = true;
6240
+ for (const timer of this.pendingTimers.values()) clearTimeout(timer);
6241
+ this.pendingTimers.clear();
6242
+ this.lastPublishedSignatureBySession.clear();
6243
+ };
6244
+ handleNcpEvent = (event) => {
6245
+ const sessionId = readContextWindowEventSessionId(event);
6246
+ if (!sessionId) return;
6247
+ if (event.type === NcpEventType.ContextWindowUpdated) {
6248
+ this.rememberPublishedContextWindow(sessionId, event.payload.contextWindow);
6249
+ return;
6250
+ }
6251
+ if (shouldRefreshContextWindowImmediately(event)) {
6252
+ this.refreshNow(sessionId);
6253
+ return;
6254
+ }
6255
+ if (shouldRefreshContextWindowDuringStream(event)) this.refreshSoon(sessionId);
6256
+ };
6257
+ refreshSoon = (sessionId) => {
6258
+ if (this.pendingTimers.has(sessionId)) return;
6259
+ const timer = setTimeout(() => {
6260
+ this.pendingTimers.delete(sessionId);
6261
+ this.refreshNow(sessionId);
6262
+ }, STREAM_REFRESH_DELAY_MS);
6263
+ this.pendingTimers.set(sessionId, timer);
6264
+ };
6265
+ refreshNow = (sessionId) => {
6266
+ const timer = this.pendingTimers.get(sessionId);
6267
+ if (timer) {
6268
+ clearTimeout(timer);
6269
+ this.pendingTimers.delete(sessionId);
6270
+ }
6271
+ this.publishContextWindow(sessionId).catch((error) => {
6272
+ console.error(`[session-context-window] failed to refresh ${sessionId}: ${formatBackgroundError(error)}`);
6273
+ });
6274
+ };
6275
+ publishContextWindow = async (sessionId) => {
6276
+ if (this.stopped) return;
6277
+ const contextWindow = await this.kernel.ncpSessionManager.getContextWindow(sessionId, this.kernel.sessionRunManager.getLiveSessionRecord(sessionId));
6278
+ if (!isContextWindowSnapshot(contextWindow) || this.stopped) return;
6279
+ const signature = createContextWindowSignature(contextWindow);
6280
+ if (this.lastPublishedSignatureBySession.get(sessionId) === signature) return;
6281
+ this.lastPublishedSignatureBySession.set(sessionId, signature);
6282
+ this.kernel.eventBus.emit(eventKeys.ncpEvent, {
6283
+ type: NcpEventType.ContextWindowUpdated,
6284
+ payload: {
6285
+ sessionId,
6286
+ contextWindow
6287
+ }
6288
+ }, {
6289
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
6290
+ source: "session-context-window"
6291
+ });
6292
+ };
6293
+ rememberPublishedContextWindow = (sessionId, contextWindow) => {
6294
+ this.lastPublishedSignatureBySession.set(sessionId, createContextWindowSignature(contextWindow));
6295
+ };
6296
+ };
6297
+ //#endregion
6298
+ //#region src/contributions/legacy-agent-run/index.ts
6299
+ var LegacyAgentRunContribution = class {
6300
+ contributions;
6301
+ started = false;
5389
6302
  constructor(kernel) {
5390
6303
  this.kernel = kernel;
6304
+ this.contributions = [new AgentRuntimeContribution(kernel), new SessionContextWindowContribution(kernel)];
5391
6305
  }
5392
6306
  start = () => {
5393
- if (this.cleanups.length > 0) return;
5394
- const nativeRuntimeFactory = new NativeAgentRuntimeFactory({
5395
- providerManager: this.kernel.llmProviders,
5396
- configManager: this.kernel.configManager,
5397
- llmUsage: this.kernel.llmUsage,
5398
- assetStore: this.kernel.assetStore,
5399
- updateToolCallResult: this.kernel.sessionRunManager.updateToolCallResult,
5400
- toolManager: this.kernel.toolManager
5401
- });
5402
- const builtinNarpRuntimeProviders = new BuiltinNarpRuntimeProviderService(this.kernel.configManager).createProviders();
5403
- const nativeRegistration = this.kernel.agentRuntimeManager.registerRuntimeProvider({
5404
- kind: "native",
5405
- label: "Native",
5406
- createRuntime: nativeRuntimeFactory.create
5407
- });
5408
- this.cleanups = [() => nativeRegistration.dispose(), ...builtinNarpRuntimeProviders.map((provider) => {
5409
- const registration = this.kernel.agentRuntimeManager.registerRuntimeProvider(provider);
5410
- return () => registration.dispose();
5411
- })];
6307
+ if (this.started) return;
6308
+ this.started = true;
6309
+ for (const contribution of this.contributions) contribution.start();
6310
+ this.kernel.agentRunRequestManager.start();
5412
6311
  };
5413
- dispose = () => {
5414
- for (const cleanup of this.cleanups) cleanup();
5415
- this.cleanups = [];
6312
+ listSessionTypes = (params) => this.kernel.agentRuntimeManager.listSessionTypes(params);
6313
+ dispose = async () => {
6314
+ if (!this.started) return;
6315
+ await this.kernel.agentRunRequestManager.dispose();
6316
+ await this.kernel.sessionRunManager.dispose();
6317
+ for (const contribution of [...this.contributions].reverse()) await contribution.dispose();
6318
+ this.started = false;
5416
6319
  };
5417
6320
  };
5418
6321
  //#endregion
@@ -5566,125 +6469,6 @@ var LearningLoopContribution = class {
5566
6469
  };
5567
6470
  };
5568
6471
  //#endregion
5569
- //#region src/contributions/session-context-window/index.ts
5570
- const STREAM_REFRESH_DELAY_MS = 1500;
5571
- function formatBackgroundError(error) {
5572
- if (error instanceof Error) return error.stack ?? error.message;
5573
- return String(error);
5574
- }
5575
- function readEventSessionId(event) {
5576
- const payload = "payload" in event ? event.payload : null;
5577
- if (!payload || typeof payload !== "object") return null;
5578
- return "sessionId" in payload && typeof payload.sessionId === "string" ? payload.sessionId.trim() || null : null;
5579
- }
5580
- function isContextWindow(value) {
5581
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
5582
- }
5583
- function createSignature(value) {
5584
- return JSON.stringify(value);
5585
- }
5586
- function shouldRefreshDuringStream(event) {
5587
- switch (event.type) {
5588
- case NcpEventType.MessageTextStart:
5589
- case NcpEventType.MessageTextDelta:
5590
- case NcpEventType.MessageTextEnd:
5591
- case NcpEventType.MessageReasoningStart:
5592
- case NcpEventType.MessageReasoningDelta:
5593
- case NcpEventType.MessageReasoningEnd:
5594
- case NcpEventType.MessageToolCallStart:
5595
- case NcpEventType.MessageToolCallArgs:
5596
- case NcpEventType.MessageToolCallArgsDelta:
5597
- case NcpEventType.MessageToolCallEnd:
5598
- case NcpEventType.MessageToolCallResult: return true;
5599
- default: return false;
5600
- }
5601
- }
5602
- function shouldRefreshImmediately(event) {
5603
- switch (event.type) {
5604
- case NcpEventType.MessageSent:
5605
- case NcpEventType.MessageCompleted:
5606
- case NcpEventType.MessageFailed:
5607
- case NcpEventType.MessageAbort:
5608
- case NcpEventType.RunFinished:
5609
- case NcpEventType.RunError: return true;
5610
- default: return false;
5611
- }
5612
- }
5613
- var SessionContextWindowContribution = class {
5614
- unsubscribeNcpEvent = null;
5615
- stopped = true;
5616
- pendingTimers = /* @__PURE__ */ new Map();
5617
- lastPublishedSignatureBySession = /* @__PURE__ */ new Map();
5618
- constructor(kernel) {
5619
- this.kernel = kernel;
5620
- }
5621
- start = () => {
5622
- if (this.unsubscribeNcpEvent) return;
5623
- this.stopped = false;
5624
- this.unsubscribeNcpEvent = this.kernel.eventBus.on(eventKeys.ncpEvent, this.handleNcpEvent);
5625
- };
5626
- dispose = () => {
5627
- this.unsubscribeNcpEvent?.();
5628
- this.unsubscribeNcpEvent = null;
5629
- this.stopped = true;
5630
- for (const timer of this.pendingTimers.values()) clearTimeout(timer);
5631
- this.pendingTimers.clear();
5632
- this.lastPublishedSignatureBySession.clear();
5633
- };
5634
- handleNcpEvent = (event) => {
5635
- const sessionId = readEventSessionId(event);
5636
- if (!sessionId) return;
5637
- if (event.type === NcpEventType.ContextWindowUpdated) {
5638
- this.rememberPublishedContextWindow(sessionId, event.payload.contextWindow);
5639
- return;
5640
- }
5641
- if (shouldRefreshImmediately(event)) {
5642
- this.refreshNow(sessionId);
5643
- return;
5644
- }
5645
- if (shouldRefreshDuringStream(event)) this.refreshSoon(sessionId);
5646
- };
5647
- refreshSoon = (sessionId) => {
5648
- if (this.pendingTimers.has(sessionId)) return;
5649
- const timer = setTimeout(() => {
5650
- this.pendingTimers.delete(sessionId);
5651
- this.refreshNow(sessionId);
5652
- }, STREAM_REFRESH_DELAY_MS);
5653
- this.pendingTimers.set(sessionId, timer);
5654
- };
5655
- refreshNow = (sessionId) => {
5656
- const timer = this.pendingTimers.get(sessionId);
5657
- if (timer) {
5658
- clearTimeout(timer);
5659
- this.pendingTimers.delete(sessionId);
5660
- }
5661
- this.publishContextWindow(sessionId).catch((error) => {
5662
- console.error(`[session-context-window] failed to refresh ${sessionId}: ${formatBackgroundError(error)}`);
5663
- });
5664
- };
5665
- publishContextWindow = async (sessionId) => {
5666
- if (this.stopped) return;
5667
- const contextWindow = await this.kernel.ncpSessionManager.getContextWindow(sessionId, this.kernel.sessionRunManager.getLiveSessionRecord(sessionId));
5668
- if (!isContextWindow(contextWindow) || this.stopped) return;
5669
- const signature = createSignature(contextWindow);
5670
- if (this.lastPublishedSignatureBySession.get(sessionId) === signature) return;
5671
- this.lastPublishedSignatureBySession.set(sessionId, signature);
5672
- this.kernel.eventBus.emit(eventKeys.ncpEvent, {
5673
- type: NcpEventType.ContextWindowUpdated,
5674
- payload: {
5675
- sessionId,
5676
- contextWindow
5677
- }
5678
- }, {
5679
- emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
5680
- source: "session-context-window"
5681
- });
5682
- };
5683
- rememberPublishedContextWindow = (sessionId, contextWindow) => {
5684
- this.lastPublishedSignatureBySession.set(sessionId, createSignature(contextWindow));
5685
- };
5686
- };
5687
- //#endregion
5688
6472
  //#region src/contributions/session-activity-preview/utils/session-activity-preview-ncp-event.utils.ts
5689
6473
  const PREVIEW_TEXT_MAX_LENGTH = 160;
5690
6474
  function readSessionId(value) {
@@ -6140,7 +6924,7 @@ var SessionSearchTool = class {
6140
6924
  }
6141
6925
  validateArgs = (args) => {
6142
6926
  const issues = [];
6143
- const query = normalizeString(args.query);
6927
+ const query = normalizeString$1(args.query);
6144
6928
  const limit = readOptionalInteger(args.limit);
6145
6929
  const includeCurrentSession = readOptionalBoolean(args.includeCurrentSession);
6146
6930
  if (!query) issues.push("query must be a non-empty string.");
@@ -6154,7 +6938,7 @@ var SessionSearchTool = class {
6154
6938
  const issues = this.validateArgs(args);
6155
6939
  if (issues.length > 0) throw new Error(issues.join(" "));
6156
6940
  return this.queryService.search({
6157
- query: normalizeString(args.query) ?? "",
6941
+ query: normalizeString$1(args.query) ?? "",
6158
6942
  limit: readOptionalInteger(args.limit),
6159
6943
  includeCurrentSession: readOptionalBoolean(args.includeCurrentSession),
6160
6944
  currentSessionId: this.context.currentSessionId
@@ -6297,7 +7081,7 @@ var SessionSpawnTool = class extends Tool {
6297
7081
  function readMetadataAccountId(metadata) {
6298
7082
  const candidates = [metadata.accountId, metadata.account_id];
6299
7083
  for (const candidate of candidates) {
6300
- const normalized = normalizeString(candidate);
7084
+ const normalized = normalizeString$1(candidate);
6301
7085
  if (normalized) return normalized;
6302
7086
  }
6303
7087
  }
@@ -6408,6 +7192,7 @@ var ToolContribution = class {
6408
7192
  };
6409
7193
  //#endregion
6410
7194
  //#region src/app/nextclaw-kernel.ts
7195
+ const AGENT_RUN_CHAIN = "branch";
6411
7196
  function resolveKernelSessionsDir(options) {
6412
7197
  const homeDir = options.homeDir?.trim();
6413
7198
  if (homeDir) return ensureDir(resolve(expandHome(homeDir), "sessions"));
@@ -6452,6 +7237,7 @@ var NextclawKernel = class {
6452
7237
  sessionRunManager;
6453
7238
  agentRuntimeManager;
6454
7239
  ncpAgentSessionJournalStore;
7240
+ agentRunContribution;
6455
7241
  contributions;
6456
7242
  gatewayController;
6457
7243
  constructor(options = {}) {
@@ -6480,7 +7266,8 @@ var NextclawKernel = class {
6480
7266
  configManager: this.configManager,
6481
7267
  eventBus: this.eventBus,
6482
7268
  ingress: this.ingress,
6483
- messageBus: this.messageBus
7269
+ messageBus: this.messageBus,
7270
+ sessionManager: this.sessions
6484
7271
  });
6485
7272
  this.skills = new SkillManager({ workspace: getWorkspacePath(this.configManager.config.agents.defaults.workspace) });
6486
7273
  this.mcpManager = new McpManager(this.configManager.loadConfig);
@@ -6521,14 +7308,15 @@ var NextclawKernel = class {
6521
7308
  ncpSessionManager: this.ncpSessionManager,
6522
7309
  sessionRunManager: this.sessionRunManager
6523
7310
  });
7311
+ this.agentRunContribution = AGENT_RUN_CHAIN === "branch" ? new KernelBranch(this) : new LegacyAgentRunContribution(this);
6524
7312
  this.contributions = [
6525
- new AgentRuntimeContribution(this),
6526
7313
  new ToolContribution(this),
6527
7314
  new SessionActivityPreviewContribution(this),
6528
- new SessionContextWindowContribution(this),
6529
- new LearningLoopContribution(this)
7315
+ new LearningLoopContribution(this),
7316
+ this.agentRunContribution
6530
7317
  ];
6531
7318
  }
7319
+ listSessionTypes = (params) => this.agentRunContribution.listSessionTypes(params);
6532
7320
  provideGatewayController = (gatewayController) => {
6533
7321
  this.gatewayController = gatewayController;
6534
7322
  };
@@ -6537,13 +7325,10 @@ var NextclawKernel = class {
6537
7325
  this.sessionSearch.start();
6538
7326
  this.mcpManager.start();
6539
7327
  for (const contribution of this.contributions) contribution.start();
6540
- this.agentRunRequestManager.start();
6541
7328
  };
6542
7329
  dispose = async () => {
6543
- await this.agentRunRequestManager.dispose();
6544
- await this.sessionRunManager.dispose();
7330
+ for (const contribution of [...this.contributions].reverse()) await contribution.dispose();
6545
7331
  this.ncpSessionManager.dispose();
6546
- for (const contribution of this.contributions) contribution.dispose();
6547
7332
  await this.agentRuntimeManager.dispose();
6548
7333
  await this.mcpManager.dispose();
6549
7334
  await this.sessionSearch.dispose();
@@ -6597,7 +7382,7 @@ async function createFilePartFromLocalPath(attachment, localPath, assetApi) {
6597
7382
  const bytes = await readFile(localPath);
6598
7383
  return {
6599
7384
  type: "file",
6600
- assetUri: (await assetApi.put({
7385
+ assetUri: (await assetApi.putBytes({
6601
7386
  fileName,
6602
7387
  mimeType: attachment.mimeType ?? null,
6603
7388
  bytes
@@ -6626,16 +7411,11 @@ async function buildUserMessageParts(params) {
6626
7411
  for (const attachment of attachments ?? []) parts.push(await attachmentToPart(attachment, assetApi));
6627
7412
  return parts;
6628
7413
  }
6629
- async function buildNcpUserMessage(params) {
7414
+ async function buildAgentRunSendPayload(params) {
6630
7415
  const { assetApi, attachments, content, metadata, sessionId } = params;
6631
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
6632
7416
  return {
6633
- id: `${sessionId}:user:${timestamp}`,
6634
7417
  sessionId,
6635
- role: "user",
6636
- status: "final",
6637
- timestamp,
6638
- parts: await buildUserMessageParts({
7418
+ content: await buildUserMessageParts({
6639
7419
  content,
6640
7420
  attachments,
6641
7421
  assetApi
@@ -6643,41 +7423,208 @@ async function buildNcpUserMessage(params) {
6643
7423
  metadata: structuredClone(metadata ?? {})
6644
7424
  };
6645
7425
  }
6646
- async function* streamPromptOverNcp(params) {
6647
- const { abortSignal, agent, attachments, content, metadata, onEvent, sessionId } = params;
6648
- const message = await buildNcpUserMessage({
6649
- sessionId,
6650
- content,
6651
- attachments,
6652
- metadata,
6653
- assetApi: agent.assetApi
6654
- });
6655
- for await (const event of agent.run({
6656
- sessionId,
6657
- message,
6658
- metadata
6659
- }, { ...abortSignal ? { signal: abortSignal } : {} })) {
6660
- onEvent?.(event);
6661
- yield event;
6662
- }
7426
+ function readEventCorrelationId(event) {
7427
+ if (!("payload" in event)) return;
7428
+ const correlationId = "correlationId" in event.payload ? event.payload.correlationId : void 0;
7429
+ return typeof correlationId === "string" && correlationId.length > 0 ? correlationId : void 0;
6663
7430
  }
6664
- async function runPromptOverNcp(params) {
6665
- let completedMessage;
6666
- for await (const event of streamPromptOverNcp(params)) {
6667
- if (event.type === NcpEventType.MessageTextDelta) {
6668
- params.onAssistantDelta?.(event.payload.delta);
6669
- continue;
7431
+ function readEventSessionId(event) {
7432
+ if (!("payload" in event)) return;
7433
+ const sessionId = "sessionId" in event.payload ? event.payload.sessionId : void 0;
7434
+ return typeof sessionId === "string" && sessionId.length > 0 ? sessionId : void 0;
7435
+ }
7436
+ function readEventRunId(event) {
7437
+ if (!("payload" in event)) return;
7438
+ const runId = "runId" in event.payload ? event.payload.runId : void 0;
7439
+ return typeof runId === "string" && runId.length > 0 ? runId : void 0;
7440
+ }
7441
+ function isTerminalEvent(event) {
7442
+ return event.type === NcpEventType.MessageFailed || event.type === NcpEventType.RunError || event.type === NcpEventType.RunFinished;
7443
+ }
7444
+ function isRunEventMatch(params) {
7445
+ const eventCorrelationId = readEventCorrelationId(params.event);
7446
+ if (eventCorrelationId) return eventCorrelationId === params.correlationId;
7447
+ if (!params.sessionId || !params.runId) return false;
7448
+ return readEventSessionId(params.event) === params.sessionId && readEventRunId(params.event) === params.runId;
7449
+ }
7450
+ var AsyncEventQueue = class {
7451
+ values = [];
7452
+ waiters = [];
7453
+ closed = false;
7454
+ failure;
7455
+ hasFailure = false;
7456
+ push = (value) => {
7457
+ if (this.closed || this.hasFailure) return;
7458
+ const waiter = this.waiters.shift();
7459
+ if (waiter) {
7460
+ waiter({
7461
+ done: false,
7462
+ value
7463
+ });
7464
+ return;
7465
+ }
7466
+ this.values.push(value);
7467
+ };
7468
+ close = () => {
7469
+ if (this.closed) return;
7470
+ this.closed = true;
7471
+ while (this.waiters.length > 0) this.waiters.shift()?.({
7472
+ done: true,
7473
+ value: void 0
7474
+ });
7475
+ };
7476
+ fail = (error) => {
7477
+ if (this.closed || this.hasFailure) return;
7478
+ this.hasFailure = true;
7479
+ this.failure = error;
7480
+ while (this.waiters.length > 0) this.waiters.shift()?.({
7481
+ done: true,
7482
+ value: void 0
7483
+ });
7484
+ };
7485
+ next = async () => {
7486
+ if (this.values.length > 0) return {
7487
+ done: false,
7488
+ value: this.values.shift()
7489
+ };
7490
+ if (this.hasFailure) throw this.failure;
7491
+ if (this.closed) return {
7492
+ done: true,
7493
+ value: void 0
7494
+ };
7495
+ return await new Promise((resolve) => {
7496
+ this.waiters.push(resolve);
7497
+ });
7498
+ };
7499
+ [Symbol.asyncIterator] = () => ({ next: this.next });
7500
+ };
7501
+ var AgentRunObserver = class {
7502
+ queue = new AsyncEventQueue();
7503
+ unsubscribe;
7504
+ abortCleanup;
7505
+ completedMessage;
7506
+ disposed = false;
7507
+ runId;
7508
+ sessionId;
7509
+ constructor(options) {
7510
+ this.options = options;
7511
+ this.unsubscribe = options.eventBus.on(eventKeys.ncpEvent, this.handleEvent);
7512
+ }
7513
+ attachHandle = (handle) => {
7514
+ this.sessionId = handle.sessionId;
7515
+ this.runId = handle.runId ?? void 0;
7516
+ const { abortSignal } = this.options;
7517
+ if (!abortSignal) return;
7518
+ const abort = () => {
7519
+ this.options.ingress.handle({
7520
+ type: ingressKeys.agentRun.abort,
7521
+ payload: {
7522
+ sessionId: handle.sessionId,
7523
+ correlationId: this.options.correlationId
7524
+ }
7525
+ }, { source: "agent-run-client" });
7526
+ };
7527
+ if (abortSignal.aborted) {
7528
+ abort();
7529
+ return;
7530
+ }
7531
+ abortSignal.addEventListener("abort", abort, { once: true });
7532
+ this.abortCleanup = () => abortSignal.removeEventListener("abort", abort);
7533
+ };
7534
+ stream = async function* () {
7535
+ for await (const event of this.queue) yield event;
7536
+ };
7537
+ waitForReply = async (options = {}) => {
7538
+ for await (const event of this.queue) {
7539
+ if (event.type === NcpEventType.MessageTextDelta) {
7540
+ options.onAssistantDelta?.(event.payload.delta);
7541
+ continue;
7542
+ }
7543
+ if (event.type === NcpEventType.MessageCompleted) {
7544
+ this.completedMessage = event.payload.message;
7545
+ continue;
7546
+ }
7547
+ if (event.type === NcpEventType.MessageFailed) throw new Error(event.payload.error.message);
7548
+ if (event.type === NcpEventType.RunError) throw new Error(event.payload.error ?? options.runErrorMessage ?? "NCP run failed.");
7549
+ if (event.type === NcpEventType.RunFinished) {
7550
+ if (!this.completedMessage) throw new Error(options.missingCompletedMessageError ?? "NCP run completed without a final assistant message.");
7551
+ return this.completedMessage;
7552
+ }
6670
7553
  }
6671
- if (event.type === NcpEventType.MessageFailed) throw new Error(event.payload.error.message);
6672
- if (event.type === NcpEventType.RunError) throw new Error(event.payload.error ?? params.runErrorMessage ?? "NCP run failed.");
6673
- if (event.type === NcpEventType.MessageCompleted) completedMessage = event.payload.message;
7554
+ throw new Error(options.missingCompletedMessageError ?? "NCP run completed without a final assistant message.");
7555
+ };
7556
+ dispose = () => {
7557
+ if (this.disposed) return;
7558
+ this.disposed = true;
7559
+ this.abortCleanup?.();
7560
+ this.unsubscribe();
7561
+ this.queue.close();
7562
+ };
7563
+ handleEvent = (event) => {
7564
+ if (this.disposed || !isRunEventMatch({
7565
+ correlationId: this.options.correlationId,
7566
+ event,
7567
+ runId: this.runId,
7568
+ sessionId: this.sessionId
7569
+ })) return;
7570
+ this.options.onEvent?.(event);
7571
+ this.queue.push(event);
7572
+ if (isTerminalEvent(event)) this.queue.close();
7573
+ };
7574
+ };
7575
+ var AgentRunClient = class {
7576
+ constructor(options) {
7577
+ this.options = options;
6674
7578
  }
6675
- if (!completedMessage) throw new Error(params.missingCompletedMessageError ?? "NCP run completed without a final assistant message.");
6676
- return {
6677
- text: extractTextFromNcpMessage(completedMessage),
6678
- completedMessage
7579
+ send = async (input) => {
7580
+ return await this.sendWithCorrelation(input, randomUUID());
6679
7581
  };
6680
- }
7582
+ sendAndWaitForReply = async (input, options = {}) => {
7583
+ const correlationId = randomUUID();
7584
+ const observer = this.prepareObserver(correlationId, options);
7585
+ try {
7586
+ const handle = await this.sendWithCorrelation(input, correlationId);
7587
+ observer.attachHandle(handle);
7588
+ const completedMessage = await observer.waitForReply(options);
7589
+ return {
7590
+ handle,
7591
+ completedMessage,
7592
+ text: extractTextFromNcpMessage(completedMessage)
7593
+ };
7594
+ } finally {
7595
+ observer.dispose();
7596
+ }
7597
+ };
7598
+ sendAndStreamEvents = async function* (input, options = {}) {
7599
+ const correlationId = randomUUID();
7600
+ const observer = this.prepareObserver(correlationId, options);
7601
+ try {
7602
+ const handle = await this.sendWithCorrelation(input, correlationId);
7603
+ observer.attachHandle(handle);
7604
+ for await (const event of observer.stream()) yield event;
7605
+ } finally {
7606
+ observer.dispose();
7607
+ }
7608
+ };
7609
+ prepareObserver = (correlationId, options) => {
7610
+ return new AgentRunObserver({
7611
+ abortSignal: options.abortSignal,
7612
+ correlationId,
7613
+ eventBus: this.options.eventBus,
7614
+ ingress: this.options.ingress,
7615
+ onEvent: options.onEvent
7616
+ });
7617
+ };
7618
+ sendWithCorrelation = async (input, correlationId) => {
7619
+ return await this.options.ingress.handle({
7620
+ type: ingressKeys.agentRun.send,
7621
+ payload: {
7622
+ ...input,
7623
+ correlationId
7624
+ }
7625
+ }, { source: "agent-run-client" });
7626
+ };
7627
+ };
6681
7628
  //#endregion
6682
7629
  //#region src/features/ncp-dispatch/utils/channel-reply.utils.ts
6683
7630
  function readString(value) {
@@ -6702,18 +7649,19 @@ function resolveChannelReplyRoute(params) {
6702
7649
  };
6703
7650
  }
6704
7651
  async function dispatchChannelReplyRoute(params) {
6705
- const { abortSignal, agent, attachments, content, metadata, onEvent, route, sessionId } = params;
7652
+ const { abortSignal, agentRunClient, assetApi, attachments, content, metadata, onEvent, route, sessionId } = params;
6706
7653
  const input = {
6707
7654
  target: {
6708
7655
  ...route.target,
6709
- ...agent.assetApi?.resolveContentPath ? { resolveAssetContentPath: agent.assetApi.resolveContentPath } : {}
7656
+ ...assetApi?.resolveContentPath ? { resolveAssetContentPath: assetApi.resolveContentPath } : {}
6710
7657
  },
6711
- eventStream: streamPromptOverNcp({
6712
- agent,
7658
+ eventStream: agentRunClient.sendAndStreamEvents(await buildAgentRunSendPayload({
6713
7659
  sessionId,
6714
7660
  content,
6715
7661
  attachments,
6716
7662
  metadata,
7663
+ assetApi
7664
+ }), {
6717
7665
  abortSignal,
6718
7666
  onEvent
6719
7667
  })
@@ -6722,22 +7670,35 @@ async function dispatchChannelReplyRoute(params) {
6722
7670
  }
6723
7671
  //#endregion
6724
7672
  //#region src/features/ncp-dispatch/utils/ncp-run-metadata.utils.ts
7673
+ const LEGACY_RUN_METADATA_KEYS = [
7674
+ "account_id",
7675
+ "agent_id",
7676
+ "chat_id",
7677
+ "preferred_model",
7678
+ "project_root",
7679
+ "runtime",
7680
+ "sender_id",
7681
+ "session_key",
7682
+ "session_type"
7683
+ ];
7684
+ function stripLegacyRunMetadata(metadata) {
7685
+ const nextMetadata = structuredClone(metadata);
7686
+ for (const key of LEGACY_RUN_METADATA_KEYS) delete nextMetadata[key];
7687
+ return nextMetadata;
7688
+ }
6725
7689
  function buildRunMetadata(params) {
6726
7690
  const { message, metadata, route } = params;
6727
7691
  return {
6728
- ...message.metadata ?? {},
6729
- ...metadata ?? {},
7692
+ ...stripLegacyRunMetadata({
7693
+ ...message.metadata ?? {},
7694
+ ...metadata ?? {}
7695
+ }),
6730
7696
  channel: message.channel,
6731
7697
  chatId: message.chatId,
6732
- chat_id: message.chatId,
6733
7698
  accountId: route.accountId,
6734
- account_id: route.accountId,
6735
7699
  agentId: route.agentId,
6736
- agent_id: route.agentId,
6737
7700
  sessionKey: route.sessionKey,
6738
- session_key: route.sessionKey,
6739
- senderId: message.senderId,
6740
- sender_id: message.senderId
7701
+ senderId: message.senderId
6741
7702
  };
6742
7703
  }
6743
7704
  //#endregion
@@ -6758,14 +7719,16 @@ var GatewayInboundProcessor = class {
6758
7719
  message,
6759
7720
  route
6760
7721
  });
7722
+ if (await this.dispatchSlashCommandMaybe(message, route, runMetadata)) return;
6761
7723
  if (await this.dispatchChannelReplyRouteMaybe(message, route, runMetadata)) return;
6762
7724
  if (message.channel !== "system") await this.runtime.messageBus.publishOutbound(createAssistantStreamResetControlMessage(message));
6763
- const result = await runPromptOverNcp({
6764
- agent: this.runtime.kernel.agentRunRequestManager,
7725
+ const result = await this.runtime.agentRunClient.sendAndWaitForReply(await buildAgentRunSendPayload({
6765
7726
  sessionId: route.sessionKey,
6766
7727
  content: message.content,
6767
7728
  attachments: message.attachments,
6768
7729
  metadata: runMetadata,
7730
+ assetApi: this.runtime.kernel.assetStore
7731
+ }), {
6769
7732
  onAssistantDelta: message.channel !== "system" ? (delta) => {
6770
7733
  if (!delta) return;
6771
7734
  this.runtime.messageBus.publishOutbound(createAssistantStreamDeltaControlMessage(message, delta));
@@ -6792,6 +7755,26 @@ var GatewayInboundProcessor = class {
6792
7755
  sessionKeyOverride: normalize(message.metadata.session_key_override)
6793
7756
  });
6794
7757
  };
7758
+ dispatchSlashCommandMaybe = async (message, route, runMetadata) => {
7759
+ if (message.channel === "system") return false;
7760
+ const trimmed = message.content.trim();
7761
+ if (!trimmed.startsWith("/")) return false;
7762
+ const result = await new CommandRegistry(this.runtime.configManager.loadConfig(), this.runtime.sessionManager).executeText(trimmed, {
7763
+ channel: message.channel,
7764
+ chatId: message.chatId,
7765
+ senderId: message.senderId,
7766
+ sessionKey: route.sessionKey
7767
+ });
7768
+ if (!result) return false;
7769
+ await this.runtime.messageBus.publishOutbound({
7770
+ channel: message.channel,
7771
+ chatId: message.chatId,
7772
+ content: result.content,
7773
+ media: [],
7774
+ metadata: runMetadata
7775
+ });
7776
+ return true;
7777
+ };
6795
7778
  dispatchChannelReplyRouteMaybe = async (message, route, runMetadata) => {
6796
7779
  if (message.channel === "system") return false;
6797
7780
  const replyRoute = resolveChannelReplyRoute({
@@ -6801,7 +7784,8 @@ var GatewayInboundProcessor = class {
6801
7784
  });
6802
7785
  if (!replyRoute) return false;
6803
7786
  await dispatchChannelReplyRoute({
6804
- agent: this.runtime.kernel.agentRunRequestManager,
7787
+ agentRunClient: this.runtime.agentRunClient,
7788
+ assetApi: this.runtime.kernel.assetStore,
6805
7789
  route: replyRoute,
6806
7790
  sessionId: route.sessionKey,
6807
7791
  content: message.content,
@@ -6881,7 +7865,7 @@ function resolveDirectRoute(params) {
6881
7865
  };
6882
7866
  }
6883
7867
  async function dispatchPromptOverNcp(params) {
6884
- const { abortSignal, agentId, attachments, channel, chatId, config, content, metadata, onAssistantDelta, agentRunRequests, sessionKey } = params;
7868
+ const { abortSignal, agentId, attachments, channel, chatId, config, content, metadata, onAssistantDelta, agentRunClient, sessionKey } = params;
6885
7869
  const { message, route } = resolveDirectRoute({
6886
7870
  config,
6887
7871
  content,
@@ -6900,15 +7884,15 @@ async function dispatchPromptOverNcp(params) {
6900
7884
  sessionKey: route.sessionKey
6901
7885
  });
6902
7886
  if (commandResult) return commandResult;
6903
- return (await runPromptOverNcp({
6904
- agent: agentRunRequests,
7887
+ return (await agentRunClient.sendAndWaitForReply(await buildAgentRunSendPayload({
6905
7888
  sessionId: route.sessionKey,
6906
7889
  content,
6907
7890
  attachments,
6908
7891
  metadata: buildRunMetadata({
6909
7892
  message,
6910
7893
  route
6911
- }),
7894
+ })
7895
+ }), {
6912
7896
  abortSignal,
6913
7897
  onAssistantDelta,
6914
7898
  missingCompletedMessageError: `session "${route.sessionKey}" completed without a final assistant message`,
@@ -6959,11 +7943,11 @@ function parseLegacyToolCalls(value) {
6959
7943
  return value.map((entry) => {
6960
7944
  if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
6961
7945
  const toolCall = entry;
6962
- const id = normalizeString(toolCall.id);
7946
+ const id = normalizeString$1(toolCall.id);
6963
7947
  const rawFunction = toolCall.function;
6964
7948
  if (!id || !rawFunction || typeof rawFunction !== "object" || Array.isArray(rawFunction)) return null;
6965
7949
  const fn = rawFunction;
6966
- const name = normalizeString(fn.name);
7950
+ const name = normalizeString$1(fn.name);
6967
7951
  const args = typeof fn.arguments === "string" ? fn.arguments : JSON.stringify(fn.arguments ?? {});
6968
7952
  if (!name) return null;
6969
7953
  return {
@@ -6981,7 +7965,7 @@ function createMessageId(sessionId, index, role, timestamp) {
6981
7965
  }
6982
7966
  function resolveMessageId(params) {
6983
7967
  const { index, message, role, sessionId, timestamp } = params;
6984
- return normalizeString(message.ncp_message_id) ?? createMessageId(sessionId, index, role, timestamp);
7968
+ return normalizeString$1(message.ncp_message_id) ?? createMessageId(sessionId, index, role, timestamp);
6985
7969
  }
6986
7970
  function isNcpMessagePart(value) {
6987
7971
  return Boolean(value) && typeof value === "object" && !Array.isArray(value) && typeof value.type === "string";
@@ -7108,13 +8092,13 @@ function toNcpMessages(sessionId, messages) {
7108
8092
  const ncpMessages = [];
7109
8093
  const assistantIndexByToolCallId = /* @__PURE__ */ new Map();
7110
8094
  messages.forEach((message, index) => {
7111
- const role = normalizeString(message.role)?.toLowerCase() ?? "assistant";
8095
+ const role = normalizeString$1(message.role)?.toLowerCase() ?? "assistant";
7112
8096
  if (role === "tool") {
7113
- const toolCallId = normalizeString(message.tool_call_id);
8097
+ const toolCallId = normalizeString$1(message.tool_call_id);
7114
8098
  if (toolCallId) {
7115
8099
  const assistantIndex = assistantIndexByToolCallId.get(toolCallId);
7116
8100
  if (assistantIndex !== void 0) {
7117
- ncpMessages[assistantIndex] = attachToolResult(ncpMessages[assistantIndex], toolCallId, structuredClone(message.content), normalizeString(message.name) ?? void 0, message.ncp_tool_result_content_items);
8101
+ ncpMessages[assistantIndex] = attachToolResult(ncpMessages[assistantIndex], toolCallId, structuredClone(message.content), normalizeString$1(message.name) ?? void 0, message.ncp_tool_result_content_items);
7118
8102
  return;
7119
8103
  }
7120
8104
  }
@@ -7147,7 +8131,7 @@ function toNcpMessages(sessionId, messages) {
7147
8131
  return ncpMessages;
7148
8132
  }
7149
8133
  function resolveLegacyEventType(message) {
7150
- const role = normalizeString(message.role)?.toLowerCase() ?? "";
8134
+ const role = normalizeString$1(message.role)?.toLowerCase() ?? "";
7151
8135
  if (role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0) return "assistant.tool_call";
7152
8136
  if (role === "tool") return "tool.result";
7153
8137
  if (role === "assistant") return "message.assistant";
@@ -7156,6 +8140,6 @@ function resolveLegacyEventType(message) {
7156
8140
  return `message.${role || "other"}`;
7157
8141
  }
7158
8142
  //#endregion
7159
- export { AgentManager, AgentRunRequestManager, AgentRuntimeManager, AgentRuntimeRegistry, AutomationManager, BuiltinNarpRuntimeProviderService, CONTEXT_COMPACTION_TIMELINE_KIND, ChannelManager, ConfigManager, ContextCompactionManager, ContextWindowPreviewManager, DEFAULT_AGENT_RUNTIME_ENTRY_ID, ExtensionManager, ExtensionPluginRegistryService, GatewayInboundProcessor, LlmProviderManager, LlmUsageManager, LlmUsageStore, McpManager, NARP_HTTP_RUNTIME_KIND, NARP_STDIO_RUNTIME_KIND, NEXTCLAW_TIMELINE_KIND_METADATA_KEY, NativeAgentRuntimeFactory, NcpAgentSessionJournalStore, NcpSessionManager, NextclawKernel, NextclawNcpContextBuilder, ProviderManagerNcpLLMApi, SessionRequestManager, SessionRunManager, SkillManager, ToolManager, UpdateManifestReader, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildNcpUserMessage, buildUserMessageParts, createAgentRuntimeSessionRequestDispatcher, createAssetTools, createLlmUsageRecord, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, getUnsignedUpdateManifest, hasLlmUsageTelemetry, isContextCompactionTimelineMessage, isReplyCapableChannel, listExtensionChannelIds, normalizeAgentRuntimeSessionTypeIcon, normalizeLlmUsageModel, normalizeOptionalString, parseSkillFrontmatter, projectNcpMessagesWithContextCompaction, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, resolveAgentHandoffDepth, resolveAgentRuntimeEntries, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolveSessionChannelContext, runGatewayInboundLoop, runPromptOverNcp, sanitizeLlmUsage, serializeUnsignedUpdateManifest, streamPromptOverNcp, stripSkillFrontmatter, syncSessionThinkingPreference, toNcpMessages, upsertContextCompactionTimelineMessage, waitForAgentRuntimeSessionReply };
8143
+ export { AgentManager, AgentRunClient, AgentRunRequestManager, AgentRuntimeManager, AgentRuntimeRegistry, AutomationManager, BuiltinNarpRuntimeProviderService, CONTEXT_COMPACTION_TIMELINE_KIND, ChannelManager, ConfigManager, ContextCompactionManager, ContextCompactionPreflightService, ContextWindowPreviewManager, DEFAULT_AGENT_RUNTIME_ENTRY_ID, ExtensionManager, GatewayInboundProcessor, LlmProviderManager, LlmUsageManager, LlmUsageStore, McpManager, NARP_HTTP_RUNTIME_KIND, NARP_STDIO_RUNTIME_KIND, NEXTCLAW_TIMELINE_KIND_METADATA_KEY, NativeAgentRuntimeFactory, NcpAgentSessionJournalStore, NcpSessionManager, NextclawKernel, NextclawNcpContextBuilder, ProviderManagerNcpLLMApi, SessionRequestManager, SessionRunManager, SkillManager, ToolManager, UpdateManifestReader, buildAgentRunSendPayload, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildSessionOrchestrationSection, buildUserMessageParts, createAgentRuntimeSessionRequestDispatcher, createAssetTools, createContextWindowSignature, createLlmUsageRecord, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, getUnsignedUpdateManifest, hasLlmUsageTelemetry, isContextCompactionTimelineMessage, isContextWindowSnapshot, isReplyCapableChannel, listExtensionChannelIds, normalizeAgentRuntimeSessionTypeIcon, normalizeLlmUsageModel, normalizeOptionalString, parseSkillFrontmatter, projectNcpMessagesWithContextCompaction, readContextWindowEventSessionId, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, resolveAgentHandoffDepth, resolveAgentRuntimeEntries, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolveNextclawNcpRunContext, resolveSessionChannelContext, runGatewayInboundLoop, sanitizeLlmUsage, serializeUnsignedUpdateManifest, shouldRefreshContextWindowDuringStream, shouldRefreshContextWindowImmediately, stripSkillFrontmatter, syncSessionThinkingPreference, toNcpMessages, upsertContextCompactionTimelineMessage, waitForAgentRuntimeSessionReply };
7160
8144
 
7161
8145
  //# sourceMappingURL=index.js.map