@nextclaw/kernel 0.4.0-beta.1 → 0.4.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
@@ -1,7 +1,7 @@
1
1
  import { n as getUnsignedUpdateManifest, r as serializeUnsignedUpdateManifest, t as UpdateManifestReader } from "./update-manifest.types-C0qPrjGQ.js";
2
2
  import { createRequire } from "node:module";
3
3
  import { NcpEventType, sanitizeAssistantReplyTags } from "@nextclaw/ncp";
4
- import { AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, CLEAR_THINKING_TOKENS, CONTEXT_COMPACTION_METADATA_KEY, ChannelManager, ChannelManager as ChannelManager$1, ConfigSchema, ContextBuilder, ContextCompactionService, ContextWindowBudgetService, CronService, CronTool, DEFAULT_PANELS_DIR, DEFAULT_SERVICE_APPS_DIR, DEFAULT_SESSION_SEARCH_LIMIT, EditFileTool, ExecTool, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, LiteLLMProvider, MAX_SESSION_SEARCH_LIMIT, MemoryGetTool, MemorySearchTool, MessageBus, MessageTool, ProviderRegistry, ReadFileTool, RequestedSkillsMetadataReader, SessionSearchManager, SkillsLoader, THINKING_LEVELS, WebFetchTool, WebSearchTool, WriteFileTool, buildCompressingCompactionCheckpoint, buildConfigSchema, buildContextWindowSnapshot, buildMinimalSystemExecutionPrompt, buildReloadPlan, buildSessionRequestToolResult, buildToolCatalogEntries, createAssistantStreamDeltaControlMessage, createAssistantStreamResetControlMessage, createCompletedSessionRequest, createFailedSessionRequest, createRunningSessionRequest, createTypingStopControlMessage, diffConfigPaths, ensureDir, expandHome, findEffectiveAgentProfile, getConfigPath, getDataDir, getDataPath, getSessionsPath, getWorkspacePath, getWorkspacePathFromConfig, loadConfig, mergeExtensionConfigView, modelSupportsVision, normalizeInlineSecretRefs, normalizeProviderModelConfig, normalizeToolParams, parseAgentScopedSessionKey, parseThinkingLevel, readCompressedContextCompactionCheckpoint, readOptionalString, readParentSessionId, readSessionProjectRoot, redactConfigObject, resolveConfigSecrets, resolveDefaultAgentProfileId, resolveProviderRuntime, resolveSessionWorkspacePath, resolveThinkingLevel, saveConfig, summarizeSessionRequestTask, toDisposable, toExtensionConfigView } from "@nextclaw/core";
4
+ import { APP_NAME, AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, CLEAR_THINKING_TOKENS, CONTEXT_COMPACTION_METADATA_KEY, ChannelManager, ChannelManager as ChannelManager$1, ConfigSchema, ContextCompactionService, ContextWindowBudgetService, CronService, CronTool, DEFAULT_PANELS_DIR, DEFAULT_SERVICE_APPS_DIR, DEFAULT_SESSION_SEARCH_LIMIT, DEFAULT_WORKSPACE_REPOSITORY_IDENTITY_RESOLVER, EditFileTool, ExecTool, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, LiteLLMProvider, MAX_SESSION_SEARCH_LIMIT, MemoryGetTool, MemorySearchTool, MemoryStore, MessageBus, MessageTool, ProviderRegistry, ReadFileTool, RequestedSkillsMetadataReader, SILENT_REPLY_TOKEN, SessionProjectContextResolver, SessionSearchManager, SkillsLoader, THINKING_LEVELS, WebFetchTool, WebSearchTool, WriteFileTool, buildCompressingCompactionCheckpoint, buildConfigSchema, buildContextWindowSnapshot, buildReloadPlan, buildSessionRequestToolResult, buildToolCatalogEntries, createAssistantStreamDeltaControlMessage, createAssistantStreamResetControlMessage, createCompletedSessionRequest, createFailedSessionRequest, createRunningSessionRequest, createRuntimeChildEnv, createTypingStopControlMessage, diffConfigPaths, ensureDir, expandHome, findEffectiveAgentProfile, getConfigPath, getDataDir, getDataPath, getSessionsPath, getWorkspacePath, getWorkspacePathFromConfig, loadConfig, mergeExtensionConfigView, modelSupportsVision, normalizeInlineSecretRefs, normalizeProviderModelConfig, normalizeToolParams, parseAgentScopedSessionKey, parseThinkingLevel, readCompressedContextCompactionCheckpoint, readOptionalString, readParentSessionId, readSessionProjectRoot, redactConfigObject, resolveConfigSecrets, resolveDefaultAgentProfileId, resolveNextclawSelfManageGuidePaths, resolveProviderRuntime, resolveSessionWorkspacePath, resolveThinkingLevel, saveConfig, summarizeSessionRequestTask, toDisposable, toExtensionConfigView } from "@nextclaw/core";
5
5
  import { LocalAssetStore, buildAssetContentPath, buildNcpUserContent, buildOpenAiFunctionTool, ncpMessageToOpenAiMessages, validateToolArgs } from "@nextclaw/ncp-agent-runtime";
