@nextclaw/kernel 0.10.2 → 0.10.3

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
@@ -1,10 +1,10 @@
1
1
  import { AUTOMATIC_UPDATE_CHECK_INTERVAL_MS, getAutomaticUpdateCheckDelay, resolveAutomaticUpdateCheckIntervalMs } from "./utils/automatic-update-check.utils.js";
2
2
  import { n as getUnsignedUpdateManifest, r as serializeUnsignedUpdateManifest, t as UpdateManifestReader } from "./update-manifest.types-C0qPrjGQ.js";
3
3
  import { createRequire } from "node:module";
4
- import { APP_NAME, AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, CLEAR_THINKING_TOKENS, CONTEXT_COMPACTION_METADATA_KEY, ConfigSchema, ContextCompactionService, ContextWindowBudgetService, CronService, CronTool, DEFAULT_PANELS_DIR, DEFAULT_SERVICE_APPS_DIR, DEFAULT_SESSION_SEARCH_LIMIT, DEFAULT_WORKSPACE_REPOSITORY_IDENTITY_RESOLVER, DIAGNOSTIC_CORRELATION_METADATA_KEY, DiagnosticRuntime, EditFileTool, ExecTool, ExtensionChannelAdapter, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, LiteLLMProvider, MAX_SESSION_SEARCH_LIMIT, MemoryGetTool, MemorySearchTool, MemoryStore, MessageBus, MessageTool, ProviderModelDiscoveryService, ProviderRegistry, ReadFileTool, RequestedSkillsMetadataReader, SILENT_REPLY_TOKEN, SessionProjectContextResolver, SessionSearchService, SkillsLoader, THINKING_LEVELS, ViewImageTool, WebFetchTool, WebSearchTool, WriteFileTool, buildCompressingCompactionCheckpoint, buildConfigSchema, buildContextWindowSnapshot, buildReloadPlan, buildSessionRequestToolResult, buildToolCatalogEntries, createAgentProfile, createAssistantStreamDeltaControlMessage, createAssistantStreamResetControlMessage, createCompletedSessionRequest, createFailedSessionRequest, createRunningSessionRequest, createRuntimeChildEnv, createTypingStopControlMessage, diffConfigPaths, ensureDir, estimateInputTokens, evaluateSilentReply, expandHome, findEffectiveAgentProfile, getConfigPath, getDataDir, getDataPath, getSessionsPath, getWorkspacePath, getWorkspacePathFromConfig, isNextclawControlMessage, loadConfig, mergeExtensionConfigView, modelSupportsVision, normalizeAgentProfileId, normalizeInlineSecretRefs, normalizeModelThinkingCapability, normalizeProviderModelConfig, normalizeToolParams, parseAgentScopedSessionKey, parseThinkingLevel, readCompressedContextCompactionCheckpoint, readOptionalString, readParentSessionId, readSessionProjectRoot, redactConfigObject, removeAgentProfile, resolveConfigSecrets, resolveDefaultAgentProfileId, resolveEffectiveAgentProfiles, resolveNextclawSelfManageGuidePaths, resolveProviderRuntime, resolveRuntimeCommandLaunch, resolveSessionProjectContext, resolveSessionWorkspacePath, resolveThinkingLevel, safeFilename, sanitizeOutboundAssistantContent, saveConfig, summarizeSessionRequestTask, toExtensionConfigView, updateAgentProfile } from "@nextclaw/core";
4
+ import { APP_NAME, AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, CLEAR_THINKING_TOKENS, CONTEXT_COMPACTION_METADATA_KEY, ConfigSchema, ContextCompactionService, ContextWindowBudgetService, CronService, CronTool, DEFAULT_PANELS_DIR, DEFAULT_SERVICE_APPS_DIR, DEFAULT_SESSION_SEARCH_LIMIT, DEFAULT_WORKSPACE_REPOSITORY_IDENTITY_RESOLVER, DIAGNOSTIC_CORRELATION_METADATA_KEY, DiagnosticRuntime, EditFileTool, ExecTool, ExtensionChannelAdapter, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, LiteLLMProvider, LocalExecutionClaimService, MAX_SESSION_SEARCH_LIMIT, MemoryGetTool, MemorySearchTool, MemoryStore, MessageBus, MessageTool, ProviderModelDiscoveryService, ProviderRegistry, ReadFileTool, RequestedSkillsMetadataReader, SILENT_REPLY_TOKEN, SessionProjectContextResolver, SessionSearchService, SkillsLoader, THINKING_LEVELS, ViewImageTool, WebFetchTool, WebSearchTool, WriteFileTool, buildCompressingCompactionCheckpoint, buildConfigSchema, buildContextWindowSnapshot, buildReloadPlan, buildSessionRequestToolResult, buildToolCatalogEntries, createAgentProfile, createAssistantStreamDeltaControlMessage, createAssistantStreamResetControlMessage, createCompletedSessionRequest, createFailedSessionRequest, createRunningSessionRequest, createRuntimeChildEnv, createTypingStopControlMessage, diffConfigPaths, ensureDir, estimateInputTokens, evaluateSilentReply, expandHome, findEffectiveAgentProfile, getConfigPath, getDataDir, getDataPath, getSessionsPath, getWorkspacePath, getWorkspacePathFromConfig, isNextclawControlMessage, loadConfig, mergeExtensionConfigView, modelSupportsVision, normalizeAgentProfileId, normalizeInlineSecretRefs, normalizeModelThinkingCapability, normalizeProviderModelConfig, normalizeToolParams, parseAgentScopedSessionKey, parseThinkingLevel, readCompressedContextCompactionCheckpoint, readOptionalString, readParentSessionId, readSessionProjectRoot, redactConfigObject, removeAgentProfile, resolveConfigSecrets, resolveDefaultAgentProfileId, resolveEffectiveAgentProfiles, resolveNextclawSelfManageGuidePaths, resolveProviderRuntime, resolveRuntimeCommandLaunch, resolveSessionProjectContext, resolveSessionWorkspacePath, resolveThinkingLevel, safeFilename, sanitizeOutboundAssistantContent, saveConfig, summarizeSessionRequestTask, toExtensionConfigView, updateAgentProfile } from "@nextclaw/core";
5
5
  import { NCP_AI_EXECUTION_METADATA_KEY, NCP_INTERNAL_VISIBILITY_METADATA_KEY, NCP_RUN_TRIGGER_METADATA_KEY, NcpEventType, OBSERVATION_EVENT_EXTENSION_TYPE, createUnavailableNcpAiExecutionMetadata, isHiddenNcpMessage, normalizeAssistantText, parseNcpRunTriggerInput, readNcpAiExecutionMetadata, sanitizeAssistantReplyTags } from "@nextclaw/ncp";
