@nextclaw/kernel 0.5.4 → 0.6.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
- 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, EditFileTool, ExecTool, ExtensionChannelAdapter, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, LiteLLMProvider, MAX_SESSION_SEARCH_LIMIT, MemoryGetTool, MemorySearchTool, MemoryStore, MessageBus, MessageTool, ProviderRegistry, ReadFileTool, RequestedSkillsMetadataReader, SILENT_REPLY_TOKEN, SessionProjectContextResolver, SessionSearchService, SkillsLoader, THINKING_LEVELS, WebFetchTool, WebSearchTool, WriteFileTool, buildCompressingCompactionCheckpoint, buildConfigSchema, buildContextWindowSnapshot, buildReloadPlan, buildSessionRequestToolResult, buildToolCatalogEntries, createAgentProfile, createAssistantStreamDeltaControlMessage, createAssistantStreamResetControlMessage, createCompletedSessionRequest, createFailedSessionRequest, createRunningSessionRequest, createRuntimeChildEnv, createTypingStopControlMessage, diffConfigPaths, ensureDir, 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, resolveSessionProjectContext, resolveSessionWorkspacePath, resolveThinkingLevel, sanitizeOutboundAssistantContent, saveConfig, summarizeSessionRequestTask, toExtensionConfigView, updateAgentProfile } from "@nextclaw/core";
4
- import { NcpEventType, sanitizeAssistantReplyTags } from "@nextclaw/ncp";
3
+ 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, EditFileTool, ExecTool, ExtensionChannelAdapter, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, LiteLLMProvider, MAX_SESSION_SEARCH_LIMIT, MemoryGetTool, MemorySearchTool, MemoryStore, MessageBus, MessageTool, 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, 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, resolveSessionProjectContext, resolveSessionWorkspacePath, resolveThinkingLevel, sanitizeOutboundAssistantContent, saveConfig, summarizeSessionRequestTask, toExtensionConfigView, updateAgentProfile } from "@nextclaw/core";
4
+ import { NcpEventType, normalizeAssistantText, sanitizeAssistantReplyTags } from "@nextclaw/ncp";
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 { CHAT_SESSION_MATERIALIZATION_METADATA_KEY, EventBus, Ingress, eventKeys, ingressKeys, isRuntimeDefaultModelValue, normalizeRuntimeModelSelectionMode } from "@nextclaw/shared";
@@ -71,6 +71,40 @@ function readRequestedAgentId(metadata) {
71
71
  return normalizeAgentProfileId(metadata.agent_id) || normalizeAgentProfileId(metadata.agentId) || null;
72
72
  }
73
73
  //#endregion
74
+ //#region src/utils/agent-run-metadata.utils.ts
75
+ const AGENT_RUN_MESSAGE_RUN_SPEC_METADATA_KEY = "run_spec";
76
+ const LEGACY_RUN_METADATA_KEYS = [
77
+ "account_id",
78
+ "agent_id",
79
+ "chat_id",
80
+ "preferred_model",
81
+ "project_root",
82
+ "runtime",
83
+ "sender_id",
84
+ "session_key",
85
+ "session_type"
86
+ ];
87
+ function stripLegacyRunMetadata(metadata) {
88
+ const nextMetadata = structuredClone(metadata);
89
+ for (const key of LEGACY_RUN_METADATA_KEYS) delete nextMetadata[key];
90
+ return nextMetadata;
91
+ }
92
+ function buildRunMetadata(params) {
93
+ const { message, metadata, route } = params;
94
+ return {
95
+ ...stripLegacyRunMetadata({
96
+ ...message.metadata ?? {},
97
+ ...metadata ?? {}
98
+ }),
99
+ channel: message.channel,
100
+ chatId: message.chatId,
101
+ accountId: route.accountId,
102
+ agentId: route.agentId,
103
+ sessionKey: route.sessionKey,
104
+ senderId: message.senderId
105
+ };
106
+ }
107
+ //#endregion
74
108
  //#region src/utils/ncp-message-bridge.utils.ts
75
109
  function normalizeString$1(value) {
76
110
  if (typeof value !== "string") return null;
@@ -169,8 +203,18 @@ function toLegacyMessages(messages, options = {}) {
169
203
  //#region src/features/context-compaction/utils/context-compaction.utils.ts
170
204
  const NEXTCLAW_TIMELINE_KIND_METADATA_KEY = "nextclaw_timeline_kind";
171
205
  const CONTEXT_COMPACTION_TIMELINE_KIND = "context_compaction";
206
+ const CONTEXT_COMPACTION_PROJECTION_METADATA_KEY = "nextclaw_context_projection";
207
+ const CONTEXT_COMPACTION_PROJECTION_KIND = "compressed_context";
172
208
  function readCheckpointTimelineText(checkpoint) {
173
- return checkpoint.status === "compressing" ? "正在压缩较早上下文" : "较早上下文已自动压缩";
209
+ return checkpoint.status === "compressing" ? "Compressing earlier context" : "Earlier context was auto-compacted";
210
+ }
211
+ function buildCompressedContextSystemText(checkpoint) {
212
+ return [
213
+ "Authoritative compressed prior conversation context for this session.",
214
+ "Continue from this context and the latest user message. Do not restart onboarding or treat missing profile fields as a new-session trigger unless the compressed context says onboarding is the active user task.",
215
+ "",
216
+ checkpoint.summary
217
+ ].join("\n");
174
218
  }
175
219
  function readContextCompactionCheckpoint(message) {
176
220
  const metadata = message.metadata;
@@ -187,15 +231,19 @@ function buildContextCompactionSummaryMessage(params) {
187
231
  return {
188
232
  id: `${sessionId}:context-compaction-summary:${checkpoint.id}:${checkpoint.updatedAt}`,
189
233
  sessionId,
190
- role: "user",
234
+ role: "service",
191
235
  status: "final",
192
236
  timestamp: checkpoint.updatedAt,
193
237
  parts: [{
194
238
  type: "text",
195
- text: checkpoint.summary
196
- }]
239
+ text: buildCompressedContextSystemText(checkpoint)
240
+ }],
241
+ metadata: { [CONTEXT_COMPACTION_PROJECTION_METADATA_KEY]: CONTEXT_COMPACTION_PROJECTION_KIND }
197
242
  };
198
243
  }
244
+ function readCheckpointCoveredUntil(checkpoint) {
245
+ return checkpoint.coveredUntil ?? checkpoint.updatedAt;
246
+ }
199
247
  function createContextCompactionMessageId() {
200
248
  return `context-compaction-message-${randomUUID()}`;
201
249
  }
@@ -221,6 +269,9 @@ function buildContextCompactionTimelineNcpMessage(params) {
221
269
  function isContextCompactionTimelineMessage(message) {
222
270
  return message?.metadata?.[NEXTCLAW_TIMELINE_KIND_METADATA_KEY] === CONTEXT_COMPACTION_TIMELINE_KIND;
223
271
  }
272
+ function isContextCompactionProjectionMessage(message) {
273
+ return message?.metadata?.[CONTEXT_COMPACTION_PROJECTION_METADATA_KEY] === CONTEXT_COMPACTION_PROJECTION_KIND;
274
+ }
224
275
  function readLatestContextCompactionCheckpoint(sessionMessages) {
225
276
  return readLatestContextCompactionMarker(sessionMessages)?.checkpoint ?? null;
226
277
  }
@@ -230,10 +281,11 @@ function buildContextCompactionModelInput(params) {
230
281
  const regularMessages = sessionMessages.filter((message) => !readContextCompactionCheckpoint(message));
231
282
  if (!marker) return regularMessages.map((message) => structuredClone(message));
232
283
  const { checkpoint } = marker;
284
+ const coveredUntil = readCheckpointCoveredUntil(checkpoint);
233
285
  return [buildContextCompactionSummaryMessage({
234
286
  checkpoint,
235
287
  sessionId
236
- }), ...regularMessages.filter((message) => Date.parse(message.timestamp) > Date.parse(checkpoint.updatedAt)).sort((left, right) => Date.parse(left.timestamp) - Date.parse(right.timestamp))].map((message) => structuredClone(message));
288
+ }), ...regularMessages.filter((message) => Date.parse(message.timestamp) > Date.parse(coveredUntil)).sort((left, right) => Date.parse(left.timestamp) - Date.parse(right.timestamp))].map((message) => structuredClone(message));
237
289
  }
