@nextclaw/kernel 0.6.10 → 0.6.11

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
@@ -2,15 +2,15 @@ import { AUTOMATIC_UPDATE_CHECK_INTERVAL_MS, getAutomaticUpdateCheckDelay, resol
2
2
  import { n as getUnsignedUpdateManifest, r as serializeUnsignedUpdateManifest, t as UpdateManifestReader } from "./update-manifest.types-C0qPrjGQ.js";
3
3
  import { createRequire } from "node:module";
4
4
  import { APP_NAME, AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, CLEAR_THINKING_TOKENS, CONTEXT_COMPACTION_METADATA_KEY, ConfigSchema, ContextCompactionService, ContextWindowBudgetService, CronService, CronTool, DEFAULT_PANELS_DIR, DEFAULT_SERVICE_APPS_DIR, DEFAULT_SESSION_SEARCH_LIMIT, DEFAULT_WORKSPACE_REPOSITORY_IDENTITY_RESOLVER, 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, safeFilename, sanitizeOutboundAssistantContent, saveConfig, summarizeSessionRequestTask, toExtensionConfigView, updateAgentProfile } from "@nextclaw/core";
5
- import { NcpEventType, normalizeAssistantText, sanitizeAssistantReplyTags } from "@nextclaw/ncp";
5
+ import { NCP_AI_EXECUTION_METADATA_KEY, NcpEventType, createUnavailableNcpAiExecutionMetadata, normalizeAssistantText, readNcpAiExecutionMetadata, sanitizeAssistantReplyTags } from "@nextclaw/ncp";
6
6
  import { LocalAssetStore, buildAssetContentPath, buildNcpUserContent, buildOpenAiFunctionTool, ncpMessageToOpenAiMessages, validateToolArgs } from "@nextclaw/ncp-agent-runtime";
7
7
  import { createHash, createHmac, randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
8
- import { CHAT_INLINE_TOKENS_METADATA_KEY, CHAT_SESSION_MATERIALIZATION_METADATA_KEY, CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND, CHAT_WORKSPACE_FILE_TOKEN_KIND, EventBus, Ingress, eventKeys, ingressKeys, isRuntimeDefaultModelValue, normalizeRuntimeModelSelectionMode } from "@nextclaw/shared";
8
+ import { CHAT_INLINE_TOKENS_METADATA_KEY, CHAT_SESSION_MATERIALIZATION_METADATA_KEY, CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND, CHAT_WORKSPACE_FILE_TOKEN_KIND, EventBus, Ingress, PANEL_APP_INLINE_HOST_CONTRACT, eventKeys, ingressKeys, isRuntimeDefaultModelValue, normalizeRuntimeModelSelectionMode, readInlineContentHeight } from "@nextclaw/shared";
9
9
  import { catchError, from, lastValueFrom, tap } from "rxjs";
10
10
  import { appendFileSync, chmodSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
11
11
  import { basename, delimiter, dirname, extname, isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
12
12
  import { execFileSync, spawn } from "node:child_process";
13
- import { access, appendFile, mkdir, open, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
13
+ import { access, appendFile, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
14
14
  import { fileURLToPath } from "node:url";
15
15
  import { BUILTIN_PROVIDER_PLUGINS } from "@nextclaw/runtime";
16
16
  import { McpRegistryService, McpServerLifecycleManager } from "@nextclaw/mcp";
@@ -658,6 +658,32 @@ const AGENT_RUN_EXECUTION_METADATA = {
658
658
  }
659
659
  }
660
660
  };
661
+ function readAgentRunStartedAt(event, fallback) {
662
+ if (event.type !== NcpEventType.RunStarted) return fallback;
663
+ return event.payload.startedAt ?? event.occurredAt ?? fallback;
664
+ }
665
+ function hasAiExecutionMetadata(event) {
666
+ return event.type === NcpEventType.RunMetadata && Boolean(readNcpAiExecutionMetadata(event.payload.metadata));
667
+ }
668
+ function createUnavailableAiExecutionMetadataEvent(params) {
669
+ const { sessionId, spec } = params;
670
+ return {
671
+ occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
672
+ type: NcpEventType.RunMetadata,
673
+ payload: {
674
+ runId: spec.runId,
675
+ sessionId,
676
+ correlationId: spec.correlationId,
677
+ metadata: { [NCP_AI_EXECUTION_METADATA_KEY]: createUnavailableNcpAiExecutionMetadata({
678
+ runId: spec.runId,
679
+ runtimeId: spec.runtimeId,
680
+ model: spec.model,
681
+ requestedModel: spec.requestedModel,
682
+ outcome: "failed"
683
+ }) }
684
+ }
685
+ };
686
+ }
661
687
  //#endregion
662
688
  //#region src/managers/agent-run-request.manager.ts
663
689
  function toAgentRunRequest(envelope) {
@@ -717,10 +743,6 @@ function readOptionalString$11(value) {
717
743
  if (typeof value !== "string") return;
718
744
  return value.trim() || void 0;
719
745
  }
720
- function readRunStartedAt(event, fallback) {
721
- if (event.type !== NcpEventType.RunStarted) return fallback;
722
- return event.payload.startedAt ?? event.occurredAt ?? fallback;
723
- }
724
746
  function createCompletedAssistantMessageEvent(params) {
725
747
  return {
726
748
  occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -781,12 +803,15 @@ function resolveRunSpec(params) {
781
803
  const { agentManager, configManager, request, runId, session } = params;
782
804
  const modelSource = request.model !== void 0 ? "request" : session.model !== void 0 ? "session" : "default";
783
805
  const model = request.model ?? session.model ?? configManager.getDefaultModel();
806
+ const agentId = request.agentId ?? session.agentId ?? agentManager.getDefaultAgentId();
784
807
  return {
785
808
  modelSource,
786
809
  spec: {
787
810
  runId,
788
- agentId: request.agentId ?? session.agentId ?? agentManager.getDefaultAgentId(),
811
+ runtimeId: session.agentRuntimeId,
812
+ agentId,
789
813
  model,
814
+ requestedModel: request.model ?? null,
790
815
  maxTokens: request.maxTokens ?? configManager.getModelMaxTokens(model),
791
816
  thinkingEffort: request.thinkingEffort ?? session.thinkingEffort ?? null,
792
817
  correlationId: request.correlationId
@@ -921,19 +946,37 @@ var AgentRunRequestManager = class {
921
946
  });
922
947
  const contextBlocks = await this.contextProviderManager.buildContext(providerRequest);
923
948
  const tools = await this.toolProviderManager.buildTools(providerRequest);
949
+ this.startRuntimeRun({
950
+ options: {
951
+ contextBlocks,
952
+ session,
953
+ sessionRun,
954
+ signal: activeRun.signal,
955
+ tools
956
+ },
957
+ requestRunStartedAt,
958
+ runtime,
959
+ spec
960
+ });
961
+ return {
962
+ sessionId: session.sessionId,
963
+ userMessageId: message.id,
964
+ runId: spec.runId,
965
+ correlationId: request.correlationId
966
+ };
967
+ };
968
+ startRuntimeRun = (params) => {
969
+ const { options, requestRunStartedAt, runtime, spec } = params;
970
+ const { session, sessionRun } = options;
924
971
  let messageCompletedSeen = false;
972
+ let executionMetadataSeen = false;
925
973
  let runtimeFailed = false;
926
974
  let runStartedAt = requestRunStartedAt;
927
- lastValueFrom(from(runtime.run(spec, {
928
- contextBlocks,
929
- session,
930
- sessionRun,
931
- signal: activeRun.signal,
932
- tools
933
- })).pipe(tap((event) => {
975
+ lastValueFrom(from(runtime.run(spec, options)).pipe(tap((event) => {
934
976
  const eventsToPublish = [];
935
977
  if (event.type === NcpEventType.RunError) runtimeFailed = true;
936
- runStartedAt = readRunStartedAt(event, runStartedAt);
978
+ if (hasAiExecutionMetadata(event)) executionMetadataSeen = true;
979
+ runStartedAt = readAgentRunStartedAt(event, runStartedAt);
937
980
  if (event.type === NcpEventType.MessageCompleted) messageCompletedSeen = true;
938
981
  if (event.type === NcpEventType.RunFinished && !messageCompletedSeen) {
939
982
  const message = findCompletedAssistantMessage(sessionRun.getSnapshot().messages, event.payload.messageId);
@@ -952,6 +995,17 @@ var AgentRunRequestManager = class {
952
995
  });
953
996
  }), catchError(async (error) => {
954
997
  runtimeFailed = true;
998
+ if (!executionMetadataSeen) {
999
+ const metadataEvent = createUnavailableAiExecutionMetadataEvent({
1000
+ spec,
1001
+ sessionId: session.sessionId
1002
+ });
1003
+ await sessionRun.applyEvents([metadataEvent]);
1004
+ this.eventBus.emit(eventKeys.ncpEvent, metadataEvent, {
1005
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
1006
+ source: "agent-run-request"
1007
+ });
1008
+ }
955
1009
  const event = createSyntheticRunErrorEvent({
956
1010
  error,
957
1011
  runId: spec.runId,
@@ -972,12 +1026,6 @@ var AgentRunRequestManager = class {
972
1026
  sessionRun
973
1027
  }).catch(() => void 0);
974
1028
  });
975
- return {
976
- sessionId: session.sessionId,
977
- userMessageId: message.id,
978
- runId: spec.runId,
979
- correlationId: request.correlationId
980
- };
981
1029
  };
982
1030
  getOrCreateSessionForRequest = async (request) => {
983
1031
  const sessionMaterialization = readSessionMaterialization(request.metadata ?? {});
@@ -1031,14 +1079,24 @@ function normalizeAgentRuntimeSessionTypeIcon(value) {
1031
1079
  ...alt ? { alt } : {}
1032
1080
  };
1033
1081
  }
1034
- const BUILTIN_RUNTIME_PRESENTATION = { hermes: {
1035
- label: "Hermes",
1036
- icon: {
1037
- kind: "image",
1038
- src: "app://runtime-icons/hermes-agent.png",
1039
- alt: "Hermes"
1082
+ const BUILTIN_RUNTIME_PRESENTATION = {
1083
+ hermes: {
1084
+ label: "Hermes",
1085
+ icon: {
1086
+ kind: "image",
1087
+ src: "app://runtime-icons/hermes-agent.png",
1088
+ alt: "Hermes"
1089
+ }
1090
+ },
1091
+ opencode: {
1092
+ label: "OpenCode",
1093
+ icon: {
1094
+ kind: "image",
1095
+ src: "app://runtime-icons/opencode.svg",
1096
+ alt: "OpenCode"
1097
+ }
1040
1098
  }
1041
- } };
1099
+ };
1042
1100
  function readString$7(value) {
1043
1101
  if (typeof value !== "string") return;
1044
1102
  return value.trim() || void 0;
@@ -3665,16 +3723,24 @@ function upsertNcpAgentSessionSummaryEvent(params) {
3665
3723
  status: "idle"
3666
3724
  };
3667
3725
  }
3668
- async function replayNcpAgentSessionEvents(events) {
3726
+ async function replayNcpAgentSessionEvents(events, seedMessages = []) {
3669
3727
  const stateManager = new DefaultNcpAgentConversationStateManager();
3670
- const knownMessageIds = /* @__PURE__ */ new Set();
3728
+ if (seedMessages.length > 0) stateManager.hydrate({
3729
+ sessionId: seedMessages[0]?.sessionId ?? "",
3730
+ messages: seedMessages
3731
+ });
3732
+ const knownMessageIds = new Set(seedMessages.map((message) => message.id));
3671
3733
  const toolResultsByCallId = /* @__PURE__ */ new Map();
3672
3734
  for (const event of events) {
3673
3735
  if (isJournalOnlyEvent(event)) continue;
3674
3736
  const replayEvent = createReplayEvent(event, toolResultsByCallId);
3675
- const bootstrapEvent = createReplayStreamingBootstrapEvent(replayEvent, knownMessageIds);
3676
- if (bootstrapEvent) await stateManager.dispatch(bootstrapEvent);
3677
- rememberReplayMessageId(replayEvent, knownMessageIds);
3737
+ const bootstrap = createReplayStreamingBootstrapEvent(replayEvent, knownMessageIds);
3738
+ if (bootstrap) {
3739
+ knownMessageIds.add(bootstrap.messageId);
3740
+ await stateManager.dispatch(bootstrap.event);
3741
+ }
3742
+ const replayMessageId = readReplayMessageId(replayEvent);
3743
+ if (replayMessageId) knownMessageIds.add(replayMessageId);
3678
3744
  await stateManager.dispatch(replayEvent);
3679
3745
  if (replayEvent.type === NcpEventType.MessageToolCallResult) toolResultsByCallId.set(replayEvent.payload.toolCallId, replayEvent.payload);
3680
3746
  }
@@ -3731,26 +3797,27 @@ function readLegacyContextCompactionMessageId(message) {
3731
3797
  function createReplayStreamingBootstrapEvent(event, knownMessageIds) {
3732
3798
  const messageId = readStreamingMessageId(event);
3733
3799
  if (!messageId || knownMessageIds.has(messageId)) return null;
3734
- knownMessageIds.add(messageId);
3735
3800
  return {
3736
- occurredAt: event.occurredAt,
3737
- type: NcpEventType.MessageSent,
3738
- payload: {
3739
- sessionId: readEventSessionId$2(event),
3740
- message: {
3741
- id: messageId,
3801
+ messageId,
3802
+ event: {
3803
+ occurredAt: event.occurredAt,
3804
+ type: NcpEventType.MessageSent,
3805
+ payload: {
3742
3806
  sessionId: readEventSessionId$2(event),
3743
- role: "assistant",
3744
- status: "streaming",
3745
- parts: [],
3746
- timestamp: readReplayPayloadTimestamp(event) ?? (/* @__PURE__ */ new Date()).toISOString()
3807
+ message: {
3808
+ id: messageId,
3809
+ sessionId: readEventSessionId$2(event),
3810
+ role: "assistant",
3811
+ status: "streaming",
3812
+ parts: [],
3813
+ timestamp: readReplayPayloadTimestamp(event) ?? (/* @__PURE__ */ new Date()).toISOString()
3814
+ }
3747
3815
  }
3748
3816
  }
3749
3817
  };
3750
3818
  }
3751
- function rememberReplayMessageId(event, knownMessageIds) {
3752
- const message = readMessageFromSummaryEvent(event);
3753
- if (message?.id) knownMessageIds.add(message.id);
3819
+ function readReplayMessageId(event) {
3820
+ return readMessageFromSummaryEvent(event)?.id ?? null;
3754
3821
  }
3755
3822
  function readEventSessionId$2(event) {
3756
3823
  const sessionId = ("payload" in event && isRecord$11(event.payload) ? event.payload : null)?.sessionId;
@@ -3805,6 +3872,15 @@ function readMessageFromSummaryEvent(event) {
3805
3872
  }
3806
3873
  //#endregion
3807
3874
  //#region src/types/session.types.ts
3875
+ var SessionMessageCursorError = class extends Error {
3876
+ constructor(message = "Invalid session message cursor.") {
3877
+ super(message);
3878
+ this.name = "SessionMessageCursorError";
3879
+ }
3880
+ };
3881
+ function isSessionMessageCursorError(error) {
3882
+ return error instanceof SessionMessageCursorError;
3883
+ }
3808
3884
  var SessionSettingsError = class extends Error {
3809
3885
  constructor(code, message) {
3810
3886
  super(message);
@@ -4502,13 +4578,40 @@ var SessionManager = class {
4502
4578
  return applyLimit((await this.options.journalStore.listSessionSummaries()).filter((summary) => !peerId || summary.peerId === peerId).map(this.workingDirResolver.withWorkingDir), options?.limit);
4503
4579
  };
4504
4580
  listSessionMessages = async (sessionId, options) => {
4581
+ const { cursor, limit } = options ?? {};
4505
4582
  const normalizedSessionId = normalizeSessionId(sessionId);
4506
4583
  if (!normalizedSessionId) return [];
4507
- return applyLimit(await this.options.journalStore.listSessionMessages(normalizedSessionId), options?.limit);
4584
+ if (limit !== void 0 || cursor) return (await this.listSessionMessagePage(normalizedSessionId, {
4585
+ limit: limit ?? 200,
4586
+ ...cursor ? { cursor } : {}
4587
+ }))?.messages ?? [];
4588
+ return await this.options.journalStore.listSessionMessages(normalizedSessionId);
4589
+ };
4590
+ listSessionMessagePage = async (sessionId, options) => {
4591
+ const { cursor, limit } = options;
4592
+ const normalizedSessionId = normalizeSessionId(sessionId);
4593
+ if (!normalizedSessionId) return null;
4594
+ const page = await this.options.journalStore.listSessionMessagePage({
4595
+ sessionId: normalizedSessionId,
4596
+ limit,
4597
+ ...cursor ? { cursor } : {}
4598
+ });
4599
+ if (!page || page.contextWindow) return page;
4600
+ const record = await this.options.journalStore.getSession(normalizedSessionId);
4601
+ if (!record) return page;
4602
+ const contextWindow = this.createSummaryFromRecord(record, true).contextWindow ?? null;
4603
+ await this.options.journalStore.updateSessionMessageProjectionContextWindow(normalizedSessionId, contextWindow);
4604
+ return {
4605
+ ...page,
4606
+ contextWindow
4607
+ };
4508
4608
  };
4509
4609
  getSession = async (sessionId) => {
4510
4610
  const record = await this.getSessionRecord(sessionId);
4511
- return record ? this.createSummaryFromRecord(record, true) : null;
4611
+ if (!record) return null;
4612
+ const summary = this.createSummaryFromRecord(record, true);
4613
+ await this.options.journalStore.updateSessionMessageProjectionContextWindow(record.sessionId, summary.contextWindow ?? null);
4614
+ return summary;
4512
4615
  };
4513
4616
  getContextWindow = async (sessionId, liveRecord) => {
4514
4617
  return (liveRecord ? this.createSummaryFromRecord(liveRecord, true) : await this.getSession(sessionId))?.contextWindow ?? null;
@@ -5369,6 +5472,51 @@ var PanelAppAgentBridgeService = class {
5369
5472
  //#endregion
5370
5473
  //#region src/utils/panel-app-bridge.utils.ts
5371
5474
  const PANEL_APP_BRIDGE_MARKER = "nextclaw:panel-app-service-actions:request";
5475
+ function getPanelAppInlineContentHeightReporterScript() {
5476
+ return `
5477
+ function installInlineContentHeightReporter() {
5478
+ const inlineHostContract = ${JSON.stringify(PANEL_APP_INLINE_HOST_CONTRACT)};
5479
+ const readInlineContentHeight = ${readInlineContentHeight.toString()};
5480
+ if (!window.location || !window.document) {
5481
+ return;
5482
+ }
5483
+ const searchParams = new URLSearchParams(window.location.search);
5484
+ if (
5485
+ searchParams.get(inlineHostContract.displayModeSearchParam) !== inlineHostContract.displayMode ||
5486
+ searchParams.get(inlineHostContract.placementSearchParam) !== inlineHostContract.placement
5487
+ ) {
5488
+ return;
5489
+ }
5490
+ const start = () => {
5491
+ const { body, documentElement } = window.document;
5492
+ if (!documentElement) {
5493
+ return;
5494
+ }
5495
+ let lastHeight = 0;
5496
+ const reportHeight = () => {
5497
+ const height = readInlineContentHeight(body, documentElement);
5498
+ if (height > 0 && height !== lastHeight) {
5499
+ lastHeight = height;
5500
+ window.parent.postMessage({ type: inlineHostContract.contentHeightMessageType, height }, "*");
5501
+ }
5502
+ };
5503
+ if (typeof window.ResizeObserver === "function") {
5504
+ const observer = new window.ResizeObserver(reportHeight);
5505
+ observer.observe(documentElement);
5506
+ if (body) {
5507
+ observer.observe(body);
5508
+ }
5509
+ }
5510
+ window.addEventListener("load", reportHeight);
5511
+ reportHeight();
5512
+ };
5513
+ if (window.document.readyState === "loading") {
5514
+ window.document.addEventListener("DOMContentLoaded", start, { once: true });
5515
+ return;
5516
+ }
5517
+ start();
5518
+ }`.trim();
5519
+ }
5372
5520
  function injectPanelAppBridgeScript(html, params) {
5373
5521
  if (html.includes(PANEL_APP_BRIDGE_MARKER)) return html;
5374
5522
  const script = `<script>${getPanelAppBridgeScript(params)}<\/script>`;
@@ -5405,6 +5553,8 @@ function getPanelAppBridgeScript(params = {
5405
5553
  });
5406
5554
  }
5407
5555
 
5556
+ ${getPanelAppInlineContentHeightReporterScript()}
5557
+
5408
5558
  function resolveApiFetchUrl(input) {
5409
5559
  const raw = typeof input === "string" || input instanceof URL ? input.toString() : input?.url;
5410
5560
  if (typeof raw !== "string") {
@@ -5502,6 +5652,7 @@ function getPanelAppBridgeScript(params = {
5502
5652
  }
5503
5653
  }
5504
5654
  });
5655
+ installInlineContentHeightReporter();
5505
5656
  })();
5506
5657
  `.trim();
5507
5658
  }
@@ -7425,6 +7576,90 @@ function resolveAgentHandoffDepth(metadata) {
7425
7576
  return Math.trunc(rawDepth);
7426
7577
  }
7427
7578
  //#endregion
7579
+ //#region src/utils/ncp-agent-session-journal-entry.utils.ts
7580
+ function isNcpAgentSessionMessageProjectionBoundaryEvent(event) {
7581
+ return event.type === NcpEventType.MessageSent || event.type === NcpEventType.MessageCompleted || event.type === NcpEventType.MessageAbort || event.type === NcpEventType.RunFinished || event.type === NcpEventType.RunError || event.type === "session.snapshot.message";
7582
+ }
7583
+ function serializeNcpAgentSessionJournalEntry(entry) {
7584
+ const serialized = JSON.stringify(entry);
7585
+ if (!serialized) throw new Error("ncp agent session journal entry serialization produced empty output");
7586
+ if (!isRecord$11(JSON.parse(serialized))) throw new Error("ncp agent session journal entry serialization produced a non-object entry");
7587
+ return serialized;
7588
+ }
7589
+ function attachNcpAgentSessionJournalTimestamp(event, timestamp) {
7590
+ if (!("payload" in event) || !isRecord$11(event.payload)) return event;
7591
+ return {
7592
+ ...event,
7593
+ payload: {
7594
+ ...event.payload,
7595
+ timestamp
7596
+ }
7597
+ };
7598
+ }
7599
+ var NcpAgentSessionJournalParser = class {
7600
+ metadata = {};
7601
+ agentId;
7602
+ createdAt = (/* @__PURE__ */ new Date()).toISOString();
7603
+ updatedAt = this.createdAt;
7604
+ nextSeq = 1;
7605
+ projectedJournalOffset = 0;
7606
+ events = [];
7607
+ append = (line, index, lineEndOffset) => {
7608
+ if (!line.trim()) return;
7609
+ const parsed = this.parseLine(line, index);
7610
+ if (!parsed) return;
7611
+ if (parsed._type === "metadata") {
7612
+ this.applyMetadata(parsed);
7613
+ return;
7614
+ }
7615
+ if (parsed._type === "event" && isRecord$11(parsed.event)) this.applyEvent(parsed, lineEndOffset);
7616
+ };
7617
+ finish = () => ({
7618
+ metadata: this.metadata,
7619
+ ...this.agentId ? { agentId: this.agentId } : {},
7620
+ createdAt: this.createdAt,
7621
+ updatedAt: this.updatedAt,
7622
+ nextSeq: this.nextSeq,
7623
+ projectedJournalOffset: this.projectedJournalOffset,
7624
+ events: this.events
7625
+ });
7626
+ parseLine = (line, index) => {
7627
+ try {
7628
+ const parsed = JSON.parse(line);
7629
+ return isRecord$11(parsed) ? parsed : null;
7630
+ } catch (error) {
7631
+ console.warn(`[ncp-agent-session-journal] skipped corrupted journal line ${index + 1}: ${error instanceof Error ? error.message : String(error)}`);
7632
+ return null;
7633
+ }
7634
+ };
7635
+ applyMetadata = (entry) => {
7636
+ this.metadata = isRecord$11(entry.metadata) ? structuredClone(entry.metadata) : {};
7637
+ this.agentId = normalizeNcpAgentId(typeof entry.agent_id === "string" ? entry.agent_id : void 0);
7638
+ this.createdAt = toIsoString(entry.created_at, this.createdAt);
7639
+ this.updatedAt = toIsoString(entry.updated_at, this.updatedAt);
7640
+ };
7641
+ applyEvent = (entry, lineEndOffset) => {
7642
+ const seq = Number(entry.seq);
7643
+ this.nextSeq = Math.max(this.nextSeq, Number.isFinite(seq) ? Math.trunc(seq) + 1 : this.nextSeq);
7644
+ const eventTimestamp = toIsoString(entry.timestamp, this.updatedAt);
7645
+ this.updatedAt = eventTimestamp;
7646
+ const replayEvent = attachNcpAgentSessionJournalTimestamp(structuredClone(entry.event), eventTimestamp);
7647
+ this.events.push(replayEvent);
7648
+ if (isNcpAgentSessionMessageProjectionBoundaryEvent(replayEvent)) this.projectedJournalOffset = lineEndOffset;
7649
+ };
7650
+ };
7651
+ function parseNcpAgentSessionJournal(raw) {
7652
+ const parser = new NcpAgentSessionJournalParser();
7653
+ let byteOffset = 0;
7654
+ const lines = raw.split("\n");
7655
+ for (const [index, line] of lines.entries()) {
7656
+ const lineEndOffset = byteOffset + Buffer.byteLength(line, "utf-8") + (index < lines.length - 1 ? 1 : 0);
7657
+ parser.append(line, index, lineEndOffset);
7658
+ byteOffset = lineEndOffset;
7659
+ }
7660
+ return parser.finish();
7661
+ }
7662
+ //#endregion
7428
7663
  //#region src/stores/ncp-agent-session-metadata.store.ts
7429
7664
  var NcpAgentSessionMetadataStore = class {
7430
7665
  constructor(journalDir) {
@@ -7467,6 +7702,266 @@ var NcpAgentSessionMetadataStore = class {
7467
7702
  };
7468
7703
  };
7469
7704
  //#endregion
7705
+ //#region src/utils/ncp-agent-session-message-projection.utils.ts
7706
+ const OFFSET_FIELD_WIDTH = 20;
7707
+ OFFSET_FIELD_WIDTH * 2 + 2;
7708
+ function serializeNcpAgentSessionMessage(message) {
7709
+ return Buffer.from(`${JSON.stringify(message)}\n`, "utf-8");
7710
+ }
7711
+ function serializeNcpAgentSessionMessageLocation(location) {
7712
+ const offset = String(location.offset).padStart(OFFSET_FIELD_WIDTH, "0");
7713
+ const length = String(location.length).padStart(OFFSET_FIELD_WIDTH, "0");
7714
+ if (offset.length !== OFFSET_FIELD_WIDTH || length.length !== OFFSET_FIELD_WIDTH) throw new Error("Session message projection exceeded its supported file size.");
7715
+ return `${offset}:${length}\n`;
7716
+ }
7717
+ function parseNcpAgentSessionMessageLocation(value) {
7718
+ const match = /^(\d{20}):(\d{20})\n$/.exec(value);
7719
+ const offset = Number(match?.[1]);
7720
+ const length = Number(match?.[2]);
7721
+ if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(length) || offset < 0 || length < 1) throw new Error("Session message projection contains an invalid offset record.");
7722
+ return {
7723
+ offset,
7724
+ length
7725
+ };
7726
+ }
7727
+ function encodeNcpAgentSessionMessageCursor(ordinal) {
7728
+ return Buffer.from(`v1:${ordinal}`, "utf-8").toString("base64url");
7729
+ }
7730
+ function decodeNcpAgentSessionMessageCursor(cursor, maximumBoundary) {
7731
+ let decoded = "";
7732
+ try {
7733
+ decoded = Buffer.from(cursor, "base64url").toString("utf-8");
7734
+ } catch {
7735
+ throw new SessionMessageCursorError();
7736
+ }
7737
+ const match = /^v1:([1-9]\d*)$/.exec(decoded);
7738
+ const ordinal = Number(match?.[1]);
7739
+ if (!Number.isSafeInteger(ordinal) || ordinal < 1 || ordinal > maximumBoundary) throw new SessionMessageCursorError();
7740
+ return ordinal;
7741
+ }
7742
+ function deduplicateNcpAgentSessionTailMessages(messages) {
7743
+ const byId = /* @__PURE__ */ new Map();
7744
+ for (const message of messages) {
7745
+ byId.delete(message.id);
7746
+ byId.set(message.id, structuredClone(message));
7747
+ }
7748
+ return [...byId.values()];
7749
+ }
7750
+ //#endregion
7751
+ //#region src/stores/ncp-agent-session-message-projection.store.ts
7752
+ const PROJECTION_VERSION = 1;
7753
+ const PROJECTION_ROOT_DIRECTORY = ".message-projections";
7754
+ var NcpAgentSessionMessageProjectionStore = class {
7755
+ constructor(journalDir, source) {
7756
+ this.journalDir = journalDir;
7757
+ this.source = source;
7758
+ }
7759
+ readMeta = async (sessionId) => {
7760
+ try {
7761
+ const parsed = JSON.parse(await readFile(this.metaPath(sessionId), "utf-8"));
7762
+ if (parsed.version !== PROJECTION_VERSION || parsed.sessionId !== sessionId || !Number.isSafeInteger(parsed.total) || !Number.isSafeInteger(parsed.projectedJournalOffset) || !Number.isSafeInteger(parsed.dataBytes) || parsed.lastMessageId !== null && typeof parsed.lastMessageId !== "string" || parsed.contextWindow !== null && !isRecord$11(parsed.contextWindow)) return null;
7763
+ const meta = parsed;
7764
+ const [dataStat, offsetsStat] = await Promise.all([stat(this.dataPath(sessionId)), stat(this.offsetsPath(sessionId))]);
7765
+ if (dataStat.size !== meta.dataBytes || offsetsStat.size !== meta.total * 42 || meta.total < 0 || meta.projectedJournalOffset < 0 || meta.dataBytes < 0) return null;
7766
+ return structuredClone(meta);
7767
+ } catch {
7768
+ return null;
7769
+ }
7770
+ };
7771
+ rebuild = async (params) => {
7772
+ const { contextWindow, messages, projectedJournalOffset, sessionId } = params;
7773
+ const projectionPath = this.projectionPath(sessionId);
7774
+ const projectionRoot = dirname(projectionPath);
7775
+ await mkdir(projectionRoot, { recursive: true });
7776
+ const temporaryPath = await mkdtemp(join(projectionRoot, ".rebuild-"));
7777
+ try {
7778
+ const dataFile = await open(join(temporaryPath, "messages.jsonl"), "w");
7779
+ let dataBytes = 0;
7780
+ try {
7781
+ const offsetsFile = await open(join(temporaryPath, "offsets.idx"), "w");
7782
+ try {
7783
+ for (const message of messages) {
7784
+ const serialized = serializeNcpAgentSessionMessage(message);
7785
+ await dataFile.write(serialized, 0, serialized.length, dataBytes);
7786
+ await offsetsFile.write(serializeNcpAgentSessionMessageLocation({
7787
+ offset: dataBytes,
7788
+ length: serialized.length
7789
+ }));
7790
+ dataBytes += serialized.length;
7791
+ }
7792
+ await Promise.all([dataFile.sync(), offsetsFile.sync()]);
7793
+ } finally {
7794
+ await offsetsFile.close();
7795
+ }
7796
+ } finally {
7797
+ await dataFile.close();
7798
+ }
7799
+ const meta = {
7800
+ version: PROJECTION_VERSION,
7801
+ sessionId,
7802
+ total: messages.length,
7803
+ lastMessageId: messages.at(-1)?.id ?? null,
7804
+ projectedJournalOffset,
7805
+ dataBytes,
7806
+ contextWindow: contextWindow ? structuredClone(contextWindow) : null
7807
+ };
7808
+ await writeFile(join(temporaryPath, "meta.json"), `${JSON.stringify(meta)}\n`, "utf-8");
7809
+ await rm(projectionPath, {
7810
+ recursive: true,
7811
+ force: true
7812
+ });
7813
+ await rename(temporaryPath, projectionPath);
7814
+ } catch (error) {
7815
+ await rm(temporaryPath, {
7816
+ recursive: true,
7817
+ force: true
7818
+ });
7819
+ throw error;
7820
+ }
7821
+ };
7822
+ synchronize = async (params) => {
7823
+ const { messages: sourceMessages, projectedJournalOffset, sessionId } = params;
7824
+ const meta = await this.readMeta(sessionId);
7825
+ if (!meta) return false;
7826
+ const messages = deduplicateNcpAgentSessionTailMessages(sourceMessages);
7827
+ const dataFile = await open(this.dataPath(sessionId), "r+");
7828
+ const offsetsFile = await open(this.offsetsPath(sessionId), "r+");
7829
+ try {
7830
+ for (const message of messages) {
7831
+ const serialized = serializeNcpAgentSessionMessage(message);
7832
+ const location = {
7833
+ offset: meta.dataBytes,
7834
+ length: serialized.length
7835
+ };
7836
+ const serializedLocation = Buffer.from(serializeNcpAgentSessionMessageLocation(location), "utf-8");
7837
+ await dataFile.write(serialized, 0, serialized.length, meta.dataBytes);
7838
+ meta.dataBytes += serialized.length;
7839
+ if (message.id === meta.lastMessageId && meta.total > 0) {
7840
+ await offsetsFile.write(serializedLocation, 0, 42, (meta.total - 1) * 42);
7841
+ continue;
7842
+ }
7843
+ await offsetsFile.write(serializedLocation, 0, 42, meta.total * 42);
7844
+ meta.total += 1;
7845
+ meta.lastMessageId = message.id;
7846
+ }
7847
+ await Promise.all([dataFile.sync(), offsetsFile.sync()]);
7848
+ } finally {
7849
+ await Promise.all([dataFile.close(), offsetsFile.close()]);
7850
+ }
7851
+ meta.projectedJournalOffset = projectedJournalOffset;
7852
+ await this.writeMeta(meta);
7853
+ return true;
7854
+ };
7855
+ updateContextWindow = async (sessionId, contextWindow) => {
7856
+ const meta = await this.readMeta(sessionId);
7857
+ if (!meta) return;
7858
+ meta.contextWindow = contextWindow ? structuredClone(contextWindow) : null;
7859
+ await this.writeMeta(meta);
7860
+ };
7861
+ listPage = async (params) => {
7862
+ const { cursor, limit, sessionId } = params;
7863
+ let meta = await this.readMeta(sessionId);
7864
+ let tailMessages = null;
7865
+ const journalStat = await stat(this.journalPath(sessionId));
7866
+ if (meta && meta.projectedJournalOffset > journalStat.size) meta = null;
7867
+ if (!meta) {
7868
+ const loaded = await this.source?.loadSession(sessionId);
7869
+ if (!loaded) return null;
7870
+ tailMessages = await this.readJournalTailMessages(sessionId, loaded.projectedJournalOffset);
7871
+ const tailMessageIds = new Set(tailMessages.map((message) => message.id));
7872
+ await this.rebuild({
7873
+ sessionId,
7874
+ messages: loaded.record.messages.filter((message) => !tailMessageIds.has(message.id)),
7875
+ projectedJournalOffset: loaded.projectedJournalOffset
7876
+ });
7877
+ meta = await this.readMeta(sessionId);
7878
+ }
7879
+ if (!meta) throw new Error(`Failed to build session message projection: ${sessionId}`);
7880
+ return await this.readPage({
7881
+ sessionId,
7882
+ limit,
7883
+ cursor,
7884
+ tailMessages: tailMessages ?? await this.readJournalTailMessages(sessionId, meta.projectedJournalOffset)
7885
+ });
7886
+ };
7887
+ readPage = async (params) => {
7888
+ const { cursor, limit: requestedLimit, sessionId, tailMessages } = params;
7889
+ const meta = await this.readMeta(sessionId);
7890
+ if (!meta) return null;
7891
+ const uniqueTailMessages = deduplicateNcpAgentSessionTailMessages(tailMessages ?? []);
7892
+ const tailById = new Map(uniqueTailMessages.map((message) => [message.id, message]));
7893
+ const additionalTailMessages = uniqueTailMessages.filter((message) => message.id !== meta.lastMessageId);
7894
+ const limit = Number.isFinite(requestedLimit) ? Math.min(200, Math.max(1, Math.trunc(requestedLimit))) : 80;
7895
+ const boundary = cursor ? decodeNcpAgentSessionMessageCursor(cursor, meta.total + 1) : meta.total + 1;
7896
+ const includeTail = !cursor;
7897
+ const stableLimit = includeTail ? Math.max(0, limit - additionalTailMessages.length) : limit;
7898
+ const endOrdinal = Math.min(meta.total, boundary - 1);
7899
+ const startOrdinal = stableLimit > 0 ? Math.max(1, endOrdinal - stableLimit + 1) : endOrdinal + 1;
7900
+ const stableMessages = startOrdinal <= endOrdinal ? await this.readMessages(sessionId, startOrdinal, endOrdinal) : [];
7901
+ const messages = stableMessages.map((message) => tailById.get(message.id) ?? message);
7902
+ if (includeTail) messages.push(...additionalTailMessages);
7903
+ const cursorOrdinal = stableMessages.length > 0 ? startOrdinal : meta.total + 1;
7904
+ return {
7905
+ messages,
7906
+ total: meta.total + additionalTailMessages.length,
7907
+ pageInfo: {
7908
+ startCursor: messages.length > 0 ? encodeNcpAgentSessionMessageCursor(cursorOrdinal) : null,
7909
+ hasPreviousPage: cursorOrdinal > 1
7910
+ },
7911
+ contextWindow: meta.contextWindow ? structuredClone(meta.contextWindow) : null
7912
+ };
7913
+ };
7914
+ readJournalTailMessages = async (sessionId, offset) => {
7915
+ const file = await open(this.journalPath(sessionId), "r");
7916
+ try {
7917
+ const fileStat = await file.stat();
7918
+ if (offset < 0 || offset > fileStat.size || offset === fileStat.size) return [];
7919
+ const buffer = Buffer.alloc(fileStat.size - offset);
7920
+ const result = await file.read(buffer, 0, buffer.length, offset);
7921
+ return await replayNcpAgentSessionEvents(parseNcpAgentSessionJournal(buffer.subarray(0, result.bytesRead).toString("utf-8")).events);
7922
+ } finally {
7923
+ await file.close();
7924
+ }
7925
+ };
7926
+ delete = async (sessionId) => {
7927
+ await rm(this.projectionPath(sessionId), {
7928
+ recursive: true,
7929
+ force: true
7930
+ });
7931
+ };
7932
+ readMessages = async (sessionId, startOrdinal, endOrdinal) => {
7933
+ const count = endOrdinal - startOrdinal + 1;
7934
+ const indexBuffer = Buffer.alloc(count * 42);
7935
+ const offsetsFile = await open(this.offsetsPath(sessionId), "r");
7936
+ const dataFile = await open(this.dataPath(sessionId), "r");
7937
+ try {
7938
+ if ((await offsetsFile.read(indexBuffer, 0, indexBuffer.length, (startOrdinal - 1) * 42)).bytesRead !== indexBuffer.length) throw new Error("Session message projection ended before the requested page.");
7939
+ const messages = [];
7940
+ for (let index = 0; index < count; index += 1) {
7941
+ const recordStart = index * 42;
7942
+ const location = parseNcpAgentSessionMessageLocation(indexBuffer.subarray(recordStart, recordStart + 42).toString("utf-8"));
7943
+ const messageBuffer = Buffer.alloc(location.length);
7944
+ if ((await dataFile.read(messageBuffer, 0, location.length, location.offset)).bytesRead !== location.length) throw new Error("Session message projection contains a truncated message.");
7945
+ messages.push(JSON.parse(messageBuffer.toString("utf-8")));
7946
+ }
7947
+ return messages;
7948
+ } finally {
7949
+ await Promise.all([offsetsFile.close(), dataFile.close()]);
7950
+ }
7951
+ };
7952
+ writeMeta = async (meta) => {
7953
+ const path = this.metaPath(meta.sessionId);
7954
+ const temporaryPath = `${path}.${process.pid}.tmp`;
7955
+ await writeFile(temporaryPath, `${JSON.stringify(meta)}\n`, "utf-8");
7956
+ await rename(temporaryPath, path);
7957
+ };
7958
+ projectionPath = (sessionId) => join(this.journalDir, PROJECTION_ROOT_DIRECTORY, safeNcpSessionFilename(sessionId));
7959
+ journalPath = (sessionId) => join(this.journalDir, `${safeNcpSessionFilename(sessionId)}.jsonl`);
7960
+ metaPath = (sessionId) => join(this.projectionPath(sessionId), "meta.json");
7961
+ dataPath = (sessionId) => join(this.projectionPath(sessionId), "messages.jsonl");
7962
+ offsetsPath = (sessionId) => join(this.projectionPath(sessionId), "offsets.idx");
7963
+ };
7964
+ //#endregion
7470
7965
  //#region src/stores/ncp-agent-session-summary-index.store.ts
7471
7966
  function stripSummaryMetadata(summary) {
7472
7967
  const { metadata: _metadata, ...nextSummary } = summary;
@@ -7543,31 +8038,21 @@ var NcpAgentSessionSummaryIndexStore = class {
7543
8038
  };
7544
8039
  //#endregion
7545
8040
  //#region src/stores/ncp-agent-session-journal.store.ts
7546
- function serializeJournalEntry(entry) {
7547
- const serialized = JSON.stringify(entry);
7548
- if (!serialized) throw new Error("ncp agent session journal entry serialization produced empty output");
7549
- if (!isRecord$11(JSON.parse(serialized))) throw new Error("ncp agent session journal entry serialization produced a non-object entry");
7550
- return serialized;
7551
- }
7552
- function attachJournalTimestamp(event, timestamp) {
7553
- if (!("payload" in event) || !isRecord$11(event.payload)) return event;
7554
- return {
7555
- ...event,
7556
- payload: {
7557
- ...event.payload,
7558
- timestamp
7559
- }
7560
- };
7561
- }
7562
8041
  var NcpAgentSessionJournalStore = class {
7563
8042
  sessions = /* @__PURE__ */ new Map();
7564
8043
  nextSeqBySession = /* @__PURE__ */ new Map();
7565
8044
  writeChains = /* @__PURE__ */ new Map();
7566
8045
  metadataStore;
8046
+ messageProjectionStore;
7567
8047
  summaryIndexStore;
7568
8048
  constructor(journalDir) {
7569
8049
  this.journalDir = journalDir;
7570
8050
  this.metadataStore = new NcpAgentSessionMetadataStore(journalDir);
8051
+ this.messageProjectionStore = new NcpAgentSessionMessageProjectionStore(journalDir, { loadSession: async (sessionId) => {
8052
+ const loaded = this.sessions.get(sessionId) ?? await this.loadSession(sessionId);
8053
+ if (loaded) this.sessions.set(sessionId, loaded);
8054
+ return loaded;
8055
+ } });
7571
8056
  this.summaryIndexStore = new NcpAgentSessionSummaryIndexStore(journalDir, async (sessionId) => this.loadSession(sessionId));
7572
8057
  }
7573
8058
  appendSessionEvent = async (params) => {
@@ -7598,6 +8083,20 @@ var NcpAgentSessionJournalStore = class {
7598
8083
  const session = await this.getSession(sessionId);
7599
8084
  return session ? session.messages.map((message) => structuredClone(message)) : [];
7600
8085
  };
8086
+ listSessionMessagePage = async (params) => {
8087
+ const sessionId = normalizeNcpSessionId(params.sessionId);
8088
+ if (!sessionId) return null;
8089
+ return await this.messageProjectionStore.listPage({
8090
+ sessionId,
8091
+ limit: params.limit,
8092
+ cursor: params.cursor
8093
+ });
8094
+ };
8095
+ updateSessionMessageProjectionContextWindow = async (sessionId, contextWindow) => {
8096
+ const normalizedSessionId = normalizeNcpSessionId(sessionId);
8097
+ if (!normalizedSessionId) return;
8098
+ await this.messageProjectionStore.updateContextWindow(normalizedSessionId, contextWindow);
8099
+ };
7601
8100
  setSessionMetadata = async (params) => {
7602
8101
  const sessionId = normalizeNcpSessionId(params.sessionId);
7603
8102
  if (!sessionId) return false;
@@ -7629,7 +8128,8 @@ var NcpAgentSessionJournalStore = class {
7629
8128
  await this.metadataStore.write(nextRecord);
7630
8129
  this.sessions.set(sessionId, {
7631
8130
  record: nextRecord,
7632
- nextSeq: loaded.nextSeq
8131
+ nextSeq: loaded.nextSeq,
8132
+ projectedJournalOffset: loaded.projectedJournalOffset
7633
8133
  });
7634
8134
  this.nextSeqBySession.set(sessionId, loaded.nextSeq);
7635
8135
  await this.summaryIndexStore.upsert(createNcpAgentSessionSummary(nextRecord));
@@ -7668,13 +8168,20 @@ var NcpAgentSessionJournalStore = class {
7668
8168
  }
7669
8169
  }
7670
8170
  }));
7671
- await writeFile(this.sessionPath(sessionId), entries.length > 0 ? `${entries.map(serializeJournalEntry).join("\n")}\n` : "", "utf-8");
8171
+ await writeFile(this.sessionPath(sessionId), entries.length > 0 ? `${entries.map(serializeNcpAgentSessionJournalEntry).join("\n")}\n` : "", "utf-8");
7672
8172
  const nextSeq = nextRecord.messages.length + 1;
8173
+ const journalStat = await stat(this.sessionPath(sessionId));
7673
8174
  this.sessions.set(sessionId, {
7674
8175
  record: nextRecord,
7675
- nextSeq
8176
+ nextSeq,
8177
+ projectedJournalOffset: journalStat.size
7676
8178
  });
7677
8179
  this.nextSeqBySession.set(sessionId, nextSeq);
8180
+ await this.messageProjectionStore.rebuild({
8181
+ sessionId,
8182
+ messages: nextRecord.messages,
8183
+ projectedJournalOffset: journalStat.size
8184
+ });
7678
8185
  await this.summaryIndexStore.upsert(createNcpAgentSessionSummary(nextRecord));
7679
8186
  };
7680
8187
  deleteSession = async (sessionId) => {
@@ -7687,6 +8194,7 @@ var NcpAgentSessionJournalStore = class {
7687
8194
  await unlink(this.sessionPath(normalizedSessionId));
7688
8195
  } catch {}
7689
8196
  await this.metadataStore.delete(normalizedSessionId);
8197
+ await this.messageProjectionStore.delete(normalizedSessionId);
7690
8198
  await this.summaryIndexStore.remove(normalizedSessionId);
7691
8199
  return existing;
7692
8200
  };
@@ -7712,6 +8220,18 @@ var NcpAgentSessionJournalStore = class {
7712
8220
  await this.appendJournalEntry(path, entry);
7713
8221
  this.nextSeqBySession.set(sessionId, nextSeq + 1);
7714
8222
  this.sessions.delete(sessionId);
8223
+ if (isNcpAgentSessionMessageProjectionBoundaryEvent(event)) {
8224
+ const journalStat = await stat(path);
8225
+ const projectionMeta = await this.messageProjectionStore.readMeta(sessionId);
8226
+ if (projectionMeta) {
8227
+ const projectionTail = await this.messageProjectionStore.readJournalTailMessages(sessionId, projectionMeta.projectedJournalOffset);
8228
+ await this.messageProjectionStore.synchronize({
8229
+ sessionId,
8230
+ messages: projectionTail,
8231
+ projectedJournalOffset: journalStat.size
8232
+ });
8233
+ }
8234
+ }
7715
8235
  await this.summaryIndexStore.upsertForEvent({
7716
8236
  sessionId,
7717
8237
  event,
@@ -7740,9 +8260,9 @@ var NcpAgentSessionJournalStore = class {
7740
8260
  } catch {
7741
8261
  return null;
7742
8262
  }
7743
- const parsedJournal = this.parseSessionJournal(raw);
8263
+ const parsedJournal = parseNcpAgentSessionJournal(raw);
7744
8264
  const { agentId, createdAt, updatedAt, metadata } = await this.metadataStore.read(sessionId, parsedJournal);
7745
- const { events, nextSeq } = parsedJournal;
8265
+ const { events, nextSeq, projectedJournalOffset } = parsedJournal;
7746
8266
  const messages = await replayNcpAgentSessionEvents(events);
7747
8267
  const record = {
7748
8268
  sessionId,
@@ -7755,53 +8275,12 @@ var NcpAgentSessionJournalStore = class {
7755
8275
  this.nextSeqBySession.set(sessionId, nextSeq);
7756
8276
  return {
7757
8277
  record,
7758
- nextSeq
7759
- };
7760
- };
7761
- parseSessionJournal = (raw) => {
7762
- let metadata = {};
7763
- let agentId;
7764
- let createdAt = (/* @__PURE__ */ new Date()).toISOString();
7765
- let updatedAt = createdAt;
7766
- let nextSeq = 1;
7767
- const events = [];
7768
- for (const [index, line] of raw.split("\n").entries()) {
7769
- if (!line.trim()) continue;
7770
- let parsed;
7771
- try {
7772
- parsed = JSON.parse(line);
7773
- } catch (error) {
7774
- console.warn(`[ncp-agent-session-journal] skipped corrupted journal line ${index + 1}: ${error instanceof Error ? error.message : String(error)}`);
7775
- continue;
7776
- }
7777
- if (!isRecord$11(parsed)) continue;
7778
- if (parsed._type === "metadata") {
7779
- metadata = isRecord$11(parsed.metadata) ? structuredClone(parsed.metadata) : {};
7780
- agentId = normalizeNcpAgentId(typeof parsed.agent_id === "string" ? parsed.agent_id : void 0);
7781
- createdAt = toIsoString(parsed.created_at, createdAt);
7782
- updatedAt = toIsoString(parsed.updated_at, updatedAt);
7783
- continue;
7784
- }
7785
- if (parsed._type === "event" && isRecord$11(parsed.event)) {
7786
- const seq = Number(parsed.seq);
7787
- nextSeq = Math.max(nextSeq, Number.isFinite(seq) ? Math.trunc(seq) + 1 : nextSeq);
7788
- const eventTimestamp = toIsoString(parsed.timestamp, updatedAt);
7789
- updatedAt = eventTimestamp;
7790
- const event = structuredClone(parsed.event);
7791
- events.push(attachJournalTimestamp(event, eventTimestamp));
7792
- }
7793
- }
7794
- return {
7795
- metadata,
7796
- ...agentId ? { agentId } : {},
7797
- createdAt,
7798
- updatedAt,
7799
8278
  nextSeq,
7800
- events
8279
+ projectedJournalOffset
7801
8280
  };
7802
8281
  };
7803
8282
  appendJournalEntry = async (path, entry) => {
7804
- await appendFile(path, `${serializeJournalEntry(entry)}\n`, "utf-8");
8283
+ await appendFile(path, `${serializeNcpAgentSessionJournalEntry(entry)}\n`, "utf-8");
7805
8284
  };
7806
8285
  ensureJournalDir = async () => {
7807
8286
  await mkdir(this.journalDir, { recursive: true });
@@ -8920,9 +9399,10 @@ var NcpAgentRuntimeWrapper = class {
8920
9399
  this.params = params;
8921
9400
  }
8922
9401
  run = async function* (spec, options) {
8923
- const { sessionRun, tools } = options;
9402
+ const { session, sessionRun, signal, tools } = options;
8924
9403
  this.currentTools = tools.map(this.toOpenAiTool);
8925
9404
  const messages = sessionRun.inbox.drain();
9405
+ let executionMetadataSeen = false;
8926
9406
  try {
8927
9407
  for (const event of this.toMessageSentEvents(messages, spec, sessionRun.sessionId)) yield await this.applyEvent(sessionRun, event);
8928
9408
  const input = {
@@ -8930,10 +9410,18 @@ var NcpAgentRuntimeWrapper = class {
8930
9410
  runId: spec.runId,
8931
9411
  messages,
8932
9412
  correlationId: spec.correlationId,
8933
- metadata: this.buildMetadata(options.session, spec),
8934
- executionContext: { cwd: options.session.workingDir }
9413
+ metadata: this.buildMetadata(session, spec),
9414
+ executionContext: { cwd: session.workingDir }
8935
9415
  };
8936
- for await (const event of this.getRuntime().run(input, { signal: options.signal })) yield await this.applyEvent(sessionRun, event);
9416
+ for await (const event of this.getRuntime().run(input, { signal })) {
9417
+ if (event.type === NcpEventType.RunMetadata && readNcpAiExecutionMetadata(event.payload.metadata)) executionMetadataSeen = true;
9418
+ const terminalOutcome = this.readTerminalOutcome(event);
9419
+ if (terminalOutcome && !executionMetadataSeen) {
9420
+ yield await this.applyEvent(sessionRun, this.createExecutionMetadataEvent(spec, session, event, terminalOutcome));
9421
+ executionMetadataSeen = true;
9422
+ }
9423
+ yield await this.applyEvent(sessionRun, event);
9424
+ }
8937
9425
  } finally {
8938
9426
  this.currentTools = [];
8939
9427
  }
@@ -8971,6 +9459,28 @@ var NcpAgentRuntimeWrapper = class {
8971
9459
  preferred_model: spec.model,
8972
9460
  thinkingEffort: spec.thinkingEffort
8973
9461
  });
9462
+ readTerminalOutcome = (event) => {
9463
+ if (event.type === NcpEventType.RunFinished) return "completed";
9464
+ if (event.type === NcpEventType.RunError) return "failed";
9465
+ if (event.type === NcpEventType.MessageAbort) return "aborted";
9466
+ return null;
9467
+ };
9468
+ createExecutionMetadataEvent = (spec, session, terminalEvent, outcome) => ({
9469
+ occurredAt: terminalEvent.occurredAt ?? (/* @__PURE__ */ new Date()).toISOString(),
9470
+ type: NcpEventType.RunMetadata,
9471
+ payload: {
9472
+ runId: spec.runId,
9473
+ sessionId: session.sessionId,
9474
+ correlationId: spec.correlationId,
9475
+ metadata: { [NCP_AI_EXECUTION_METADATA_KEY]: createUnavailableNcpAiExecutionMetadata({
9476
+ runId: spec.runId,
9477
+ runtimeId: spec.runtimeId,
9478
+ model: spec.model,
9479
+ requestedModel: spec.requestedModel,
9480
+ outcome
9481
+ }) }
9482
+ }
9483
+ });
8974
9484
  toOpenAiTool = (tool) => buildOpenAiFunctionTool({
8975
9485
  name: tool.name,
8976
9486
  description: tool.description,
@@ -9489,17 +9999,39 @@ var SkillsContextProvider = class {
9489
9999
  };
9490
10000
  //#endregion
9491
10001
  //#region src/contributions/context-provider/providers/tooling-context.provider.ts
10002
+ function readSearchApiKey(searchConfig, provider) {
10003
+ if (provider === "bocha") return searchConfig?.providers?.bocha?.apiKey?.trim() ?? "";
10004
+ if (provider === "tavily") return searchConfig?.providers?.tavily?.apiKey?.trim() ?? "";
10005
+ return searchConfig?.providers?.brave?.apiKey?.trim() ?? "";
10006
+ }
10007
+ function renderWebSearchReadiness(params) {
10008
+ const { searchConfig, toolNames } = params;
10009
+ if (!toolNames.includes("web_search")) return "web_search is unavailable in this turn.";
10010
+ const provider = searchConfig?.provider ?? "bocha";
10011
+ if (!(searchConfig?.enabledProviders ?? ["bocha"]).includes(provider)) return `web_search is not ready: provider ${provider} is not enabled.`;
10012
+ if (!readSearchApiKey(searchConfig, provider)) return `web_search is not ready: provider ${provider} has no API key configured.`;
10013
+ return `web_search is ready with provider ${provider}.`;
10014
+ }
9492
10015
  var ToolingContextProvider = class {
9493
10016
  constructor(context) {
9494
10017
  this.context = context;
9495
10018
  }
9496
10019
  provide = async (request) => {
9497
- const { toolCatalog } = await this.context.resolve(request);
10020
+ const { runContext, toolCatalog } = await this.context.resolve(request);
9498
10021
  return [[
9499
10022
  "## Tooling",
9500
10023
  "Tool availability (filtered by policy):",
9501
10024
  "Tool names are case-sensitive. Call tools exactly as listed.",
9502
10025
  ...toolCatalog.length > 0 ? toolCatalog.map((tool) => `- ${tool.name}: ${tool.description ?? "No description available"}`) : ["- No tools available for this turn."],
10026
+ "Web access policy:",
10027
+ `- ${renderWebSearchReadiness({
10028
+ searchConfig: runContext.profile.searchConfig,
10029
+ toolNames: toolCatalog.map((tool) => tool.name)
10030
+ })}`,
10031
+ "- web_search and Agent Browser are distinct capabilities: web_search is for open-ended source discovery; Agent Browser operates real browser pages.",
10032
+ "- In this policy, Agent Browser means the external agent-browser CLI documented by the builtin skill. web_fetch, Chrome/Edge DevTools MCP, and Browser Connector are separate capabilities and must not be called Agent Browser.",
10033
+ "- Prefer a ready web_search for open-ended discovery. If it is unavailable, not configured, or returns an error, and browser navigation can still make progress, briefly tell the user you are switching to browser automation, then read and follow the builtin agent-browser skill.",
10034
+ "- Do not describe Agent Browser output as web_search output, and do not silently install its external CLI.",
9503
10035
  "TOOLS.md does not control tool availability; it is user guidance for how to use external tools.",
9504
10036
  "For long waits, avoid rapid poll loops: use exec with enough yieldMs.",
9505
10037
  "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.",
@@ -11857,6 +12389,6 @@ function resolveLegacyEventType(message) {
11857
12389
  return `message.${role || "other"}`;
11858
12390
  }
11859
12391
  //#endregion
11860
- export { AUTOMATIC_UPDATE_CHECK_INTERVAL_MS, 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, PROJECT_TEMPLATE_IDS, PanelAppAssetTokenService, PanelAppError, PanelAppManager, PreferenceError, PreferenceManager, ProjectError, ProjectManager, ProviderManagerNcpLLMApi, SERVICE_APP_MANIFEST_FILE_NAME, ServiceAppError, ServiceAppManager, SessionManager, SessionRequestManager, SessionSettingsError, SkillManager, UpdateManifestReader, buildAgentRunSendPayload, buildContextCompactionModelInput, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildNextclawNcpRunContext, buildServiceActionId, createAgentRuntimeSessionRequestDispatcher, createAssetTools, createContextCompactionMessageId, createContextWindowSignature, createLlmUsageRecord, describeAgentRuntimeSessionTypes, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, getAutomaticUpdateCheckDelay, getServiceActionName, getServiceAppManifestPath, getUnsignedUpdateManifest, hasLlmUsageTelemetry, isContextCompactionProjectionMessage, isContextCompactionTimelineMessage, isContextWindowSnapshot, isPanelAppAgentCapability, isPanelAppError, isPreferenceError, isProjectError, isReplyCapableChannel, isServiceAppError, isSessionSettingsError, listExtensionChannelIds, listServiceAppManifestActions, mergeServiceAppRuntimeActions, normalizeAgentRuntimeSessionTypeIcon, normalizeLlmUsageModel, normalizeOptionalString, parseServiceAppManifest, parseSkillFrontmatter, readContextWindowEventSessionId, readLatestContextCompactionCheckpoint, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, readServiceAppManifest, resolveAgentRuntimeEntries, resolveAutomaticUpdateCheckIntervalMs, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolveSessionChannelContext, runGatewayInboundLoop, sanitizeLlmUsage, serializeUnsignedUpdateManifest, shouldRefreshContextWindowDuringStream, shouldRefreshContextWindowImmediately, stripSkillFrontmatter, syncSessionThinkingPreference, toNcpMessages, waitForAgentRuntimeSessionReply };
12392
+ export { AUTOMATIC_UPDATE_CHECK_INTERVAL_MS, 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, PROJECT_TEMPLATE_IDS, PanelAppAssetTokenService, PanelAppError, PanelAppManager, PreferenceError, PreferenceManager, ProjectError, ProjectManager, ProviderManagerNcpLLMApi, SERVICE_APP_MANIFEST_FILE_NAME, ServiceAppError, ServiceAppManager, SessionManager, SessionMessageCursorError, SessionRequestManager, SessionSettingsError, SkillManager, UpdateManifestReader, buildAgentRunSendPayload, buildContextCompactionModelInput, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildNextclawNcpRunContext, buildServiceActionId, createAgentRuntimeSessionRequestDispatcher, createAssetTools, createContextCompactionMessageId, createContextWindowSignature, createLlmUsageRecord, describeAgentRuntimeSessionTypes, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, getAutomaticUpdateCheckDelay, getServiceActionName, getServiceAppManifestPath, getUnsignedUpdateManifest, hasLlmUsageTelemetry, isContextCompactionProjectionMessage, isContextCompactionTimelineMessage, isContextWindowSnapshot, isPanelAppAgentCapability, isPanelAppError, isPreferenceError, isProjectError, isReplyCapableChannel, isServiceAppError, isSessionMessageCursorError, isSessionSettingsError, listExtensionChannelIds, listServiceAppManifestActions, mergeServiceAppRuntimeActions, normalizeAgentRuntimeSessionTypeIcon, normalizeLlmUsageModel, normalizeOptionalString, parseServiceAppManifest, parseSkillFrontmatter, readContextWindowEventSessionId, readLatestContextCompactionCheckpoint, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, readServiceAppManifest, resolveAgentRuntimeEntries, resolveAutomaticUpdateCheckIntervalMs, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolveSessionChannelContext, runGatewayInboundLoop, sanitizeLlmUsage, serializeUnsignedUpdateManifest, shouldRefreshContextWindowDuringStream, shouldRefreshContextWindowImmediately, stripSkillFrontmatter, syncSessionThinkingPreference, toNcpMessages, waitForAgentRuntimeSessionReply };
11861
12393
 
11862
12394
  //# sourceMappingURL=index.js.map