6
+ import { LocalAssetStore, buildAssetContentPath, buildOpenAiFunctionTool, isTextLikeAsset, ncpMessageToOpenAiMessages, validateToolArgs } from "@nextclaw/ncp-agent-runtime";
6
7
  import { CHAT_CONTINUATION_TARGET_MESSAGE_METADATA_KEY, CHAT_CONVERSATION_EXCERPT_TOKEN_KIND, CHAT_INLINE_TOKENS_METADATA_KEY, CHAT_INLINE_TOKENS_SCHEMA_VERSION, CHAT_PROJECT_TOKEN_KIND, CHAT_SESSION_MATERIALIZATION_METADATA_KEY, CHAT_SYSTEM_OBJECT_TOKEN_KIND, CHAT_UI_RESOURCE_TOKEN_KIND, CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND, CHAT_WORKSPACE_EXCERPT_TOKEN_KIND, CHAT_WORKSPACE_FILE_TOKEN_KIND, Contribution as Contribution$1, EventBus, EventBus as EventBus$1, Ingress, Ingress as Ingress$1, PANEL_APP_INLINE_HOST_CONTRACT, PANEL_APP_SCROLL_RESTORATION_CONTRACT, SYSTEM_OBJECT_REFERENCE_DEFAULT_LIMIT, SYSTEM_OBJECT_REFERENCE_MAX_LIMIT, SYSTEM_OBJECT_TYPE_CRON_JOB, SYSTEM_OBJECT_TYPE_INBOX_DELIVERY, UI_CONTENT_PARAMS_HOST_CONTRACT, classifyDiagnosticError, createSystemObjectReferenceUri, createTypedKey, eventKeys, eventKeys as eventKeys$1, ingressKeys, ingressKeys as ingressKeys$1, isRuntimeDefaultModelValue, isSilentReplyNcpMessage, normalizeRuntimeModelSelectionMode, parseSystemObjectReferenceUri, readChatUiResourceReference, readInlineContentHeight, readSystemObjectResolvedReference, readUiContentParams } from "@nextclaw/shared";