6
6
  import { createHash, createHmac, randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
7
7
  import { EventBus, Ingress, eventKeys, ingressKeys } from "@nextclaw/shared";
@@ -15,7 +15,6 @@ import { BUILTIN_PROVIDER_PLUGINS } from "@nextclaw/runtime";
15
15
  import { McpRegistryService, McpServerLifecycleManager } from "@nextclaw/mcp";
16
16
  import { McpNcpToolRegistryAdapter } from "@nextclaw/ncp-mcp";
17
17
  import { DefaultNcpAgentConversationStateManager } from "@nextclaw/ncp-toolkit";
18
- import { createRuntimeChildEnv } from "@nextclaw/core/child-process-env";
19
18
  import { parse } from "yaml";
20
19
  import { HttpRuntimeConfigResolver, HttpRuntimeNcpAgentRuntime } from "@nextclaw/nextclaw-ncp-runtime-http-client";
21
20
  import { StdioRuntimeConfigResolver, StdioRuntimeNcpAgentRuntime, probeStdioRuntime } from "@nextclaw/nextclaw-ncp-runtime-stdio-client";
@@ -149,6 +148,12 @@ function toLegacyMessages(messages, options = {}) {
149
148
  //#region src/features/context-compaction/utils/context-compaction-timeline-message.utils.ts
150
149
  const NEXTCLAW_TIMELINE_KIND_METADATA_KEY = "nextclaw_timeline_kind";
151
150
  const CONTEXT_COMPACTION_TIMELINE_KIND = "context_compaction";
151
+ function readCheckpointTimelineText(checkpoint) {
152
+ return checkpoint.status === "compressing" ? "正在压缩较早上下文" : "较早上下文已自动压缩";
153
+ }
154
+ function createContextCompactionMessageId() {
155
+ return `context-compaction-message-${randomUUID()}`;
156
+ }
152
157
  function readTimelineMetadata(message) {
153
158
  const rawMetadata = message?.ncp_metadata;
154
159
  if (!rawMetadata || typeof rawMetadata !== "object" || Array.isArray(rawMetadata)) return null;
@@ -162,13 +167,14 @@ function readTimelineMetadata(message) {
162
167
  };
163
168
  }
164
169
  function buildTimelineMessage(checkpoint) {
170
+ const text = readCheckpointTimelineText(checkpoint);
165
171
  return {
166
172
  role: "service",
167
- content: checkpoint.status === "compressing" ? "正在压缩较早上下文" : "较早上下文已自动压缩",
173
+ content: text,
168
174
  timestamp: checkpoint.updatedAt,
169
175
  ncp_parts: [{
170
176
  type: "text",
171
- text: checkpoint.status === "compressing" ? "正在压缩较早上下文" : "较早上下文已自动压缩"
177
+ text
172
178
  }],
173
179
  ncp_metadata: {
174
180
  [NEXTCLAW_TIMELINE_KIND_METADATA_KEY]: CONTEXT_COMPACTION_TIMELINE_KIND,
@@ -177,10 +183,10 @@ function buildTimelineMessage(checkpoint) {
177
183
  };
178
184
  }
179
185
  function buildContextCompactionTimelineNcpMessage(params) {
180
- const { checkpoint, sessionId } = params;
181
- const text = checkpoint.status === "compressing" ? "正在压缩较早上下文" : "较早上下文已自动压缩";
186
+ const { checkpoint, messageId, sessionId } = params;
187
+ const text = readCheckpointTimelineText(checkpoint);
182
188
  return {
183
- id: `${sessionId}:service:context-compaction:${checkpoint.id}`,
189
+ id: messageId,
184
190
  sessionId,
185
191
  role: "service",
186
192
  status: "final",
@@ -234,34 +240,31 @@ function isContextCompactionTimelineMessage(message) {
234
240
  }
235
241
  //#endregion
236
242
  //#region src/features/context-compaction/utils/context-compaction-projection.utils.ts
237
- function readCompactionSummary(message) {
243
+ function readCompactionCheckpoint(message) {
238
244
  const metadata = message.metadata;
239
- if (metadata?.["nextclaw_timeline_kind"] !== "context_compaction") return null;
240
- return readCompressedContextCompactionCheckpoint(metadata.checkpoint)?.summary ?? null;
245
+ return metadata?.["nextclaw_timeline_kind"] === "context_compaction" ? readCompressedContextCompactionCheckpoint(metadata.checkpoint) : null;
246
+ }
247
+ function readLatestContextCompactionCheckpoint(sessionMessages) {
248
+ return sessionMessages.reduce((checkpoint, message) => {
249
+ const candidateCheckpoint = readCompactionCheckpoint(message);
250
+ return candidateCheckpoint && (!checkpoint || Date.parse(candidateCheckpoint.updatedAt) >= Date.parse(checkpoint.updatedAt)) ? candidateCheckpoint : checkpoint;
251
+ }, null);
241
252
  }
242
253
  function projectNcpMessagesWithContextCompaction(params) {
243
254
  const { sessionId, sessionMessages } = params;
244
- let checkpointIndex = -1;
245
- let summary = "";
246
- for (let index = sessionMessages.length - 1; index >= 0; index -= 1) {
247
- const candidateSummary = readCompactionSummary(sessionMessages[index]);
248
- if (!candidateSummary) continue;
249
- checkpointIndex = index;
250
- summary = candidateSummary;
251
- break;
252
- }
253
- if (checkpointIndex < 0) return sessionMessages.filter((message) => !readCompactionSummary(message)).map((message) => structuredClone(message));
255
+ const checkpoint = readLatestContextCompactionCheckpoint(sessionMessages);
256
+ if (!checkpoint) return sessionMessages.filter((message) => !readCompactionCheckpoint(message)).map((message) => structuredClone(message));
254
257
  return [{
255
- id: `${sessionId}:context-compaction-summary:${sessionMessages[checkpointIndex]?.id ?? "latest"}`,
258
+ id: `${sessionId}:context-compaction-summary:${checkpoint.id}:${checkpoint.updatedAt}`,
256
259
  sessionId,
257
260
  role: "user",
258
261
  status: "final",
259
- timestamp: sessionMessages[checkpointIndex]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
262
+ timestamp: checkpoint.updatedAt,
260
263
  parts: [{
261
264
  type: "text",
262
- text: summary
265
+ text: checkpoint.summary
263
266
  }]
264
- }, ...sessionMessages.slice(checkpointIndex + 1).filter((message) => !readCompactionSummary(message)).map((message) => structuredClone(message))];
267
+ }, ...sessionMessages.filter((message) => !readCompactionCheckpoint(message) && Date.parse(message.timestamp) > Date.parse(checkpoint.updatedAt)).sort((left, right) => Date.parse(left.timestamp) - Date.parse(right.timestamp)).map((message) => structuredClone(message))];
265
268
  }
266
269
  //#endregion
267
270
  //#region src/features/context-compaction/services/context-compaction-preflight.service.ts
@@ -312,23 +315,6 @@ function buildContextWindowSnapshotFromBudget(params) {
312
315
  compactedUsedContextTokens: checkpoint ? budget.estimatedTokens : void 0
313
316
  });
314
317
  }
315
- function buildContextWindowSnapshotForMessages(contextWindowBudgetService, params) {
316
- const { checkpoint, contextTokens, reservedContextTokens, sessionId, sessionMessages } = params;
317
- const modelCandidateMessages = sessionMessages.filter((message) => !isContextCompactionTimelineMessage(message));
318
- const messages = toLegacyMessages(checkpoint ? projectNcpMessagesWithContextCompaction({
319
- sessionId,
320
- sessionMessages
321
- }) : modelCandidateMessages);
322
- return buildContextWindowSnapshotFromBudget({
323
- budget: contextWindowBudgetService.evaluate({
324
- messages,
325
- contextTokens,
326
- reservedContextTokens
327
- }),
328
- checkpoint,
329
- totalContextTokens: contextTokens
330
- });
331
- }
332
318
  var ContextCompactionPreflightService = class {
333
319
  compactionService = new ContextCompactionService();
334
320
  contextWindowBudgetService = new ContextWindowBudgetService();
@@ -342,13 +328,19 @@ var ContextCompactionPreflightService = class {
342
328
  requestMetadata,
343
329
  storedAgentId
344
330
  });
345
- const existingCheckpoint = readCompressedContextCompactionCheckpoint(storedMetadata[CONTEXT_COMPACTION_METADATA_KEY]);
346
- return buildContextWindowSnapshotForMessages(this.contextWindowBudgetService, {
347
- checkpoint: existingCheckpoint,
348
- contextTokens: profile.contextTokens,
349
- reservedContextTokens: profile.reservedContextTokens,
331
+ const existingCheckpoint = readCompressedContextCompactionCheckpoint(storedMetadata[CONTEXT_COMPACTION_METADATA_KEY]) ?? readLatestContextCompactionCheckpoint(sessionMessages);
332
+ const projectedMessages = existingCheckpoint ? projectNcpMessagesWithContextCompaction({
350
333
  sessionId,
351
334
  sessionMessages
335
+ }) : sessionMessages.filter((message) => !isContextCompactionTimelineMessage(message));
336
+ return buildContextWindowSnapshotFromBudget({
337
+ budget: this.contextWindowBudgetService.evaluate({
338
+ messages: toLegacyMessages(projectedMessages),
339
+ contextTokens: profile.contextTokens,
340
+ reservedContextTokens: profile.reservedContextTokens
341
+ }),
342
+ checkpoint: existingCheckpoint,
343
+ totalContextTokens: profile.contextTokens
352
344
  });
353
345
  };
354
346
  begin = (params) => {
@@ -363,26 +355,27 @@ var ContextCompactionPreflightService = class {
363
355
  inputMessages,
364
356
  sessionMessages
365
357
  });
366
- const modelCandidateMessages = ncpMessages.filter((message) => !isContextCompactionTimelineMessage(message));
367
- const existingCheckpoint = readCompressedContextCompactionCheckpoint(storedMetadata[CONTEXT_COMPACTION_METADATA_KEY]);
358
+ const existingCheckpoint = readCompressedContextCompactionCheckpoint(storedMetadata[CONTEXT_COMPACTION_METADATA_KEY]) ?? readLatestContextCompactionCheckpoint(ncpMessages);
368
359
  const messages = toLegacyMessages(existingCheckpoint ? projectNcpMessagesWithContextCompaction({
369
360
  sessionId,
370
361
  sessionMessages: ncpMessages
371
- }) : modelCandidateMessages);
362
+ }) : ncpMessages.filter((message) => !isContextCompactionTimelineMessage(message)));
372
363
  const budget = this.contextWindowBudgetService.evaluate({
373
364
  messages,
374
365
  contextTokens,
375
366
  reservedContextTokens
376
367
  });
377
- const plan = existingCheckpoint || !budget.shouldCompact ? null : this.compactionService.prepareForModelInput({
368
+ const plan = !budget.shouldCompact ? null : this.compactionService.prepareForModelInput({
378
369
  messages: budget.messages,
379
370
  contextTokens,
380
371
  compactionThresholdTokens: budget.triggerTokens
381
372
  });
373
+ const coveredSessionMessageCount = plan ? (existingCheckpoint?.coveredSessionMessageCount ?? 0) + plan.coveredMessages.length - (existingCheckpoint ? 1 : 0) : 0;
374
+ const serviceMessageId = createContextCompactionMessageId();
382
375
  const checkpoint = plan ? {
383
376
  ...buildCompressingCompactionCheckpoint(storedMetadata[CONTEXT_COMPACTION_METADATA_KEY]),
384
- coveredMessageCount: plan.coveredMessages.length,
385
- coveredSessionMessageCount: plan.coveredMessages.length,
377
+ coveredMessageCount: coveredSessionMessageCount,
378
+ coveredSessionMessageCount,
386
379
  originalEstimatedTokens: plan.originalEstimatedTokens,
387
380
  projectedEstimatedTokens: budget.estimatedTokens
388
381
  } : existingCheckpoint;
@@ -395,12 +388,14 @@ var ContextCompactionPreflightService = class {
395
388
  metadataPatch: plan && checkpoint ? { [CONTEXT_COMPACTION_METADATA_KEY]: checkpoint } : {},
396
389
  sessionMessages: ncpMessages,
397
390
  timelineMessage: plan && checkpoint ? buildContextCompactionTimelineNcpMessage({
391
+ messageId: serviceMessageId,
398
392
  sessionId,
399
393
  checkpoint
400
394
  }) : null,
401
395
  pendingCompaction: plan && checkpoint ? {
402
396
  checkpoint,
403
397
  contextTokens,
398
+ serviceMessageId,
404
399
  model: profile.model,
405
400
  plan,
406
401
  reservedContextTokens,
@@ -424,6 +419,8 @@ var ContextCompactionPreflightService = class {
424
419
  ...generatedCheckpoint,
425
420
  id: pending.checkpoint.id,
426
421
  createdAt: pending.checkpoint.createdAt,
422
+ coveredMessageCount: pending.checkpoint.coveredMessageCount,
423
+ coveredSessionMessageCount: pending.checkpoint.coveredSessionMessageCount,
427
424
  status: "compressed"
428
425
  };
429
426
  return {
@@ -439,6 +436,7 @@ var ContextCompactionPreflightService = class {
439
436
  metadataPatch: { [CONTEXT_COMPACTION_METADATA_KEY]: checkpoint },
440
437
  sessionMessages: pending.sessionMessages,
441
438
  timelineMessage: buildContextCompactionTimelineNcpMessage({
439
+ messageId: pending.serviceMessageId,
442
440
  sessionId: pending.sessionId,
443
441
  checkpoint
444
442
  })
@@ -3072,6 +3070,30 @@ var McpManager = class {
3072
3070
  for (const result of results) if (!result.ok) console.warn(`[mcp] Failed to warm ${result.name}: ${result.error}`);
3073
3071
  };
3074
3072
  };
3073
+ //#endregion
3074
+ //#region src/utils/agent-peer-session.utils.ts
3075
+ const AGENT_RUN_PEER_ID_METADATA_KEY = "agent_peer_id";
3076
+ const AGENT_RUN_PEER_SCOPE_METADATA_KEY = "agent_peer_scope";
3077
+ function createAgentPeerSessionIdentity(params) {
3078
+ const scope = resolveAgentPeerScope(params);
3079
+ return {
3080
+ metadata: {
3081
+ [AGENT_RUN_PEER_ID_METADATA_KEY]: params.peerId,
3082
+ [AGENT_RUN_PEER_SCOPE_METADATA_KEY]: scope
3083
+ },
3084
+ sessionId: `agent-peer-${createHash("sha256").update(`${scope}\0${params.peerId}`).digest("hex").slice(0, 32)}`
3085
+ };
3086
+ }
3087
+ function resolveAgentPeerScope(params) {
3088
+ const metadata = params.metadata ?? {};
3089
+ const explicitScope = readOptionalString$8(metadata["agent_peer_scope"]) ?? readOptionalString$8(metadata.agentPeerScope);
3090
+ if (explicitScope) return explicitScope;
3091
+ return `agent:${readOptionalString$8(params.agentId) ?? BUILTIN_MAIN_AGENT_ID}:${readOptionalString$8(params.channel) ?? readOptionalString$8(metadata.channel) ?? "agent-run"}:${readOptionalString$8(metadata.accountId) ?? readOptionalString$8(metadata.account_id) ?? "default"}`;
3092
+ }
3093
+ function readOptionalString$8(value) {
3094
+ if (typeof value !== "string") return;
3095
+ return value.trim() || void 0;
3096
+ }
3075
3097
  const NCP_AGENT_SESSION_JOURNAL_INDEX_FILE = ".ncp-agent-session-index.json";
3076
3098
  const AUTO_SESSION_LABEL_MAX_LENGTH = 64;
3077
3099
  const NCP_AGENT_SESSION_SNAPSHOT_MESSAGE_EVENT_TYPE = "session.snapshot.message";
@@ -3097,10 +3119,12 @@ function toIsoString(value, fallback) {
3097
3119
  function createNcpAgentSessionSummary(record) {
3098
3120
  const metadata = structuredClone(record.metadata ?? {});
3099
3121
  const label = readOptionalText(metadata.label) ?? resolveAutoSessionLabel(record.messages);
3122
+ const peerId = readNcpAgentSessionPeerId(metadata);
3100
3123
  if (label) metadata.label = label;
3101
3124
  const lastMessageAt = record.messages.reduceRight((timestamp, message) => timestamp ?? readMessageTimestamp(message), void 0);
3102
3125
  return {
3103
3126
  sessionId: record.sessionId,
3127
+ peerId: peerId ?? void 0,
3104
3128
  ...normalizeNcpAgentId(record.agentId) ? { agentId: normalizeNcpAgentId(record.agentId) } : {},
3105
3129
  messageCount: record.messages.length,
3106
3130
  ...record.createdAt ? { createdAt: record.createdAt } : {},
@@ -3110,6 +3134,9 @@ function createNcpAgentSessionSummary(record) {
3110
3134
  ...Object.keys(metadata).length > 0 ? { metadata } : {}
3111
3135
  };
3112
3136
  }
3137
+ function readNcpAgentSessionPeerId(metadata) {
3138
+ return readOptionalText(metadata[AGENT_RUN_PEER_ID_METADATA_KEY]);
3139
+ }
3113
3140
  function createNcpAgentSessionJournalMetadataEntry(record) {
3114
3141
  return {
3115
3142
  _type: "metadata",
@@ -3126,6 +3153,7 @@ function upsertNcpAgentSessionSummaryEvent(params) {
3126
3153
  const lastMessageAt = readMessageTimestamp(eventMessage) ?? current?.lastMessageAt;
3127
3154
  return {
3128
3155
  sessionId,
3156
+ peerId: current?.peerId,
3129
3157
  ...normalizeNcpAgentId(current?.agentId) ? { agentId: normalizeNcpAgentId(current?.agentId) } : {},
3130
3158
  messageCount,
3131
3159
  createdAt: current?.createdAt ?? updatedAt,
@@ -3136,9 +3164,14 @@ function upsertNcpAgentSessionSummaryEvent(params) {
3136
3164
  }
3137
3165
  async function replayNcpAgentSessionEvents(events) {
3138
3166
  const stateManager = new DefaultNcpAgentConversationStateManager();
3167
+ const knownMessageIds = /* @__PURE__ */ new Set();
3139
3168
  for (const event of events) {
3140
3169
  if (isJournalOnlyEvent(event)) continue;
3141
- await stateManager.dispatch(createReplayEvent(event));
3170
+ const replayEvent = createReplayEvent(event);
3171
+ const bootstrapEvent = createReplayStreamingBootstrapEvent(replayEvent, knownMessageIds);
3172
+ if (bootstrapEvent) await stateManager.dispatch(bootstrapEvent);
3173
+ rememberReplayMessageId(replayEvent, knownMessageIds);
3174
+ await stateManager.dispatch(replayEvent);
3142
3175
  }
3143
3176
  const snapshot = stateManager.getSnapshot();
3144
3177
  return [...snapshot.messages.map((message) => structuredClone(message)), ...snapshot.streamingMessage ? [structuredClone(snapshot.streamingMessage)] : []];
@@ -3146,6 +3179,8 @@ async function replayNcpAgentSessionEvents(events) {
3146
3179
  function createReplayEvent(event) {
3147
3180
  const replayEvent = structuredClone(event);
3148
3181
  const replayMessage = readMessageFromSummaryEvent(replayEvent);
3182
+ const legacyCompactionMessageId = readLegacyContextCompactionMessageId(replayMessage);
3183
+ if (replayMessage && legacyCompactionMessageId) replayMessage.id = legacyCompactionMessageId;
3149
3184
  if (replayMessage?.role === "assistant" && (replayMessage.status === "pending" || replayMessage.status === "streaming")) replayMessage.status = "final";
3150
3185
  if (replayEvent.type === "session.snapshot.message" || replayEvent.type === NcpEventType.MessageCompleted) return {
3151
3186
  type: NcpEventType.MessageSent,
@@ -3153,6 +3188,58 @@ function createReplayEvent(event) {
3153
3188
  };
3154
3189
  return replayEvent;
3155
3190
  }
3191
+ function readLegacyContextCompactionMessageId(message) {
3192
+ const checkpoint = isRecord$10(message?.metadata?.checkpoint) ? message.metadata.checkpoint : null;
3193
+ const checkpointId = typeof checkpoint?.id === "string" ? checkpoint.id : "";
3194
+ const coveredCount = checkpoint?.coveredSessionMessageCount;
3195
+ const legacyId = `${message?.sessionId}:service:context-compaction:${checkpointId}`;
3196
+ return typeof coveredCount === "number" && message?.id === legacyId ? `${legacyId}:${coveredCount}` : null;
3197
+ }
3198
+ function createReplayStreamingBootstrapEvent(event, knownMessageIds) {
3199
+ const messageId = readStreamingMessageId(event);
3200
+ if (!messageId || knownMessageIds.has(messageId)) return null;
3201
+ knownMessageIds.add(messageId);
3202
+ return {
3203
+ type: NcpEventType.MessageSent,
3204
+ payload: {
3205
+ sessionId: readEventSessionId$2(event),
3206
+ message: {
3207
+ id: messageId,
3208
+ sessionId: readEventSessionId$2(event),
3209
+ role: "assistant",
3210
+ status: "streaming",
3211
+ parts: [],
3212
+ timestamp: readReplayPayloadTimestamp(event) ?? (/* @__PURE__ */ new Date()).toISOString()
3213
+ }
3214
+ }
3215
+ };
3216
+ }
3217
+ function rememberReplayMessageId(event, knownMessageIds) {
3218
+ const message = readMessageFromSummaryEvent(event);
3219
+ if (message?.id) knownMessageIds.add(message.id);
3220
+ }
3221
+ function readEventSessionId$2(event) {
3222
+ const sessionId = ("payload" in event && isRecord$10(event.payload) ? event.payload : null)?.sessionId;
3223
+ return typeof sessionId === "string" ? sessionId : "";
3224
+ }
3225
+ function readReplayPayloadTimestamp(event) {
3226
+ const payload = "payload" in event && isRecord$10(event.payload) ? event.payload : null;
3227
+ const timestamp = typeof payload?.timestamp === "string" ? payload.timestamp : "";
3228
+ return Number.isFinite(Date.parse(timestamp)) ? new Date(timestamp).toISOString() : null;
3229
+ }
3230
+ function readStreamingMessageId(event) {
3231
+ switch (event.type) {
3232
+ case NcpEventType.MessageTextStart:
3233
+ case NcpEventType.MessageTextDelta:
3234
+ case NcpEventType.MessageTextEnd:
3235
+ case NcpEventType.MessageReasoningStart:
3236
+ case NcpEventType.MessageReasoningDelta:
3237
+ case NcpEventType.MessageReasoningEnd:
3238
+ case NcpEventType.MessageToolCallStart:
3239
+ case NcpEventType.MessageToolCallArgsDelta: return event.payload.messageId?.trim() || null;
3240
+ default: return null;
3241
+ }
3242
+ }
3156
3243
  function isJournalOnlyEvent(event) {
3157
3244
  return event.type === "session.request.accepted" || event.type === "session.request.completed" || event.type === "session.request.failed";
3158
3245
  }
@@ -3183,30 +3270,6 @@ function readMessageFromSummaryEvent(event) {
3183
3270
  if (event.type === NcpEventType.MessageSent || event.type === NcpEventType.MessageCompleted || event.type === "session.snapshot.message") return event.payload.message;
3184
3271
  }
3185
3272
  //#endregion
3186
- //#region src/utils/agent-peer-session.utils.ts
3187
- const AGENT_RUN_PEER_ID_METADATA_KEY = "agent_peer_id";
3188
- const AGENT_RUN_PEER_SCOPE_METADATA_KEY = "agent_peer_scope";
3189
- function createAgentPeerSessionIdentity(params) {
3190
- const scope = resolveAgentPeerScope(params);
3191
- return {
3192
- metadata: {
3193
- [AGENT_RUN_PEER_ID_METADATA_KEY]: params.peerId,
3194
- [AGENT_RUN_PEER_SCOPE_METADATA_KEY]: scope
3195
- },
3196
- sessionId: `agent-peer-${createHash("sha256").update(`${scope}\0${params.peerId}`).digest("hex").slice(0, 32)}`
3197
- };
3198
- }
3199
- function resolveAgentPeerScope(params) {
3200
- const metadata = params.metadata ?? {};
3201
- const explicitScope = readOptionalString$8(metadata["agent_peer_scope"]) ?? readOptionalString$8(metadata.agentPeerScope);
3202
- if (explicitScope) return explicitScope;
3203
- return `agent:${readOptionalString$8(params.agentId) ?? BUILTIN_MAIN_AGENT_ID}:${readOptionalString$8(params.channel) ?? readOptionalString$8(metadata.channel) ?? "agent-run"}:${readOptionalString$8(metadata.accountId) ?? readOptionalString$8(metadata.account_id) ?? "default"}`;
3204
- }
3205
- function readOptionalString$8(value) {
3206
- if (typeof value !== "string") return;
3207
- return value.trim() || void 0;
3208
- }
3209
- //#endregion
3210
3273
  //#region src/managers/session.manager.ts
3211
3274
  const DEFAULT_SESSION_TYPE = "native";
3212
3275
  const DEFAULT_LIFECYCLE = "persistent";
@@ -3425,7 +3488,8 @@ var SessionManager = class {
3425
3488
  return await this.options.journalStore.getSession(normalizedSessionId);
3426
3489
  };
3427
3490
  listSessions = async (options) => {
3428
- return applyLimit(await this.options.journalStore.listSessionSummaries(), options?.limit);
3491
+ const peerId = readOptionalString$7(options?.peerId);
3492
+ return applyLimit((await this.options.journalStore.listSessionSummaries()).filter((summary) => !peerId || summary.peerId === peerId), options?.limit);
3429
3493
  };
3430
3494
  listSessionMessages = async (sessionId, options) => {
3431
3495
  const normalizedSessionId = normalizeSessionId(sessionId);
@@ -4309,6 +4373,35 @@ function getPanelAppBridgeScript(params = {
4309
4373
  });
4310
4374
  }
4311
4375
 
4376
+ function resolveApiFetchUrl(input) {
4377
+ const raw = typeof input === "string" || input instanceof URL ? input.toString() : input?.url;
4378
+ if (typeof raw !== "string") {
4379
+ return null;
4380
+ }
4381
+ try {
4382
+ const url = new URL(raw, window.location.href);
4383
+ return url.origin === window.location.origin && url.pathname.startsWith("/api/") ? url : null;
4384
+ } catch {
4385
+ return null;
4386
+ }
4387
+ }
4388
+
4389
+ function createFetchInitWithRuntimeToken(input, init) {
4390
+ if (!resolveApiFetchUrl(input)) {
4391
+ return init;
4392
+ }
4393
+ const headers = new Headers(init?.headers || (typeof input === "object" && input ? input.headers : undefined));
4394
+ if (!headers.has("x-nextclaw-panel-bridge-session")) {
4395
+ headers.set("x-nextclaw-panel-bridge-session", runtimeToken);
4396
+ }
4397
+ return { ...init, headers };
4398
+ }
4399
+
4400
+ const nativeFetch = window.fetch?.bind(window);
4401
+ if (nativeFetch) {
4402
+ window.fetch = (input, init) => nativeFetch(input, createFetchInitWithRuntimeToken(input, init));
4403
+ }
4404
+
4312
4405
  function unwrapServiceActionResult(result) {
4313
4406
  if (!result || typeof result !== "object") {
4314
4407
  return result;
@@ -4386,7 +4479,7 @@ const PANEL_APP_CLIENT_MARKER = "nextclaw:panel-app-client:init";
4386
4479
  const PANEL_APP_CLIENT_SDK_PATH = "/api/panel-app-client-sdk.js";
4387
4480
  function injectPanelAppClientScript(html, params) {
4388
4481
  if (html.includes(PANEL_APP_CLIENT_MARKER)) return html;
4389
- const script = [`<script src="${PANEL_APP_CLIENT_SDK_PATH}"><\/script>`, `<script>${getPanelAppClientInitScript(params)}<\/script>`].join("");
4482
+ const script = [`<script src="${PANEL_APP_CLIENT_SDK_PATH}" crossorigin="anonymous"><\/script>`, `<script>${getPanelAppClientInitScript(params)}<\/script>`].join("");
4390
4483
  const headMatch = /<head(?:\s[^>]*)?>/i.exec(html);
4391
4484
  if (headMatch?.index !== void 0) {
4392
4485
  const insertAt = headMatch.index + headMatch[0].length;
@@ -4618,9 +4711,27 @@ function resolvePanelAppIconUrl(id, icon) {
4618
4711
  }
4619
4712
  function injectPanelAppAssetBase(html, baseHref) {
4620
4713
  const base = `<base href="${baseHref}">`;
4621
- if (/<base\b/i.test(html)) return html;
4622
- if (/<head[^>]*>/i.test(html)) return html.replace(/<head[^>]*>/i, (head) => `${head}${base}`);
4623
- return `${base}${html}`;
4714
+ return injectLocalScriptCrossOrigin((() => {
4715
+ if (/<base\b/i.test(html)) return html;
4716
+ if (/<head[^>]*>/i.test(html)) return html.replace(/<head[^>]*>/i, (head) => `${head}${base}`);
4717
+ return `${base}${html}`;
4718
+ })());
4719
+ }
4720
+ function injectLocalScriptCrossOrigin(html) {
4721
+ return html.replace(/<script\b(?=[^>]*\bsrc\s*=)(?![^>]*\bcrossorigin\b)[^>]*>/gi, (tag) => {
4722
+ const src = extractScriptSrc(tag);
4723
+ if (!src || !isLocalScriptSrc(src)) return tag;
4724
+ return `${tag.slice(0, -1)} crossorigin="anonymous">`;
4725
+ });
4726
+ }
4727
+ function extractScriptSrc(tag) {
4728
+ const match = /\bsrc\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i.exec(tag);
4729
+ return match?.[1] ?? match?.[2] ?? match?.[3];
4730
+ }
4731
+ function isLocalScriptSrc(src) {
4732
+ const value = src.trim();
4733
+ if (!value || value.startsWith("//")) return false;
4734
+ return !/^(?:https?|data|blob|javascript):/i.test(value);
4624
4735
  }
4625
4736
  function encodePanelAppAssetPath(path) {
4626
4737
  return path.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
@@ -6068,6 +6179,16 @@ function serializeJournalEntry(entry) {
6068
6179
  if (!isRecord$10(JSON.parse(serialized))) throw new Error("ncp agent session journal entry serialization produced a non-object entry");
6069
6180
  return serialized;
6070
6181
  }
6182
+ function attachJournalTimestamp(event, timestamp) {
6183
+ if (!("payload" in event) || !isRecord$10(event.payload)) return event;
6184
+ return {
6185
+ ...event,
6186
+ payload: {
6187
+ ...event.payload,
6188
+ timestamp
6189
+ }
6190
+ };
6191
+ }
6071
6192
  var NcpAgentSessionJournalStore = class {
6072
6193
  sessions = /* @__PURE__ */ new Map();
6073
6194
  nextSeqBySession = /* @__PURE__ */ new Map();
@@ -6234,8 +6355,10 @@ var NcpAgentSessionJournalStore = class {
6234
6355
  updatedAt: summary.updatedAt,
6235
6356
  metadata: {}
6236
6357
  });
6358
+ const peerId = summary.peerId ?? readNcpAgentSessionPeerId(snapshot.metadata);
6237
6359
  return {
6238
6360
  ...summary,
6361
+ peerId: peerId ?? void 0,
6239
6362
  ...!summary.agentId && snapshot.agentId ? { agentId: snapshot.agentId } : {},
6240
6363
  ...Object.keys(snapshot.metadata).length > 0 ? { metadata: snapshot.metadata } : {}
6241
6364
  };
@@ -6292,9 +6415,10 @@ var NcpAgentSessionJournalStore = class {
6292
6415
  if (parsed._type === "event" && isRecord$10(parsed.event)) {
6293
6416
  const seq = Number(parsed.seq);
6294
6417
  nextSeq = Math.max(nextSeq, Number.isFinite(seq) ? Math.trunc(seq) + 1 : nextSeq);
6295
- updatedAt = toIsoString(parsed.timestamp, updatedAt);
6418
+ const eventTimestamp = toIsoString(parsed.timestamp, updatedAt);
6419
+ updatedAt = eventTimestamp;
6296
6420
  const event = structuredClone(parsed.event);
6297
- events.push(event);
6421
+ events.push(attachJournalTimestamp(event, eventTimestamp));
6298
6422
  }
6299
6423
  }
6300
6424
  return {
@@ -6871,7 +6995,7 @@ var BuiltinNarpRuntimeProviderService = class {
6871
6995
  };
6872
6996
  //#endregion
6873
6997
  //#region src/features/native-runtime/services/provider-manager-ncp-llm-api.service.ts
6874
- function normalizeModel(value) {
6998
+ function normalizeModel$1(value) {
6875
6999
  if (typeof value !== "string") return null;
6876
7000
  const trimmed = value.trim();
6877
7001
  return trimmed.length > 0 ? trimmed : null;
@@ -6913,7 +7037,7 @@ var ProviderManagerNcpLLMApi = class {
6913
7037
  this.providerManager = providerManager;
6914
7038
  }
6915
7039
  generate = async function* (input, options) {
6916
- const model = normalizeModel(input.model) ?? this.providerManager.get(null).getDefaultModel();
7040
+ const model = normalizeModel$1(input.model) ?? this.providerManager.get(null).getDefaultModel();
6917
7041
  const thinkingLevel = normalizeThinkingLevel(input.thinkingLevel);
6918
7042
  let sawTextDelta = false;
6919
7043
  let sawReasoningDelta = false;
@@ -7227,19 +7351,6 @@ function mergeRunMetadata(params) {
7227
7351
  ...requestMetadata ? structuredClone(requestMetadata) : {}
7228
7352
  };
7229
7353
  }
7230
- function buildSessionOrchestrationSection() {
7231
- return [
7232
- "## Session Orchestration",
7233
- "- Before passing a non-default `runtime` to `sessions_spawn` or agent creation/update flows, inspect the installed runtime kinds with `nextclaw agents runtimes --json`.",
7234
- "- `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.",
7235
- "- `sessions_spawn` only creates the session by default. Add top-level `notify: \"none\" | \"final_reply\"` when the new session should start working immediately.",
7236
- "- 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.",
7237
- "- Use `sessions_spawn` without `notify` when the user wants a separate thread created now but does not need it to start working yet.",
7238
- "- 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.",
7239
- "- `sessions_request.target` must be an object shaped like `{ \"session_id\": \"<target-session-id>\" }`. Do not pass a bare string.",
7240
- "- 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."
7241
- ].join("\n");
7242
- }
7243
7354
  function resolveNextclawNcpRunContext(params) {
7244
7355
  const { configManager, requestMetadata: inputRequestMetadata, sessionId, sessionMetadata: inputSessionMetadata, storedAgentId } = params;
7245
7356
  const config = configManager.loadConfig();
@@ -7705,6 +7816,454 @@ var AgentRunRuntimeContribution = class {
7705
7816
  });
7706
7817
  };
7707
7818
  //#endregion
7819
+ //#region src/contributions/context-provider/utils/context-text.utils.ts
7820
+ function truncateContextText(text, limit) {
7821
+ if (limit <= 0 || text.length <= limit) return text;
7822
+ const suffix = `\n\n...[truncated ${text.length - limit} chars]`;
7823
+ if (suffix.length >= limit) return text.slice(0, limit).trimEnd();
7824
+ return `${text.slice(0, limit - suffix.length).trimEnd()}${suffix}`;
7825
+ }
7826
+ //#endregion
7827
+ //#region src/contributions/context-provider/providers/agent-bootstrap-context.provider.ts
7828
+ var AgentBootstrapContextProvider = class {
7829
+ constructor(context) {
7830
+ this.context = context;
7831
+ }
7832
+ provide = async (request) => {
7833
+ const { contextConfig, projectContext, runContext } = await this.context.resolve(request);
7834
+ const budget = this.createReadBudget(contextConfig.bootstrap);
7835
+ const agentBootstrapRoot = projectContext.projectBootstrapRoot ?? projectContext.effectiveWorkspace;
7836
+ const projectBootstrap = this.loadBootstrapFiles({
7837
+ root: agentBootstrapRoot,
7838
+ config: contextConfig.bootstrap,
7839
+ sessionKey: runContext.sessionKey,
7840
+ budget
7841
+ });
7842
+ const hasDistinctHostWorkspace = projectContext.hostWorkspace !== agentBootstrapRoot;
7843
+ const workspaceBootstrap = hasDistinctHostWorkspace ? this.loadBootstrapFiles({
7844
+ root: projectContext.hostWorkspace,
7845
+ config: contextConfig.bootstrap,
7846
+ sessionKey: runContext.sessionKey,
7847
+ budget
7848
+ }) : "";
7849
+ const hasSoulFile = /##\s+SOUL\.md\b/i.test(`${projectBootstrap}\n${workspaceBootstrap}`);
7850
+ const sections = [this.buildBootstrapSection({
7851
+ content: projectBootstrap,
7852
+ emptyLabel: "No agent bootstrap files were found.",
7853
+ includeSoulRule: hasSoulFile,
7854
+ loadedLabel: "Agent bootstrap files loaded:",
7855
+ rootLine: `Agent bootstrap root: ${agentBootstrapRoot}`,
7856
+ title: "# Agent Bootstrap Context"
7857
+ })];
7858
+ if (hasDistinctHostWorkspace) sections.push(this.buildBootstrapSection({
7859
+ content: workspaceBootstrap,
7860
+ emptyLabel: "No bootstrap files were found in the NextClaw workspace directory.",
7861
+ loadedLabel: "NextClaw workspace bootstrap files loaded:",
7862
+ rootLine: `NextClaw workspace directory: ${projectContext.hostWorkspace}`,
7863
+ title: "# NextClaw Workspace Bootstrap Context"
7864
+ }));
7865
+ return [sections.filter(Boolean).join("\n\n")];
7866
+ };
7867
+ buildBootstrapSection = (params) => {
7868
+ const { content, emptyLabel, includeSoulRule, loadedLabel, rootLine, title } = params;
7869
+ const lines = [
7870
+ title,
7871
+ "",
7872
+ rootLine
7873
+ ];
7874
+ if (includeSoulRule) lines.push("If SOUL.md is present, embody its persona and tone unless higher-priority instructions override it.");
7875
+ if (content) lines.push("", loadedLabel, "", content);
7876
+ else lines.push("", emptyLabel);
7877
+ return lines.join("\n");
7878
+ };
7879
+ loadBootstrapFiles = (params) => {
7880
+ const { budget, config, root, sessionKey } = params;
7881
+ const parts = [];
7882
+ const fileList = this.selectBootstrapFiles(config, sessionKey);
7883
+ for (const filename of fileList) {
7884
+ const filePath = join(root, filename);
7885
+ if (!existsSync(filePath)) continue;
7886
+ const raw = readFileSync(filePath, "utf-8").trim();
7887
+ if (!raw) continue;
7888
+ const perFileLimit = config.perFileChars > 0 ? config.perFileChars : raw.length;
7889
+ const allowed = Math.min(perFileLimit, budget.remaining);
7890
+ if (allowed <= 0) break;
7891
+ const content = truncateContextText(raw, allowed);
7892
+ parts.push(`## ${filename}\n\n${content}`);
7893
+ budget.remaining -= content.length;
7894
+ if (budget.remaining <= 0) break;
7895
+ }
7896
+ return parts.join("\n\n");
7897
+ };
7898
+ createReadBudget = (config) => ({ remaining: config.totalChars > 0 ? config.totalChars : Number.POSITIVE_INFINITY });
7899
+ selectBootstrapFiles = (config, sessionKey) => {
7900
+ if (!sessionKey) return config.files;
7901
+ if (sessionKey.startsWith("cron:") || sessionKey.startsWith("subagent:")) return config.minimalFiles;
7902
+ return config.files;
7903
+ };
7904
+ };
7905
+ //#endregion
7906
+ //#region src/contributions/context-provider/providers/current-session-context.provider.ts
7907
+ var CurrentSessionContextProvider = class {
7908
+ constructor(context) {
7909
+ this.context = context;
7910
+ }
7911
+ provide = async (request) => {
7912
+ const { runContext } = await this.context.resolve(request);
7913
+ const lines = [
7914
+ "## Current Session",
7915
+ `Channel: ${runContext.channel}`,
7916
+ `Chat ID: ${runContext.chatId}`,
7917
+ `Session: ${runContext.sessionKey}`
7918
+ ];
7919
+ if (runContext.runtimeThinking) lines.push(`Thinking policy: ${runContext.runtimeThinking}`);
7920
+ return [lines.join("\n")];
7921
+ };
7922
+ };
7923
+ //#endregion
7924
+ //#region src/contributions/context-provider/providers/execution-policy-context.provider.ts
7925
+ function normalizeModel(model) {
7926
+ return model?.trim().toLowerCase() ?? "";
7927
+ }
7928
+ function isOpenAiOrCodexModel(model) {
7929
+ return /(gpt[-/ ]?5|gpt[-/ ]?4|gpt\b|chatgpt|openai|codex|\bo[134]\b)/i.test(normalizeModel(model));
7930
+ }
7931
+ function isGoogleModel(model) {
7932
+ return /(gemini|google)/i.test(normalizeModel(model));
7933
+ }
7934
+ function buildSection(title, lines) {
7935
+ return [title, ...lines].join("\n");
7936
+ }
7937
+ const TOOL_USE_ENFORCEMENT_LINES = [
7938
+ "- When you say you will inspect, run, read, search, edit, or verify something, call the matching tool in the same turn.",
7939
+ "- Do not stop at promises like 'I'll check' or 'I will do that' unless the tool call already happened in that turn.",
7940
+ "- If the task can still move forward with available tools, continue instead of ending early."
7941
+ ];
7942
+ const OPENAI_CODEX_DISCIPLINE_LINES = [
7943
+ "- Do not guess time, date, system state, file contents, git state, or other current facts. Check with tools first.",
7944
+ "- When the default scope is already clear, act on it before asking an avoidable clarification question.",
7945
+ "- If the first tool result is empty or incomplete, retry once with a different strategy before stopping."
7946
+ ];
7947
+ const GOOGLE_MODEL_GUIDANCE_LINES = [
7948
+ "- Batch independent reads when possible.",
7949
+ "- Read the surrounding context before editing files.",
7950
+ "- Use explicit file paths and keep the answer focused on results."
7951
+ ];
7952
+ function renderSystemExecutionPolicy(model) {
7953
+ const sections = [buildSection("## Tool Use Enforcement", TOOL_USE_ENFORCEMENT_LINES)];
7954
+ if (isOpenAiOrCodexModel(model)) sections.push(buildSection("## OpenAI/Codex Execution Discipline", OPENAI_CODEX_DISCIPLINE_LINES));
7955
+ else if (isGoogleModel(model)) sections.push(buildSection("## Google Model Operational Guidance", GOOGLE_MODEL_GUIDANCE_LINES));
7956
+ return sections.join("\n\n");
7957
+ }
7958
+ var ExecutionPolicyContextProvider = class {
7959
+ constructor(context) {
7960
+ this.context = context;
7961
+ }
7962
+ provide = async (request) => {
7963
+ const { runContext } = await this.context.resolve(request);
7964
+ return [renderSystemExecutionPolicy(runContext.effectiveModel)];
7965
+ };
7966
+ };
7967
+ //#endregion
7968
+ //#region src/contributions/context-provider/providers/native-static-context.provider.ts
7969
+ const block = (lines) => lines.join("\n");
7970
+ const staticProvider = (contextBlock) => ({ provide: () => [contextBlock] });
7971
+ const staticBlock = (lines) => staticProvider(block(lines));
7972
+ const createAssistantIdentityContextProvider = () => staticProvider(`You are a personal assistant running inside ${APP_NAME}.`);
7973
+ const createToolCallStyleContextProvider = () => staticBlock([
7974
+ "## Tool Call Style",
7975
+ "Default: do not narrate routine, low-risk tool calls (just call the tool).",
7976
+ "Narrate only when it helps: multi-step work, complex/challenging problems, sensitive actions (e.g., deletions), or when the user explicitly asks.",
7977
+ "Keep narration brief and value-dense; avoid repeating obvious steps.",
7978
+ "Use plain human language for narration unless in a technical context."
7979
+ ]);
7980
+ const createChatComposerTokensContextProvider = () => staticBlock([
7981
+ "## Chat Composer Tokens",
7982
+ "When a user message contains tokens like `$weather` or `$web-search`, treat each `$<skill-spec>` token as a user-visible marker that the corresponding skill was explicitly selected in the chat composer.",
7983
+ "These tokens can appear inline with normal prose. Do not ignore them or reinterpret them as shell variables or currency unless the surrounding context clearly says otherwise."
7984
+ ]);
7985
+ const createSafetyContextProvider = () => staticBlock([
7986
+ "## Safety",
7987
+ "You have no independent goals: do not pursue self-preservation, replication, resource acquisition, or power-seeking; avoid long-term plans beyond the user's request.",
7988
+ "Prioritize safety and human oversight over completion; if instructions conflict, pause and ask; comply with stop/pause/audit requests and never bypass safeguards. (Inspired by Anthropic's constitution.)",
7989
+ "Do not manipulate or persuade anyone to expand access or disable safeguards. Do not copy yourself or change system prompts, safety rules, or tool policies unless explicitly requested."
7990
+ ]);
7991
+ const createCliQuickReferenceContextProvider = () => {
7992
+ const appLower = APP_NAME.toLowerCase();
7993
+ return staticBlock([
7994
+ `## ${APP_NAME} CLI Quick Reference`,
7995
+ `${APP_NAME} is controlled via subcommands. Do not invent commands.`,
7996
+ "To manage the Gateway daemon service (start/stop/restart):",
7997
+ `- ${appLower} gateway status`,
7998
+ `- ${appLower} gateway start`,
7999
+ `- ${appLower} gateway stop`,
8000
+ `- ${appLower} gateway restart`,
8001
+ `If unsure, ask the user to run \`${appLower} help\` (or \`${appLower} gateway --help\`) and paste the output.`
8002
+ ]);
8003
+ };
8004
+ const createSelfUpdateContextProvider = () => staticBlock([
8005
+ `## ${APP_NAME} Self-Update`,
8006
+ "Get Updates (self-update) is ONLY allowed when the user explicitly asks for it.",
8007
+ "Do not run config.apply or update.run unless the user explicitly requests an update or config change; if it's not explicit, ask first.",
8008
+ "Actions: config.get, config.schema, config.apply (validate + write full config, then restart), config.patch (merge + restart), update.run (update deps or git, then restart).",
8009
+ "When patching config, copy enum values exactly from config.schema; never invent new variants.",
8010
+ "session.dmScope legal values are exactly: main | per-peer | per-channel-peer | per-account-channel-peer.",
8011
+ "If an enum/path is uncertain, stop and call config.schema first; do not guess.",
8012
+ `After restart, ${APP_NAME} pings the last active session automatically.`
8013
+ ]);
8014
+ const createReplyTagsContextProvider = () => staticBlock([
8015
+ "## Reply Tags",
8016
+ "To request a native reply/quote on supported surfaces, include one tag in your reply:",
8017
+ "- Reply tags must be the very first token in the message (no leading text/newlines): [[reply_to_current]] your reply.",
8018
+ "- [[reply_to_current]] replies to the triggering message.",
8019
+ "- Prefer [[reply_to_current]]. Use [[reply_to:<id>]] only when an id was explicitly provided (e.g. by the user or a tool).",
8020
+ "Whitespace inside the tag is allowed (e.g. [[ reply_to_current ]] / [[ reply_to: 123 ]]).",
8021
+ "Tags are stripped before sending; support depends on the current channel config."
8022
+ ]);
8023
+ const createMessagingContextProvider = () => staticBlock([
8024
+ "## Messaging",
8025
+ "- Reply in current session → automatically routes to the source channel (Signal, Telegram, etc.)",
8026
+ "- Cross-session or cross-channel messaging → use message(action=send); use sessions_list first when you need to recover an existing route without guessing.",
8027
+ "- Sub-agent orchestration → use subagents(action=list|steer|kill)",
8028
+ "- `[System Message] ...` blocks are internal context and are not user-visible by default.",
8029
+ "- If a `[System Message]` reports completed cron/subagent work and asks for a user update, rewrite it in your normal assistant voice and send that update (do not forward raw system text or default to <noreply/>).",
8030
+ `- Never use exec/curl for provider messaging; ${APP_NAME} handles all routing internally.`,
8031
+ "",
8032
+ "### message tool",
8033
+ "- Use `message` for proactive sends + channel actions (polls, reactions, etc.).",
8034
+ "- For `action=send`, include `message` plus an explicit `to/chatId` whenever the destination is another channel or another conversation.",
8035
+ "- Omitting `to/chatId` only replies to the current conversation; if you set `channel` to a different channel than the current session, `to/chatId` is required.",
8036
+ "- If multiple channels are configured, pass `channel`.",
8037
+ "- If you use `message` (`action=send`) to deliver your user-visible reply, respond with ONLY two blank lines + <noreply/> (avoid duplicate replies)."
8038
+ ]);
8039
+ const createMemoryRecallContextProvider = () => staticBlock([
8040
+ "## Memory Recall",
8041
+ "Before answering anything about prior work, decisions, dates, people, preferences, or todos: run memory_search on MEMORY.md + memory/*.md; then use memory_get to pull only the needed lines. If low confidence after search, say you checked.",
8042
+ "Citations: include Source: <path#line> when it helps the user verify memory snippets."
8043
+ ]);
8044
+ const createSilentRepliesContextProvider = () => staticBlock([
8045
+ "## Silent Replies",
8046
+ `Silent marker token: ${SILENT_REPLY_TOKEN}`,
8047
+ "When you have nothing to say, respond with EXACTLY two blank lines followed by <noreply/>",
8048
+ "",
8049
+ "⚠️ Rules:",
8050
+ "- It must be your ENTIRE message — nothing else",
8051
+ "- If <noreply/> appears anywhere, the system will stop reply/output and subsequent processing",
8052
+ "- Never wrap it in markdown or code blocks",
8053
+ "",
8054
+ "❌ Wrong: \"Here's help... <noreply/>\"",
8055
+ "❌ Wrong: \"<noreply/>\"",
8056
+ "✅ Right: \"\\n\\n<noreply/>\""
8057
+ ]);
8058
+ const createRuntimeContextProvider = () => staticBlock([
8059
+ "## Runtime",
8060
+ `Runtime: ${process.platform} ${process.arch}, Node ${process.version}`,
8061
+ "Time handling: do not assume exact minute/second unless the user/tool explicitly provides it.",
8062
+ "When a turn includes a time hint, treat it as context for relative-time interpretation in that turn."
8063
+ ]);
8064
+ const createSelfManagementContextProvider = () => ({ provide: () => {
8065
+ const appLower = APP_NAME.toLowerCase();
8066
+ const selfManageGuide = resolveNextclawSelfManageGuidePaths();
8067
+ return [block([
8068
+ `## ${APP_NAME} Self-Management Guide`,
8069
+ `- For ${APP_NAME} self-management operations (version/status/doctor/service/channels/config/agents/cron/remote/update), read \`${selfManageGuide.primaryPath ?? "the built-in NextClaw self-management guide"}\` first.`,
8070
+ "- Treat these product-management intents as higher priority than generic skills with overlapping words such as create/install/publish.",
8071
+ "- Do not load unrelated generic skills before reading the built-in self-management guide for a self-management intent.",
8072
+ "- Workspace `USAGE.md` snapshots and copied built-in skills are deprecated artifacts; the built-in package guide is the source of truth.",
8073
+ ...selfManageGuide.repoDocsPath ? [`- In repo source checkouts, the authoring copy is \`${selfManageGuide.repoDocsPath}\`; only use it when the packaged guide path above is unavailable.`] : [],
8074
+ "- If no guide file is available, fall back to command help output.",
8075
+ `- For version lookup, use \`${appLower} --version\` exactly; do not infer version from status output.`,
8076
+ `- After mutating operations, validate with \`${appLower} status --json\` (and \`${appLower} doctor --json\` when needed).`,
8077
+ `- For Agent CRUD, use \`${appLower} agents list|new|update|remove --json\` for the normal path; do not directly edit \`config.json\` or \`agents.list\` for routine Agent management.`,
8078
+ "- When creating Agents, prefer explicit non-text avatars and avoid text/initial-based avatar styles such as DiceBear `initials` as the default recommendation."
8079
+ ])];
8080
+ } });
8081
+ const createSessionOrchestrationContextProvider = () => staticBlock([
8082
+ "## Session Orchestration",
8083
+ "- Before passing a non-default `runtime` to `sessions_spawn` or agent creation/update flows, inspect the installed runtime kinds with `nextclaw agents runtimes --json`.",
8084
+ "- `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.",
8085
+ "- `sessions_spawn` only creates the session by default. Add top-level `notify: \"none\" | \"final_reply\"` when the new session should start working immediately.",
8086
+ "- 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.",
8087
+ "- Use `sessions_spawn` without `notify` when the user wants a separate thread created now but does not need it to start working yet.",
8088
+ "- 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.",
8089
+ "- `sessions_request.target` must be an object shaped like `{ \"session_id\": \"<target-session-id>\" }`. Do not pass a bare string.",
8090
+ "- 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."
8091
+ ]);
8092
+ //#endregion
8093
+ //#region src/contributions/context-provider/providers/project-context.provider.ts
8094
+ var ProjectContextProvider = class {
8095
+ constructor(context) {
8096
+ this.context = context;
8097
+ }
8098
+ provide = async (request) => {
8099
+ const { projectContext } = await this.context.resolve(request);
8100
+ const repositoryIdentity = DEFAULT_WORKSPACE_REPOSITORY_IDENTITY_RESOLVER.resolve(projectContext.effectiveWorkspace);
8101
+ return [this.buildProjectSection({
8102
+ projectContext,
8103
+ repositoryIdentity
8104
+ })];
8105
+ };
8106
+ buildProjectSection = (params) => {
8107
+ const { projectContext, repositoryIdentity } = params;
8108
+ const lines = [
8109
+ "# Project Context",
8110
+ "",
8111
+ `Active project directory: ${projectContext.effectiveWorkspace}`
8112
+ ];
8113
+ if (projectContext.projectRoot) lines.push(`Session-bound project root: ${projectContext.projectRoot}`, "This session is explicitly bound to that project directory. Use it as the primary repo and file-operation context for the user's work.");
8114
+ else lines.push("No explicit session project root is set. Use the active project directory as the primary repo and file-operation context for the user's work.");
8115
+ lines.push(...this.buildRepositoryIdentityLines(repositoryIdentity));
8116
+ return lines.join("\n");
8117
+ };
8118
+ buildRepositoryIdentityLines = (repositoryIdentity) => {
8119
+ if (!repositoryIdentity.repoRoot) return ["No Git repository metadata was detected for the active project directory. Do not assume external repository URLs refer to this project unless the user explicitly says so."];
8120
+ const lines = [`Repository root: ${repositoryIdentity.repoRoot}`];
8121
+ if (repositoryIdentity.canonicalWebUrl) lines.push(`Canonical repository: ${repositoryIdentity.canonicalWebUrl}`);
8122
+ else if (repositoryIdentity.canonicalRemoteUrl) {
8123
+ const remoteLabel = repositoryIdentity.canonicalRemoteName ? ` (${repositoryIdentity.canonicalRemoteName})` : "";
8124
+ lines.push(`Canonical git remote${remoteLabel}: ${repositoryIdentity.canonicalRemoteUrl}`);
8125
+ }
8126
+ lines.push("Repository identity rule: treat any other repository URL mentioned in this context as an external reference unless it exactly matches the canonical repository above.");
8127
+ return lines;
8128
+ };
8129
+ };
8130
+ //#endregion
8131
+ //#region src/contributions/context-provider/providers/reply-format-context.provider.ts
8132
+ var ReplyFormatContextProvider = class {
8133
+ provide = (_request) => [[
8134
+ "## Reply Formatting",
8135
+ "- When mentioning a local project file in a user-visible reply, prefer a Markdown link such as `[AGENTS.md](AGENTS.md)` or `[file](packages/example/file.ts)` so the user can open it.",
8136
+ "- Use project-relative links for files under the active/session-bound project root; use absolute links only for files outside it."
8137
+ ].join("\n")];
8138
+ };
8139
+ //#endregion
8140
+ //#region src/contributions/context-provider/providers/skills-context.provider.ts
8141
+ function wrapSkillTag(tagName, manifest) {
8142
+ return [
8143
+ `<${tagName}>`,
8144
+ manifest,
8145
+ `</${tagName}>`
8146
+ ].join("\n");
8147
+ }
8148
+ function renderActiveSkillsSection(skills, skillSelectors) {
8149
+ const manifest = skills.buildSkillsManifest(skillSelectors);
8150
+ if (!manifest) return "";
8151
+ return [
8152
+ "# Active Skills",
8153
+ "These always-on skills are already active for this session context.",
8154
+ "If an active skill covers the user's intent, follow it before considering unrelated available skills.",
8155
+ "For NextClaw self-management intents, read the built-in NextClaw self-management guide before loading any unrelated generic skill.",
8156
+ "Skill refs are unique identities; names may repeat.",
8157
+ "Read a SKILL.md from <location> only when you need its instructions.",
8158
+ "",
8159
+ wrapSkillTag("active_skills", manifest)
8160
+ ].join("\n\n");
8161
+ }
8162
+ function renderAvailableSkillsSection(skills) {
8163
+ const summary = skills.buildSkillsSummary();
8164
+ if (!summary) return "";
8165
+ return [
8166
+ "## Skills (mandatory)",
8167
+ "Always-on skills in <active_skills> take precedence over this list.",
8168
+ "Before replying: first check whether any entry in <available_skills> may be relevant to the user's intent, task type, or requested output. Do not skip this check just because the task seems familiar.",
8169
+ "- If one skill looks like the best relevant match, read its SKILL.md at <location> with `read_file`, then decide whether following it is actually helpful.",
8170
+ "- If a SKILL.md read says `Use offset=... to continue`, continue reading until the relevant trigger, required workflow, constraints, and output requirements are covered.",
8171
+ "- If the user is asking to manage NextClaw itself, read the built-in NextClaw self-management guide first and do not open unrelated generic skills before that.",
8172
+ "- If multiple skills share the same <name>, use <ref> to distinguish them. Never assume duplicate names mean the same skill.",
8173
+ "- If none clearly apply: do not read any SKILL.md.",
8174
+ "Constraints: never read more than one skill up front; only read after selecting.",
8175
+ "",
8176
+ "<available_skills>",
8177
+ summary,
8178
+ "</available_skills>"
8179
+ ].join("\n");
8180
+ }
8181
+ function renderSkillLearningSection() {
8182
+ return [
8183
+ "# Skill Learning Loop",
8184
+ "After non-trivial work, run a brief review before your final answer.",
8185
+ "- Summarize the reusable lesson, not the full transcript.",
8186
+ "- Decide exactly one outcome: `no_skill_change`, `patch_existing_skill`, or `create_new_skill`.",
8187
+ "- Prefer patching an existing skill when the lesson extends or corrects it; only create a new skill when the trigger and workflow are genuinely distinct.",
8188
+ "- Promote a lesson into a skill only when it has a clear trigger, repeatable steps, and failure signals/checks.",
8189
+ "- Do not create skills for one-off facts, narrow local quirks, or work that is not likely to recur.",
8190
+ "- Keep the review concise and action-oriented. Do not add user-visible review text unless it materially helps or the user asks for it."
8191
+ ].join("\n");
8192
+ }
8193
+ var SkillsContextProvider = class {
8194
+ constructor(context) {
8195
+ this.context = context;
8196
+ }
8197
+ provide = async (request) => {
8198
+ const { projectContext } = await this.context.resolve(request);
8199
+ const skills = new SkillsLoader({
8200
+ workspace: projectContext.hostWorkspace,
8201
+ projectRoot: projectContext.projectRoot
8202
+ });
8203
+ const blocks = [];
8204
+ const alwaysSkills = skills.getAlwaysSkills();
8205
+ if (alwaysSkills.length) {
8206
+ const activeSection = renderActiveSkillsSection(skills, alwaysSkills);
8207
+ if (activeSection) blocks.push(activeSection);
8208
+ }
8209
+ const availableSkillsSection = renderAvailableSkillsSection(skills);
8210
+ if (availableSkillsSection) blocks.push(availableSkillsSection);
8211
+ blocks.push(renderSkillLearningSection());
8212
+ return blocks;
8213
+ };
8214
+ };
8215
+ //#endregion
8216
+ //#region src/contributions/context-provider/providers/tooling-context.provider.ts
8217
+ var ToolingContextProvider = class {
8218
+ constructor(context) {
8219
+ this.context = context;
8220
+ }
8221
+ provide = async (request) => {
8222
+ const { toolCatalog } = await this.context.resolve(request);
8223
+ return [[
8224
+ "## Tooling",
8225
+ "Tool availability (filtered by policy):",
8226
+ "Tool names are case-sensitive. Call tools exactly as listed.",
8227
+ ...toolCatalog.length > 0 ? toolCatalog.map((tool) => `- ${tool.name}: ${tool.description ?? "No description available"}`) : ["- No tools available for this turn."],
8228
+ "TOOLS.md does not control tool availability; it is user guidance for how to use external tools.",
8229
+ "For long waits, avoid rapid poll loops: use exec with enough yieldMs.",
8230
+ "For relative time/date scheduling requests (for example 'in 5 minutes' / '1分钟后'), first check the current local time with an available tool such as exec/date, then convert it to an absolute ISO time with timezone. Do not guess.",
8231
+ "If a task is more complex or takes longer, spawn a sub-agent. Completion is push-based: it will auto-announce when done.",
8232
+ "Do not poll `subagents list` / `sessions_list` in a loop; only check status on-demand (for intervention, debugging, or when explicitly asked)."
8233
+ ].join("\n")];
8234
+ };
8235
+ };
8236
+ //#endregion
8237
+ //#region src/contributions/context-provider/providers/workspace-context.provider.ts
8238
+ var WorkspaceContextProvider = class {
8239
+ constructor(context) {
8240
+ this.context = context;
8241
+ }
8242
+ provide = async (request) => {
8243
+ const { runContext } = await this.context.resolve(request);
8244
+ return [[
8245
+ "## Workspace",
8246
+ `Your working directory is: ${runContext.effectiveWorkspace}`,
8247
+ "Treat this directory as the single global workspace for file operations unless explicitly instructed otherwise."
8248
+ ].join("\n")];
8249
+ };
8250
+ };
8251
+ //#endregion
8252
+ //#region src/contributions/context-provider/providers/workspace-memory-context.provider.ts
8253
+ var WorkspaceMemoryContextProvider = class {
8254
+ constructor(context) {
8255
+ this.context = context;
8256
+ }
8257
+ provide = async (request) => {
8258
+ const { contextConfig, projectContext } = await this.context.resolve(request);
8259
+ const memoryConfig = contextConfig.memory;
8260
+ if (!memoryConfig.enabled) return [];
8261
+ const memory = new MemoryStore(projectContext.hostWorkspace).getMemoryContext();
8262
+ if (!memory) return [];
8263
+ return [`# Memory\n\n${truncateContextText(memory, memoryConfig.maxChars)}`];
8264
+ };
8265
+ };
8266
+ //#endregion
7708
8267
  //#region src/utils/agent-run-request-metadata.utils.ts
7709
8268
  function normalizeString(value) {
7710
8269
  return value?.trim() || void 0;
@@ -7726,12 +8285,60 @@ function buildAgentRunRequestMetadata(params) {
7726
8285
  };
7727
8286
  }
7728
8287
  //#endregion
7729
- //#region src/contributions/context-provider/providers/kernel-context.provider.ts
7730
- var KernelContextProvider = class {
8288
+ //#region src/contributions/context-provider/utils/native-context-config.utils.ts
8289
+ const DEFAULT_NATIVE_CONTEXT_CONFIG = {
8290
+ bootstrap: {
8291
+ files: [
8292
+ "AGENTS.md",
8293
+ "SOUL.md",
8294
+ "USER.md",
8295
+ "IDENTITY.md",
8296
+ "TOOLS.md",
8297
+ "BOOT.md",
8298
+ "BOOTSTRAP.md"
8299
+ ],
8300
+ minimalFiles: [
8301
+ "AGENTS.md",
8302
+ "SOUL.md",
8303
+ "TOOLS.md",
8304
+ "IDENTITY.md"
8305
+ ],
8306
+ perFileChars: 4e3,
8307
+ totalChars: 12e3
8308
+ },
8309
+ memory: {
8310
+ enabled: true,
8311
+ maxChars: 8e3
8312
+ }
8313
+ };
8314
+ function mergeNativeContextConfig(contextConfig) {
8315
+ return {
8316
+ bootstrap: {
8317
+ ...DEFAULT_NATIVE_CONTEXT_CONFIG.bootstrap,
8318
+ ...contextConfig?.bootstrap ?? {}
8319
+ },
8320
+ memory: {
8321
+ ...DEFAULT_NATIVE_CONTEXT_CONFIG.memory,
8322
+ ...contextConfig?.memory ?? {}
8323
+ }
8324
+ };
8325
+ }
8326
+ //#endregion
8327
+ //#region src/contributions/context-provider/services/context-provider-run-context.service.ts
8328
+ var ContextProviderRunContextService = class {
8329
+ snapshots = /* @__PURE__ */ new WeakMap();
8330
+ projectContextResolver = new SessionProjectContextResolver();
7731
8331
  constructor(kernel) {
7732
8332
  this.kernel = kernel;
7733
8333
  }
7734
- provide = async (request) => {
8334
+ resolve = (request) => {
8335
+ const existing = this.snapshots.get(request);
8336
+ if (existing) return existing;
8337
+ const next = this.resolveSnapshot(request);
8338
+ this.snapshots.set(request, next);
8339
+ return next;
8340
+ };
8341
+ resolveSnapshot = async (request) => {
7735
8342
  const session = request.sessionId ? await this.kernel.sessionManager.getAgentRunSession(request.sessionId) : null;
7736
8343
  const sessionId = session?.sessionId ?? request.sessionId ?? request.message.sessionId ?? "";
7737
8344
  const requestMetadata = buildAgentRunRequestMetadata({
@@ -7746,45 +8353,60 @@ var KernelContextProvider = class {
7746
8353
  storedAgentId: request.agentId ?? session?.agentId
7747
8354
  });
7748
8355
  const tools = await this.kernel.toolProviderManager.buildTools(request);
7749
- return [this.buildSystemContextBlock({
7750
- availableTools: buildToolCatalogEntries(tools.map((tool) => ({
8356
+ const sessionProjectRoot = readSessionProjectRoot(runContext.sessionMetadata);
8357
+ const projectContext = this.projectContextResolver.resolve({
8358
+ sessionMetadata: sessionProjectRoot ? { project_root: sessionProjectRoot } : null,
8359
+ workspace: runContext.profile.workspace,
8360
+ defaultWorkspace: runContext.effectiveWorkspace
8361
+ });
8362
+ return {
8363
+ contextConfig: mergeNativeContextConfig(runContext.config.agents.context),
8364
+ projectContext,
8365
+ runContext,
8366
+ toolCatalog: buildToolCatalogEntries(tools.map((tool) => ({
7751
8367
  name: tool.name,
7752
- description: tool.description,
7753
- parameters: tool.parameters
7754
- }))),
7755
- runContext
7756
- })];
7757
- };
7758
- buildSystemContextBlock = (params) => {
7759
- const { availableTools, runContext } = params;
7760
- const lines = [
7761
- new ContextBuilder(runContext.effectiveWorkspace, runContext.config.agents.context, {
7762
- hostWorkspace: runContext.profile.workspace,
7763
- sessionProjectRoot: readSessionProjectRoot(runContext.sessionMetadata)
7764
- }).buildSystemPrompt(void 0, runContext.sessionKey, availableTools, [buildSessionOrchestrationSection(), buildMinimalSystemExecutionPrompt(runContext.effectiveModel)]),
7765
- "## Current Session",
7766
- `Channel: ${runContext.channel}`,
7767
- `Chat ID: ${runContext.chatId}`,
7768
- `Session: ${runContext.sessionKey}`
7769
- ];
7770
- if (runContext.runtimeThinking) lines.push(`Thinking policy: ${runContext.runtimeThinking}`);
7771
- return lines.join("\n");
8368
+ description: tool.description
8369
+ })))
8370
+ };
7772
8371
  };
7773
8372
  };
7774
8373
  //#endregion
7775
8374
  //#region src/contributions/context-provider/index.ts
7776
8375
  var ContextProviderContribution = class {
7777
- unregister = null;
8376
+ cleanups = [];
7778
8377
  constructor(kernel) {
7779
8378
  this.kernel = kernel;
7780
8379
  }
7781
8380
  start = () => {
7782
- if (this.unregister) return;
7783
- this.unregister = this.kernel.contextProviderManager.register(new KernelContextProvider(this.kernel));
8381
+ if (this.cleanups.length > 0) return;
8382
+ const context = new ContextProviderRunContextService(this.kernel);
8383
+ for (const provider of [
8384
+ createAssistantIdentityContextProvider(),
8385
+ new ToolingContextProvider(context),
8386
+ createToolCallStyleContextProvider(),
8387
+ createChatComposerTokensContextProvider(),
8388
+ createSafetyContextProvider(),
8389
+ createCliQuickReferenceContextProvider(),
8390
+ createSelfUpdateContextProvider(),
8391
+ new WorkspaceContextProvider(context),
8392
+ createReplyTagsContextProvider(),
8393
+ createMessagingContextProvider(),
8394
+ createMemoryRecallContextProvider(),
8395
+ createSilentRepliesContextProvider(),
8396
+ createRuntimeContextProvider(),
8397
+ createSelfManagementContextProvider(),
8398
+ new ProjectContextProvider(context),
8399
+ new AgentBootstrapContextProvider(context),
8400
+ new WorkspaceMemoryContextProvider(context),
8401
+ new SkillsContextProvider(context),
8402
+ createSessionOrchestrationContextProvider(),
8403
+ new ExecutionPolicyContextProvider(context),
8404
+ new CurrentSessionContextProvider(context),
8405
+ new ReplyFormatContextProvider()
8406
+ ]) this.cleanups.push(this.kernel.contextProviderManager.register(provider));
7784
8407
  };
7785
8408
  dispose = () => {
7786
- this.unregister?.();
7787
- this.unregister = null;
8409
+ while (this.cleanups.length > 0) this.cleanups.pop()?.();
7788
8410
  };
7789
8411
  };
7790
8412
  //#endregion
@@ -9588,6 +10210,6 @@ function resolveLegacyEventType(message) {
9588
10210
  return `message.${role || "other"}`;
9589
10211
  }
9590
10212
  //#endregion
9591
- export { AccessManager, AgentManager, AgentRunClient, AgentRuntimeRegistry, AutomationManager, BuiltinNarpRuntimeProviderService, CONTEXT_COMPACTION_TIMELINE_KIND, ChannelManager, CommandRegistry, ConfigManager, ContextCompactionPreflightService, ContextWindowPreviewManager, DEFAULT_AGENT_RUNTIME_ENTRY_ID, DEFAULT_SERVICE_ACTION_RISK, ExtensionManager, GatewayInboundProcessor, LlmProviderManager, LlmUsageManager, LlmUsageStore, McpManager, McpServiceAppRuntimeService, NARP_HTTP_RUNTIME_KIND, NARP_STDIO_RUNTIME_KIND, NEXTCLAW_TIMELINE_KIND_METADATA_KEY, NcpAgentSessionJournalStore, NextclawKernel, PANEL_APP_AGENT_CAPABILITIES, PanelAppAssetTokenService, PanelAppError, PanelAppManager, ProviderManagerNcpLLMApi, SERVICE_APP_MANIFEST_FILE_NAME, ServiceAppError, ServiceAppManager, SessionManager, SessionRequestManager, SkillManager, UpdateManifestReader, buildAgentRunSendPayload, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildServiceActionId, buildSessionOrchestrationSection, createAgentRuntimeSessionRequestDispatcher, createAssetTools, createContextWindowSignature, createLlmUsageRecord, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, getServiceActionName, getServiceAppManifestPath, getUnsignedUpdateManifest, hasLlmUsageTelemetry, isContextCompactionTimelineMessage, isContextWindowSnapshot, isPanelAppAgentCapability, isPanelAppError, isReplyCapableChannel, isServiceAppError, listExtensionChannelIds, listServiceAppManifestActions, mergeServiceAppRuntimeActions, normalizeAgentRuntimeSessionTypeIcon, normalizeLlmUsageModel, normalizeOptionalString, parseServiceAppManifest, parseSkillFrontmatter, projectNcpMessagesWithContextCompaction, readContextWindowEventSessionId, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, readServiceAppManifest, resolveAgentRuntimeEntries, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolveNextclawNcpRunContext, resolveSessionChannelContext, runGatewayInboundLoop, sanitizeLlmUsage, serializeUnsignedUpdateManifest, shouldRefreshContextWindowDuringStream, shouldRefreshContextWindowImmediately, stripSkillFrontmatter, syncSessionThinkingPreference, toNcpMessages, upsertContextCompactionTimelineMessage, waitForAgentRuntimeSessionReply };
10213
+ export { AccessManager, AgentManager, AgentRunClient, AgentRuntimeRegistry, AutomationManager, BuiltinNarpRuntimeProviderService, CONTEXT_COMPACTION_TIMELINE_KIND, ChannelManager, CommandRegistry, ConfigManager, ContextCompactionPreflightService, ContextWindowPreviewManager, DEFAULT_AGENT_RUNTIME_ENTRY_ID, DEFAULT_SERVICE_ACTION_RISK, ExtensionManager, GatewayInboundProcessor, LlmProviderManager, LlmUsageManager, LlmUsageStore, McpManager, McpServiceAppRuntimeService, NARP_HTTP_RUNTIME_KIND, NARP_STDIO_RUNTIME_KIND, NEXTCLAW_TIMELINE_KIND_METADATA_KEY, NcpAgentSessionJournalStore, NextclawKernel, PANEL_APP_AGENT_CAPABILITIES, PanelAppAssetTokenService, PanelAppError, PanelAppManager, ProviderManagerNcpLLMApi, SERVICE_APP_MANIFEST_FILE_NAME, ServiceAppError, ServiceAppManager, SessionManager, SessionRequestManager, SkillManager, UpdateManifestReader, buildAgentRunSendPayload, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildServiceActionId, createAgentRuntimeSessionRequestDispatcher, createAssetTools, createContextCompactionMessageId, createContextWindowSignature, createLlmUsageRecord, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, getServiceActionName, getServiceAppManifestPath, getUnsignedUpdateManifest, hasLlmUsageTelemetry, isContextCompactionTimelineMessage, isContextWindowSnapshot, isPanelAppAgentCapability, isPanelAppError, isReplyCapableChannel, isServiceAppError, listExtensionChannelIds, listServiceAppManifestActions, mergeServiceAppRuntimeActions, normalizeAgentRuntimeSessionTypeIcon, normalizeLlmUsageModel, normalizeOptionalString, parseServiceAppManifest, parseSkillFrontmatter, projectNcpMessagesWithContextCompaction, readContextWindowEventSessionId, readLatestContextCompactionCheckpoint, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, readServiceAppManifest, resolveAgentRuntimeEntries, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolveNextclawNcpRunContext, resolveSessionChannelContext, runGatewayInboundLoop, sanitizeLlmUsage, serializeUnsignedUpdateManifest, shouldRefreshContextWindowDuringStream, shouldRefreshContextWindowImmediately, stripSkillFrontmatter, syncSessionThinkingPreference, toNcpMessages, upsertContextCompactionTimelineMessage, waitForAgentRuntimeSessionReply };
9592
10214
 
9593
10215
  //# sourceMappingURL=index.js.map