238
290
  function readContextWindowEventSessionId(event) {
239
291
  const payload = "payload" in event ? event.payload : null;
@@ -277,6 +329,17 @@ function shouldRefreshContextWindowImmediately(event) {
277
329
  //#region src/features/context-compaction/services/context-compaction-preflight.service.ts
278
330
  const SUMMARY_MAX_TOKENS = 4e3;
279
331
  const SUMMARY_SOURCE_MAX_CHARS = 12e4;
332
+ const SUMMARY_SOURCE_HEAD_MESSAGES = 2;
333
+ const SUMMARY_SOURCE_TAIL_MESSAGES = 8;
334
+ const SUMMARY_SOURCE_STRING_HEAD_CHARS = 6e3;
335
+ const SUMMARY_SOURCE_STRING_TAIL_CHARS = 6e3;
336
+ function buildContextBlockMessage(contextBlocks = []) {
337
+ const contextContent = contextBlocks.map((block) => block.trim()).filter(Boolean).join("\n\n");
338
+ return contextContent ? [{
339
+ role: "system",
340
+ content: contextContent
341
+ }] : [];
342
+ }
280
343
  function mergeInputMessages(params) {
281
344
  const messages = params.sessionMessages.map((message) => structuredClone(message));
282
345
  const seen = new Set(messages.map((message) => message.id));
@@ -286,10 +349,47 @@ function mergeInputMessages(params) {
286
349
  }
287
350
  return messages;
288
351
  }
352
+ function toCompactionSourceMessage(message) {
353
+ return {
354
+ role: message.role,
355
+ content: message.content,
356
+ timestamp: message.timestamp,
357
+ ncp_message_id: message.ncp_message_id
358
+ };
359
+ }
289
360
  function stringifyCompactionSource(messages) {
290
- const json = JSON.stringify(messages, null, 2);
361
+ const sourceMessages = messages.map(toCompactionSourceMessage);
362
+ const json = JSON.stringify(sourceMessages, null, 2);
291
363
  if (json.length <= SUMMARY_SOURCE_MAX_CHARS) return json;
292
- return `${json.slice(0, SUMMARY_SOURCE_MAX_CHARS).trimEnd()}\n[truncated_source]`;
364
+ const tailStart = Math.max(SUMMARY_SOURCE_HEAD_MESSAGES, sourceMessages.length - SUMMARY_SOURCE_TAIL_MESSAGES);
365
+ const compactedMessages = [
366
+ ...sourceMessages.slice(0, SUMMARY_SOURCE_HEAD_MESSAGES),
367
+ ...tailStart > SUMMARY_SOURCE_HEAD_MESSAGES ? [{
368
+ role: "system",
369
+ content: `[${tailStart - SUMMARY_SOURCE_HEAD_MESSAGES} middle messages omitted from compaction source]`
370
+ }] : [],
371
+ ...sourceMessages.slice(tailStart)
372
+ ];
373
+ const compactedJson = JSON.stringify(compactedMessages, (_key, value) => truncateSummarySourceString(value), 2);
374
+ if (compactedJson.length <= SUMMARY_SOURCE_MAX_CHARS) return compactedJson;
375
+ const marker = "\n[truncated_compaction_source_middle]\n";
376
+ const headChars = Math.floor((SUMMARY_SOURCE_MAX_CHARS - 38) / 2);
377
+ const tailChars = SUMMARY_SOURCE_MAX_CHARS - 38 - headChars;
378
+ return `${compactedJson.slice(0, headChars).trimEnd()}${marker}${compactedJson.slice(-tailChars).trimStart()}`;
379
+ }
380
+ function truncateSummarySourceString(value) {
381
+ if (typeof value === "string") {
382
+ if (value.length <= SUMMARY_SOURCE_STRING_HEAD_CHARS + SUMMARY_SOURCE_STRING_TAIL_CHARS) return value;
383
+ return [
384
+ value.slice(0, SUMMARY_SOURCE_STRING_HEAD_CHARS).trimEnd(),
385
+ `[${value.length - SUMMARY_SOURCE_STRING_HEAD_CHARS - SUMMARY_SOURCE_STRING_TAIL_CHARS} chars omitted]`,
386
+ value.slice(-SUMMARY_SOURCE_STRING_TAIL_CHARS).trimStart()
387
+ ].join("\n");
388
+ }
389
+ return value;
390
+ }
391
+ function normalizeCompactionSummary(content) {
392
+ return normalizeAssistantText(content, "think-tags").text.trim();
293
393
  }
294
394
  function buildContextWindowSnapshotFromBudget(params) {
295
395
  const { budget, checkpoint, totalContextTokens } = params;
@@ -313,7 +413,7 @@ var ContextCompactionPreflightService = class {
313
413
  this.providerManager = providerManager;
314
414
  }
315
415
  preview = (params) => {
316
- const { requestMetadata, sessionId, sessionMessages, storedAgentId, storedMetadata } = params;
416
+ const { contextBlocks = [], requestMetadata, sessionId, sessionMessages, storedAgentId, storedMetadata } = params;
317
417
  const profile = this.resolveCompactionProfile({
318
418
  requestMetadata,
319
419
  storedAgentId
@@ -323,9 +423,10 @@ var ContextCompactionPreflightService = class {
323
423
  sessionId,
324
424
  sessionMessages
325
425
  }) : sessionMessages.filter((message) => !isContextCompactionTimelineMessage(message));
426
+ const messages = [...buildContextBlockMessage(contextBlocks), ...toLegacyMessages(projectedMessages)];
326
427
  return buildContextWindowSnapshotFromBudget({
327
428
  budget: this.contextWindowBudgetService.evaluate({
328
- messages: toLegacyMessages(projectedMessages),
429
+ messages,
329
430
  contextTokens: profile.contextTokens,
330
431
  reservedContextTokens: profile.reservedContextTokens
331
432
  }),
@@ -334,7 +435,7 @@ var ContextCompactionPreflightService = class {
334
435
  });
335
436
  };
336
437
  begin = (params) => {
337
- const { inputMessages, requestMetadata, sessionId, sessionMessages, storedAgentId, storedMetadata } = params;
438
+ const { contextBlocks = [], inputMessages, requestMetadata, sessionId, sessionMessages, storedAgentId, storedMetadata } = params;
338
439
  const profile = this.resolveCompactionProfile({
339
440
  requestMetadata,
340
441
  storedAgentId
@@ -345,10 +446,11 @@ var ContextCompactionPreflightService = class {
345
446
  sessionMessages
346
447
  });
347
448
  const existingCheckpoint = readCompressedContextCompactionCheckpoint(storedMetadata[CONTEXT_COMPACTION_METADATA_KEY]) ?? readLatestContextCompactionCheckpoint(ncpMessages);
348
- const messages = toLegacyMessages(existingCheckpoint ? buildContextCompactionModelInput({
449
+ const projectedMessages = existingCheckpoint ? buildContextCompactionModelInput({
349
450
  sessionId,
350
451
  sessionMessages: ncpMessages
351
- }) : ncpMessages.filter((message) => !isContextCompactionTimelineMessage(message)));
452
+ }) : ncpMessages.filter((message) => !isContextCompactionTimelineMessage(message));
453
+ const messages = [...buildContextBlockMessage(contextBlocks), ...toLegacyMessages(projectedMessages)];
352
454
  const budget = this.contextWindowBudgetService.evaluate({
353
455
  messages,
354
456
  contextTokens,
@@ -434,7 +536,7 @@ var ContextCompactionPreflightService = class {
434
536
  generateSummary = async (params) => {
435
537
  if (!this.providerManager) throw new Error("context compaction summary generation requires a provider manager");
436
538
  const { messages, model } = params;
437
- const summary = (await this.providerManager.chat({
539
+ const response = await this.providerManager.chat({
438
540
  model,
439
541
  maxTokens: SUMMARY_MAX_TOKENS,
440
542
  messages: [{
@@ -442,7 +544,10 @@ var ContextCompactionPreflightService = class {
442
544
  content: [
443
545
  "You are NextClaw's context compactor for a coding agent session.",
444
546
  "Create a complete compressed working context that will replace all prior conversation messages in a future model request.",
445
- "The model will not receive a raw recent-message tail, so preserve the latest user intent and recent turns with high fidelity inside the summary.",
547
+ "Only the latest current input may remain raw, so preserve the active task, latest user intent, latest assistant response, and recent turns with high fidelity inside the summary.",
548
+ "Always include a 'Continuation Contract' section that states what the next assistant response should remember and how it should continue the session.",
549
+ "Do not turn missing user profile, assistant nickname, or onboarding fields into blockers unless onboarding is the active user task in the latest turns.",
550
+ "If the latest user message is a short greeting, preserve the prior active task and last assistant stance so the next response does not restart as a fresh session.",
446
551
  "Preserve user goals, explicit instructions, decisions, files touched or inspected, code changes, commands run, test results, failures, blockers, current task state, and exact next steps.",
447
552
  "Do not invent facts. If something is uncertain, mark it as uncertain.",
448
553
  "Return Markdown only. Start with '# Compressed Working Context'."
@@ -452,12 +557,14 @@ var ContextCompactionPreflightService = class {
452
557
  content: [
453
558
  "Compress these runtime messages into a reusable working context.",
454
559
  "Include a 'Recent High-Fidelity Context' section for the latest important user/assistant turns.",
560
+ "Include a 'Continuation Contract' section after the recent context.",
455
561
  "",
456
562
  "Messages JSON:",
457
563
  stringifyCompactionSource(messages)
458
564
  ].join("\n")
459
565
  }]
460
- })).content?.trim();
566
+ });
567
+ const summary = response.content ? normalizeCompactionSummary(response.content) : "";
461
568
  if (!summary) throw new Error("context compaction summary is empty");
462
569
  return summary;
463
570
  };
@@ -501,6 +608,7 @@ var AgentRunContextCompactionManager = class {
501
608
  }
502
609
  runPreflight = async (input) => {
503
610
  const beginResult = this.preflightService.begin({
611
+ contextBlocks: input.contextBlocks,
504
612
  inputMessages: [],
505
613
  requestMetadata: input.metadata,
506
614
  sessionId: input.sessionId,
@@ -517,6 +625,7 @@ var AgentRunContextCompactionManager = class {
517
625
  if (Object.keys(result.metadataPatch).length > 0) await this.sessionManager.patchSessionMetadata(sessionId, result.metadataPatch);
518
626
  if (!result.timelineMessage) return [];
519
627
  return [{
628
+ occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
520
629
  type: NcpEventType.MessageSent,
521
630
  payload: {
522
631
  sessionId,
@@ -584,6 +693,37 @@ function readOptionalString$10(value) {
584
693
  if (typeof value !== "string") return;
585
694
  return value.trim() || void 0;
586
695
  }
696
+ function readRunStartedAt(event, fallback) {
697
+ if (event.type !== NcpEventType.RunStarted) return fallback;
698
+ return event.payload.startedAt ?? event.occurredAt ?? fallback;
699
+ }
700
+ function createCompletedAssistantMessageEvent(params) {
701
+ return {
702
+ occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
703
+ type: NcpEventType.MessageCompleted,
704
+ payload: {
705
+ sessionId: params.sessionId,
706
+ message: params.message,
707
+ correlationId: params.correlationId
708
+ }
709
+ };
710
+ }
711
+ function createSyntheticRunErrorEvent(params) {
712
+ const { correlationId, error, runId, sessionId, startedAt } = params;
713
+ const endedAt = (/* @__PURE__ */ new Date()).toISOString();
714
+ return {
715
+ occurredAt: endedAt,
716
+ type: NcpEventType.RunError,
717
+ payload: {
718
+ error: error instanceof Error ? error.message : String(error),
719
+ runId,
720
+ sessionId,
721
+ correlationId,
722
+ startedAt,
723
+ endedAt
724
+ }
725
+ };
726
+ }
587
727
  function readSessionMaterialization(metadata) {
588
728
  const value = metadata[CHAT_SESSION_MATERIALIZATION_METADATA_KEY];
589
729
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
@@ -613,6 +753,46 @@ function findCompletedAssistantMessage(messages, messageId) {
613
753
  function readMessageTask(message) {
614
754
  return message.parts.flatMap((part) => (part.type === "text" || part.type === "rich-text" || part.type === "reasoning") && part.text.trim() ? [part.text.trim()] : [])[0] ?? "Session";
615
755
  }
756
+ function resolveRunSpec(params) {
757
+ const { agentManager, configManager, request, runId, session } = params;
758
+ const modelSource = request.model !== void 0 ? "request" : session.model !== void 0 ? "session" : "default";
759
+ const model = request.model ?? session.model ?? configManager.getDefaultModel();
760
+ return {
761
+ modelSource,
762
+ spec: {
763
+ runId,
764
+ agentId: request.agentId ?? session.agentId ?? agentManager.getDefaultAgentId(),
765
+ model,
766
+ maxTokens: request.maxTokens ?? configManager.getModelMaxTokens(model),
767
+ thinkingEffort: request.thinkingEffort ?? session.thinkingEffort ?? null,
768
+ correlationId: request.correlationId
769
+ }
770
+ };
771
+ }
772
+ function attachRunSpecMetadata(params) {
773
+ const { message, modelSource, request, session, spec, startedAt } = params;
774
+ const metadata = structuredClone(message.metadata ?? {});
775
+ metadata[AGENT_RUN_MESSAGE_RUN_SPEC_METADATA_KEY] = {
776
+ version: 1,
777
+ runId: spec.runId,
778
+ startedAt,
779
+ sessionId: session.sessionId,
780
+ agentRuntimeId: session.agentRuntimeId,
781
+ agentId: spec.agentId,
782
+ model: spec.model,
783
+ modelSource,
784
+ requestedModel: request.model ?? null,
785
+ maxTokens: spec.maxTokens,
786
+ thinkingEffort: spec.thinkingEffort,
787
+ projectRoot: request.projectRoot ?? session.projectRoot ?? null,
788
+ workingDir: session.workingDir ?? null,
789
+ correlationId: spec.correlationId ?? null
790
+ };
791
+ return {
792
+ ...message,
793
+ metadata
794
+ };
795
+ }
616
796
  var AgentRunRequestManager = class {
617
797
  cleanups = [];
618
798
  started = false;
@@ -642,7 +822,12 @@ var AgentRunRequestManager = class {
642
822
  };
643
823
  handleAbortRequest = async (envelope) => {
644
824
  if (!envelope.payload?.sessionId) throw new Error("Invalid agent run abort request.");
645
- await this.abort({ sessionId: envelope.payload.sessionId });
825
+ await this.abort({
826
+ sessionId: envelope.payload.sessionId,
827
+ runId: envelope.payload.runId,
828
+ correlationId: envelope.payload.correlationId,
829
+ reason: envelope.payload.reason
830
+ });
646
831
  };
647
832
  handleSessionMessageRequest = async (envelope) => {
648
833
  if (!envelope.payload) throw new Error("Invalid agent run session message request.");
@@ -658,16 +843,10 @@ var AgentRunRequestManager = class {
658
843
  send = async (request) => {
659
844
  const session = await this.getOrCreateSessionForRequest(request);
660
845
  const sessionRun = this.sessionRunManager.getSessionRun(session.sessionId) ?? await this.sessionRunManager.createSessionRun(session.sessionId);
661
- const message = {
846
+ const baseMessage = {
662
847
  ...request.message,
663
848
  sessionId: session.sessionId
664
849
  };
665
- const providerRequest = {
666
- ...request,
667
- sessionId: session.sessionId,
668
- message
669
- };
670
- sessionRun.inbox.enqueue(message);
671
850
  let stopPublishingRunStatus = () => void 0;
672
851
  stopPublishingRunStatus = sessionRun.onStatusChange((status) => {
673
852
  this.eventBus.emit(eventKeys.sessionRunStatus, {
@@ -680,6 +859,7 @@ var AgentRunRequestManager = class {
680
859
  if (status === "idle") stopPublishingRunStatus();
681
860
  });
682
861
  this.cleanups.push(stopPublishingRunStatus);
862
+ const requestRunStartedAt = (/* @__PURE__ */ new Date()).toISOString();
683
863
  const activeRun = (() => {
684
864
  try {
685
865
  return sessionRun.beginRun();
@@ -688,16 +868,27 @@ var AgentRunRequestManager = class {
688
868
  throw error;
689
869
  }
690
870
  })();
691
- const model = request.model ?? session.model ?? this.configManager.getDefaultModel();
692
- const agentId = request.agentId ?? session.agentId ?? this.agentManager.getDefaultAgentId();
693
- const spec = {
871
+ const { modelSource, spec } = resolveRunSpec({
872
+ agentManager: this.agentManager,
873
+ configManager: this.configManager,
874
+ request,
694
875
  runId: activeRun.runId,
695
- agentId,
696
- model,
697
- maxTokens: request.maxTokens ?? this.configManager.getModelMaxTokens(model),
698
- thinkingEffort: request.thinkingEffort ?? session.thinkingEffort ?? null,
699
- correlationId: request.correlationId
876
+ session
877
+ });
878
+ const message = attachRunSpecMetadata({
879
+ message: baseMessage,
880
+ modelSource,
881
+ request,
882
+ session,
883
+ spec,
884
+ startedAt: requestRunStartedAt
885
+ });
886
+ const providerRequest = {
887
+ ...request,
888
+ sessionId: session.sessionId,
889
+ message
700
890
  };
891
+ sessionRun.inbox.enqueue(message);
701
892
  const runtime = this.agentRuntimeManager.getOrCreate({
702
893
  agentRuntimeId: session.agentRuntimeId,
703
894
  session,
@@ -707,6 +898,7 @@ var AgentRunRequestManager = class {
707
898
  const tools = await this.toolProviderManager.buildTools(providerRequest);
708
899
  let messageCompletedSeen = false;
709
900
  let runtimeFailed = false;
901
+ let runStartedAt = requestRunStartedAt;
710
902
  lastValueFrom(from(runtime.run(spec, {
711
903
  contextBlocks,
712
904
  session,
@@ -716,18 +908,16 @@ var AgentRunRequestManager = class {
716
908
  })).pipe(tap((event) => {
717
909
  const eventsToPublish = [];
718
910
  if (event.type === NcpEventType.RunError) runtimeFailed = true;
911
+ runStartedAt = readRunStartedAt(event, runStartedAt);
719
912
  if (event.type === NcpEventType.MessageCompleted) messageCompletedSeen = true;
720
913
  if (event.type === NcpEventType.RunFinished && !messageCompletedSeen) {
721
914
  const message = findCompletedAssistantMessage(sessionRun.getSnapshot().messages, event.payload.messageId);
722
915
  if (!message) throw new Error(`Run finished without a final assistant message for session "${session.sessionId}".`);
723
- eventsToPublish.push({
724
- type: NcpEventType.MessageCompleted,
725
- payload: {
726
- sessionId: event.payload.sessionId ?? session.sessionId,
727
- message,
728
- correlationId: event.payload.correlationId
729
- }
730
- });
916
+ eventsToPublish.push(createCompletedAssistantMessageEvent({
917
+ sessionId: event.payload.sessionId ?? session.sessionId,
918
+ message,
919
+ correlationId: event.payload.correlationId
920
+ }));
731
921
  messageCompletedSeen = true;
732
922
  }
733
923
  eventsToPublish.push(event);
@@ -737,15 +927,13 @@ var AgentRunRequestManager = class {
737
927
  });
738
928
  }), catchError(async (error) => {
739
929
  runtimeFailed = true;
740
- const event = {
741
- type: NcpEventType.RunError,
742
- payload: {
743
- error: error instanceof Error ? error.message : String(error),
744
- runId: spec.runId,
745
- sessionId: session.sessionId,
746
- correlationId: spec.correlationId
747
- }
748
- };
930
+ const event = createSyntheticRunErrorEvent({
931
+ error,
932
+ runId: spec.runId,
933
+ sessionId: session.sessionId,
934
+ correlationId: spec.correlationId,
935
+ startedAt: runStartedAt
936
+ });
749
937
  await sessionRun.applyEvents([event]);
750
938
  this.eventBus.emit(eventKeys.ncpEvent, event, {
751
939
  emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -786,7 +974,7 @@ var AgentRunRequestManager = class {
786
974
  });
787
975
  };
788
976
  abort = async (request) => {
789
- this.sessionRunManager.getSessionRun(request.sessionId)?.abortRun(request.runId);
977
+ this.sessionRunManager.getSessionRun(request.sessionId)?.abortRun(request.runId, request.reason);
790
978
  };
791
979
  };
792
980
  //#endregion
@@ -2816,11 +3004,11 @@ var ExtensionManager = class {
2816
3004
  //#endregion
2817
3005
  //#region src/utils/model-message-vision.utils.ts
2818
3006
  const IMAGE_OMITTED_TEXT = "[Image omitted: the selected model is not configured for vision input.]";
2819
- function isRecord$12(value) {
3007
+ function isRecord$11(value) {
2820
3008
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2821
3009
  }
2822
3010
  function isImageContentPart(value) {
2823
- if (!isRecord$12(value)) return false;
3011
+ if (!isRecord$11(value)) return false;
2824
3012
  const type = value.type;
2825
3013
  return type === "image_url" || type === "input_image";
2826
3014
  }
@@ -2836,7 +3024,7 @@ function normalizeContentWithoutVision(content) {
2836
3024
  };
2837
3025
  });
2838
3026
  if (!sawImage) return content;
2839
- const textParts = parts.filter((part) => isRecord$12(part) && part.type === "text" && typeof part.text === "string").map((part) => part.text.trim()).filter(Boolean);
3027
+ const textParts = parts.filter((part) => isRecord$11(part) && part.type === "text" && typeof part.text === "string").map((part) => part.text.trim()).filter(Boolean);
2840
3028
  if (textParts.length === parts.length) return textParts.join("\n\n");
2841
3029
  return parts;
2842
3030
  }
@@ -3399,7 +3587,7 @@ function safeNcpSessionFilename(value) {
3399
3587
  function normalizeNcpAgentId(agentId) {
3400
3588
  return agentId?.trim().toLowerCase() || void 0;
3401
3589
  }
3402
- function isRecord$11(value) {
3590
+ function isRecord$10(value) {
3403
3591
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3404
3592
  }
3405
3593
  function toIsoString(value, fallback) {
@@ -3470,6 +3658,7 @@ async function replayNcpAgentSessionEvents(events) {
3470
3658
  }
3471
3659
  function createReplayEvent(event, toolResultsByCallId) {
3472
3660
  const replayEvent = structuredClone(event);
3661
+ const occurredAt = readReplayEventOccurredAt(replayEvent);
3473
3662
  const replayMessage = readMessageFromSummaryEvent(replayEvent);
3474
3663
  const legacyCompactionMessageId = readLegacyContextCompactionMessageId(replayMessage);
3475
3664
  if (replayMessage && legacyCompactionMessageId) replayMessage.id = legacyCompactionMessageId;
@@ -3477,12 +3666,17 @@ function createReplayEvent(event, toolResultsByCallId) {
3477
3666
  if (replayEvent.type === "session.snapshot.message" || replayEvent.type === NcpEventType.MessageCompleted) {
3478
3667
  replayEvent.payload.message = mergeReplayCompletedToolResults(replayEvent.payload.message, toolResultsByCallId);
3479
3668
  return {
3669
+ occurredAt,
3480
3670
  type: NcpEventType.MessageSent,
3481
3671
  payload: replayEvent.payload
3482
3672
  };
3483
3673
  }
3484
3674
  return replayEvent;
3485
3675
  }
3676
+ function readReplayEventOccurredAt(event) {
3677
+ if (!("occurredAt" in event) || typeof event.occurredAt !== "string") return;
3678
+ return event.occurredAt;
3679
+ }
3486
3680
  function mergeReplayCompletedToolResults(message, toolResultsByCallId) {
3487
3681
  let changed = false;
3488
3682
  const parts = message.parts.map((part) => {
@@ -3503,7 +3697,7 @@ function mergeReplayCompletedToolResults(message, toolResultsByCallId) {
3503
3697
  } : message;
3504
3698
  }
3505
3699
  function readLegacyContextCompactionMessageId(message) {
3506
- const checkpoint = isRecord$11(message?.metadata?.checkpoint) ? message.metadata.checkpoint : null;
3700
+ const checkpoint = isRecord$10(message?.metadata?.checkpoint) ? message.metadata.checkpoint : null;
3507
3701
  const checkpointId = typeof checkpoint?.id === "string" ? checkpoint.id : "";
3508
3702
  const coveredCount = checkpoint?.coveredSessionMessageCount;
3509
3703
  const legacyId = `${message?.sessionId}:service:context-compaction:${checkpointId}`;
@@ -3514,6 +3708,7 @@ function createReplayStreamingBootstrapEvent(event, knownMessageIds) {
3514
3708
  if (!messageId || knownMessageIds.has(messageId)) return null;
3515
3709
  knownMessageIds.add(messageId);
3516
3710
  return {
3711
+ occurredAt: event.occurredAt,
3517
3712
  type: NcpEventType.MessageSent,
3518
3713
  payload: {
3519
3714
  sessionId: readEventSessionId$2(event),
@@ -3533,11 +3728,11 @@ function rememberReplayMessageId(event, knownMessageIds) {
3533
3728
  if (message?.id) knownMessageIds.add(message.id);
3534
3729
  }
3535
3730
  function readEventSessionId$2(event) {
3536
- const sessionId = ("payload" in event && isRecord$11(event.payload) ? event.payload : null)?.sessionId;
3731
+ const sessionId = ("payload" in event && isRecord$10(event.payload) ? event.payload : null)?.sessionId;
3537
3732
  return typeof sessionId === "string" ? sessionId : "";
3538
3733
  }
3539
3734
  function readReplayPayloadTimestamp(event) {
3540
- const payload = "payload" in event && isRecord$11(event.payload) ? event.payload : null;
3735
+ const payload = "payload" in event && isRecord$10(event.payload) ? event.payload : null;
3541
3736
  const timestamp = typeof payload?.timestamp === "string" ? payload.timestamp : "";
3542
3737
  return Number.isFinite(Date.parse(timestamp)) ? new Date(timestamp).toISOString() : null;
3543
3738
  }
@@ -3771,12 +3966,12 @@ function createProjection(sessionId, preview) {
3771
3966
  };
3772
3967
  }
3773
3968
  function formatErrorStatus(error) {
3774
- if (typeof error === "string" && error.trim()) return `运行出错:${truncatePreviewText(error)}`;
3969
+ if (typeof error === "string" && error.trim()) return `Run failed: ${truncatePreviewText(error)}`;
3775
3970
  if (error && typeof error === "object" && "message" in error) {
3776
3971
  const message = error.message;
3777
- if (typeof message === "string" && message.trim()) return `运行出错:${truncatePreviewText(message)}`;
3972
+ if (typeof message === "string" && message.trim()) return `Run failed: ${truncatePreviewText(message)}`;
3778
3973
  }
3779
- return "运行出错";
3974
+ return "Run failed";
3780
3975
  }
3781
3976
  function readToolCallId(value) {
3782
3977
  if (typeof value !== "string") return null;
@@ -3784,13 +3979,13 @@ function readToolCallId(value) {
3784
3979
  return trimmed.length > 0 ? trimmed : null;
3785
3980
  }
3786
3981
  function formatToolDoneStatus(toolName) {
3787
- return toolName ? `工具调用完成:${toolName}` : "工具调用完成";
3982
+ return toolName ? `Tool call completed: ${toolName}` : "Tool call completed";
3788
3983
  }
3789
3984
  function createSessionActivityPreviewFromNcpEvent(event, timestamp, options = {}) {
3790
3985
  switch (event.type) {
3791
3986
  case NcpEventType.RunStarted: return createProjection(readSessionId(event.payload.sessionId), {
3792
3987
  state: "running",
3793
- statusText: "正在思考",
3988
+ statusText: "Thinking",
3794
3989
  timestamp
3795
3990
  });
3796
3991
  case NcpEventType.RunFinished: return createProjection(readSessionId(event.payload.sessionId), {
@@ -3817,7 +4012,7 @@ function createSessionActivityPreviewFromNcpEvent(event, timestamp, options = {}
3817
4012
  return createProjection(readSessionId(event.payload.sessionId), {
3818
4013
  state: "completed",
3819
4014
  replyText: text,
3820
- timestamp: event.payload.message.timestamp || timestamp
4015
+ timestamp
3821
4016
  });
3822
4017
  }
3823
4018
  case NcpEventType.MessageFailed: return createProjection(readSessionId(event.payload.sessionId), {
@@ -3825,9 +4020,13 @@ function createSessionActivityPreviewFromNcpEvent(event, timestamp, options = {}
3825
4020
  statusText: formatErrorStatus(event.payload.error),
3826
4021
  timestamp
3827
4022
  });
4023
+ case NcpEventType.MessageAbort: return createProjection(readSessionId(event.payload.sessionId), {
4024
+ state: "cancelled",
4025
+ timestamp
4026
+ });
3828
4027
  case NcpEventType.MessageToolCallStart: return createProjection(readSessionId(event.payload.sessionId), {
3829
4028
  state: "running",
3830
- statusText: `正在调用工具:${event.payload.toolName}`,
4029
+ statusText: `Calling tool: ${event.payload.toolName}`,
3831
4030
  timestamp
3832
4031
  });
3833
4032
  case NcpEventType.MessageToolCallEnd:
@@ -3850,9 +4049,10 @@ const SESSION_ACTIVITY_PREVIEW_STATES = new Set([
3850
4049
  "running",
3851
4050
  "completed",
3852
4051
  "failed",
4052
+ "cancelled",
3853
4053
  "idle"
3854
4054
  ]);
3855
- function isRecord$10(value) {
4055
+ function isRecord$9(value) {
3856
4056
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3857
4057
  }
3858
4058
  function readOptionalString$7(value) {
@@ -3861,7 +4061,7 @@ function readOptionalString$7(value) {
3861
4061
  return trimmed.length > 0 ? trimmed : void 0;
3862
4062
  }
3863
4063
  function readSessionActivityPreviewMetadata(value) {
3864
- if (!isRecord$10(value)) return null;
4064
+ if (!isRecord$9(value)) return null;
3865
4065
  const state = value.state;
3866
4066
  const timestamp = readOptionalString$7(value.timestamp);
3867
4067
  if (!SESSION_ACTIVITY_PREVIEW_STATES.has(state) || !timestamp) return null;
@@ -3937,7 +4137,7 @@ var SessionActivityPreviewEventService = class {
3937
4137
  };
3938
4138
  readToolName = (sessionId, toolCallId) => this.toolNames.get(this.createToolNameKey(sessionId, toolCallId)) ?? null;
3939
4139
  clearFinishedRunToolNames = (event) => {
3940
- if (event.type !== NcpEventType.RunFinished && event.type !== NcpEventType.RunError) return;
4140
+ if (event.type !== NcpEventType.RunFinished && event.type !== NcpEventType.RunError && event.type !== NcpEventType.MessageAbort) return;
3941
4141
  const sessionId = event.payload.sessionId;
3942
4142
  for (const key of this.toolNames.keys()) if (key.startsWith(`${sessionId}:`)) this.toolNames.delete(key);
3943
4143
  };
@@ -4526,12 +4726,12 @@ var PanelAppCapabilityGrantStore = class {
4526
4726
  };
4527
4727
  };
4528
4728
  function normalizeStoreData$2(value) {
4529
- if (!isRecord$9(value) || value.version !== 1 || !isRecord$9(value.grants)) return structuredClone(EMPTY_GRANTS$2);
4729
+ if (!isRecord$8(value) || value.version !== 1 || !isRecord$8(value.grants)) return structuredClone(EMPTY_GRANTS$2);
4530
4730
  const grants = {};
4531
4731
  for (const [callerKey, callerValue] of Object.entries(value.grants)) {
4532
- if (!isRecord$9(callerValue) || !isRecord$9(callerValue.capabilities)) continue;
4732
+ if (!isRecord$8(callerValue) || !isRecord$8(callerValue.capabilities)) continue;
4533
4733
  const capabilities = {};
4534
- for (const [capability, grantValue] of Object.entries(callerValue.capabilities)) if (isRecord$9(grantValue) && typeof grantValue.grantedAt === "string") capabilities[capability] = { grantedAt: grantValue.grantedAt };
4734
+ for (const [capability, grantValue] of Object.entries(callerValue.capabilities)) if (isRecord$8(grantValue) && typeof grantValue.grantedAt === "string") capabilities[capability] = { grantedAt: grantValue.grantedAt };
4535
4735
  grants[callerKey] = { capabilities };
4536
4736
  }
4537
4737
  return {
@@ -4542,7 +4742,7 @@ function normalizeStoreData$2(value) {
4542
4742
  function getCallerKey(caller) {
4543
4743
  return `${caller.surface}:${caller.appId}`;
4544
4744
  }
4545
- function isRecord$9(value) {
4745
+ function isRecord$8(value) {
4546
4746
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4547
4747
  }
4548
4748
  function isMissingFileError$3(error) {
@@ -4588,19 +4788,19 @@ var PanelAppClientGrantStore = class {
4588
4788
  };
4589
4789
  };
4590
4790
  function normalizeStoreData$1(value) {
4591
- if (!isRecord$8(value) || value.version !== 1 || !isRecord$8(value.grants)) return structuredClone(EMPTY_GRANTS$1);
4791
+ if (!isRecord$7(value) || value.version !== 1 || !isRecord$7(value.grants)) return structuredClone(EMPTY_GRANTS$1);
4592
4792
  const grants = {};
4593
- for (const [appId, grant] of Object.entries(value.grants)) if (isRecord$8(grant) && typeof grant.grantedAt === "string") grants[appId] = { grantedAt: grant.grantedAt };
4793
+ for (const [appId, grant] of Object.entries(value.grants)) if (isRecord$7(grant) && typeof grant.grantedAt === "string") grants[appId] = { grantedAt: grant.grantedAt };
4594
4794
  return {
4595
4795
  grants,
4596
4796
  version: 1
4597
4797
  };
4598
4798
  }
4599
- function isRecord$8(value) {
4799
+ function isRecord$7(value) {
4600
4800
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4601
4801
  }
4602
4802
  function isMissingFileError$2(error) {
4603
- return isRecord$8(error) && error.code === "ENOENT";
4803
+ return isRecord$7(error) && error.code === "ENOENT";
4604
4804
  }
4605
4805
  //#endregion
4606
4806
  //#region src/services/agent-run-client.service.ts
@@ -4819,7 +5019,7 @@ const PANEL_APP_AGENT_MAX_CONTEXT_CHARS = 8e4;
4819
5019
  function normalizePanelAppGenerateObjectInput(input) {
4820
5020
  const peerId = input.peerId.trim();
4821
5021
  const prompt = input.prompt.trim();
4822
- if (!peerId || !prompt || !isRecord$7(input.schema)) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "invalid generateObject request");
5022
+ if (!peerId || !prompt || !isRecord$6(input.schema)) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "invalid generateObject request");
4823
5023
  if (prompt.length > PANEL_APP_AGENT_MAX_PROMPT_CHARS) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "generateObject prompt is too large");
4824
5024
  if (stringifyJsonValue(input.context ?? null).length > PANEL_APP_AGENT_MAX_CONTEXT_CHARS) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "generateObject context is too large");
4825
5025
  return {
@@ -4960,7 +5160,7 @@ function readStructuredResultEvent(event, resultToolCallId) {
4960
5160
  };
4961
5161
  }
4962
5162
  function assertToolResultContent(content) {
4963
- if (!isRecord$7(content) || content.ok !== false || !isRecord$7(content.error)) return;
5163
+ if (!isRecord$6(content) || content.ok !== false || !isRecord$6(content.error)) return;
4964
5164
  if (content.error.code === "invalid_tool_arguments") throw new PanelAppError("AGENT_OBJECT_RESULT_SCHEMA_INVALID", "agent object result did not match the schema");
4965
5165
  throw new PanelAppError("AGENT_OBJECT_REQUEST_FAILED", typeof content.error.message === "string" ? content.error.message : "agent object request failed");
4966
5166
  }
@@ -4976,7 +5176,7 @@ function stringifyJsonValue(value) {
4976
5176
  throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "value is not JSON serializable");
4977
5177
  }
4978
5178
  }
4979
- function isRecord$7(value) {
5179
+ function isRecord$6(value) {
4980
5180
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4981
5181
  }
4982
5182
  //#endregion
@@ -5250,7 +5450,7 @@ function parsePanelAppFolderManifest(raw) {
5250
5450
  } catch (error) {
5251
5451
  throw new Error(`panel-app.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
5252
5452
  }
5253
- if (!isRecord$6(parsed)) throw new Error("panel-app.json must contain an object.");
5453
+ if (!isRecord$5(parsed)) throw new Error("panel-app.json must contain an object.");
5254
5454
  const id = readOptionalString$6(parsed, "id");
5255
5455
  if (id && !PANEL_APP_ID_PATTERN.test(id)) throw new Error("panel app id must be kebab-case.");
5256
5456
  return {
@@ -5337,7 +5537,7 @@ function readStringArray$1(value, key) {
5337
5537
  if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) throw new Error(`panel app ${key} must be a string array.`);
5338
5538
  return [...new Set(value.map((entry) => entry.trim()).filter(Boolean))];
5339
5539
  }
5340
- function isRecord$6(value) {
5540
+ function isRecord$5(value) {
5341
5541
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
5342
5542
  }
5343
5543
  function normalizeTextValue(value) {
@@ -6174,12 +6374,12 @@ var ServiceActionGrantStore = class {
6174
6374
  };
6175
6375
  };
6176
6376
  function normalizeStoreData(value) {
6177
- if (!isRecord$5(value) || value.version !== 1 || !isRecord$5(value.grants)) return structuredClone(EMPTY_GRANTS);
6377
+ if (!isRecord$4(value) || value.version !== 1 || !isRecord$4(value.grants)) return structuredClone(EMPTY_GRANTS);
6178
6378
  const grants = {};
6179
6379
  for (const [callerKey, callerValue] of Object.entries(value.grants)) {
6180
- if (!isRecord$5(callerValue) || !isRecord$5(callerValue.actions)) continue;
6380
+ if (!isRecord$4(callerValue) || !isRecord$4(callerValue.actions)) continue;
6181
6381
  const actions = {};
6182
- for (const [actionId, actionValue] of Object.entries(callerValue.actions)) if (isRecord$5(actionValue) && typeof actionValue.grantedAt === "string" && isServiceActionRisk(actionValue.risk)) actions[actionId] = {
6382
+ for (const [actionId, actionValue] of Object.entries(callerValue.actions)) if (isRecord$4(actionValue) && typeof actionValue.grantedAt === "string" && isServiceActionRisk(actionValue.risk)) actions[actionId] = {
6183
6383
  grantedAt: actionValue.grantedAt,
6184
6384
  risk: actionValue.risk
6185
6385
  };
@@ -6193,11 +6393,11 @@ function normalizeStoreData(value) {
6193
6393
  function isServiceActionRisk(value) {
6194
6394
  return value === "read" || value === "write" || value === "external" || value === "dangerous";
6195
6395
  }
6196
- function isRecord$5(value) {
6396
+ function isRecord$4(value) {
6197
6397
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
6198
6398
  }
6199
6399
  function isMissingFileError(error) {
6200
- return isRecord$5(error) && error.code === "ENOENT";
6400
+ return isRecord$4(error) && error.code === "ENOENT";
6201
6401
  }
6202
6402
  //#endregion
6203
6403
  //#region src/utils/service-app-manifest.utils.ts
@@ -6222,7 +6422,7 @@ function parseServiceAppManifest(raw) {
6222
6422
  } catch (error) {
6223
6423
  throw new Error(`service-app.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
6224
6424
  }
6225
- if (!isRecord$4(parsed)) throw new Error("service-app.json must contain an object.");
6425
+ if (!isRecord$3(parsed)) throw new Error("service-app.json must contain an object.");
6226
6426
  const id = readRequiredString$3(parsed, "id");
6227
6427
  if (!SERVICE_APP_ID_PATTERN.test(id)) throw new Error("service app id must be kebab-case.");
6228
6428
  const protocol = readOptionalString$5(parsed, "protocol") ?? "mcp";
@@ -6240,16 +6440,16 @@ function parseServiceAppManifest(raw) {
6240
6440
  }
6241
6441
  function readManifestActions(value) {
6242
6442
  if (value === void 0) throw new Error("service app actions are required.");
6243
- if (!isRecord$4(value)) throw new Error("service app actions must be an object.");
6443
+ if (!isRecord$3(value)) throw new Error("service app actions must be an object.");
6244
6444
  if (Object.keys(value).length === 0) throw new Error("service app actions cannot be empty.");
6245
6445
  const actions = {};
6246
6446
  for (const [name, action] of Object.entries(value)) {
6247
6447
  if (!name.trim()) throw new Error("service app action name cannot be empty.");
6248
- if (!isRecord$4(action)) throw new Error(`service app action ${name} must be an object.`);
6448
+ if (!isRecord$3(action)) throw new Error(`service app action ${name} must be an object.`);
6249
6449
  const risk = readOptionalString$5(action, "risk");
6250
6450
  if (risk !== void 0 && !SERVICE_ACTION_RISKS.has(risk)) throw new Error(`service app action ${name} has invalid risk.`);
6251
6451
  const inputSchema = action.inputSchema;
6252
- if (inputSchema !== void 0 && !isRecord$4(inputSchema)) throw new Error(`service app action ${name} inputSchema must be an object.`);
6452
+ if (inputSchema !== void 0 && !isRecord$3(inputSchema)) throw new Error(`service app action ${name} inputSchema must be an object.`);
6253
6453
  actions[name] = {
6254
6454
  risk,
6255
6455
  title: readOptionalString$5(action, "title"),
@@ -6277,7 +6477,7 @@ function readStringArray(value, key) {
6277
6477
  if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) throw new Error(`service app ${key} must be a string array.`);
6278
6478
  return value;
6279
6479
  }
6280
- function isRecord$4(value) {
6480
+ function isRecord$3(value) {
6281
6481
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
6282
6482
  }
6283
6483
  //#endregion
@@ -6602,11 +6802,11 @@ var SessionRun = class {
6602
6802
  signal: controller.signal
6603
6803
  };
6604
6804
  };
6605
- abortRun = (runId) => {
6805
+ abortRun = (runId, reason) => {
6606
6806
  if (!this.activeRunId || !this.activeRunController) return false;
6607
6807
  if (runId && this.activeRunId !== runId) return false;
6608
6808
  const wasRunning = this.isRunning();
6609
- this.activeRunController.abort();
6809
+ this.activeRunController.abort(reason);
6610
6810
  this.activeRunController = null;
6611
6811
  this.activeRunId = null;
6612
6812
  this.emitStatusChangeIfNeeded(wasRunning);
@@ -6615,7 +6815,11 @@ var SessionRun = class {
6615
6815
  isRunning = () => this.activeRunId !== null;
6616
6816
  dispose = () => {
6617
6817
  const wasRunning = this.isRunning();
6618
- this.activeRunController?.abort();
6818
+ this.activeRunController?.abort({
6819
+ code: "abort-error",
6820
+ message: "Session run owner was disposed; the current run was cancelled.",
6821
+ details: { source: "session-run-manager" }
6822
+ });
6619
6823
  this.activeRunController = null;
6620
6824
  this.activeRunId = null;
6621
6825
  this.emitStatusChangeIfNeeded(wasRunning);
@@ -6700,7 +6904,7 @@ function parseFrontmatterBlock(raw) {
6700
6904
  function parseYamlFrontmatter(raw) {
6701
6905
  try {
6702
6906
  const parsed = parse(raw);
6703
- return isRecord$3(parsed) ? parsed : {};
6907
+ return isRecord$2(parsed) ? parsed : {};
6704
6908
  } catch (error) {
6705
6909
  const message = error instanceof Error ? error.message : String(error);
6706
6910
  throw new Error(`Invalid SKILL.md frontmatter: ${message}`);
@@ -6712,7 +6916,7 @@ function readString$3(record, ...names) {
6712
6916
  }
6713
6917
  function readLocalizedTextMap(record, ...names) {
6714
6918
  const value = readValue(record, names);
6715
- if (!isRecord$3(value)) return;
6919
+ if (!isRecord$2(value)) return;
6716
6920
  const localized = Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string" && entry[1].trim().length > 0).map(([locale, text]) => [normalizeLocaleTag(locale), text.trim()]));
6717
6921
  return Object.keys(localized).length > 0 ? localized : void 0;
6718
6922
  }
@@ -6736,7 +6940,7 @@ function normalizeFrontmatterKey(raw) {
6736
6940
  function normalizeLocaleTag(raw) {
6737
6941
  return raw.trim().toLowerCase();
6738
6942
  }
6739
- function isRecord$3(value) {
6943
+ function isRecord$2(value) {
6740
6944
  return typeof value === "object" && value !== null && !Array.isArray(value);
6741
6945
  }
6742
6946
  //#endregion
@@ -6902,7 +7106,7 @@ var NcpAgentSessionMetadataStore = class {
6902
7106
  read = async (sessionId, activitySnapshot) => {
6903
7107
  try {
6904
7108
  const parsed = JSON.parse(await readFile(this.metadataPath(sessionId), "utf-8"));
6905
- if (!isRecord$11(parsed) || parsed._type !== "metadata" || !isRecord$11(parsed.metadata)) throw new Error(`invalid ncp agent session metadata sidecar: ${sessionId}`);
7109
+ if (!isRecord$10(parsed) || parsed._type !== "metadata" || !isRecord$10(parsed.metadata)) throw new Error(`invalid ncp agent session metadata sidecar: ${sessionId}`);
6906
7110
  const createdAt = toIsoString(parsed.created_at, activitySnapshot.createdAt);
6907
7111
  const agentId = normalizeNcpAgentId(typeof parsed.agent_id === "string" ? parsed.agent_id : void 0);
6908
7112
  return {
@@ -7003,11 +7207,11 @@ var NcpAgentSessionSummaryIndexStore = class {
7003
7207
  function serializeJournalEntry(entry) {
7004
7208
  const serialized = JSON.stringify(entry);
7005
7209
  if (!serialized) throw new Error("ncp agent session journal entry serialization produced empty output");
7006
- if (!isRecord$11(JSON.parse(serialized))) throw new Error("ncp agent session journal entry serialization produced a non-object entry");
7210
+ if (!isRecord$10(JSON.parse(serialized))) throw new Error("ncp agent session journal entry serialization produced a non-object entry");
7007
7211
  return serialized;
7008
7212
  }
7009
7213
  function attachJournalTimestamp(event, timestamp) {
7010
- if (!("payload" in event) || !isRecord$11(event.payload)) return event;
7214
+ if (!("payload" in event) || !isRecord$10(event.payload)) return event;
7011
7215
  return {
7012
7216
  ...event,
7013
7217
  payload: {
@@ -7231,15 +7435,15 @@ var NcpAgentSessionJournalStore = class {
7231
7435
  console.warn(`[ncp-agent-session-journal] skipped corrupted journal line ${index + 1}: ${error instanceof Error ? error.message : String(error)}`);
7232
7436
  continue;
7233
7437
  }
7234
- if (!isRecord$11(parsed)) continue;
7438
+ if (!isRecord$10(parsed)) continue;
7235
7439
  if (parsed._type === "metadata") {
7236
- metadata = isRecord$11(parsed.metadata) ? structuredClone(parsed.metadata) : {};
7440
+ metadata = isRecord$10(parsed.metadata) ? structuredClone(parsed.metadata) : {};
7237
7441
  agentId = normalizeNcpAgentId(typeof parsed.agent_id === "string" ? parsed.agent_id : void 0);
7238
7442
  createdAt = toIsoString(parsed.created_at, createdAt);
7239
7443
  updatedAt = toIsoString(parsed.updated_at, updatedAt);
7240
7444
  continue;
7241
7445
  }
7242
- if (parsed._type === "event" && isRecord$11(parsed.event)) {
7446
+ if (parsed._type === "event" && isRecord$10(parsed.event)) {
7243
7447
  const seq = Number(parsed.seq);
7244
7448
  nextSeq = Math.max(nextSeq, Number.isFinite(seq) ? Math.trunc(seq) + 1 : nextSeq);
7245
7449
  const eventTimestamp = toIsoString(parsed.timestamp, updatedAt);
@@ -8276,7 +8480,61 @@ var AgentRunModelInputBudgeter = class {
8276
8480
  };
8277
8481
  };
8278
8482
  //#endregion
8483
+ //#region src/utils/agent-onboarding-context.utils.ts
8484
+ const ALWAYS_SKIPPED_COMPACTED_BOOTSTRAP_FILES = new Set(["BOOT.MD", "BOOTSTRAP.MD"]);
8485
+ function normalizeBootstrapFilename(filename) {
8486
+ return filename.trim().toUpperCase();
8487
+ }
8488
+ function shouldSkipCompactedSessionBootstrapFile(filename, content) {
8489
+ const normalized = normalizeBootstrapFilename(filename);
8490
+ if (ALWAYS_SKIPPED_COMPACTED_BOOTSTRAP_FILES.has(normalized)) return true;
8491
+ if (normalized === "IDENTITY.MD") return /Fill this in during your first conversation/i.test(content);
8492
+ if (normalized === "USER.MD") return /Learn about the person you are helping/i.test(content);
8493
+ return false;
8494
+ }
8495
+ function stripCompactedSessionOnboardingSections(block) {
8496
+ const lines = block.split("\n");
8497
+ const output = [];
8498
+ let index = 0;
8499
+ while (index < lines.length) {
8500
+ const line = lines[index] ?? "";
8501
+ const heading = line.match(/^##\s+(.+?)\s*$/);
8502
+ if (!heading) {
8503
+ output.push(line);
8504
+ index += 1;
8505
+ continue;
8506
+ }
8507
+ const sectionLines = [line];
8508
+ index += 1;
8509
+ while (index < lines.length && !/^##\s+/.test(lines[index] ?? "")) {
8510
+ sectionLines.push(lines[index] ?? "");
8511
+ index += 1;
8512
+ }
8513
+ if (shouldSkipCompactedSessionBootstrapFile(heading[1] ?? "", sectionLines.join("\n"))) continue;
8514
+ output.push(...sectionLines);
8515
+ }
8516
+ return output.join("\n").trim();
8517
+ }
8518
+ //#endregion
8279
8519
  //#region src/services/agent-run-model-input-builder.service.ts
8520
+ function readSystemContent(messages) {
8521
+ return messages.filter((message) => message.role === "system").map((message) => message.content.trim()).filter(Boolean);
8522
+ }
8523
+ function partitionProjectedMessages(messages) {
8524
+ const compressedContextBlocks = [];
8525
+ const conversationMessages = [];
8526
+ for (const message of messages) {
8527
+ if (!isContextCompactionProjectionMessage(message)) {
8528
+ conversationMessages.push(message);
8529
+ continue;
8530
+ }
8531
+ compressedContextBlocks.push(...readSystemContent(ncpMessageToOpenAiMessages(message)));
8532
+ }
8533
+ return {
8534
+ compressedContextBlocks,
8535
+ conversationMessages
8536
+ };
8537
+ }
8280
8538
  var AgentRunModelInputBuilder = class {
8281
8539
  constructor(messageProjector, modelInputBudgeter, assetStore = null) {
8282
8540
  this.messageProjector = messageProjector;
@@ -8284,15 +8542,17 @@ var AgentRunModelInputBuilder = class {
8284
8542
  this.assetStore = assetStore;
8285
8543
  }
8286
8544
  build = async (request) => {
8287
- const contextContent = request.contextBlocks.map((block) => block.trim()).filter(Boolean).join("\n\n");
8545
+ const { compressedContextBlocks, conversationMessages: projectedConversationMessages } = partitionProjectedMessages(this.messageProjector.project({
8546
+ sessionId: request.sessionId,
8547
+ messages: request.messages
8548
+ }));
8549
+ const contextBlocks = compressedContextBlocks.length > 0 ? request.contextBlocks.map(stripCompactedSessionOnboardingSections) : request.contextBlocks;
8550
+ const contextContent = [...compressedContextBlocks, ...contextBlocks].map((block) => block.trim()).filter(Boolean).join("\n\n");
8288
8551
  const contextMessages = contextContent ? [{
8289
8552
  role: "system",
8290
8553
  content: contextContent
8291
8554
  }] : [];
8292
- const conversationMessages = this.messageProjector.project({
8293
- sessionId: request.sessionId,
8294
- messages: request.messages
8295
- }).flatMap((message) => ncpMessageToOpenAiMessages(message, { assetStore: this.assetStore }));
8555
+ const conversationMessages = projectedConversationMessages.flatMap((message) => ncpMessageToOpenAiMessages(message, { assetStore: this.assetStore }));
8296
8556
  const pruned = await this.modelInputBudgeter.prune({
8297
8557
  spec: request.spec,
8298
8558
  messages: [...contextMessages, ...conversationMessages]
@@ -8349,6 +8609,7 @@ var NcpAgentRuntimeWrapper = class {
8349
8609
  return this.runtime;
8350
8610
  };
8351
8611
  toMessageSentEvents = (messages, spec, sessionId) => messages.map((message) => ({
8612
+ occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
8352
8613
  type: NcpEventType.MessageSent,
8353
8614
  payload: {
8354
8615
  sessionId,
@@ -8405,10 +8666,11 @@ var AgentRunRuntimeContribution = class {
8405
8666
  createRuntime: () => new DefaultNcpAgentRuntime({
8406
8667
  llmApi: new ProviderManagerNcpLLMApi(this.kernel.llmProviders),
8407
8668
  modelInputBuilder: this.modelInputBuilder,
8408
- runPreflight: async ({ spec, sessionRun }) => {
8669
+ runPreflight: async ({ contextBlocks, spec, sessionRun }) => {
8409
8670
  const session = await this.kernel.sessionManager.getAgentRunSession(sessionRun.sessionId);
8410
8671
  return await this.kernel.contextCompactionManager.runPreflight({
8411
8672
  agentId: spec.agentId,
8673
+ contextBlocks,
8412
8674
  messages: sessionRun.getSnapshot().messages,
8413
8675
  metadata: session.metadata,
8414
8676
  sessionId: sessionRun.sessionId
@@ -8453,11 +8715,13 @@ var AgentBootstrapContextProvider = class {
8453
8715
  provide = async (request) => {
8454
8716
  const { contextConfig, projectContext, runContext } = await this.context.resolve(request);
8455
8717
  const budget = this.createReadBudget(contextConfig.bootstrap);
8718
+ const compactedSession = this.hasCompressedContext(runContext.sessionMetadata);
8456
8719
  const agentBootstrapRoot = projectContext.projectBootstrapRoot ?? projectContext.effectiveWorkspace;
8457
8720
  const projectBootstrap = this.loadBootstrapFiles({
8458
8721
  root: agentBootstrapRoot,
8459
8722
  config: contextConfig.bootstrap,
8460
8723
  sessionKey: runContext.sessionKey,
8724
+ compactedSession,
8461
8725
  budget
8462
8726
  });
8463
8727
  const hasDistinctHostWorkspace = projectContext.hostWorkspace !== agentBootstrapRoot;
@@ -8465,6 +8729,7 @@ var AgentBootstrapContextProvider = class {
8465
8729
  root: projectContext.hostWorkspace,
8466
8730
  config: contextConfig.bootstrap,
8467
8731
  sessionKey: runContext.sessionKey,
8732
+ compactedSession,
8468
8733
  budget
8469
8734
  }) : "";
8470
8735
  const hasSoulFile = /##\s+SOUL\.md\b/i.test(`${projectBootstrap}\n${workspaceBootstrap}`);
@@ -8498,14 +8763,15 @@ var AgentBootstrapContextProvider = class {
8498
8763
  return lines.join("\n");
8499
8764
  };
8500
8765
  loadBootstrapFiles = (params) => {
8501
- const { budget, config, root, sessionKey } = params;
8766
+ const { budget, compactedSession, config, root, sessionKey } = params;
8502
8767
  const parts = [];
8503
- const fileList = this.selectBootstrapFiles(config, sessionKey);
8768
+ const fileList = this.selectBootstrapFiles(config, sessionKey, compactedSession);
8504
8769
  for (const filename of fileList) {
8505
8770
  const filePath = join(root, filename);
8506
8771
  if (!existsSync(filePath)) continue;
8507
8772
  const raw = readFileSync(filePath, "utf-8").trim();
8508
8773
  if (!raw) continue;
8774
+ if (compactedSession && shouldSkipCompactedSessionBootstrapFile(filename, raw)) continue;
8509
8775
  const perFileLimit = config.perFileChars > 0 ? config.perFileChars : raw.length;
8510
8776
  const allowed = Math.min(perFileLimit, budget.remaining);
8511
8777
  if (allowed <= 0) break;
@@ -8517,11 +8783,13 @@ var AgentBootstrapContextProvider = class {
8517
8783
  return parts.join("\n\n");
8518
8784
  };
8519
8785
  createReadBudget = (config) => ({ remaining: config.totalChars > 0 ? config.totalChars : Number.POSITIVE_INFINITY });
8520
- selectBootstrapFiles = (config, sessionKey) => {
8521
- if (!sessionKey) return config.files;
8786
+ selectBootstrapFiles = (config, sessionKey, compactedSession = false) => {
8787
+ if (!sessionKey) return this.filterCompactedSessionFiles(config.files, compactedSession);
8522
8788
  if (sessionKey.startsWith("cron:") || sessionKey.startsWith("subagent:")) return config.minimalFiles;
8523
- return config.files;
8789
+ return this.filterCompactedSessionFiles(config.files, compactedSession);
8524
8790
  };
8791
+ filterCompactedSessionFiles = (files, compactedSession) => compactedSession ? files.filter((filename) => !shouldSkipCompactedSessionBootstrapFile(filename, "")) : [...files];
8792
+ hasCompressedContext = (metadata) => Boolean(readCompressedContextCompactionCheckpoint(metadata?.[CONTEXT_COMPACTION_METADATA_KEY]));
8525
8793
  };
8526
8794
  //#endregion
8527
8795
  //#region src/contributions/context-provider/providers/current-session-context.provider.ts
@@ -8601,9 +8869,11 @@ const createToolCallStyleContextProvider = () => staticBlock([
8601
8869
  const createInlineInteractiveSurfaceContextProvider = () => staticBlock([
8602
8870
  "## Inline Interactive Surfaces",
8603
8871
  "Do not make every UI an inline card. Choose inline only when the intended result is a compact, immediately usable card or short interaction; use the side panel for normal Panel Apps, long reading, rich editing, file browsing, large tables, multi-page workflows, or sustained workspaces.",
8604
- "When you choose an inline card, call `show_content` with `type=\"panel_app\"` and `placement=\"inline\"` so the user can try it directly in the chat. Do not wait for the user to say \"inline\" or \"placement\" after you have already chosen the card form.",
8872
+ "Inline Panel App display is Markdown-only: in the final reply, output a `nextclaw-inline` fenced JSON block so the display remains message content.",
8873
+ "`show_panel_app` is side-panel only. Never call `show_panel_app` for inline display, including when the user asks which Panel Apps are suitable for inline display or says \"show/display them inline\".",
8874
+ "For ordinary local HTML files or page prototypes, call `show_file` with `path` and `viewer=\"rendered\"`; use `viewer=\"source\"` when the user needs to inspect source text. Markdown file links open source by default; append `?viewer=rendered` only when the link itself should open the rendered HTML view. Do not convert a plain HTML file into a Panel App just to preview it.",
8605
8875
  "A Panel Card must be designed card-first: prefer a landscape composition where width carries the main information and the card is wider than it is tall; collapse to one column only in narrow containers. Core value must be visible in the first 220-420px, with no horizontal scrolling, no reliance on document-level internal scrolling, compact controls, at most one primary action, clear loading/empty/error states, and an obvious expand path for details.",
8606
- "Typical Panel Card fits: weather cards, calculators, timers, checklists, pickers, compact forms, previews, and small dashboards. If the UI needs more space than a card, choose `placement=\"side_panel\"` instead. Inline hosts may pass `nextclawDisplayMode=card` and `nextclawPlacement=inline`; use those hints to render a compact card layout instead of a full page."
8876
+ "Typical Panel Card fits: weather cards, calculators, timers, checklists, pickers, compact forms, previews, and small dashboards. If the UI needs more space than a card, use the side panel instead. Inline hosts may pass `nextclawDisplayMode=card` and `nextclawPlacement=inline`; use those hints to render a compact card layout instead of a full page."
8607
8877
  ]);
8608
8878
  const createChatComposerTokensContextProvider = () => staticBlock([
8609
8879
  "## Chat Composer Tokens",
@@ -8758,7 +9028,7 @@ var ProjectContextProvider = class {
8758
9028
  //#endregion
8759
9029
  //#region src/contributions/context-provider/providers/reply-format-context.provider.ts
8760
9030
  var ReplyFormatContextProvider = class {
8761
- provide = (_request) => ["## Reply Formatting Contract\nGoal: local openable files mentioned in user-visible replies must be clickable.\nAllowed form: use Markdown links only, with a plain text label and an openable href: [MEMORY.md](MEMORY.md), [file](packages/example/file.ts), [notes.md](/Users/example/Documents/notes.md).\nPath choice: use project-relative hrefs for files under the active/session project root, and absolute hrefs for local files outside it.\nForbidden forms: bare file names or paths, inline-code file names, bold-only file names, code-styled link labels, code blocks for file references, and unlinked comma-separated file lists.\nExamples: bad `MEMORY.md` -> good [MEMORY.md](MEMORY.md); bad `memory/` -> good [memory/](memory/); bad `2026-03-07.md` / `feishu-notes.md` -> good [2026-03-07.md](memory/2026-03-07.md) / [feishu-notes.md](memory/feishu-notes.md).\nSelf-check before sending: scan the final visible reply for local file names or paths. If every concrete file cannot be linked, remove the exact names and summarize instead."];
9031
+ provide = (_request) => ["## Reply Formatting Contract\nGoal: openable files in user-visible replies must be clickable, and inert inline display declarations are only for content that should appear as part of the reply.\nFile links: use Markdown links only, with a plain text label and an openable href: [MEMORY.md](MEMORY.md), [file](packages/example/file.ts), [notes.md](/Users/example/Documents/notes.md). Use project-relative hrefs for files under the active/session project root, and absolute hrefs for local files outside it. File links open source by default; use a viewer query such as [preview.html](preview.html?viewer=rendered) only when the link should open the rendered HTML view.\nInline display: when the final reply should include a non-clickable inline display placeholder, output a fenced `nextclaw-inline` JSON block:\n```nextclaw-inline\n{\"target\":{\"type\":\"panel_app\",\"payload\":{\"appId\":\"timer\"}},\"title\":\"Timer\"}\n```\nSupported targets are `panel_app`, `json`, `file`, and `url`. Prefer `panel_app` for inline Panel App display; use `file` and `url` only as non-clickable placeholders when a clickable link is not intended; use `json` for inert JSON snapshots.\nIt is display-only: no opening, executing, or tool action. Never call `show_panel_app` for inline display; `show_panel_app` is only for immediately opening a Panel App outside the final reply in the side panel. Use Markdown links for clickable resources and show_file/show_url/show_panel_app tools only when you want the UI to immediately show or run content outside the final reply.\nForbidden forms: bare file names or paths, inline-code file names, bold-only file names, code-styled link labels, code blocks for file references, action semantics inside `nextclaw-inline`, tool calls for inline display, and unlinked comma-separated file lists.\nExamples: bad `MEMORY.md` -> good [MEMORY.md](MEMORY.md); bad `memory/` -> good [memory/](memory/); bad `2026-03-07.md` / `feishu-notes.md` -> good [2026-03-07.md](memory/2026-03-07.md) / [feishu-notes.md](memory/feishu-notes.md).\nSelf-check before sending: scan the final visible reply for local file names or paths. If every concrete file cannot be linked or intentionally represented by `nextclaw-inline`, remove the exact names and summarize instead."];
8762
9032
  };
8763
9033
  //#endregion
8764
9034
  //#region src/contributions/context-provider/providers/skills-context.provider.ts
@@ -9115,6 +9385,7 @@ var ContextWindowContribution = class {
9115
9385
  if (this.lastPublishedSignatureBySession.get(sessionId) === signature) return;
9116
9386
  this.lastPublishedSignatureBySession.set(sessionId, signature);
9117
9387
  this.kernel.eventBus.emit(eventKeys.ncpEvent, {
9388
+ occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
9118
9389
  type: NcpEventType.ContextWindowUpdated,
9119
9390
  payload: {
9120
9391
  contextWindow,
@@ -9314,6 +9585,10 @@ var CoreToolProvider = class {
9314
9585
  new WriteFileTool(allowedDir),
9315
9586
  new EditFileTool(allowedDir),
9316
9587
  new ListDirTool(allowedDir),
9588
+ new ViewImageTool({
9589
+ allowedDir,
9590
+ workingDir: workspace
9591
+ }),
9317
9592
  execTool,
9318
9593
  new WebSearchTool(searchConfig),
9319
9594
  new WebFetchTool(),
@@ -9577,7 +9852,7 @@ var SessionRequestTool = class {
9577
9852
  };
9578
9853
  //#endregion
9579
9854
  //#region src/tools/session-search.tools.ts
9580
- function isRecord$2(value) {
9855
+ function isRecord$1(value) {
9581
9856
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
9582
9857
  }
9583
9858
  function readOptionalInteger(value) {
@@ -9627,7 +9902,7 @@ var SessionSearchTool = class {
9627
9902
  return issues;
9628
9903
  };
9629
9904
  execute = async (args) => {
9630
- if (!isRecord$2(args)) throw new Error("session_search requires an object argument.");
9905
+ if (!isRecord$1(args)) throw new Error("session_search requires an object argument.");
9631
9906
  const issues = this.validateArgs(args);
9632
9907
  if (issues.length > 0) throw new Error(issues.join(" "));
9633
9908
  return this.queryService.search({
@@ -9811,90 +10086,89 @@ var SessionToolProvider = class {
9811
10086
  };
9812
10087
  //#endregion
9813
10088
  //#region src/tools/show-content.tools.ts
9814
- const SHOW_CONTENT_TOOL_NAME = "show_content";
9815
- function isRecord$1(value) {
9816
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
9817
- }
10089
+ const FILE_PURPOSES = [
10090
+ "read",
10091
+ "preview",
10092
+ "edit"
10093
+ ];
10094
+ const URL_PURPOSES = ["read", "preview"];
10095
+ const PANEL_APP_PURPOSES = ["preview", "interact"];
10096
+ const FILE_VIEWERS = [
10097
+ "auto",
10098
+ "source",
10099
+ "rendered"
10100
+ ];
9818
10101
  function readRequiredString(value, key) {
9819
10102
  if (typeof value !== "string" || value.trim().length === 0) throw new Error(`${key} must be a non-empty string.`);
9820
10103
  return value.trim();
9821
10104
  }
9822
10105
  function readOptionalString$1(value) {
9823
10106
  if (typeof value !== "string") return;
9824
- const trimmed = value.trim();
9825
- return trimmed.length > 0 ? trimmed : void 0;
10107
+ return value.trim() || void 0;
9826
10108
  }
9827
10109
  function readOptionalPositiveInteger(value, key) {
9828
10110
  if (typeof value === "undefined") return;
9829
10111
  if (typeof value !== "number" || !Number.isInteger(value) || value < 1) throw new Error(`${key} must be a positive integer.`);
9830
10112
  return value;
9831
10113
  }
9832
- function readPurpose(value) {
9833
- const normalized = readOptionalString$1(value);
9834
- if (!normalized) return;
9835
- if (normalized === "read" || normalized === "preview" || normalized === "edit" || normalized === "interact") return normalized;
9836
- throw new Error("purpose must be \"read\", \"preview\", \"edit\", or \"interact\".");
9837
- }
9838
- function readPlacement(value) {
10114
+ function readOptionalEnum(value, key, allowed) {
9839
10115
  const normalized = readOptionalString$1(value);
9840
10116
  if (!normalized) return;
9841
- if (normalized === "inline" || normalized === "side_panel") return normalized;
9842
- throw new Error("placement must be \"inline\" or \"side_panel\".");
9843
- }
9844
- function readPayload(params) {
9845
- if (!isRecord$1(params.payload)) throw new Error("payload must be an object.");
9846
- return params.payload;
10117
+ if (allowed.includes(normalized)) return normalized;
10118
+ const expected = allowed.map((item) => `"${item}"`).join(", ");
10119
+ throw new Error(`${key} must be ${expected}.`);
9847
10120
  }
9848
10121
  function readUrl(value) {
9849
- const url = readRequiredString(value, "payload.url");
10122
+ const url = readRequiredString(value, "url");
9850
10123
  let parsed;
9851
10124
  try {
9852
10125
  parsed = new URL(url);
9853
10126
  } catch {
9854
- throw new Error("payload.url must be a valid URL.");
10127
+ throw new Error("url must be a valid URL.");
9855
10128
  }
9856
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error("payload.url must use http or https.");
10129
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error("url must use http or https.");
9857
10130
  return parsed.toString();
9858
10131
  }
9859
- function normalizeShowContentArgs(args) {
10132
+ function readCommonRequestFields(params, allowedPurposes) {
10133
+ return {
10134
+ title: readOptionalString$1(params.title),
10135
+ purpose: readOptionalEnum(params.purpose, "purpose", allowedPurposes)
10136
+ };
10137
+ }
10138
+ function normalizeShowFileArgs(args) {
9860
10139
  const params = normalizeToolParams(args);
9861
- const type = readRequiredString(params.type, "type");
9862
- const payload = readPayload(params);
9863
- const title = readOptionalString$1(params.title);
9864
- const purpose = readPurpose(params.purpose);
9865
- const placement = readPlacement(params.placement);
9866
- if (type === "file") return {
10140
+ return {
9867
10141
  target: {
9868
- type,
10142
+ type: "file",
9869
10143
  payload: {
9870
- path: readRequiredString(payload.path, "payload.path"),
9871
- line: readOptionalPositiveInteger(payload.line, "payload.line"),
9872
- column: readOptionalPositiveInteger(payload.column, "payload.column")
10144
+ path: readRequiredString(params.path, "path"),
10145
+ line: readOptionalPositiveInteger(params.line, "line"),
10146
+ column: readOptionalPositiveInteger(params.column, "column"),
10147
+ viewer: readOptionalEnum(params.viewer, "viewer", FILE_VIEWERS) ?? "source"
9873
10148
  }
9874
10149
  },
9875
- title,
9876
- purpose,
9877
- placement
10150
+ ...readCommonRequestFields(params, FILE_PURPOSES)
9878
10151
  };
9879
- if (type === "url") return {
10152
+ }
10153
+ function normalizeShowUrlArgs(args) {
10154
+ const params = normalizeToolParams(args);
10155
+ return {
9880
10156
  target: {
9881
- type,
9882
- payload: { url: readUrl(payload.url) }
10157
+ type: "url",
10158
+ payload: { url: readUrl(params.url) }
9883
10159
  },
9884
- title,
9885
- purpose,
9886
- placement
10160
+ ...readCommonRequestFields(params, URL_PURPOSES)
9887
10161
  };
9888
- if (type === "panel_app") return {
10162
+ }
10163
+ function normalizeShowPanelAppArgs(args) {
10164
+ const params = normalizeToolParams(args);
10165
+ return {
9889
10166
  target: {
9890
- type,
9891
- payload: { appId: readRequiredString(payload.appId, "payload.appId") }
10167
+ type: "panel_app",
10168
+ payload: { appId: readRequiredString(params.appId, "appId") }
9892
10169
  },
9893
- title,
9894
- purpose,
9895
- placement
10170
+ ...readCommonRequestFields(params, PANEL_APP_PURPOSES)
9896
10171
  };
9897
- throw new Error("type must be \"file\", \"url\", or \"panel_app\".");
9898
10172
  }
9899
10173
  function summarizeTarget(target) {
9900
10174
  if (target.type === "file") return target.payload.path;
@@ -9909,63 +10183,25 @@ function createShowContentEventPayload(request, context) {
9909
10183
  target: request.target,
9910
10184
  title: request.title,
9911
10185
  purpose: request.purpose,
9912
- placement: request.placement
9913
- };
9914
- }
9915
- var ShowContentTool = class {
9916
- name = SHOW_CONTENT_TOOL_NAME;
9917
- description = [
9918
- "Show file, URL, or panel app content in the current chat UI.",
9919
- "placement=\"inline\" embeds a lightweight panel_app as an interactive chat card so the user can try it directly in the conversation.",
9920
- "placement=\"side_panel\" opens content in the right side panel for larger reading, editing, or sustained workflows.",
9921
- "Choose the placement yourself: after creating a lightweight Panel App such as a weather card, calculator, timer, checklist, picker, form, preview, or small dashboard, call this tool with placement=\"inline\" without waiting for the user to ask to see it.",
9922
- "Omit placement only when preserving the existing default side panel behavior is intentional."
9923
- ].join(" ");
9924
- parameters = {
9925
- type: "object",
9926
- properties: {
9927
- type: {
9928
- type: "string",
9929
- enum: [
9930
- "file",
9931
- "url",
9932
- "panel_app"
9933
- ],
9934
- description: "Content target type."
9935
- },
9936
- title: {
9937
- type: "string",
9938
- description: "Optional title for the shown content."
9939
- },
9940
- purpose: {
9941
- type: "string",
9942
- enum: [
9943
- "read",
9944
- "preview",
9945
- "edit",
9946
- "interact"
9947
- ],
9948
- description: "Optional user intent for the content."
9949
- },
9950
- placement: {
9951
- type: "string",
9952
- enum: ["inline", "side_panel"],
9953
- description: "Optional display placement. \"inline\" embeds a lightweight panel app as an interactive chat card; \"side_panel\" opens it in the right panel for larger workflows. Choose the appropriate placement yourself; defaults to the existing side panel behavior when omitted."
9954
- },
9955
- payload: {
9956
- type: "object",
9957
- description: "Type-specific fields: file={path,line,column}; url={url}; panel_app={appId}.",
9958
- additionalProperties: true
9959
- }
9960
- },
9961
- required: ["type", "payload"],
9962
- additionalProperties: false
10186
+ placement: "side_panel"
9963
10187
  };
9964
- constructor(eventBus) {
10188
+ }
10189
+ var ShowContentDisplayTool = class {
10190
+ constructor(eventBus, spec) {
9965
10191
  this.eventBus = eventBus;
10192
+ this.spec = spec;
10193
+ }
10194
+ get name() {
10195
+ return this.spec.name;
10196
+ }
10197
+ get description() {
10198
+ return this.spec.description;
10199
+ }
10200
+ get parameters() {
10201
+ return this.spec.parameters;
9966
10202
  }
9967
10203
  execute = async (args, context) => {
9968
- const request = normalizeShowContentArgs(args);
10204
+ const request = this.spec.normalize(args);
9969
10205
  this.eventBus.emit(eventKeys.uiShowContent, createShowContentEventPayload(request, context), { source: "kernel" });
9970
10206
  return {
9971
10207
  ok: true,
@@ -9974,13 +10210,108 @@ var ShowContentTool = class {
9974
10210
  };
9975
10211
  };
9976
10212
  };
10213
+ const SHOW_CONTENT_TOOL_SPECS = [
10214
+ {
10215
+ name: "show_file",
10216
+ description: "Show a local file in the current chat UI. Defaults to source text. Use viewer=\"rendered\" for rendered HTML/page previews and viewer=\"source\" for source text.",
10217
+ parameters: {
10218
+ type: "object",
10219
+ properties: {
10220
+ path: {
10221
+ type: "string",
10222
+ description: "Local file path to show."
10223
+ },
10224
+ title: {
10225
+ type: "string",
10226
+ description: "Optional title for the shown content."
10227
+ },
10228
+ purpose: {
10229
+ type: "string",
10230
+ enum: FILE_PURPOSES,
10231
+ description: "Optional user intent."
10232
+ },
10233
+ line: {
10234
+ type: "integer",
10235
+ minimum: 1,
10236
+ description: "Optional 1-based line number."
10237
+ },
10238
+ column: {
10239
+ type: "integer",
10240
+ minimum: 1,
10241
+ description: "Optional 1-based column number."
10242
+ },
10243
+ viewer: {
10244
+ type: "string",
10245
+ enum: FILE_VIEWERS,
10246
+ description: "Optional file viewer mode."
10247
+ }
10248
+ },
10249
+ required: ["path"],
10250
+ additionalProperties: false
10251
+ },
10252
+ normalize: normalizeShowFileArgs
10253
+ },
10254
+ {
10255
+ name: "show_url",
10256
+ description: "Show an http or https URL in the current chat UI browser. Use this for local development servers such as Vite, Next.js, Storybook, or any running web app URL.",
10257
+ parameters: {
10258
+ type: "object",
10259
+ properties: {
10260
+ url: {
10261
+ type: "string",
10262
+ description: "HTTP or HTTPS URL to show."
10263
+ },
10264
+ title: {
10265
+ type: "string",
10266
+ description: "Optional title for the shown content."
10267
+ },
10268
+ purpose: {
10269
+ type: "string",
10270
+ enum: URL_PURPOSES,
10271
+ description: "Optional user intent."
10272
+ }
10273
+ },
10274
+ required: ["url"],
10275
+ additionalProperties: false
10276
+ },
10277
+ normalize: normalizeShowUrlArgs
10278
+ },
10279
+ {
10280
+ name: "show_panel_app",
10281
+ description: "Open a Panel App in the current chat UI as an immediate tool-driven preview. This tool is side-panel only. For inline Panel App display in a final reply, do not call this tool; output a Markdown nextclaw-inline fenced JSON block instead.",
10282
+ parameters: {
10283
+ type: "object",
10284
+ properties: {
10285
+ appId: {
10286
+ type: "string",
10287
+ description: "Installed Panel App id to show."
10288
+ },
10289
+ title: {
10290
+ type: "string",
10291
+ description: "Optional title for the shown content."
10292
+ },
10293
+ purpose: {
10294
+ type: "string",
10295
+ enum: PANEL_APP_PURPOSES,
10296
+ description: "Optional user intent."
10297
+ }
10298
+ },
10299
+ required: ["appId"],
10300
+ additionalProperties: false
10301
+ },
10302
+ normalize: normalizeShowPanelAppArgs
10303
+ }
10304
+ ];
10305
+ function createShowContentTools(eventBus) {
10306
+ return SHOW_CONTENT_TOOL_SPECS.map((spec) => new ShowContentDisplayTool(eventBus, spec));
10307
+ }
9977
10308
  //#endregion
9978
10309
  //#region src/contributions/tool-provider/providers/show-content-tool.provider.ts
9979
10310
  var ShowContentToolProvider = class {
9980
10311
  constructor(eventBus) {
9981
10312
  this.eventBus = eventBus;
9982
10313
  }
9983
- provide = () => [new ShowContentTool(this.eventBus)];
10314
+ provide = () => createShowContentTools(this.eventBus);
9984
10315
  };
9985
10316
  //#endregion
9986
10317
  //#region src/contributions/tool-provider/providers/structured-result-tool.provider.ts
@@ -10349,39 +10680,6 @@ async function dispatchChannelReplyRoute(params) {
10349
10680
  await route.channel.consumeNcpReply(input);
10350
10681
  }
10351
10682
  //#endregion
10352
- //#region src/utils/agent-run-metadata.utils.ts
10353
- const LEGACY_RUN_METADATA_KEYS = [
10354
- "account_id",
10355
- "agent_id",
10356
- "chat_id",
10357
- "preferred_model",
10358
- "project_root",
10359
- "runtime",
10360
- "sender_id",
10361
- "session_key",
10362
- "session_type"
10363
- ];
10364
- function stripLegacyRunMetadata(metadata) {
10365
- const nextMetadata = structuredClone(metadata);
10366
- for (const key of LEGACY_RUN_METADATA_KEYS) delete nextMetadata[key];
10367
- return nextMetadata;
10368
- }
10369
- function buildRunMetadata(params) {
10370
- const { message, metadata, route } = params;
10371
- return {
10372
- ...stripLegacyRunMetadata({
10373
- ...message.metadata ?? {},
10374
- ...metadata ?? {}
10375
- }),
10376
- channel: message.channel,
10377
- chatId: message.chatId,
10378
- accountId: route.accountId,
10379
- agentId: route.agentId,
10380
- sessionKey: route.sessionKey,
10381
- senderId: message.senderId
10382
- };
10383
- }
10384
- //#endregion
10385
10683
  //#region src/features/ncp-dispatch/services/gateway-inbound-processor.service.ts
10386
10684
  function formatUserFacingError(error, maxChars = 320) {
10387
10685
  const normalized = (error instanceof Error ? error.message || error.name : String(error ?? "Unknown error")).replace(/\s+/g, " ").trim();
@@ -10820,6 +11118,6 @@ function resolveLegacyEventType(message) {
10820
11118
  return `message.${role || "other"}`;
10821
11119
  }
10822
11120
  //#endregion
10823
- export { AccessManager, AgentManager, AgentRunClient, 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, PreferenceError, PreferenceManager, ProviderManagerNcpLLMApi, SERVICE_APP_MANIFEST_FILE_NAME, ServiceAppError, ServiceAppManager, SessionManager, SessionRequestManager, SkillManager, UpdateManifestReader, buildAgentRunSendPayload, buildContextCompactionModelInput, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildNextclawNcpRunContext, buildServiceActionId, createAgentRuntimeSessionRequestDispatcher, createAssetTools, createContextCompactionMessageId, createContextWindowSignature, createLlmUsageRecord, describeAgentRuntimeSessionTypes, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, getServiceActionName, getServiceAppManifestPath, getUnsignedUpdateManifest, hasLlmUsageTelemetry, isContextCompactionTimelineMessage, isContextWindowSnapshot, isPanelAppAgentCapability, isPanelAppError, isPreferenceError, isReplyCapableChannel, isServiceAppError, listExtensionChannelIds, listServiceAppManifestActions, mergeServiceAppRuntimeActions, normalizeAgentRuntimeSessionTypeIcon, normalizeLlmUsageModel, normalizeOptionalString, parseServiceAppManifest, parseSkillFrontmatter, readContextWindowEventSessionId, readLatestContextCompactionCheckpoint, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, readServiceAppManifest, resolveAgentRuntimeEntries, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolveSessionChannelContext, runGatewayInboundLoop, sanitizeLlmUsage, serializeUnsignedUpdateManifest, shouldRefreshContextWindowDuringStream, shouldRefreshContextWindowImmediately, stripSkillFrontmatter, syncSessionThinkingPreference, toNcpMessages, waitForAgentRuntimeSessionReply };
11121
+ export { AccessManager, AgentManager, AgentRunClient, AutomationManager, BuiltinNarpRuntimeProviderService, CONTEXT_COMPACTION_PROJECTION_KIND, CONTEXT_COMPACTION_PROJECTION_METADATA_KEY, 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, PreferenceError, PreferenceManager, ProviderManagerNcpLLMApi, SERVICE_APP_MANIFEST_FILE_NAME, ServiceAppError, ServiceAppManager, SessionManager, SessionRequestManager, SkillManager, UpdateManifestReader, buildAgentRunSendPayload, buildContextCompactionModelInput, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildNextclawNcpRunContext, buildServiceActionId, createAgentRuntimeSessionRequestDispatcher, createAssetTools, createContextCompactionMessageId, createContextWindowSignature, createLlmUsageRecord, describeAgentRuntimeSessionTypes, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, getServiceActionName, getServiceAppManifestPath, getUnsignedUpdateManifest, hasLlmUsageTelemetry, isContextCompactionProjectionMessage, isContextCompactionTimelineMessage, isContextWindowSnapshot, isPanelAppAgentCapability, isPanelAppError, isPreferenceError, isReplyCapableChannel, isServiceAppError, listExtensionChannelIds, listServiceAppManifestActions, mergeServiceAppRuntimeActions, normalizeAgentRuntimeSessionTypeIcon, normalizeLlmUsageModel, normalizeOptionalString, parseServiceAppManifest, parseSkillFrontmatter, readContextWindowEventSessionId, readLatestContextCompactionCheckpoint, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, readServiceAppManifest, resolveAgentRuntimeEntries, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolveSessionChannelContext, runGatewayInboundLoop, sanitizeLlmUsage, serializeUnsignedUpdateManifest, shouldRefreshContextWindowDuringStream, shouldRefreshContextWindowImmediately, stripSkillFrontmatter, syncSessionThinkingPreference, toNcpMessages, waitForAgentRuntimeSessionReply };
10824
11122
 
10825
11123
  //# sourceMappingURL=index.js.map