7
- import { LocalAssetStore, buildAssetContentPath, buildNcpUserContent, buildOpenAiFunctionTool, isTextLikeAsset, ncpMessageToOpenAiMessages, validateToolArgs } from "@nextclaw/ncp-agent-runtime";
8
8
  import { createHash, createHmac, randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
9
9
  import { catchError, filter, from, lastValueFrom, tap } from "rxjs";
10
10
  import { DefaultNcpAgentConversationStateManager, insertMessageByTimeline, mergeToolExecutionTiming } from "@nextclaw/ncp-toolkit";
@@ -64,6 +64,7 @@ var AgentManager = class {
64
64
  return {
65
65
  ...profile,
66
66
  contextTokens,
67
+ maxToolIterations: profile.maxToolIterations ?? config.agents.defaults.maxToolIterations,
67
68
  model: profile.model ?? config.agents.defaults.model,
68
69
  reservedContextTokens: ContextWindowBudgetService.resolveReservedContextTokens({
69
70
  contextTokens,
@@ -188,135 +189,6 @@ var ContextCompactionJournalRecoveryService = class {
188
189
  };
189
190
  };
190
191
  //#endregion
191
- //#region src/utils/agent-run-metadata.utils.ts
192
- const AGENT_RUN_MESSAGE_RUN_SPEC_METADATA_KEY = "run_spec";
193
- const LEGACY_RUN_METADATA_KEYS = [
194
- "account_id",
195
- "agent_id",
196
- "chat_id",
197
- "preferred_model",
198
- "project_root",
199
- "runtime",
200
- "sender_id",
201
- "session_key",
202
- "session_type"
203
- ];
204
- function stripLegacyRunMetadata(metadata) {
205
- const nextMetadata = structuredClone(metadata);
206
- for (const key of LEGACY_RUN_METADATA_KEYS) delete nextMetadata[key];
207
- return nextMetadata;
208
- }
209
- function buildRunMetadata(params) {
210
- const { message, metadata, route } = params;
211
- return {
212
- ...stripLegacyRunMetadata({
213
- ...message.metadata ?? {},
214
- ...metadata ?? {}
215
- }),
216
- channel: message.channel,
217
- chatId: message.chatId,
218
- accountId: route.accountId,
219
- agentId: route.agentId,
220
- sessionKey: route.sessionKey,
221
- senderId: message.senderId
222
- };
223
- }
224
- //#endregion
225
- //#region src/utils/ncp-message-bridge.utils.ts
226
- function normalizeString$1(value) {
227
- if (typeof value !== "string") return null;
228
- const trimmed = value.trim();
229
- return trimmed.length > 0 ? trimmed : null;
230
- }
231
- function ensureIsoTimestamp(value, fallback) {
232
- if (typeof value !== "string") return fallback;
233
- const timestamp = Date.parse(value);
234
- if (!Number.isFinite(timestamp)) return fallback;
235
- return new Date(timestamp).toISOString();
236
- }
237
- function serializeToolArgs(args) {
238
- if (typeof args === "string") return args;
239
- return JSON.stringify(args ?? {});
240
- }
241
- function serializeLegacyContent(parts) {
242
- const text = parts.filter(isTextLikePart).map((part) => part.text).join("");
243
- if (text.length > 0) return text;
244
- if (parts.length === 0) return "";
245
- return structuredClone(parts);
246
- }
247
- function extractTextFromNcpMessage(message) {
248
- if (!message) return "";
249
- return (message.role === "assistant" ? sanitizeAssistantReplyTags(message) : message).parts.filter(isTextLikePart).map((part) => part.text).join("");
250
- }
251
- function isTextLikePart(part) {
252
- return part.type === "text" || part.type === "rich-text";
253
- }
254
- function buildLegacyUserContent(parts, options = {}) {
255
- const content = buildNcpUserContent(parts, { assetStore: options.assetStore });
256
- if (content === "") return serializeLegacyContent(parts);
257
- return content;
258
- }
259
- function buildLegacyAssistantMessages(message, timestamp) {
260
- const textContent = extractTextFromNcpMessage(message);
261
- const reasoningParts = message.parts.filter((part) => part.type === "reasoning");
262
- const reasoningContent = reasoningParts.map((part) => part.text).join("");
263
- const toolInvocations = message.parts.filter((part) => part.type === "tool-invocation");
264
- const assistantMessage = {
265
- role: "assistant",
266
- content: textContent,
267
- timestamp,
268
- ncp_message_id: message.id,
269
- ncp_parts: structuredClone(message.parts)
270
- };
271
- if (typeof message.metadata?.reply_to === "string" && message.metadata.reply_to.trim().length > 0) assistantMessage.reply_to = message.metadata.reply_to.trim();
272
- if (reasoningParts.length > 0) assistantMessage.reasoning_content = reasoningContent;
273
- if (toolInvocations.length > 0) assistantMessage.tool_calls = toolInvocations.map((toolInvocation, index) => ({
274
- id: toolInvocation.toolCallId ?? `${message.id}:tool:${index}`,
275
- type: "function",
276
- function: {
277
- name: toolInvocation.toolName,
278
- arguments: serializeToolArgs(toolInvocation.args)
279
- }
280
- }));
281
- const messages = [assistantMessage];
282
- for (const toolInvocation of toolInvocations) {
283
- if (toolInvocation.state !== "result") continue;
284
- messages.push({
285
- role: "tool",
286
- name: toolInvocation.toolName,
287
- tool_call_id: toolInvocation.toolCallId,
288
- content: typeof toolInvocation.result === "string" ? toolInvocation.result : JSON.stringify(toolInvocation.result ?? null),
289
- ...toolInvocation.resultContentItems ? { ncp_tool_result_content_items: structuredClone(toolInvocation.resultContentItems) } : {},
290
- timestamp,
291
- ncp_message_id: message.id
292
- });
293
- }
294
- return messages;
295
- }
296
- function buildLegacyNonAssistantMessage(message, timestamp, options) {
297
- return {
298
- role: message.role === "service" && options.serviceRole === "system" ? "system" : message.role,
299
- content: buildLegacyUserContent(message.parts, options),
300
- timestamp,
301
- ncp_message_id: message.id,
302
- ncp_parts: structuredClone(message.parts),
303
- ...message.metadata ? { ncp_metadata: structuredClone(message.metadata) } : {}
304
- };
305
- }
306
- function toLegacyMessages(messages, options = {}) {
307
- const legacyMessages = [];
308
- for (const rawMessage of messages) {
309
- const message = rawMessage.role === "assistant" ? sanitizeAssistantReplyTags(rawMessage) : rawMessage;
310
- const timestamp = ensureIsoTimestamp(message.timestamp, (/* @__PURE__ */ new Date()).toISOString());
311
- if (message.role === "assistant") {
312
- legacyMessages.push(...buildLegacyAssistantMessages(message, timestamp));
313
- continue;
314
- }
315
- legacyMessages.push(buildLegacyNonAssistantMessage(message, timestamp, options));
316
- }
317
- return legacyMessages;
318
- }
319
- //#endregion
320
192
  //#region src/utils/agent-model-input-budget.utils.ts
321
193
  function buildContextBlockInputMessages(contextBlocks = []) {
322
194
  const contextContent = contextBlocks.map((block) => block.trim()).filter(Boolean).join("\n\n");
@@ -938,6 +810,13 @@ function shouldRefreshContextWindowImmediately(event) {
938
810
  }
939
811
  //#endregion
940
812
  //#region src/features/context-compaction/services/context-compaction-preflight.service.ts
813
+ function toCompactionModelMessages(messages, assetStore) {
814
+ return messages.flatMap((message) => ncpMessageToOpenAiMessages(message, { assetStore }).map((providerMessage) => ({
815
+ ...providerMessage,
816
+ ncp_message_id: message.id,
817
+ timestamp: message.timestamp
818
+ })));
819
+ }
941
820
  function estimateCompactionProjectionOverhead(phase) {
942
821
  const summaryPlanOverhead = estimateInputTokens({
943
822
  role: "user",
@@ -1006,8 +885,9 @@ var ContextCompactionPreflightService = class {
1006
885
  compactionService = new ContextCompactionService();
1007
886
  contextWindowBudgetService = new ContextWindowBudgetService();
1008
887
  summaryGenerationService;
1009
- constructor(agentManager, providerManager) {
888
+ constructor(agentManager, providerManager, assetStore = null) {
1010
889
  this.agentManager = agentManager;
890
+ this.assetStore = assetStore;
1011
891
  this.summaryGenerationService = new ContextCompactionSummaryGenerationService(providerManager);
1012
892
  }
1013
893
  preview = (params) => {
@@ -1025,7 +905,7 @@ var ContextCompactionPreflightService = class {
1025
905
  const contextBlockInputTokens = estimateInputTokens(contextBlockMessages);
1026
906
  const toolInputTokens = estimateToolInputTokens(tools);
1027
907
  const snapshotFixedInputTokens = fixedInputTokens ?? contextBlockInputTokens + toolInputTokens;
1028
- const messages = [...fixedInputTokens === void 0 ? contextBlockMessages : [], ...toLegacyMessages(projectedMessages)];
908
+ const messages = [...fixedInputTokens === void 0 ? contextBlockMessages : [], ...toCompactionModelMessages(projectedMessages, this.assetStore)];
1029
909
  return buildContextWindowSnapshotFromBudget({
1030
910
  budget: this.contextWindowBudgetService.evaluate({
1031
911
  messages,
@@ -1057,7 +937,7 @@ var ContextCompactionPreflightService = class {
1057
937
  sessionMessages: ncpMessages
1058
938
  }).messages : ncpMessages.filter((message) => !isContextCompactionTimelineMessage(message));
1059
939
  const contextBlockMessages = buildContextBlockInputMessages(contextBlocks);
1060
- const messages = [...contextBlockMessages, ...toLegacyMessages(projectedMessages)];
940
+ const messages = [...contextBlockMessages, ...toCompactionModelMessages(projectedMessages, this.assetStore)];
1061
941
  const preservableUserMessageIds = ncpMessages.flatMap((message) => message.role === "user" ? [message.id] : []);
1062
942
  const fixedInputTokens = estimateToolInputTokens(tools);
1063
943
  const snapshotFixedInputTokens = estimateInputTokens(contextBlockMessages) + fixedInputTokens;
@@ -1069,6 +949,7 @@ var ContextCompactionPreflightService = class {
1069
949
  });
1070
950
  const plan = trigger === "automatic" && !budget.shouldCompact ? null : this.compactionService.prepareForModelInput({
1071
951
  messages,
952
+ coverLeadingSystemMessage: contextBlockMessages.length === 0 && isContextCompactionProjectionMessage(projectedMessages[0]),
1072
953
  contextTokens,
1073
954
  compactionThresholdTokens: trigger === "manual" ? 0 : budget.triggerTokens,
1074
955
  fixedInputTokens,
@@ -1156,7 +1037,7 @@ var ContextCompactionPreflightService = class {
1156
1037
  sessionMessages: [...pending.sessionMessages, timelineMessage]
1157
1038
  }).messages;
1158
1039
  const budget = this.contextWindowBudgetService.evaluate({
1159
- messages: [...pending.contextBlockMessages, ...toLegacyMessages(projectedMessages, { serviceRole: "system" })],
1040
+ messages: [...pending.contextBlockMessages, ...toCompactionModelMessages(projectedMessages, this.assetStore)],
1160
1041
  contextTokens: pending.contextTokens,
1161
1042
  fixedInputTokens: pending.plan.fixedInputTokens,
1162
1043
  reservedContextTokens: pending.reservedContextTokens
@@ -1193,11 +1074,11 @@ const MINIMUM_CONFIGURABLE_CONTEXT_TOKENS = 1e3;
1193
1074
  var AgentContextWindowManager = class {
1194
1075
  runInputBudgetBySession = /* @__PURE__ */ new Map();
1195
1076
  preflightService;
1196
- constructor(agentManager, contextProviderManager, toolProviderManager) {
1077
+ constructor(agentManager, contextProviderManager, toolProviderManager, assetStore = null) {
1197
1078
  this.agentManager = agentManager;
1198
1079
  this.contextProviderManager = contextProviderManager;
1199
1080
  this.toolProviderManager = toolProviderManager;
1200
- this.preflightService = new ContextCompactionPreflightService(agentManager);
1081
+ this.preflightService = new ContextCompactionPreflightService(agentManager, void 0, assetStore);
1201
1082
  }
1202
1083
  resolveRunSurface = async (request) => {
1203
1084
  const surface = await this.buildRunSurface(request);
@@ -1332,8 +1213,8 @@ var AgentContextWindowManager = class {
1332
1213
  //#region src/managers/agent-run-context-compaction.manager.ts
1333
1214
  var AgentRunContextCompactionManager = class {
1334
1215
  preflightService;
1335
- constructor(agentManager, providerManager) {
1336
- this.preflightService = new ContextCompactionPreflightService(agentManager, providerManager);
1216
+ constructor(agentManager, providerManager, assetStore = null) {
1217
+ this.preflightService = new ContextCompactionPreflightService(agentManager, providerManager, assetStore);
1337
1218
  }
1338
1219
  runPreflight = (input) => {
1339
1220
  return this.run(input, "automatic", input.phase ?? "pre-run");
@@ -1965,6 +1846,40 @@ function createUnavailableAiExecutionMetadataEvent(params) {
1965
1846
  };
1966
1847
  }
1967
1848
  //#endregion
1849
+ //#region src/utils/agent-run-metadata.utils.ts
1850
+ const AGENT_RUN_MESSAGE_RUN_SPEC_METADATA_KEY = "run_spec";
1851
+ const LEGACY_RUN_METADATA_KEYS = [
1852
+ "account_id",
1853
+ "agent_id",
1854
+ "chat_id",
1855
+ "preferred_model",
1856
+ "project_root",
1857
+ "runtime",
1858
+ "sender_id",
1859
+ "session_key",
1860
+ "session_type"
1861
+ ];
1862
+ function stripLegacyRunMetadata(metadata) {
1863
+ const nextMetadata = structuredClone(metadata);
1864
+ for (const key of LEGACY_RUN_METADATA_KEYS) delete nextMetadata[key];
1865
+ return nextMetadata;
1866
+ }
1867
+ function buildRunMetadata(params) {
1868
+ const { message, metadata, route } = params;
1869
+ return {
1870
+ ...stripLegacyRunMetadata({
1871
+ ...message.metadata ?? {},
1872
+ ...metadata ?? {}
1873
+ }),
1874
+ channel: message.channel,
1875
+ chatId: message.chatId,
1876
+ accountId: route.accountId,
1877
+ agentId: route.agentId,
1878
+ sessionKey: route.sessionKey,
1879
+ senderId: message.senderId
1880
+ };
1881
+ }
1882
+ //#endregion
1968
1883
  //#region src/utils/agent-run-request.utils.ts
1969
1884
  function readOptionalString$12(value) {
1970
1885
  if (typeof value !== "string") return;
@@ -2102,6 +2017,7 @@ function resolveRunSpec(params) {
2102
2017
  agentId: session.agentId ?? request.agentId ?? defaultAgentId,
2103
2018
  model,
2104
2019
  requestedModel: request.model ?? null,
2020
+ maxToolIterations: params.maxToolIterations,
2105
2021
  maxTokens: request.maxTokens ?? modelMaxTokens,
2106
2022
  thinkingEffort: request.thinkingEffort ?? session.thinkingEffort ?? null,
2107
2023
  correlationId: request.correlationId
@@ -2121,6 +2037,7 @@ function attachRunSpecMetadata(params) {
2121
2037
  model: spec.model,
2122
2038
  modelSource,
2123
2039
  requestedModel: request.model ?? null,
2040
+ maxToolIterations: spec.maxToolIterations,
2124
2041
  maxTokens: spec.maxTokens,
2125
2042
  thinkingEffort: spec.thinkingEffort,
2126
2043
  projectRoot: request.projectRoot ?? session.projectRoot ?? null,
@@ -2399,7 +2316,7 @@ var AgentRunRequestManager = class {
2399
2316
  runtimeRuns;
2400
2317
  pendingInputs;
2401
2318
  started = false;
2402
- constructor(agentRuntimeManager, agentManager, configManager, agentContextWindowManager, eventBus, ingress, sessionManager, sessionRunManager, diagnostics) {
2319
+ constructor(agentRuntimeManager, agentManager, configManager, agentContextWindowManager, eventBus, ingress, sessionManager, sessionRunManager, diagnostics, executionClaims) {
2403
2320
  this.agentRuntimeManager = agentRuntimeManager;
2404
2321
  this.agentManager = agentManager;
2405
2322
  this.configManager = configManager;
@@ -2409,6 +2326,7 @@ var AgentRunRequestManager = class {
2409
2326
  this.sessionManager = sessionManager;
2410
2327
  this.sessionRunManager = sessionRunManager;
2411
2328
  this.diagnostics = diagnostics;
2329
+ this.executionClaims = executionClaims;
2412
2330
  this.pendingInputs = new AgentRunInputDeliveryService(agentRuntimeManager, sessionManager, sessionRunManager, this.publishRunQueueUpdated);
2413
2331
  this.idempotency = new AgentRunRequestIdempotencyService(sessionManager, sessionRunManager);
2414
2332
  this.runtimeRuns = new AgentRuntimeRunObserverService({
@@ -2509,20 +2427,17 @@ var AgentRunRequestManager = class {
2509
2427
  startQueuedRun = async (sessionRun, activeRequest) => {
2510
2428
  const { request, session } = activeRequest;
2511
2429
  const requestRunStartedAt = (/* @__PURE__ */ new Date()).toISOString();
2512
- const model = request.model ?? session.model ?? this.configManager.getDefaultModel();
2513
- const { modelSource, spec } = resolveRunSpec({
2514
- defaultAgentId: this.agentManager.getDefaultAgentId(),
2515
- model,
2516
- modelMaxTokens: this.configManager.getModelMaxTokens(model),
2517
- request,
2518
- runId: activeRequest.runId,
2519
- session
2520
- });
2430
+ const { modelSource, spec } = this.resolveQueuedRunSpec(activeRequest);
2521
2431
  const trigger = resolveRunTriggerMetadata({
2522
2432
  request,
2523
2433
  spec,
2524
2434
  startedAt: requestRunStartedAt
2525
2435
  });
2436
+ const executionClaim = await this.claimSessionExecutionOrFail({
2437
+ requestRunStartedAt,
2438
+ sessionRun,
2439
+ spec
2440
+ });
2526
2441
  const message = attachRunSpecMetadata({
2527
2442
  message: {
2528
2443
  ...request.message,
@@ -2566,10 +2481,10 @@ var AgentRunRequestManager = class {
2566
2481
  spec,
2567
2482
  trigger
2568
2483
  });
2569
- await sessionRun.applyEvents([messageSentEvent, triggerEvent]);
2570
- this.publishNcpEvent(messageSentEvent);
2571
- this.publishNcpEvent(triggerEvent);
2572
2484
  try {
2485
+ await sessionRun.applyEvents([messageSentEvent, triggerEvent]);
2486
+ this.publishNcpEvent(messageSentEvent);
2487
+ this.publishNcpEvent(triggerEvent);
2573
2488
  const runtime = this.agentRuntimeManager.getOrCreate({
2574
2489
  agentRuntimeId: session.agentRuntimeId,
2575
2490
  session,
@@ -2588,9 +2503,11 @@ var AgentRunRequestManager = class {
2588
2503
  requestRunStartedAt,
2589
2504
  runtime,
2590
2505
  spec,
2591
- parentCorrelationId
2506
+ parentCorrelationId,
2507
+ executionClaim
2592
2508
  });
2593
2509
  } catch (error) {
2510
+ this.releaseExecutionClaim(executionClaim);
2594
2511
  const classification = classifyDiagnosticError(error, activeRequest.signal);
2595
2512
  this.diagnostics?.record({
2596
2513
  domain: "agent.run",
@@ -2614,10 +2531,54 @@ var AgentRunRequestManager = class {
2614
2531
  throw error;
2615
2532
  }
2616
2533
  };
2617
- startRuntimeRun = (params) => this.runtimeRuns.start({
2618
- ...params,
2619
- onSettled: this.startNextQueuedRun
2620
- });
2534
+ resolveQueuedRunSpec = (activeRequest) => {
2535
+ const { request, session } = activeRequest;
2536
+ const model = request.model ?? session.model ?? this.configManager.getDefaultModel();
2537
+ const defaultAgentId = this.agentManager.getDefaultAgentId();
2538
+ const agentId = session.agentId ?? request.agentId ?? defaultAgentId;
2539
+ const maxToolIterations = this.agentManager.resolveAgentProfileForRun({
2540
+ agentId,
2541
+ requestMetadata: request.metadata,
2542
+ storedAgentId: session.agentId
2543
+ }).maxToolIterations;
2544
+ return resolveRunSpec({
2545
+ defaultAgentId,
2546
+ model,
2547
+ modelMaxTokens: this.configManager.getModelMaxTokens(model),
2548
+ maxToolIterations,
2549
+ request,
2550
+ runId: activeRequest.runId,
2551
+ session
2552
+ });
2553
+ };
2554
+ claimSessionExecutionOrFail = async (params) => {
2555
+ const { requestRunStartedAt, sessionRun, spec } = params;
2556
+ const acquired = this.executionClaims?.tryAcquire(`session:${sessionRun.sessionId}`);
2557
+ if (!acquired || acquired.acquired) return acquired?.claim;
2558
+ const error = /* @__PURE__ */ new Error(`Session already has an active run owned by another NextClaw process: ${sessionRun.sessionId}`);
2559
+ await this.publishRunStartupFailure({
2560
+ error,
2561
+ requestRunStartedAt,
2562
+ sessionRun,
2563
+ spec
2564
+ });
2565
+ this.startNextQueuedRun(sessionRun);
2566
+ throw error;
2567
+ };
2568
+ startRuntimeRun = (params) => {
2569
+ const { executionClaim, ...runtimeParams } = params;
2570
+ this.runtimeRuns.start({
2571
+ ...runtimeParams,
2572
+ onSettled: (sessionRun) => {
2573
+ this.releaseExecutionClaim(executionClaim);
2574
+ this.startNextQueuedRun(sessionRun);
2575
+ }
2576
+ });
2577
+ };
2578
+ releaseExecutionClaim = (claim) => {
2579
+ if (!claim) return;
2580
+ claim.release();
2581
+ };
2621
2582
  publishRunStartupFailure = async (params) => {
2622
2583
  const { error, requestRunStartedAt, sessionRun, spec } = params;
2623
2584
  const metadataEvent = createUnavailableAiExecutionMetadataEvent({
@@ -3809,25 +3770,40 @@ var AppPackageManager = class {
3809
3770
  throw error;
3810
3771
  }
3811
3772
  };
3812
- listActiveComponentSources = async () => {
3773
+ listActiveComponentSources = async () => (await this.listActiveComponentSourcesWithDiagnostics()).sources;
3774
+ listActiveComponentSourcesWithDiagnostics = async () => {
3813
3775
  const records = await this.registryService.listApps();
3814
- return (await Promise.all(records.filter((record) => record.enabled).map(async (record) => {
3815
- await this.installationService.assertVersionIntegrity(record.appId, record.activeVersion);
3816
- const version = record.installedVersions[record.activeVersion];
3817
- if (!version || version.manifestSchemaVersion !== 2) return [];
3818
- return (version.components ?? []).map((component) => ({
3819
- kind: component.kind,
3820
- id: component.id,
3821
- packageId: record.appId,
3822
- packageVersion: record.activeVersion,
3823
- sourcePath: component.componentDirectory,
3824
- manifestPath: component.manifestPath,
3825
- dataDirectory: record.dataDirectory,
3826
- instanceId: record.defaultInstance.id,
3827
- storage: record.defaultInstance.storage,
3828
- ...this.resolveSecurity(version, record.appId)
3829
- }));
3830
- }))).flat();
3776
+ const results = await Promise.all(records.filter((record) => record.enabled).map(async (record) => {
3777
+ try {
3778
+ await this.installationService.assertVersionIntegrity(record.appId, record.activeVersion);
3779
+ const version = record.installedVersions[record.activeVersion];
3780
+ if (!version || version.manifestSchemaVersion !== 2) return { sources: [] };
3781
+ return { sources: (version.components ?? []).map((component) => ({
3782
+ kind: component.kind,
3783
+ id: component.id,
3784
+ packageId: record.appId,
3785
+ packageVersion: record.activeVersion,
3786
+ sourcePath: component.componentDirectory,
3787
+ manifestPath: component.manifestPath,
3788
+ dataDirectory: record.dataDirectory,
3789
+ instanceId: record.defaultInstance.id,
3790
+ storage: record.defaultInstance.storage,
3791
+ ...this.resolveSecurity(version, record.appId)
3792
+ })) };
3793
+ } catch (error) {
3794
+ return {
3795
+ sources: [],
3796
+ unavailablePackage: {
3797
+ appId: record.appId,
3798
+ message: error instanceof Error ? error.message : String(error)
3799
+ }
3800
+ };
3801
+ }
3802
+ }));
3803
+ return {
3804
+ sources: results.flatMap((result) => result.sources),
3805
+ unavailablePackages: results.flatMap((result) => result.unavailablePackage ?? [])
3806
+ };
3831
3807
  };
3832
3808
  listOperations = async () => await this.operationManager.list();
3833
3809
  startOperation = async (input) => {
@@ -8050,6 +8026,26 @@ function isMissingFileError$2(error) {
8050
8026
  return isRecord$13(error) && error.code === "ENOENT";
8051
8027
  }
8052
8028
  //#endregion
8029
+ //#region src/utils/ncp-message-bridge.utils.ts
8030
+ function normalizeString$1(value) {
8031
+ if (typeof value !== "string") return null;
8032
+ const trimmed = value.trim();
8033
+ return trimmed.length > 0 ? trimmed : null;
8034
+ }
8035
+ function ensureIsoTimestamp(value, fallback) {
8036
+ if (typeof value !== "string") return fallback;
8037
+ const timestamp = Date.parse(value);
8038
+ if (!Number.isFinite(timestamp)) return fallback;
8039
+ return new Date(timestamp).toISOString();
8040
+ }
8041
+ function extractTextFromNcpMessage(message) {
8042
+ if (!message) return "";
8043
+ return (message.role === "assistant" ? sanitizeAssistantReplyTags(message) : message).parts.filter(isTextLikePart).map((part) => part.text).join("");
8044
+ }
8045
+ function isTextLikePart(part) {
8046
+ return part.type === "text" || part.type === "rich-text";
8047
+ }
8048
+ //#endregion
8053
8049
  //#region src/services/agent-run-client.service.ts
8054
8050
  function readEventCorrelationId(event) {
8055
8051
  if (!("payload" in event)) return;
@@ -9174,7 +9170,8 @@ var PanelAppManager = class {
9174
9170
  return {
9175
9171
  workspacePath,
9176
9172
  panelsPath,
9177
- entries: (await Promise.all(sources.map(({ source, packageSource }) => this.entryPresenter.build(source, appState.apps[encodePanelAppId(source.sourceName)] ?? {}, packageSource, appState.mainSidebarAppIds)))).sort(this.entryPresenter.compare)
9173
+ entries: (await Promise.all(sources.map(({ source, packageSource }) => this.entryPresenter.build(source, appState.apps[encodePanelAppId(source.sourceName)] ?? {}, packageSource, appState.mainSidebarAppIds)))).sort(this.entryPresenter.compare),
9174
+ unavailablePackages: await this.params.listPackageComponentDiagnostics?.() ?? []
9178
9175
  };
9179
9176
  };
9180
9177
  getPanelAppContent = async (id, sourcePath) => {
@@ -17672,6 +17669,13 @@ var SessionManager = class {
17672
17669
  const summaryReadOptions = !peerId && limit !== void 0 ? { limit } : void 0;
17673
17670
  return applyLimit((await journalStore.listSessionSummaries(summaryReadOptions)).filter((summary) => !peerId || summary.peerId === peerId).map(this.workingDirResolver.withWorkingDir), limit);
17674
17671
  };
17672
+ listSessionPage = async (options) => {
17673
+ const result = await this.options.journalStore.listSessionSummaryPage(options);
17674
+ return {
17675
+ ...result,
17676
+ sessions: result.sessions.map(this.workingDirResolver.withWorkingDir)
17677
+ };
17678
+ };
17675
17679
  listSessionMessages = async (sessionId, options) => {
17676
17680
  const { cursor, limit } = options ?? {};
17677
17681
  const normalizedSessionId = normalizeSessionId(sessionId);
@@ -18915,6 +18919,28 @@ const SQLITE_DATABASE_FILE = ".ncp-agent-session-catalog.sqlite";
18915
18919
  const CATALOG_SCHEMA_VERSION = 1;
18916
18920
  const MIGRATION_STATUS_KEY = "migration_status";
18917
18921
  const MIGRATION_COMPLETE = "complete";
18922
+ function buildCatalogFilter(query) {
18923
+ const normalized = query?.trim().toLocaleLowerCase();
18924
+ if (!normalized) return {
18925
+ sql: "deleted_at IS NULL",
18926
+ params: []
18927
+ };
18928
+ const pattern = `%${normalized.replace(/[\\%_]/g, "\\$&")}%`;
18929
+ return {
18930
+ sql: `deleted_at IS NULL AND (
18931
+ LOWER(session_id) LIKE ? ESCAPE '\\' OR
18932
+ LOWER(COALESCE(peer_id, '')) LIKE ? ESCAPE '\\' OR
18933
+ LOWER(COALESCE(agent_id, '')) LIKE ? ESCAPE '\\' OR
18934
+ LOWER(metadata_json) LIKE ? ESCAPE '\\'
18935
+ )`,
18936
+ params: [
18937
+ pattern,
18938
+ pattern,
18939
+ pattern,
18940
+ pattern
18941
+ ]
18942
+ };
18943
+ }
18918
18944
  function readEventMessage(event) {
18919
18945
  if (event.type === NcpEventType.MessageSent || event.type === NcpEventType.MessageCompleted || event.type === "session.snapshot.message") return event.payload.message;
18920
18946
  }
@@ -18963,11 +18989,25 @@ var NcpAgentSessionSummaryIndexStore = class {
18963
18989
  const row = this.db().prepare("SELECT * FROM sessions WHERE session_id = ? AND deleted_at IS NULL LIMIT 1").get(normalizeNcpSessionId(sessionId));
18964
18990
  return row ? structuredClone(rowToSummary(row)) : null;
18965
18991
  };
18966
- list = async () => {
18992
+ list = async (limit) => {
18967
18993
  await this.ensureReady();
18994
+ const query = `SELECT * FROM sessions
18995
+ WHERE deleted_at IS NULL
18996
+ ORDER BY COALESCE(last_message_at, created_at, updated_at) DESC, session_id DESC`;
18997
+ return (limit === void 0 ? this.db().prepare(query).all() : this.db().prepare(`${query} LIMIT ?`).all(limit)).map((row) => structuredClone(rowToSummary(row)));
18998
+ };
18999
+ listPage = async (options) => {
19000
+ await this.ensureReady();
19001
+ const filter = buildCatalogFilter(options.query);
18968
19002
  return this.db().prepare(`SELECT * FROM sessions
18969
- WHERE deleted_at IS NULL
18970
- ORDER BY COALESCE(last_message_at, created_at, updated_at) DESC, session_id DESC`).all().map((row) => structuredClone(rowToSummary(row)));
19003
+ WHERE ${filter.sql}
19004
+ ORDER BY COALESCE(last_message_at, created_at, updated_at) DESC, session_id DESC
19005
+ LIMIT ? OFFSET ?`).all(...filter.params, options.limit, options.offset).map((row) => structuredClone(rowToSummary(row)));
19006
+ };
19007
+ count = async (query) => {
19008
+ await this.ensureReady();
19009
+ const filter = buildCatalogFilter(query);
19010
+ return this.db().prepare(`SELECT COUNT(*) AS total FROM sessions WHERE ${filter.sql}`).get(...filter.params).total;
18971
19011
  };
18972
19012
  upsert = async (summary) => {
18973
19013
  await this.ensureReady();
@@ -19144,11 +19184,25 @@ var NcpAgentSessionSummaryReadStore = class {
19144
19184
  this.options = options;
19145
19185
  }
19146
19186
  list = async (limit) => {
19147
- const summaries = await this.options.listIndexedSummaries();
19148
- return await mapWithConcurrency(limit === void 0 ? summaries : summaries.slice(0, Math.max(0, limit)), this.withMetadata);
19187
+ const normalizedLimit = limit === void 0 || limit === Number.POSITIVE_INFINITY ? void 0 : Math.max(0, Math.trunc(limit));
19188
+ const summaries = await this.options.summaryIndex.list(normalizedLimit);
19189
+ return await mapWithConcurrency(normalizedLimit === void 0 ? summaries : summaries.slice(0, normalizedLimit), this.withMetadata);
19190
+ };
19191
+ listPage = async (options) => {
19192
+ const page = Math.max(1, Math.trunc(options.page));
19193
+ const pageSize = Math.max(1, Math.trunc(options.pageSize));
19194
+ const [summaries, total] = await Promise.all([this.options.summaryIndex.listPage({
19195
+ offset: (page - 1) * pageSize,
19196
+ limit: pageSize,
19197
+ ...options.query?.trim() ? { query: options.query.trim() } : {}
19198
+ }), this.options.summaryIndex.count(options.query)]);
19199
+ return {
19200
+ sessions: await mapWithConcurrency(summaries, this.withMetadata),
19201
+ total
19202
+ };
19149
19203
  };
19150
19204
  get = async (sessionId) => {
19151
- const indexed = await this.options.getIndexedSummary(sessionId);
19205
+ const indexed = await this.options.summaryIndex.get(sessionId);
19152
19206
  if (indexed) return await this.withMetadata(indexed);
19153
19207
  const messageCount = await this.options.readProjectedMessageCount(sessionId);
19154
19208
  if (messageCount === null) return null;
@@ -19221,8 +19275,7 @@ var NcpAgentSessionJournalStore = class {
19221
19275
  }
19222
19276
  });
19223
19277
  this.summaryReadStore = new NcpAgentSessionSummaryReadStore({
19224
- getIndexedSummary: (sessionId) => this.summaryIndexStore.get(sessionId),
19225
- listIndexedSummaries: () => this.summaryIndexStore.list(),
19278
+ summaryIndex: this.summaryIndexStore,
19226
19279
  readJournalModifiedAt: async (sessionId) => (await stat(this.sessionPath(sessionId))).mtime.toISOString(),
19227
19280
  readMetadata: (sessionId, fallback) => this.metadataStore.read(sessionId, fallback),
19228
19281
  readProjectedMessageCount: async (sessionId) => (await this.messageProjectionStore.readMeta(sessionId))?.total ?? null
@@ -19253,6 +19306,7 @@ var NcpAgentSessionJournalStore = class {
19253
19306
  listSessionSummaries = async (options) => {
19254
19307
  return await this.summaryReadStore.list(options?.limit);
19255
19308
  };
19309
+ listSessionSummaryPage = (options) => this.summaryReadStore.listPage(options);
19256
19310
  getSessionSummary = async (sessionId) => {
19257
19311
  const normalizedSessionId = normalizeNcpSessionId(sessionId);
19258
19312
  if (!normalizedSessionId) return null;
@@ -19604,7 +19658,7 @@ var NextclawKernel = class {
19604
19658
  this.assetStore = new LocalAssetStore({ rootDir: resolve(getDataDir(), "assets") });
19605
19659
  this.control = new NextclawKernelControlManager();
19606
19660
  this.agents = new AgentManager(this.configManager);
19607
- this.agentContextWindowManager = new AgentContextWindowManager(this.agents, this.contextProviderManager, this.toolProviderManager);
19661
+ this.agentContextWindowManager = new AgentContextWindowManager(this.agents, this.contextProviderManager, this.toolProviderManager, this.assetStore);
19608
19662
  this.accessManager = new AccessManager({
19609
19663
  configManager: this.configManager,
19610
19664
  homeDir: options.homeDir
@@ -19633,7 +19687,8 @@ var NextclawKernel = class {
19633
19687
  configManager: this.configManager,
19634
19688
  eventBus: this.eventBus,
19635
19689
  ingress: this.ingress,
19636
- listPackageComponentSources: this.appPackageManager.listActiveComponentSources
19690
+ listPackageComponentSources: this.appPackageManager.listActiveComponentSources,
19691
+ listPackageComponentDiagnostics: async () => (await this.appPackageManager.listActiveComponentSourcesWithDiagnostics()).unavailablePackages
19637
19692
  });
19638
19693
  this.preferenceManager = new PreferenceManager({ storePath: resolveKernelPreferenceStorePath(options) });
19639
19694
  ({appDataManager: this.appDataManager, serviceAppManager: this.serviceAppManager} = createKernelServiceAppManagers({
@@ -19671,10 +19726,10 @@ var NextclawKernel = class {
19671
19726
  ingress: this.ingress
19672
19727
  })
19673
19728
  });
19674
- this.contextCompactionManager = new AgentRunContextCompactionManager(this.agents, this.llmProviders);
19729
+ this.contextCompactionManager = new AgentRunContextCompactionManager(this.agents, this.llmProviders, this.assetStore);
19675
19730
  this.sessionRunManager = new SessionRunManager(this.sessionManager, options.productActivitySink);
19676
19731
  this.sessionContextCompactionManager = new SessionContextCompactionManager(this.agentRuntimeManager, this.eventBus, this.sessionManager, this.sessionRunManager);
19677
- this.agentRunRequestManager = new AgentRunRequestManager(this.agentRuntimeManager, this.agents, this.configManager, this.agentContextWindowManager, this.eventBus, this.ingress, this.sessionManager, this.sessionRunManager, this.diagnostics);
19732
+ this.agentRunRequestManager = new AgentRunRequestManager(this.agentRuntimeManager, this.agents, this.configManager, this.agentContextWindowManager, this.eventBus, this.ingress, this.sessionManager, this.sessionRunManager, this.diagnostics, new LocalExecutionClaimService(resolve(sessionsDir, ".execution-claims", "session-runs")));
19678
19733
  this.contributions = this.createContributions();
19679
19734
  }
19680
19735
  createContributions = () => [