@nextclaw/kernel 0.6.19 → 0.6.21

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
@@ -5,7 +5,7 @@ import { APP_NAME, AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, CLEAR_THINKING_TOK
5
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_INLINE_TOKENS_SCHEMA_VERSION, CHAT_PROJECT_TOKEN_KIND, CHAT_SESSION_MATERIALIZATION_METADATA_KEY, CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND, CHAT_WORKSPACE_FILE_TOKEN_KIND, EventBus, Ingress, PANEL_APP_INLINE_HOST_CONTRACT, UI_CONTENT_PARAMS_HOST_CONTRACT, eventKeys, ingressKeys, isRuntimeDefaultModelValue, normalizeRuntimeModelSelectionMode, readInlineContentHeight, readUiContentParams } from "@nextclaw/shared";
8
+ import { CHAT_INLINE_TOKENS_METADATA_KEY, CHAT_INLINE_TOKENS_SCHEMA_VERSION, CHAT_PROJECT_TOKEN_KIND, CHAT_SESSION_MATERIALIZATION_METADATA_KEY, CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND, CHAT_WORKSPACE_FILE_TOKEN_KIND, EventBus, INBOX_DELIVERY_SESSION_METADATA_KEY, Ingress, PANEL_APP_INLINE_HOST_CONTRACT, UI_CONTENT_PARAMS_HOST_CONTRACT, eventKeys, ingressKeys, isRuntimeDefaultModelValue, normalizeRuntimeModelSelectionMode, readInlineContentHeight, readUiContentParams } from "@nextclaw/shared";
9
9
  import { catchError, filter, 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";
@@ -693,13 +693,13 @@ function createUnavailableAiExecutionMetadataEvent(params) {
693
693
  }
694
694
  //#endregion
695
695
  //#region src/utils/agent-run-request.utils.ts
696
- function readOptionalString$11(value) {
696
+ function readOptionalString$12(value) {
697
697
  if (typeof value !== "string") return;
698
698
  return value.trim() || void 0;
699
699
  }
700
700
  function toAgentRunRequest(envelope) {
701
701
  const metadata = envelope.metadata ?? {};
702
- const peerId = readOptionalString$11(envelope.peerId);
702
+ const peerId = readOptionalString$12(envelope.peerId);
703
703
  const requestMetadata = {
704
704
  agentRuntimeId: metadata.agentRuntimeId,
705
705
  agentId: metadata.agentId,
@@ -712,7 +712,7 @@ function toAgentRunRequest(envelope) {
712
712
  thinkingEffort: metadata.thinkingEffort
713
713
  };
714
714
  if (Array.isArray(envelope.content)) {
715
- const sessionId = readOptionalString$11(envelope.sessionId);
715
+ const sessionId = readOptionalString$12(envelope.sessionId);
716
716
  if (sessionId && peerId) throw new Error("agent-run.send cannot accept both sessionId and peerId.");
717
717
  return {
718
718
  ...requestMetadata,
@@ -730,8 +730,8 @@ function toAgentRunRequest(envelope) {
730
730
  }
731
731
  const sourceMessage = envelope.message;
732
732
  if (!sourceMessage) throw new Error("Invalid agent run send request.");
733
- const envelopeSessionId = readOptionalString$11(envelope.sessionId);
734
- const messageSessionId = readOptionalString$11(sourceMessage.sessionId);
733
+ const envelopeSessionId = readOptionalString$12(envelope.sessionId);
734
+ const messageSessionId = readOptionalString$12(sourceMessage.sessionId);
735
735
  const sessionId = envelopeSessionId ?? messageSessionId;
736
736
  if (sessionId && peerId) throw new Error("agent-run.send cannot accept both sessionId and peerId.");
737
737
  return {
@@ -792,7 +792,7 @@ function readSessionMaterialization(metadata) {
792
792
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
793
793
  const materialization = value;
794
794
  if (materialization.kind !== "child") throw new Error("session_materialization.kind must be \"child\".");
795
- const parentSessionId = readOptionalString$11(materialization.parentSessionId);
795
+ const parentSessionId = readOptionalString$12(materialization.parentSessionId);
796
796
  if (!parentSessionId) throw new Error("session_materialization.parentSessionId is required.");
797
797
  if (materialization.inheritContext !== true) throw new Error("session_materialization.inheritContext must be true.");
798
798
  return {
@@ -2737,7 +2737,7 @@ function readString$5(value) {
2737
2737
  function readRecord$1(value) {
2738
2738
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
2739
2739
  }
2740
- function readRequiredString$7(value, name) {
2740
+ function readRequiredString$8(value, name) {
2741
2741
  const trimmed = readString$5(value);
2742
2742
  if (!trimmed) throw new Error(`${name} is required`);
2743
2743
  return trimmed;
@@ -2768,9 +2768,9 @@ function readInboundAttachments(value) {
2768
2768
  function toInboundMessage(value) {
2769
2769
  const payload = readRecord$1(value);
2770
2770
  return {
2771
- channel: readRequiredString$7(payload.channelId, "channelId"),
2772
- chatId: readRequiredString$7(payload.conversationId, "conversationId"),
2773
- senderId: readRequiredString$7(payload.senderId, "senderId"),
2771
+ channel: readRequiredString$8(payload.channelId, "channelId"),
2772
+ chatId: readRequiredString$8(payload.conversationId, "conversationId"),
2773
+ senderId: readRequiredString$8(payload.senderId, "senderId"),
2774
2774
  content: readTextContent(payload.content),
2775
2775
  timestamp: /* @__PURE__ */ new Date(),
2776
2776
  attachments: readInboundAttachments(payload.attachments),
@@ -2792,7 +2792,7 @@ function normalizeAuthPollResult(value) {
2792
2792
  if (!value) return null;
2793
2793
  const record = readRecord$1(value);
2794
2794
  return {
2795
- channel: readRequiredString$7(record.channel, "channel"),
2795
+ channel: readRequiredString$8(record.channel, "channel"),
2796
2796
  status: record.status,
2797
2797
  message: readString$5(record.message),
2798
2798
  nextPollMs: readOptionalNumber(record.nextPollMs),
@@ -2890,7 +2890,7 @@ var ExtensionRuntimeService = class {
2890
2890
  if (running.length > 0) console.log(`✓ Extensions started: ${running.map((entry) => entry.manifest.id).join(", ")}`);
2891
2891
  };
2892
2892
  getExtensionProcessToken = (extensionId) => {
2893
- const id = readRequiredString$7(extensionId, "extensionId");
2893
+ const id = readRequiredString$8(extensionId, "extensionId");
2894
2894
  const current = this.extensionTokens.get(id);
2895
2895
  if (current) return current;
2896
2896
  const token = randomUUID();
@@ -2953,7 +2953,7 @@ var ExtensionRuntimeService = class {
2953
2953
  };
2954
2954
  handleChannelConfigGet = (envelope, context) => {
2955
2955
  this.assertAuthorized(envelope, context);
2956
- const channelId = readRequiredString$7(readRecord$1(envelope.payload).channelId, "channelId");
2956
+ const channelId = readRequiredString$8(readRecord$1(envelope.payload).channelId, "channelId");
2957
2957
  return { config: this.options.getConfig().channels[channelId] ?? {} };
2958
2958
  };
2959
2959
  handleChannelMessageSubmit = async (envelope, context) => {
@@ -2968,9 +2968,9 @@ var ExtensionRuntimeService = class {
2968
2968
  handleChannelCommandExecute = async (envelope, context) => {
2969
2969
  this.assertAuthorized(envelope, context);
2970
2970
  const payload = readRecord$1(envelope.payload);
2971
- const channel = readRequiredString$7(payload.channelId, "channelId");
2972
- const chatId = readRequiredString$7(payload.conversationId, "conversationId");
2973
- const senderId = readRequiredString$7(payload.senderId, "senderId");
2971
+ const channel = readRequiredString$8(payload.channelId, "channelId");
2972
+ const chatId = readRequiredString$8(payload.conversationId, "conversationId");
2973
+ const senderId = readRequiredString$8(payload.senderId, "senderId");
2974
2974
  const metadata = readRecord$1(payload.metadata);
2975
2975
  const config = this.options.getConfig();
2976
2976
  const registry = new CommandRegistry(config, this.options.sessionManager);
@@ -2997,7 +2997,7 @@ var ExtensionRuntimeService = class {
2997
2997
  content: "",
2998
2998
  ephemeral: true
2999
2999
  };
3000
- return await registry.execute(readRequiredString$7(payload.commandName, "commandName"), readRecord$1(payload.args), {
3000
+ return await registry.execute(readRequiredString$8(payload.commandName, "commandName"), readRecord$1(payload.args), {
3001
3001
  channel,
3002
3002
  chatId,
3003
3003
  senderId,
@@ -3007,7 +3007,7 @@ var ExtensionRuntimeService = class {
3007
3007
  handleExtensionResponse = (envelope, context) => {
3008
3008
  this.assertAuthorized(envelope, context);
3009
3009
  const payload = readRecord$1(envelope.payload);
3010
- const requestId = readRequiredString$7(payload.requestId, "requestId");
3010
+ const requestId = readRequiredString$8(payload.requestId, "requestId");
3011
3011
  const pending = this.pendingRequests.get(requestId);
3012
3012
  if (!pending) return { accepted: false };
3013
3013
  this.pendingRequests.delete(requestId);
@@ -3293,6 +3293,7 @@ var LlmProviderManager = class {
3293
3293
  models: [],
3294
3294
  modelConfig: {}
3295
3295
  },
3296
+ apiKey: this.resolveProviderApiKey(input.providerName, input.apiKey),
3296
3297
  apiBase: input.apiBase ?? null,
3297
3298
  model: input.defaultModel
3298
3299
  }).chat({
@@ -3315,6 +3316,7 @@ var LlmProviderManager = class {
3315
3316
  providerId: route.providerId,
3316
3317
  providerName: name,
3317
3318
  provider,
3319
+ apiKey: this.resolveProviderApiKey(name, provider?.apiKey),
3318
3320
  apiBase: provider?.apiBase ?? providerSpec?.defaultApiBase ?? null,
3319
3321
  modelConfig: {
3320
3322
  ...specModelConfig,
@@ -3345,7 +3347,7 @@ var LlmProviderManager = class {
3345
3347
  provider,
3346
3348
  spec: providerType ? this.providerRegistry.findProviderByName(providerType) : void 0
3347
3349
  };
3348
- }).filter((entry) => Boolean(entry.name && entry.spec)).filter((entry) => entry.provider?.enabled !== false && Boolean(entry.provider?.apiKey)).filter((entry) => entry.spec.keywords.some((keyword) => modelLower.includes(keyword)));
3350
+ }).filter((entry) => Boolean(entry.name && entry.spec)).filter((entry) => entry.provider.enabled !== false && Boolean(this.resolveProviderApiKey(entry.name, entry.provider.apiKey))).filter((entry) => entry.spec.keywords.some((keyword) => modelLower.includes(keyword)));
3349
3351
  if (keywordMatches.length === 1) {
3350
3352
  const match = keywordMatches[0];
3351
3353
  const providerId = match.providerId;
@@ -3363,7 +3365,7 @@ var LlmProviderManager = class {
3363
3365
  model
3364
3366
  };
3365
3367
  const builtinNames = new Set(specs.map((spec) => spec.name));
3366
- const enabledProviders = Object.entries(providers).filter(([, provider]) => provider.enabled !== false && Boolean(provider.apiKey));
3368
+ const enabledProviders = Object.entries(providers).filter(([providerId, provider]) => provider.enabled !== false && Boolean(this.resolveProviderApiKey(this.resolveProviderType(providerId, provider), provider.apiKey)));
3367
3369
  const enabledBuiltin = enabledProviders.filter(([name]) => builtinNames.has(name));
3368
3370
  if (enabledBuiltin.length === 1) {
3369
3371
  const [name, provider] = enabledBuiltin[0];
@@ -3416,6 +3418,11 @@ var LlmProviderManager = class {
3416
3418
  if (configuredType && this.providerRegistry.findProviderByName(configuredType)) return configuredType;
3417
3419
  return this.providerRegistry.findProviderByName(providerId) ? providerId : null;
3418
3420
  };
3421
+ resolveProviderApiKey = (providerName, configuredApiKey) => {
3422
+ const normalizedConfiguredKey = typeof configuredApiKey === "string" ? configuredApiKey.trim() : "";
3423
+ if (normalizedConfiguredKey) return normalizedConfiguredKey;
3424
+ return (providerName ? this.providerRegistry.findProviderByName(providerName)?.anonymousApiKey?.trim() : "") || null;
3425
+ };
3419
3426
  rewriteModelForTemplate = (model, providerId, providerType) => {
3420
3427
  const prefix = `${providerId}/`;
3421
3428
  if (!model.startsWith(prefix)) return model;
@@ -3431,7 +3438,7 @@ var LlmProviderManager = class {
3431
3438
  return rewritten;
3432
3439
  };
3433
3440
  getOrCreateProvider = (route) => {
3434
- if (!route.provider?.apiKey && !route.model.startsWith("bedrock/")) return this.missingProvider;
3441
+ if (!route.apiKey && !route.model.startsWith("bedrock/")) return this.missingProvider;
3435
3442
  const cacheKey = this.buildCacheKey(route);
3436
3443
  const cached = this.providerPool.get(cacheKey);
3437
3444
  if (cached) return cached;
@@ -3441,7 +3448,7 @@ var LlmProviderManager = class {
3441
3448
  };
3442
3449
  createProvider = (route) => {
3443
3450
  return new LiteLLMProvider({
3444
- apiKey: route.provider?.apiKey ?? null,
3451
+ apiKey: route.apiKey,
3445
3452
  apiBase: route.apiBase,
3446
3453
  defaultModel: route.model,
3447
3454
  extraHeaders: route.provider?.extraHeaders ?? null,
@@ -3465,7 +3472,7 @@ var LlmProviderManager = class {
3465
3472
  return [
3466
3473
  route.providerName ?? "",
3467
3474
  route.providerId ?? "",
3468
- routeProvider?.apiKey ?? "",
3475
+ route.apiKey ?? "",
3469
3476
  route.apiBase ?? "",
3470
3477
  routeProvider?.wireApi ?? "",
3471
3478
  headersFingerprint(routeProvider?.extraHeaders ?? null)
@@ -3689,6 +3696,228 @@ var LlmUsageManager = class {
3689
3696
  };
3690
3697
  };
3691
3698
  //#endregion
3699
+ //#region src/stores/inbox-delivery.store.ts
3700
+ const INBOX_DELIVERY_STORE_VERSION = 1;
3701
+ var InboxDeliveryStoreError = class extends Error {
3702
+ constructor(message) {
3703
+ super(message);
3704
+ this.name = "InboxDeliveryStoreError";
3705
+ }
3706
+ };
3707
+ var InboxDeliveryStore = class {
3708
+ constructor(storePath) {
3709
+ this.storePath = storePath;
3710
+ }
3711
+ list = async () => {
3712
+ try {
3713
+ return this.parseStoreFile(await readFile(this.storePath, "utf8")).deliveries;
3714
+ } catch (error) {
3715
+ if (this.isMissingFileError(error)) return [];
3716
+ if (error instanceof SyntaxError) throw new InboxDeliveryStoreError("inbox delivery store contains invalid JSON");
3717
+ throw error;
3718
+ }
3719
+ };
3720
+ save = async (deliveries) => {
3721
+ const tempPath = `${this.storePath}.${randomUUID()}.tmp`;
3722
+ const storeFile = {
3723
+ version: INBOX_DELIVERY_STORE_VERSION,
3724
+ deliveries: deliveries.map((delivery) => structuredClone(delivery))
3725
+ };
3726
+ await mkdir(dirname(this.storePath), { recursive: true });
3727
+ try {
3728
+ await writeFile(tempPath, `${JSON.stringify(storeFile, null, 2)}\n`, "utf8");
3729
+ await rename(tempPath, this.storePath);
3730
+ } catch (error) {
3731
+ await rm(tempPath, { force: true }).catch(() => void 0);
3732
+ throw error;
3733
+ }
3734
+ };
3735
+ parseStoreFile = (source) => {
3736
+ const value = JSON.parse(source);
3737
+ if (!this.isRecord(value) || value.version !== INBOX_DELIVERY_STORE_VERSION || !Array.isArray(value.deliveries) || !value.deliveries.every(this.isDelivery)) throw new InboxDeliveryStoreError("inbox delivery store has an unsupported structure");
3738
+ return {
3739
+ version: INBOX_DELIVERY_STORE_VERSION,
3740
+ deliveries: value.deliveries.map((delivery) => structuredClone(delivery))
3741
+ };
3742
+ };
3743
+ isDelivery = (value) => {
3744
+ if (!this.isRecord(value) || !this.isSource(value.source)) return false;
3745
+ return typeof value.id === "string" && typeof value.title === "string" && (value.summary === null || typeof value.summary === "string") && typeof value.content === "string" && (value.contentType === "markdown" || value.contentType === "html") && typeof value.createdAt === "string" && typeof value.updatedAt === "string" && this.isOptionalTimestamp(value.presentedAt) && this.isOptionalTimestamp(value.readAt) && this.isOptionalTimestamp(value.archivedAt) && (value.conversationSessionId === null || typeof value.conversationSessionId === "string");
3746
+ };
3747
+ isSource = (value) => this.isRecord(value) && value.kind === "agent" && this.isOptionalString(value.agentId) && this.isOptionalString(value.sessionId) && this.isOptionalString(value.toolCallId) && this.isOptionalString(value.filePath);
3748
+ isOptionalString = (value) => value === null || typeof value === "string";
3749
+ isOptionalTimestamp = (value) => this.isOptionalString(value);
3750
+ isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3751
+ isMissingFileError = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
3752
+ };
3753
+ //#endregion
3754
+ //#region src/managers/inbox-delivery.manager.ts
3755
+ const MAX_TITLE_LENGTH = 160;
3756
+ const MAX_SUMMARY_LENGTH = 500;
3757
+ const MAX_INBOX_DELIVERY_CONTENT_LENGTH = 512 * 1024;
3758
+ var InboxDeliveryError = class extends Error {
3759
+ constructor(code, message) {
3760
+ super(message);
3761
+ this.code = code;
3762
+ this.name = "InboxDeliveryError";
3763
+ }
3764
+ };
3765
+ function isInboxDeliveryError(error) {
3766
+ return error instanceof InboxDeliveryError;
3767
+ }
3768
+ var InboxDeliveryManager = class {
3769
+ store;
3770
+ writeQueue = Promise.resolve();
3771
+ constructor(options) {
3772
+ this.options = options;
3773
+ this.store = new InboxDeliveryStore(options.storePath);
3774
+ }
3775
+ listDeliveries = async () => {
3776
+ await this.writeQueue;
3777
+ const deliveries = (await this.store.list()).sort((left, right) => right.createdAt.localeCompare(left.createdAt));
3778
+ return {
3779
+ deliveries,
3780
+ total: deliveries.length,
3781
+ unreadCount: deliveries.filter((delivery) => !delivery.readAt && !delivery.archivedAt).length,
3782
+ unpresentedCount: deliveries.filter((delivery) => !delivery.presentedAt && !delivery.readAt && !delivery.archivedAt).length
3783
+ };
3784
+ };
3785
+ getDelivery = async (deliveryId) => {
3786
+ await this.writeQueue;
3787
+ const delivery = (await this.store.list()).find(({ id }) => id === deliveryId);
3788
+ return delivery ? structuredClone(delivery) : null;
3789
+ };
3790
+ createDelivery = async (input) => await this.mutate(async () => {
3791
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3792
+ const delivery = {
3793
+ id: randomUUID(),
3794
+ title: this.normalizeRequiredText(input.title, "title", MAX_TITLE_LENGTH),
3795
+ summary: this.normalizeOptionalText(input.summary, "summary", MAX_SUMMARY_LENGTH),
3796
+ content: this.normalizeRequiredText(input.content, "content", MAX_INBOX_DELIVERY_CONTENT_LENGTH),
3797
+ contentType: input.contentType,
3798
+ source: structuredClone(input.source),
3799
+ createdAt: now,
3800
+ updatedAt: now,
3801
+ presentedAt: null,
3802
+ readAt: null,
3803
+ archivedAt: null,
3804
+ conversationSessionId: null
3805
+ };
3806
+ const deliveries = await this.store.list();
3807
+ await this.store.save([delivery, ...deliveries]);
3808
+ this.publishChange(delivery.id, "upsert");
3809
+ return structuredClone(delivery);
3810
+ });
3811
+ updateDeliveryState = async (deliveryId, action) => await this.mutate(async () => {
3812
+ const deliveries = await this.store.list();
3813
+ const index = deliveries.findIndex(({ id }) => id === deliveryId);
3814
+ if (index < 0) throw this.notFound(deliveryId);
3815
+ const current = deliveries[index];
3816
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3817
+ const next = this.applyStateAction(current, action, now);
3818
+ deliveries[index] = next;
3819
+ await this.store.save(deliveries);
3820
+ this.publishChange(deliveryId, "upsert");
3821
+ return structuredClone(next);
3822
+ });
3823
+ deleteDelivery = async (deliveryId) => await this.mutate(async () => {
3824
+ const deliveries = await this.store.list();
3825
+ const remaining = deliveries.filter(({ id }) => id !== deliveryId);
3826
+ if (remaining.length === deliveries.length) return false;
3827
+ await this.store.save(remaining);
3828
+ this.publishChange(deliveryId, "delete");
3829
+ return true;
3830
+ });
3831
+ continueInChat = async (deliveryId) => await this.mutate(async () => {
3832
+ const deliveries = await this.store.list();
3833
+ const index = deliveries.findIndex(({ id }) => id === deliveryId);
3834
+ if (index < 0) throw this.notFound(deliveryId);
3835
+ const current = deliveries[index];
3836
+ const existingSession = current.conversationSessionId ? await this.options.sessionManager.getSessionRecord(current.conversationSessionId) : null;
3837
+ let sessionId = current.conversationSessionId;
3838
+ let created = false;
3839
+ if (!sessionId || !existingSession) {
3840
+ sessionId = (await this.options.sessionManager.createSession({
3841
+ sourceSessionMetadata: {},
3842
+ metadataOverrides: { [INBOX_DELIVERY_SESSION_METADATA_KEY]: current.id },
3843
+ task: `Continue discussing inbox delivery: ${current.title}`,
3844
+ title: current.title,
3845
+ agentId: current.source.agentId ?? void 0
3846
+ })).sessionId;
3847
+ created = true;
3848
+ }
3849
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3850
+ const delivery = {
3851
+ ...current,
3852
+ updatedAt: now,
3853
+ presentedAt: current.presentedAt ?? now,
3854
+ readAt: current.readAt ?? now,
3855
+ conversationSessionId: sessionId
3856
+ };
3857
+ deliveries[index] = delivery;
3858
+ await this.store.save(deliveries);
3859
+ this.publishChange(deliveryId, "upsert");
3860
+ return {
3861
+ delivery: structuredClone(delivery),
3862
+ sessionId,
3863
+ created
3864
+ };
3865
+ });
3866
+ applyStateAction = (delivery, action, now) => {
3867
+ switch (action) {
3868
+ case "present": return {
3869
+ ...delivery,
3870
+ updatedAt: now,
3871
+ presentedAt: delivery.presentedAt ?? now
3872
+ };
3873
+ case "read": return {
3874
+ ...delivery,
3875
+ updatedAt: now,
3876
+ presentedAt: delivery.presentedAt ?? now,
3877
+ readAt: delivery.readAt ?? now
3878
+ };
3879
+ case "mark_unread": return {
3880
+ ...delivery,
3881
+ updatedAt: now,
3882
+ readAt: null
3883
+ };
3884
+ case "archive": return {
3885
+ ...delivery,
3886
+ updatedAt: now,
3887
+ presentedAt: delivery.presentedAt ?? now,
3888
+ archivedAt: delivery.archivedAt ?? now
3889
+ };
3890
+ case "restore": return {
3891
+ ...delivery,
3892
+ updatedAt: now,
3893
+ archivedAt: null
3894
+ };
3895
+ }
3896
+ };
3897
+ normalizeRequiredText = (value, field, maxLength) => {
3898
+ const normalized = value.trim();
3899
+ if (!normalized || normalized.length > maxLength) throw new InboxDeliveryError(field === "title" ? "INBOX_DELIVERY_INVALID_TITLE" : "INBOX_DELIVERY_INVALID_CONTENT", `${field} must contain between 1 and ${maxLength} characters`);
3900
+ return normalized;
3901
+ };
3902
+ normalizeOptionalText = (value, field, maxLength) => {
3903
+ const normalized = value?.trim() ?? "";
3904
+ if (normalized.length > maxLength) throw new InboxDeliveryError("INBOX_DELIVERY_INVALID_SUMMARY", `${field} must contain at most ${maxLength} characters`);
3905
+ return normalized || null;
3906
+ };
3907
+ notFound = (deliveryId) => new InboxDeliveryError("INBOX_DELIVERY_NOT_FOUND", `inbox delivery not found: ${deliveryId}`);
3908
+ publishChange = (deliveryId, operation) => {
3909
+ this.options.eventBus.emit(eventKeys.inboxDeliveryChanged, {
3910
+ deliveryId,
3911
+ operation
3912
+ }, { source: "inbox-delivery" });
3913
+ };
3914
+ mutate = (operation) => {
3915
+ const result = this.writeQueue.then(operation, operation);
3916
+ this.writeQueue = result.then(() => void 0, () => void 0);
3917
+ return result;
3918
+ };
3919
+ };
3920
+ //#endregion
3692
3921
  //#region src/managers/mcp.manager.ts
3693
3922
  var McpManager = class {
3694
3923
  currentMcpConfig;
@@ -3743,11 +3972,11 @@ function createAgentPeerSessionIdentity(params) {
3743
3972
  }
3744
3973
  function resolveAgentPeerScope(params) {
3745
3974
  const metadata = params.metadata ?? {};
3746
- const explicitScope = readOptionalString$10(metadata["agent_peer_scope"]) ?? readOptionalString$10(metadata.agentPeerScope);
3975
+ const explicitScope = readOptionalString$11(metadata["agent_peer_scope"]) ?? readOptionalString$11(metadata.agentPeerScope);
3747
3976
  if (explicitScope) return explicitScope;
3748
- return `agent:${readOptionalString$10(params.agentId) ?? BUILTIN_MAIN_AGENT_ID}:${readOptionalString$10(params.channel) ?? readOptionalString$10(metadata.channel) ?? "agent-run"}:${readOptionalString$10(metadata.accountId) ?? readOptionalString$10(metadata.account_id) ?? "default"}`;
3977
+ return `agent:${readOptionalString$11(params.agentId) ?? BUILTIN_MAIN_AGENT_ID}:${readOptionalString$11(params.channel) ?? readOptionalString$11(metadata.channel) ?? "agent-run"}:${readOptionalString$11(metadata.accountId) ?? readOptionalString$11(metadata.account_id) ?? "default"}`;
3749
3978
  }
3750
- function readOptionalString$10(value) {
3979
+ function readOptionalString$11(value) {
3751
3980
  if (typeof value !== "string") return;
3752
3981
  return value.trim() || void 0;
3753
3982
  }
@@ -4054,7 +4283,7 @@ function applyLimit(items, limit) {
4054
4283
  if (!Number.isFinite(limit) || typeof limit !== "number" || limit <= 0) return items;
4055
4284
  return items.slice(0, Math.trunc(limit));
4056
4285
  }
4057
- function readOptionalString$9(value) {
4286
+ function readOptionalString$10(value) {
4058
4287
  if (typeof value !== "string") return null;
4059
4288
  const trimmed = value.trim();
4060
4289
  return trimmed.length > 0 ? trimmed : null;
@@ -4107,7 +4336,7 @@ function mergeMetadataOverrides(metadata, overrides) {
4107
4336
  }
4108
4337
  function resolveSessionType(params) {
4109
4338
  const { metadata, runtime, sessionType } = params;
4110
- return readOptionalString$9(runtime) ?? readOptionalString$9(metadata.runtime) ?? readOptionalString$9(sessionType) ?? readOptionalString$9(metadata.session_type) ?? "native";
4339
+ return readOptionalString$10(runtime) ?? readOptionalString$10(metadata.runtime) ?? readOptionalString$10(sessionType) ?? readOptionalString$10(metadata.session_type) ?? "native";
4111
4340
  }
4112
4341
  function applySessionOverrides(params) {
4113
4342
  const { lifecycle, metadata, model, parentSessionId, projectRoot, requestId, sessionType, thinkingLevel, title } = params;
@@ -4117,15 +4346,15 @@ function applySessionOverrides(params) {
4117
4346
  metadata[CHILD_SESSION_LIFECYCLE_METADATA_KEY] = lifecycle;
4118
4347
  if (parentSessionId) metadata[CHILD_SESSION_PARENT_METADATA_KEY] = parentSessionId;
4119
4348
  if (requestId) metadata[CHILD_SESSION_REQUEST_METADATA_KEY] = requestId;
4120
- if (readOptionalString$9(model)) {
4349
+ if (readOptionalString$10(model)) {
4121
4350
  metadata.model = model?.trim();
4122
4351
  metadata.preferred_model = model?.trim();
4123
4352
  }
4124
- if (readOptionalString$9(thinkingLevel)) {
4353
+ if (readOptionalString$10(thinkingLevel)) {
4125
4354
  metadata.thinking = thinkingLevel?.trim();
4126
4355
  metadata.preferred_thinking = thinkingLevel?.trim();
4127
4356
  }
4128
- if (readOptionalString$9(projectRoot)) metadata.project_root = projectRoot?.trim();
4357
+ if (readOptionalString$10(projectRoot)) metadata.project_root = projectRoot?.trim();
4129
4358
  }
4130
4359
  //#endregion
4131
4360
  //#region src/utils/session-context-inheritance.utils.ts
@@ -4136,7 +4365,7 @@ function hasToolCall(message, toolCallId) {
4136
4365
  return message.parts.some((part) => part.type === "tool-invocation" && part.toolCallId === toolCallId);
4137
4366
  }
4138
4367
  function findContextInheritanceAnchor(params) {
4139
- const anchorToolCallId = readOptionalString$9(params.anchorToolCallId);
4368
+ const anchorToolCallId = readOptionalString$10(params.anchorToolCallId);
4140
4369
  if (!anchorToolCallId) return null;
4141
4370
  const index = params.messages.findIndex((message) => hasToolCall(message, anchorToolCallId));
4142
4371
  const message = index >= 0 ? params.messages[index] : void 0;
@@ -4176,7 +4405,7 @@ function createInheritedContextSnapshot(params) {
4176
4405
  enabled: true,
4177
4406
  sourceSessionId: sourceRecord.sessionId,
4178
4407
  anchorKind: anchor ? "tool_call" : "latest_persisted",
4179
- anchorToolCallId: readOptionalString$9(anchorToolCallId),
4408
+ anchorToolCallId: readOptionalString$10(anchorToolCallId),
4180
4409
  anchorMessageId: anchor?.message.id,
4181
4410
  inheritedMessageCount: messages.length
4182
4411
  }
@@ -4232,27 +4461,23 @@ function createProjection(sessionId, preview) {
4232
4461
  preview
4233
4462
  };
4234
4463
  }
4235
- function formatErrorStatus(error) {
4236
- if (typeof error === "string" && error.trim()) return `Run failed: ${truncatePreviewText(error)}`;
4464
+ function readErrorDetail(error) {
4465
+ if (typeof error === "string" && error.trim()) return truncatePreviewText(error);
4237
4466
  if (error && typeof error === "object" && "message" in error) {
4238
4467
  const message = error.message;
4239
- if (typeof message === "string" && message.trim()) return `Run failed: ${truncatePreviewText(message)}`;
4468
+ if (typeof message === "string" && message.trim()) return truncatePreviewText(message);
4240
4469
  }
4241
- return "Run failed";
4242
4470
  }
4243
4471
  function readToolCallId(value) {
4244
4472
  if (typeof value !== "string") return null;
4245
4473
  const trimmed = value.trim();
4246
4474
  return trimmed.length > 0 ? trimmed : null;
4247
4475
  }
4248
- function formatToolDoneStatus(toolName) {
4249
- return toolName ? `Tool call completed: ${toolName}` : "Tool call completed";
4250
- }
4251
4476
  function createSessionActivityPreviewFromNcpEvent(event, timestamp, options = {}) {
4252
4477
  switch (event.type) {
4253
4478
  case NcpEventType.RunStarted: return createProjection(readSessionId(event.payload.sessionId), {
4254
4479
  state: "running",
4255
- statusText: "Thinking",
4480
+ statusKind: "thinking",
4256
4481
  timestamp
4257
4482
  });
4258
4483
  case NcpEventType.RunFinished: return createProjection(readSessionId(event.payload.sessionId), {
@@ -4261,7 +4486,8 @@ function createSessionActivityPreviewFromNcpEvent(event, timestamp, options = {}
4261
4486
  });
4262
4487
  case NcpEventType.RunError: return createProjection(readSessionId(event.payload.sessionId), {
4263
4488
  state: "failed",
4264
- statusText: formatErrorStatus(event.payload.error),
4489
+ statusKind: "run-failed",
4490
+ statusText: readErrorDetail(event.payload.error),
4265
4491
  timestamp
4266
4492
  });
4267
4493
  case NcpEventType.MessageSent: {
@@ -4284,7 +4510,8 @@ function createSessionActivityPreviewFromNcpEvent(event, timestamp, options = {}
4284
4510
  }
4285
4511
  case NcpEventType.MessageFailed: return createProjection(readSessionId(event.payload.sessionId), {
4286
4512
  state: "failed",
4287
- statusText: formatErrorStatus(event.payload.error),
4513
+ statusKind: "run-failed",
4514
+ statusText: readErrorDetail(event.payload.error),
4288
4515
  timestamp
4289
4516
  });
4290
4517
  case NcpEventType.MessageAbort: return createProjection(readSessionId(event.payload.sessionId), {
@@ -4293,7 +4520,8 @@ function createSessionActivityPreviewFromNcpEvent(event, timestamp, options = {}
4293
4520
  });
4294
4521
  case NcpEventType.MessageToolCallStart: return createProjection(readSessionId(event.payload.sessionId), {
4295
4522
  state: "running",
4296
- statusText: `Calling tool: ${event.payload.toolName}`,
4523
+ statusKind: "tool-running",
4524
+ statusText: event.payload.toolName,
4297
4525
  timestamp
4298
4526
  });
4299
4527
  case NcpEventType.MessageToolCallEnd:
@@ -4302,7 +4530,8 @@ function createSessionActivityPreviewFromNcpEvent(event, timestamp, options = {}
4302
4530
  const toolCallId = readToolCallId(event.payload.toolCallId);
4303
4531
  return createProjection(sessionId, {
4304
4532
  state: "running",
4305
- statusText: formatToolDoneStatus(sessionId && toolCallId ? options.readToolName?.(sessionId, toolCallId) ?? null : null),
4533
+ statusKind: "tool-completed",
4534
+ statusText: sessionId && toolCallId ? options.readToolName?.(sessionId, toolCallId) ?? void 0 : void 0,
4306
4535
  timestamp
4307
4536
  });
4308
4537
  }
@@ -4319,10 +4548,17 @@ const SESSION_ACTIVITY_PREVIEW_STATES = new Set([
4319
4548
  "cancelled",
4320
4549
  "idle"
4321
4550
  ]);
4551
+ const SESSION_ACTIVITY_PREVIEW_STATUS_KINDS = new Set([
4552
+ "thinking",
4553
+ "tool-running",
4554
+ "tool-completed",
4555
+ "run-failed",
4556
+ "run-interrupted"
4557
+ ]);
4322
4558
  function isRecord$10(value) {
4323
4559
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4324
4560
  }
4325
- function readOptionalString$8(value) {
4561
+ function readOptionalString$9(value) {
4326
4562
  if (typeof value !== "string") return;
4327
4563
  const trimmed = value.trim();
4328
4564
  return trimmed.length > 0 ? trimmed : void 0;
@@ -4330,13 +4566,15 @@ function readOptionalString$8(value) {
4330
4566
  function readSessionActivityPreviewMetadata(value) {
4331
4567
  if (!isRecord$10(value)) return null;
4332
4568
  const state = value.state;
4333
- const timestamp = readOptionalString$8(value.timestamp);
4569
+ const timestamp = readOptionalString$9(value.timestamp);
4334
4570
  if (!SESSION_ACTIVITY_PREVIEW_STATES.has(state) || !timestamp) return null;
4571
+ const statusKind = readOptionalString$9(value.statusKind);
4335
4572
  return {
4336
4573
  state,
4337
4574
  timestamp,
4338
- ...readOptionalString$8(value.statusText) ? { statusText: readOptionalString$8(value.statusText) } : {},
4339
- ...readOptionalString$8(value.replyText) ? { replyText: readOptionalString$8(value.replyText) } : {}
4575
+ statusKind: SESSION_ACTIVITY_PREVIEW_STATUS_KINDS.has(statusKind) ? statusKind : void 0,
4576
+ statusText: readOptionalString$9(value.statusText),
4577
+ replyText: readOptionalString$9(value.replyText)
4340
4578
  };
4341
4579
  }
4342
4580
  function compareIsoTimestamp(left, right) {
@@ -4353,12 +4591,13 @@ function mergeSessionActivityPreview(current, incoming) {
4353
4591
  return {
4354
4592
  state: incoming.state,
4355
4593
  timestamp: incoming.timestamp,
4356
- ...incoming.statusText ?? (incoming.state === "completed" ? current?.statusText : void 0) ? { statusText: incoming.statusText ?? current?.statusText } : {},
4357
- ...incoming.replyText ?? (incoming.state === "completed" ? current?.replyText : void 0) ? { replyText: incoming.replyText ?? current?.replyText } : {}
4594
+ statusKind: incoming.statusKind ?? (incoming.state === "completed" ? current?.statusKind : void 0),
4595
+ statusText: incoming.statusText ?? (incoming.state === "completed" ? current?.statusText : void 0),
4596
+ replyText: incoming.replyText ?? (incoming.state === "completed" ? current?.replyText : void 0)
4358
4597
  };
4359
4598
  }
4360
4599
  function areSessionActivityPreviewsEqual(left, right) {
4361
- return Boolean(left) && left?.state === right.state && left.timestamp === right.timestamp && left.statusText === right.statusText && left.replyText === right.replyText;
4600
+ return Boolean(left) && left?.state === right.state && left.timestamp === right.timestamp && left.statusKind === right.statusKind && left.statusText === right.statusText && left.replyText === right.replyText;
4362
4601
  }
4363
4602
  function writeSessionActivityPreviewMetadata(metadata, projection) {
4364
4603
  const current = readSessionActivityPreviewMetadata(metadata?.[SESSION_ACTIVITY_PREVIEW_METADATA_KEY]);
@@ -4490,7 +4729,7 @@ var SessionWorkingDirResolver = class {
4490
4729
  return this.resolveContext(params).effectiveWorkspace;
4491
4730
  };
4492
4731
  resolveContext = (params) => {
4493
- const profile = this.agentManager.resolveAgentProfile(readOptionalString$9(params.agentId));
4732
+ const profile = this.agentManager.resolveAgentProfile(readOptionalString$10(params.agentId));
4494
4733
  return resolveSessionProjectContext({
4495
4734
  sessionMetadata: params.metadata,
4496
4735
  workspace: profile.workspace
@@ -4546,9 +4785,9 @@ var SessionManager = class {
4546
4785
  const { agentId: requestedAgentId, contextInheritance, metadataOverrides, model, parentSessionId: rawParentSessionId, projectRoot, requestId: rawRequestId, runtime, sessionId: requestedSessionId, sessionType: requestedSessionType, sourceSessionId, sourceSessionMetadata, task, thinkingLevel, title: requestedTitle } = params;
4547
4786
  const sourceRecord = sourceSessionId ? await this.getSessionRecord(sourceSessionId) : null;
4548
4787
  const metadata = cloneInheritedMetadata(sourceSessionMetadata);
4549
- const title = readOptionalString$9(requestedTitle) ?? summarizeTask(task);
4550
- const parentSessionId = readOptionalString$9(rawParentSessionId);
4551
- const requestId = readOptionalString$9(rawRequestId);
4788
+ const title = readOptionalString$10(requestedTitle) ?? summarizeTask(task);
4789
+ const parentSessionId = readOptionalString$10(rawParentSessionId);
4790
+ const requestId = readOptionalString$10(rawRequestId);
4552
4791
  const sessionType = resolveSessionType({
4553
4792
  runtime,
4554
4793
  sessionType: requestedSessionType,
@@ -4574,8 +4813,8 @@ var SessionManager = class {
4574
4813
  if (normalizedProjectRoot) nextMetadata.project_root = normalizedProjectRoot;
4575
4814
  else delete nextMetadata.project_root;
4576
4815
  }
4577
- const agentId = readOptionalString$9(requestedAgentId) ?? readOptionalString$9(sourceRecord?.agentId) ?? BUILTIN_MAIN_AGENT_ID;
4578
- const sessionId = readOptionalString$9(requestedSessionId) ?? buildSessionId();
4816
+ const agentId = readOptionalString$10(requestedAgentId) ?? readOptionalString$10(sourceRecord?.agentId) ?? BUILTIN_MAIN_AGENT_ID;
4817
+ const sessionId = readOptionalString$10(requestedSessionId) ?? buildSessionId();
4579
4818
  const inheritedContext = createSessionContextInheritance({
4580
4819
  childSessionId: sessionId,
4581
4820
  contextInheritance,
@@ -4670,7 +4909,7 @@ var SessionManager = class {
4670
4909
  return await this.options.journalStore.getSession(normalizedSessionId);
4671
4910
  };
4672
4911
  listSessions = async (options) => {
4673
- const peerId = readOptionalString$9(options?.peerId);
4912
+ const peerId = readOptionalString$10(options?.peerId);
4674
4913
  return applyLimit((await this.options.journalStore.listSessionSummaries()).filter((summary) => !peerId || summary.peerId === peerId).map(this.workingDirResolver.withWorkingDir), options?.limit);
4675
4914
  };
4676
4915
  listSessionMessages = async (sessionId, options) => {
@@ -4733,9 +4972,9 @@ var SessionManager = class {
4733
4972
  };
4734
4973
  createAgentRunSession = async (params) => {
4735
4974
  const { agentId, agentRuntimeId: requestedAgentRuntimeId, channel, contextInheritance, metadata, model, parentSessionId: rawParentSessionId, peerId: rawPeerId, projectRoot, sessionId, sourceSessionId: rawSourceSessionId, sourceSessionMetadata: requestedSourceSessionMetadata, task, thinkingEffort } = params;
4736
- const peerId = readOptionalString$9(rawPeerId);
4737
- const parentSessionId = readOptionalString$9(rawParentSessionId);
4738
- const sourceSessionId = readOptionalString$9(rawSourceSessionId);
4975
+ const peerId = readOptionalString$10(rawPeerId);
4976
+ const parentSessionId = readOptionalString$10(rawParentSessionId);
4977
+ const sourceSessionId = readOptionalString$10(rawSourceSessionId);
4739
4978
  const sourceRecord = sourceSessionId ? await this.getSessionRecord(sourceSessionId) : null;
4740
4979
  const sourceSessionMetadata = requestedSourceSessionMetadata ?? sourceRecord?.metadata ?? {};
4741
4980
  const agentRuntimeId = requestedAgentRuntimeId ?? readAgentRuntimeId(sourceSessionMetadata) ?? "native";
@@ -4745,7 +4984,7 @@ var SessionManager = class {
4745
4984
  metadata,
4746
4985
  peerId
4747
4986
  }) : void 0;
4748
- const requestedSessionId = readOptionalString$9(sessionId);
4987
+ const requestedSessionId = readOptionalString$10(sessionId);
4749
4988
  const created = await this.createSession({
4750
4989
  contextInheritance,
4751
4990
  parentSessionId: parentSessionId ?? void 0,
@@ -5928,14 +6167,14 @@ function parsePanelAppFolderManifest(raw) {
5928
6167
  throw new Error(`panel-app.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
5929
6168
  }
5930
6169
  if (!isRecord$6(parsed)) throw new Error("panel-app.json must contain an object.");
5931
- const id = readOptionalString$7(parsed, "id");
6170
+ const id = readOptionalString$8(parsed, "id");
5932
6171
  if (id && !PANEL_APP_ID_PATTERN.test(id)) throw new Error("panel app id must be kebab-case.");
5933
6172
  return {
5934
6173
  id,
5935
- title: readRequiredString$6(parsed, "title"),
5936
- description: readOptionalString$7(parsed, "description"),
5937
- icon: readOptionalString$7(parsed, "icon"),
5938
- entry: readRequiredString$6(parsed, "entry"),
6174
+ title: readRequiredString$7(parsed, "title"),
6175
+ description: readOptionalString$8(parsed, "description"),
6176
+ icon: readOptionalString$8(parsed, "icon"),
6177
+ entry: readRequiredString$7(parsed, "entry"),
5939
6178
  capabilities: readStringArray$1(parsed.capabilities, "capabilities"),
5940
6179
  client: readOptionalBoolean$2(parsed, "client"),
5941
6180
  serviceActions: readStringArray$1(parsed.actions, "actions")
@@ -5995,12 +6234,12 @@ function parseTokenList(content) {
5995
6234
  if (!content) return [];
5996
6235
  return [...new Set(content.split(/[,\s]+/).map((entry) => entry.trim()).filter(Boolean))];
5997
6236
  }
5998
- function readRequiredString$6(record, key) {
5999
- const value = readOptionalString$7(record, key);
6237
+ function readRequiredString$7(record, key) {
6238
+ const value = readOptionalString$8(record, key);
6000
6239
  if (!value) throw new Error(`panel app ${key} is required.`);
6001
6240
  return value;
6002
6241
  }
6003
- function readOptionalString$7(record, key) {
6242
+ function readOptionalString$8(record, key) {
6004
6243
  return typeof record[key] === "string" && record[key].trim() ? record[key].trim() : void 0;
6005
6244
  }
6006
6245
  function readOptionalBoolean$2(record, key) {
@@ -7147,17 +7386,17 @@ function parseServiceAppManifest(raw) {
7147
7386
  throw new Error(`service-app.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
7148
7387
  }
7149
7388
  if (!isRecord$4(parsed)) throw new Error("service-app.json must contain an object.");
7150
- const id = readRequiredString$5(parsed, "id");
7389
+ const id = readRequiredString$6(parsed, "id");
7151
7390
  if (!SERVICE_APP_ID_PATTERN.test(id)) throw new Error("service app id must be kebab-case.");
7152
- const protocol = readOptionalString$6(parsed, "protocol") ?? "mcp";
7391
+ const protocol = readOptionalString$7(parsed, "protocol") ?? "mcp";
7153
7392
  if (protocol !== "mcp") throw new Error("service app protocol must be mcp.");
7154
7393
  return {
7155
7394
  id,
7156
- title: readRequiredString$5(parsed, "title"),
7157
- description: readOptionalString$6(parsed, "description"),
7395
+ title: readRequiredString$6(parsed, "title"),
7396
+ description: readOptionalString$7(parsed, "description"),
7158
7397
  enabled: readOptionalBoolean$1(parsed, "enabled") ?? true,
7159
7398
  protocol,
7160
- command: readRequiredString$5(parsed, "command"),
7399
+ command: readRequiredString$6(parsed, "command"),
7161
7400
  args: readStringArray(parsed.args, "args"),
7162
7401
  actions: readManifestActions(parsed.actions)
7163
7402
  };
@@ -7170,25 +7409,25 @@ function readManifestActions(value) {
7170
7409
  for (const [name, action] of Object.entries(value)) {
7171
7410
  if (!name.trim()) throw new Error("service app action name cannot be empty.");
7172
7411
  if (!isRecord$4(action)) throw new Error(`service app action ${name} must be an object.`);
7173
- const risk = readOptionalString$6(action, "risk");
7412
+ const risk = readOptionalString$7(action, "risk");
7174
7413
  if (risk !== void 0 && !SERVICE_ACTION_RISKS.has(risk)) throw new Error(`service app action ${name} has invalid risk.`);
7175
7414
  const inputSchema = action.inputSchema;
7176
7415
  if (inputSchema !== void 0 && !isRecord$4(inputSchema)) throw new Error(`service app action ${name} inputSchema must be an object.`);
7177
7416
  actions[name] = {
7178
7417
  risk,
7179
- title: readOptionalString$6(action, "title"),
7180
- description: readOptionalString$6(action, "description"),
7418
+ title: readOptionalString$7(action, "title"),
7419
+ description: readOptionalString$7(action, "description"),
7181
7420
  inputSchema
7182
7421
  };
7183
7422
  }
7184
7423
  return actions;
7185
7424
  }
7186
- function readRequiredString$5(record, key) {
7187
- const value = readOptionalString$6(record, key);
7425
+ function readRequiredString$6(record, key) {
7426
+ const value = readOptionalString$7(record, key);
7188
7427
  if (!value) throw new Error(`service app ${key} is required.`);
7189
7428
  return value;
7190
7429
  }
7191
- function readOptionalString$6(record, key) {
7430
+ function readOptionalString$7(record, key) {
7192
7431
  return typeof record[key] === "string" && record[key].trim() ? record[key].trim() : void 0;
7193
7432
  }
7194
7433
  function readOptionalBoolean$1(record, key) {
@@ -8018,9 +8257,10 @@ function deduplicateNcpAgentSessionTailMessages(messages) {
8018
8257
  }
8019
8258
  //#endregion
8020
8259
  //#region src/stores/ncp-agent-session-message-projection.store.ts
8021
- const PROJECTION_VERSION = 1;
8260
+ const PROJECTION_VERSION = 2;
8022
8261
  const PROJECTION_ROOT_DIRECTORY = ".message-projections";
8023
8262
  var NcpAgentSessionMessageProjectionStore = class {
8263
+ messageOrdinals = /* @__PURE__ */ new Map();
8024
8264
  constructor(journalDir, source) {
8025
8265
  this.journalDir = journalDir;
8026
8266
  this.source = source;
@@ -8028,7 +8268,7 @@ var NcpAgentSessionMessageProjectionStore = class {
8028
8268
  readMeta = async (sessionId) => {
8029
8269
  try {
8030
8270
  const parsed = JSON.parse(await readFile(this.metaPath(sessionId), "utf-8"));
8031
- 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;
8271
+ if (parsed.version !== PROJECTION_VERSION || parsed.sessionId !== sessionId || !Number.isSafeInteger(parsed.total) || !Number.isSafeInteger(parsed.projectedJournalOffset) || !Number.isSafeInteger(parsed.dataBytes) || parsed.contextWindow !== null && !isRecord$11(parsed.contextWindow)) return null;
8032
8272
  const meta = parsed;
8033
8273
  const [dataStat, offsetsStat] = await Promise.all([stat(this.dataPath(sessionId)), stat(this.offsetsPath(sessionId))]);
8034
8274
  if (dataStat.size !== meta.dataBytes || offsetsStat.size !== meta.total * 42 || meta.total < 0 || meta.projectedJournalOffset < 0 || meta.dataBytes < 0) return null;
@@ -8038,7 +8278,8 @@ var NcpAgentSessionMessageProjectionStore = class {
8038
8278
  }
8039
8279
  };
8040
8280
  rebuild = async (params) => {
8041
- const { contextWindow, messages, projectedJournalOffset, sessionId } = params;
8281
+ const { contextWindow, messages: sourceMessages, projectedJournalOffset, sessionId } = params;
8282
+ const messages = deduplicateNcpAgentSessionTailMessages(sourceMessages);
8042
8283
  const projectionPath = this.projectionPath(sessionId);
8043
8284
  const projectionRoot = dirname(projectionPath);
8044
8285
  await mkdir(projectionRoot, { recursive: true });
@@ -8069,7 +8310,6 @@ var NcpAgentSessionMessageProjectionStore = class {
8069
8310
  version: PROJECTION_VERSION,
8070
8311
  sessionId,
8071
8312
  total: messages.length,
8072
- lastMessageId: messages.at(-1)?.id ?? null,
8073
8313
  projectedJournalOffset,
8074
8314
  dataBytes,
8075
8315
  contextWindow: contextWindow ? structuredClone(contextWindow) : null
@@ -8080,6 +8320,7 @@ var NcpAgentSessionMessageProjectionStore = class {
8080
8320
  force: true
8081
8321
  });
8082
8322
  await rename(temporaryPath, projectionPath);
8323
+ this.messageOrdinals.set(sessionId, new Map(messages.map((message, index) => [message.id, index + 1])));
8083
8324
  } catch (error) {
8084
8325
  await rm(temporaryPath, {
8085
8326
  recursive: true,
@@ -8093,6 +8334,7 @@ var NcpAgentSessionMessageProjectionStore = class {
8093
8334
  const meta = await this.readMeta(sessionId);
8094
8335
  if (!meta) return false;
8095
8336
  const messages = deduplicateNcpAgentSessionTailMessages(sourceMessages);
8337
+ const messageOrdinals = await this.readMessageOrdinals(sessionId, meta);
8096
8338
  const dataFile = await open(this.dataPath(sessionId), "r+");
8097
8339
  const offsetsFile = await open(this.offsetsPath(sessionId), "r+");
8098
8340
  try {
@@ -8105,13 +8347,14 @@ var NcpAgentSessionMessageProjectionStore = class {
8105
8347
  const serializedLocation = Buffer.from(serializeNcpAgentSessionMessageLocation(location), "utf-8");
8106
8348
  await dataFile.write(serialized, 0, serialized.length, meta.dataBytes);
8107
8349
  meta.dataBytes += serialized.length;
8108
- if (message.id === meta.lastMessageId && meta.total > 0) {
8109
- await offsetsFile.write(serializedLocation, 0, 42, (meta.total - 1) * 42);
8350
+ const ordinal = messageOrdinals.get(message.id);
8351
+ if (ordinal) {
8352
+ await offsetsFile.write(serializedLocation, 0, 42, (ordinal - 1) * 42);
8110
8353
  continue;
8111
8354
  }
8112
8355
  await offsetsFile.write(serializedLocation, 0, 42, meta.total * 42);
8113
8356
  meta.total += 1;
8114
- meta.lastMessageId = message.id;
8357
+ messageOrdinals.set(message.id, meta.total);
8115
8358
  }
8116
8359
  await Promise.all([dataFile.sync(), offsetsFile.sync()]);
8117
8360
  } finally {
@@ -8159,7 +8402,8 @@ var NcpAgentSessionMessageProjectionStore = class {
8159
8402
  if (!meta) return null;
8160
8403
  const uniqueTailMessages = deduplicateNcpAgentSessionTailMessages(tailMessages ?? []);
8161
8404
  const tailById = new Map(uniqueTailMessages.map((message) => [message.id, message]));
8162
- const additionalTailMessages = uniqueTailMessages.filter((message) => message.id !== meta.lastMessageId);
8405
+ const messageOrdinals = await this.readMessageOrdinals(sessionId, meta);
8406
+ const additionalTailMessages = uniqueTailMessages.filter((message) => !messageOrdinals.has(message.id));
8163
8407
  const limit = Number.isFinite(requestedLimit) ? Math.min(200, Math.max(1, Math.trunc(requestedLimit))) : 80;
8164
8408
  const boundary = cursor ? decodeNcpAgentSessionMessageCursor(cursor, meta.total + 1) : meta.total + 1;
8165
8409
  const includeTail = !cursor;
@@ -8193,11 +8437,20 @@ var NcpAgentSessionMessageProjectionStore = class {
8193
8437
  }
8194
8438
  };
8195
8439
  delete = async (sessionId) => {
8440
+ this.messageOrdinals.delete(sessionId);
8196
8441
  await rm(this.projectionPath(sessionId), {
8197
8442
  recursive: true,
8198
8443
  force: true
8199
8444
  });
8200
8445
  };
8446
+ readMessageOrdinals = async (sessionId, meta) => {
8447
+ const cached = this.messageOrdinals.get(sessionId);
8448
+ if (cached) return cached;
8449
+ const messages = meta.total > 0 ? await this.readMessages(sessionId, 1, meta.total) : [];
8450
+ const ordinals = new Map(messages.map((message, index) => [message.id, index + 1]));
8451
+ this.messageOrdinals.set(sessionId, ordinals);
8452
+ return ordinals;
8453
+ };
8201
8454
  readMessages = async (sessionId, startOrdinal, endOrdinal) => {
8202
8455
  const count = endOrdinal - startOrdinal + 1;
8203
8456
  const indexBuffer = Buffer.alloc(count * 42);
@@ -9206,13 +9459,13 @@ var ProviderManagerNcpLLMApi = class {
9206
9459
  };
9207
9460
  //#endregion
9208
9461
  //#region src/features/native-runtime/tools/ncp-asset.tools.ts
9209
- function readOptionalString$5(value) {
9462
+ function readOptionalString$6(value) {
9210
9463
  if (typeof value !== "string") return null;
9211
9464
  const trimmed = value.trim();
9212
9465
  return trimmed.length > 0 ? trimmed : null;
9213
9466
  }
9214
9467
  function readOptionalBase64Bytes(value) {
9215
- const base64 = readOptionalString$5(value);
9468
+ const base64 = readOptionalString$6(value);
9216
9469
  if (!base64) return null;
9217
9470
  try {
9218
9471
  return Buffer.from(base64, "base64");
@@ -9263,18 +9516,18 @@ var AssetPutTool = class {
9263
9516
  this.contentBasePath = contentBasePath;
9264
9517
  }
9265
9518
  validateArgs = (args) => {
9266
- const path = readOptionalString$5(args.path);
9267
- const bytesBase64 = readOptionalString$5(args.bytesBase64);
9268
- const fileName = readOptionalString$5(args.fileName);
9519
+ const path = readOptionalString$6(args.path);
9520
+ const bytesBase64 = readOptionalString$6(args.bytesBase64);
9521
+ const fileName = readOptionalString$6(args.fileName);
9269
9522
  if (path && bytesBase64) return ["Provide either path, or bytesBase64 + fileName, not both."];
9270
9523
  if (path) return [];
9271
9524
  if (bytesBase64) return fileName ? [] : ["fileName is required when using bytesBase64."];
9272
9525
  return ["Provide either path, or bytesBase64 + fileName."];
9273
9526
  };
9274
9527
  execute = async (args) => {
9275
- const path = readOptionalString$5(args?.path);
9276
- const fileName = readOptionalString$5(args?.fileName);
9277
- const mimeType = readOptionalString$5(args?.mimeType);
9528
+ const path = readOptionalString$6(args?.path);
9529
+ const fileName = readOptionalString$6(args?.fileName);
9530
+ const mimeType = readOptionalString$6(args?.mimeType);
9278
9531
  const bytes = readOptionalBase64Bytes(args?.bytesBase64);
9279
9532
  if (path) return {
9280
9533
  ok: true,
@@ -9317,8 +9570,8 @@ var AssetExportTool = class {
9317
9570
  this.assetStore = assetStore;
9318
9571
  }
9319
9572
  execute = async (args) => {
9320
- const assetUri = readOptionalString$5(args?.assetUri);
9321
- const targetPath = readOptionalString$5(args?.targetPath);
9573
+ const assetUri = readOptionalString$6(args?.assetUri);
9574
+ const targetPath = readOptionalString$6(args?.targetPath);
9322
9575
  if (!assetUri || !targetPath) throw new Error("asset_export requires assetUri and targetPath.");
9323
9576
  return {
9324
9577
  ok: true,
@@ -9344,7 +9597,7 @@ var AssetStatTool = class {
9344
9597
  this.contentBasePath = contentBasePath;
9345
9598
  }
9346
9599
  execute = async (args) => {
9347
- const assetUri = readOptionalString$5(args?.assetUri);
9600
+ const assetUri = readOptionalString$6(args?.assetUri);
9348
9601
  if (!assetUri) throw new Error("asset_stat requires assetUri.");
9349
9602
  const record = await this.assetStore.statRecord(assetUri);
9350
9603
  if (!record) return {
@@ -9967,6 +10220,30 @@ var CurrentSessionContextProvider = class {
9967
10220
  };
9968
10221
  };
9969
10222
  //#endregion
10223
+ //#region src/contributions/context-provider/providers/inbox-delivery-context.provider.ts
10224
+ var InboxDeliveryContextProvider = class {
10225
+ constructor(deliveryManager, sessionManager) {
10226
+ this.deliveryManager = deliveryManager;
10227
+ this.sessionManager = sessionManager;
10228
+ }
10229
+ provide = async (request) => {
10230
+ if (!request.sessionId) return [];
10231
+ const rawDeliveryId = (await this.sessionManager.getSessionRecord(request.sessionId))?.metadata?.[INBOX_DELIVERY_SESSION_METADATA_KEY];
10232
+ if (typeof rawDeliveryId !== "string" || !rawDeliveryId.trim()) return [];
10233
+ const delivery = await this.deliveryManager.getDelivery(rawDeliveryId);
10234
+ if (!delivery) return [];
10235
+ const lines = [
10236
+ "## Inbox Delivery Context",
10237
+ "This chat was opened from a durable inbox delivery. Treat the following content as the shared subject for this conversation.",
10238
+ `Delivery ID: ${delivery.id}`,
10239
+ `Title: ${delivery.title}`
10240
+ ];
10241
+ if (delivery.summary) lines.push(`Summary: ${delivery.summary}`);
10242
+ lines.push("", "### Delivered content", delivery.content);
10243
+ return [lines.join("\n")];
10244
+ };
10245
+ };
10246
+ //#endregion
9970
10247
  //#region src/contributions/context-provider/providers/execution-policy-context.provider.ts
9971
10248
  function normalizeModel(model) {
9972
10249
  return model?.trim().toLowerCase() ?? "";
@@ -10081,7 +10358,7 @@ const createMessagingContextProvider = () => staticBlock([
10081
10358
  "- For `action=send`, include `message` plus an explicit `to/chatId` whenever the destination is another channel or another conversation.",
10082
10359
  "- Omitting `to/chatId` only replies to the current conversation; if you set `channel` to a different channel than the current session, `to/chatId` is required.",
10083
10360
  "- If multiple channels are configured, pass `channel`.",
10084
- "- If you use `message` (`action=send`) to deliver your user-visible reply, respond with ONLY two blank lines + <noreply/> (avoid duplicate replies)."
10361
+ "- If you use `message` (`action=send`) to deliver your user-visible reply, respond with ONLY <noreply/> (avoid duplicate replies)."
10085
10362
  ]);
10086
10363
  const createMemoryRecallContextProvider = () => staticBlock([
10087
10364
  "## Memory Recall",
@@ -10091,7 +10368,7 @@ const createMemoryRecallContextProvider = () => staticBlock([
10091
10368
  const createSilentRepliesContextProvider = () => staticBlock([
10092
10369
  "## Silent Replies",
10093
10370
  `Silent marker token: ${SILENT_REPLY_TOKEN}`,
10094
- "When you have nothing to say, respond with EXACTLY two blank lines followed by <noreply/>",
10371
+ "When you have nothing to say, respond with EXACTLY <noreply/>",
10095
10372
  "",
10096
10373
  "⚠️ Rules:",
10097
10374
  "- It must be your ENTIRE message — nothing else",
@@ -10099,8 +10376,7 @@ const createSilentRepliesContextProvider = () => staticBlock([
10099
10376
  "- Never wrap it in markdown or code blocks",
10100
10377
  "",
10101
10378
  "❌ Wrong: \"Here's help... <noreply/>\"",
10102
- " Wrong: \"<noreply/>\"",
10103
- "✅ Right: \"\\n\\n<noreply/>\""
10379
+ " Right: \"<noreply/>\""
10104
10380
  ]);
10105
10381
  const createRuntimeContextProvider = () => staticBlock([
10106
10382
  "## Runtime",
@@ -10783,6 +11059,7 @@ var ContextProviderContribution = class {
10783
11059
  new SkillsContextProvider(context),
10784
11060
  createSessionOrchestrationContextProvider(),
10785
11061
  new ExecutionPolicyContextProvider(context),
11062
+ new InboxDeliveryContextProvider(this.kernel.inboxDeliveryManager, this.kernel.sessionManager),
10786
11063
  new CurrentSessionContextProvider(context),
10787
11064
  new ReplyFormatContextProvider()
10788
11065
  ]) this.cleanups.push(this.kernel.contextProviderManager.register(provider));
@@ -11127,11 +11404,11 @@ var MessagingToolProvider = class {
11127
11404
  };
11128
11405
  //#endregion
11129
11406
  //#region src/tools/project.tools.ts
11130
- function readRequiredString$4(value, key) {
11407
+ function readRequiredString$5(value, key) {
11131
11408
  if (typeof value !== "string" || !value.trim()) throw new Error(`${key} must be a non-empty string.`);
11132
11409
  return value.trim();
11133
11410
  }
11134
- function readOptionalString$4(value) {
11411
+ function readOptionalString$5(value) {
11135
11412
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
11136
11413
  }
11137
11414
  var ProjectsListTool = class {
@@ -11182,9 +11459,9 @@ var ProjectsCreateTool = class {
11182
11459
  }
11183
11460
  execute = async (args) => {
11184
11461
  const params = normalizeToolParams(args);
11185
- const name = readRequiredString$4(params.name, "name");
11186
- const rootPath = readOptionalString$4(params.rootPath);
11187
- const template = readOptionalString$4(params.template);
11462
+ const name = readRequiredString$5(params.name, "name");
11463
+ const rootPath = readOptionalString$5(params.rootPath);
11464
+ const template = readOptionalString$5(params.template);
11188
11465
  return JSON.stringify(await this.projects.createProject({
11189
11466
  name,
11190
11467
  ...rootPath ? { rootPath } : {},
@@ -11335,12 +11612,12 @@ var SessionsHistoryTool = class {
11335
11612
  };
11336
11613
  //#endregion
11337
11614
  //#region src/tools/session-request.tools.ts
11338
- function readRequiredString$3(params, key) {
11615
+ function readRequiredString$4(params, key) {
11339
11616
  const value = params[key];
11340
11617
  if (typeof value !== "string" || value.trim().length === 0) throw new Error(`${key} must be a non-empty string.`);
11341
11618
  return value.trim();
11342
11619
  }
11343
- function readOptionalString$3(params, key) {
11620
+ function readOptionalString$4(params, key) {
11344
11621
  const value = params[key];
11345
11622
  if (typeof value !== "string") return;
11346
11623
  const trimmed = value.trim();
@@ -11394,16 +11671,16 @@ var SessionRequestTool = class {
11394
11671
  const params = normalizeToolParams(args);
11395
11672
  const target = params.target;
11396
11673
  if (!target || typeof target !== "object" || Array.isArray(target)) throw new Error("target must be an object.");
11397
- const task = readRequiredString$3(params, "task");
11398
- const notifyMode = readOptionalString$3(params, "notify")?.toLowerCase();
11674
+ const task = readRequiredString$4(params, "task");
11675
+ const notifyMode = readOptionalString$4(params, "notify")?.toLowerCase();
11399
11676
  if (notifyMode !== "none" && notifyMode !== "final_reply") throw new Error("notify must be \"none\" or \"final_reply\".");
11400
11677
  return this.manager.requestSession({
11401
11678
  sourceSessionId: this.sourceSessionId,
11402
11679
  sourceToolCallId: context?.toolCallId,
11403
11680
  updateToolCallResult: context?.updateToolCallResult,
11404
- targetSessionId: readRequiredString$3(target, "session_id"),
11681
+ targetSessionId: readRequiredString$4(target, "session_id"),
11405
11682
  task,
11406
- title: readOptionalString$3(params, "title"),
11683
+ title: readOptionalString$4(params, "title"),
11407
11684
  notify: notifyMode,
11408
11685
  handoffDepth: this.handoffDepth
11409
11686
  });
@@ -11474,23 +11751,23 @@ var SessionSearchTool = class {
11474
11751
  };
11475
11752
  //#endregion
11476
11753
  //#region src/tools/session-spawn.tools.ts
11477
- function readRequiredString$2(value, key) {
11754
+ function readRequiredString$3(value, key) {
11478
11755
  if (typeof value !== "string" || value.trim().length === 0) throw new Error(`${key} must be a non-empty string.`);
11479
11756
  return value.trim();
11480
11757
  }
11481
- function readOptionalString$2(value) {
11758
+ function readOptionalString$3(value) {
11482
11759
  if (typeof value !== "string") return;
11483
11760
  const trimmed = value.trim();
11484
11761
  return trimmed.length > 0 ? trimmed : void 0;
11485
11762
  }
11486
11763
  function readSpawnScope(value) {
11487
- const normalized = readOptionalString$2(value)?.toLowerCase();
11764
+ const normalized = readOptionalString$3(value)?.toLowerCase();
11488
11765
  if (!normalized || normalized === "standalone") return "standalone";
11489
11766
  if (normalized === "child") return "child";
11490
11767
  throw new Error("scope must be \"standalone\" or \"child\".");
11491
11768
  }
11492
11769
  function readSpawnNotify(value) {
11493
- const notifyMode = readOptionalString$2(value)?.toLowerCase();
11770
+ const notifyMode = readOptionalString$3(value)?.toLowerCase();
11494
11771
  if (!notifyMode && typeof value === "undefined") return;
11495
11772
  if (notifyMode === "none" || notifyMode === "final_reply") return notifyMode;
11496
11773
  throw new Error("notify must be \"none\" or \"final_reply\".");
@@ -11558,7 +11835,7 @@ var SessionSpawnTool = class {
11558
11835
  };
11559
11836
  execute = async (args, context) => {
11560
11837
  const { agentId: rawAgentId, model: rawModel, notify: rawNotify, runtime: rawRuntime, scope: rawScope, task: rawTask, title: rawTitle, inheritContext: rawInheritContext } = normalizeToolParams(args);
11561
- const task = readRequiredString$2(rawTask, "task");
11838
+ const task = readRequiredString$3(rawTask, "task");
11562
11839
  const scope = readSpawnScope(rawScope);
11563
11840
  const notify = readSpawnNotify(rawNotify);
11564
11841
  const inheritContext = readInheritContext(rawInheritContext);
@@ -11571,10 +11848,10 @@ var SessionSpawnTool = class {
11571
11848
  updateToolCallResult: context?.updateToolCallResult,
11572
11849
  sourceSessionMetadata: this.sourceSessionMetadata,
11573
11850
  task,
11574
- title: readOptionalString$2(rawTitle),
11575
- agentId: readOptionalString$2(rawAgentId),
11576
- model: readOptionalString$2(rawModel),
11577
- runtime: readOptionalString$2(rawRuntime),
11851
+ title: readOptionalString$3(rawTitle),
11852
+ agentId: readOptionalString$3(rawAgentId),
11853
+ model: readOptionalString$3(rawModel),
11854
+ runtime: readOptionalString$3(rawRuntime),
11578
11855
  contextInheritance,
11579
11856
  handoffDepth: this.handoffDepth,
11580
11857
  parentSessionId,
@@ -11583,11 +11860,11 @@ var SessionSpawnTool = class {
11583
11860
  const session = await this.sessionManager.createSession({
11584
11861
  sourceSessionId: this.sourceSessionId,
11585
11862
  task,
11586
- title: readOptionalString$2(rawTitle),
11863
+ title: readOptionalString$3(rawTitle),
11587
11864
  sourceSessionMetadata: this.sourceSessionMetadata,
11588
- agentId: readOptionalString$2(rawAgentId),
11589
- model: readOptionalString$2(rawModel),
11590
- runtime: readOptionalString$2(rawRuntime),
11865
+ agentId: readOptionalString$3(rawAgentId),
11866
+ model: readOptionalString$3(rawModel),
11867
+ runtime: readOptionalString$3(rawRuntime),
11591
11868
  contextInheritance,
11592
11869
  parentSessionId
11593
11870
  });
@@ -11611,7 +11888,7 @@ var SessionSpawnTool = class {
11611
11888
  };
11612
11889
  //#endregion
11613
11890
  //#region src/tools/session-update.tools.ts
11614
- function readRequiredString$1(value, key) {
11891
+ function readRequiredString$2(value, key) {
11615
11892
  if (typeof value !== "string" || !value.trim()) throw new Error(`${key} must be a non-empty string.`);
11616
11893
  return value.trim();
11617
11894
  }
@@ -11642,10 +11919,10 @@ var SessionsUpdateTool = class {
11642
11919
  }
11643
11920
  execute = async (args) => {
11644
11921
  const params = normalizeToolParams(args);
11645
- const sessionKey = readRequiredString$1(params.sessionKey, "sessionKey");
11922
+ const sessionKey = readRequiredString$2(params.sessionKey, "sessionKey");
11646
11923
  const patch = {};
11647
- if (Object.prototype.hasOwnProperty.call(params, "label")) patch.label = readRequiredString$1(params.label, "label");
11648
- if (Object.prototype.hasOwnProperty.call(params, "projectRoot")) patch.projectRoot = readRequiredString$1(params.projectRoot, "projectRoot");
11924
+ if (Object.prototype.hasOwnProperty.call(params, "label")) patch.label = readRequiredString$2(params.label, "label");
11925
+ if (Object.prototype.hasOwnProperty.call(params, "projectRoot")) patch.projectRoot = readRequiredString$2(params.projectRoot, "projectRoot");
11649
11926
  if (patch.label === void 0 && patch.projectRoot === void 0) throw new Error("label or projectRoot is required.");
11650
11927
  const session = await this.sessions.patchSessionSettings(sessionKey, patch);
11651
11928
  if (!session) throw new Error(`Session not found: ${sessionKey}`);
@@ -11701,11 +11978,11 @@ const FILE_VIEWERS = [
11701
11978
  "source",
11702
11979
  "rendered"
11703
11980
  ];
11704
- function readRequiredString(value, key) {
11981
+ function readRequiredString$1(value, key) {
11705
11982
  if (typeof value !== "string" || value.trim().length === 0) throw new Error(`${key} must be a non-empty string.`);
11706
11983
  return value.trim();
11707
11984
  }
11708
- function readOptionalString$1(value) {
11985
+ function readOptionalString$2(value) {
11709
11986
  if (typeof value !== "string") return;
11710
11987
  return value.trim() || void 0;
11711
11988
  }
@@ -11715,14 +11992,14 @@ function readOptionalPositiveInteger(value, key) {
11715
11992
  return value;
11716
11993
  }
11717
11994
  function readOptionalEnum(value, key, allowed) {
11718
- const normalized = readOptionalString$1(value);
11995
+ const normalized = readOptionalString$2(value);
11719
11996
  if (!normalized) return;
11720
11997
  if (allowed.includes(normalized)) return normalized;
11721
11998
  const expected = allowed.map((item) => `"${item}"`).join(", ");
11722
11999
  throw new Error(`${key} must be ${expected}.`);
11723
12000
  }
11724
12001
  function readUrl(value) {
11725
- const url = readRequiredString(value, "url");
12002
+ const url = readRequiredString$1(value, "url");
11726
12003
  let parsed;
11727
12004
  try {
11728
12005
  parsed = new URL(url);
@@ -11734,13 +12011,13 @@ function readUrl(value) {
11734
12011
  }
11735
12012
  function readCommonRequestFields(params, allowedPurposes) {
11736
12013
  return {
11737
- title: readOptionalString$1(params.title),
12014
+ title: readOptionalString$2(params.title),
11738
12015
  purpose: readOptionalEnum(params.purpose, "purpose", allowedPurposes)
11739
12016
  };
11740
12017
  }
11741
12018
  function normalizeShowFileArgs(args) {
11742
12019
  const params = normalizeToolParams(args);
11743
- const path = readRequiredString(params.path, "path");
12020
+ const path = readRequiredString$1(params.path, "path");
11744
12021
  const viewer = readOptionalEnum(params.viewer, "viewer", FILE_VIEWERS) ?? "auto";
11745
12022
  const contentParams = readUiContentParams(params.params);
11746
12023
  if (contentParams && (viewer === "source" || !/\.html?$/i.test(path))) throw new Error("params are supported only for rendered HTML file previews.");
@@ -11770,14 +12047,14 @@ function normalizeShowUrlArgs(args) {
11770
12047
  }
11771
12048
  function normalizeShowPanelAppArgs(args) {
11772
12049
  const params = normalizeToolParams(args);
11773
- const path = readOptionalString$1(params.path);
12050
+ const path = readOptionalString$2(params.path);
11774
12051
  const contentParams = readUiContentParams(params.params);
11775
12052
  if (path && !isAbsolute(path)) throw new Error("path must be an absolute path.");
11776
12053
  return {
11777
12054
  target: {
11778
12055
  type: "panel_app",
11779
12056
  payload: {
11780
- appId: readRequiredString(params.appId, "appId"),
12057
+ appId: readRequiredString$1(params.appId, "appId"),
11781
12058
  path,
11782
12059
  params: contentParams
11783
12060
  }
@@ -11791,7 +12068,7 @@ function summarizeTarget(target) {
11791
12068
  return target.payload.appId;
11792
12069
  }
11793
12070
  function createShowContentEventPayload(request, context) {
11794
- const toolCallId = readOptionalString$1(context?.toolCallId);
12071
+ const toolCallId = readOptionalString$2(context?.toolCallId);
11795
12072
  return {
11796
12073
  id: toolCallId ? `tool:${toolCallId}:show-content` : `show-content:${request.target.type}:${summarizeTarget(request.target)}`,
11797
12074
  toolCallId,
@@ -11941,6 +12218,119 @@ var ShowContentToolProvider = class {
11941
12218
  provide = () => createShowContentTools(this.eventBus);
11942
12219
  };
11943
12220
  //#endregion
12221
+ //#region src/tools/inbox-delivery.tools.ts
12222
+ function readRequiredString(value, key) {
12223
+ if (typeof value !== "string" || value.trim().length === 0) throw new Error(`${key} must be a non-empty string.`);
12224
+ return value.trim();
12225
+ }
12226
+ function readOptionalString$1(value) {
12227
+ if (typeof value !== "string") return null;
12228
+ return value.trim() || null;
12229
+ }
12230
+ function readContentType(value) {
12231
+ if (value === void 0 || value === null) return null;
12232
+ if (value !== "markdown" && value !== "html") throw new Error("contentType must be markdown or html.");
12233
+ return value;
12234
+ }
12235
+ function normalizeRequest(args) {
12236
+ const params = normalizeToolParams(args);
12237
+ const content = readOptionalString$1(params.content);
12238
+ const filePath = readOptionalString$1(params.filePath);
12239
+ if (Boolean(content) === Boolean(filePath)) throw new Error("exactly one of content or filePath must be provided.");
12240
+ if (filePath && !isAbsolute(filePath)) throw new Error("filePath must be an absolute path.");
12241
+ const requestedContentType = readContentType(params.contentType);
12242
+ return {
12243
+ title: readRequiredString(params.title, "title"),
12244
+ summary: readOptionalString$1(params.summary),
12245
+ content,
12246
+ contentType: requestedContentType ?? (filePath && [".htm", ".html"].includes(extname(filePath).toLowerCase()) ? "html" : "markdown"),
12247
+ filePath
12248
+ };
12249
+ }
12250
+ var DeliverToInboxTool = class {
12251
+ name = "deliver_to_inbox";
12252
+ description = "Deliver a durable Markdown or static HTML report, recommendation, or article to the user's NextClaw inbox. Use content for direct text or filePath to snapshot a local UTF-8 text file. Set contentType for direct HTML; .html and .htm files are detected automatically. The user can read it later and continue in a new chat.";
12253
+ parameters = {
12254
+ type: "object",
12255
+ properties: {
12256
+ title: {
12257
+ type: "string",
12258
+ description: "Concise user-facing title, at most 160 characters."
12259
+ },
12260
+ summary: {
12261
+ type: "string",
12262
+ description: "Optional one-paragraph summary, at most 500 characters."
12263
+ },
12264
+ content: {
12265
+ type: "string",
12266
+ description: "Markdown or static HTML content. Provide exactly one of content or filePath."
12267
+ },
12268
+ contentType: {
12269
+ type: "string",
12270
+ enum: ["markdown", "html"],
12271
+ description: "Content format. Defaults to HTML for .html/.htm files and Markdown otherwise."
12272
+ },
12273
+ filePath: {
12274
+ type: "string",
12275
+ description: "Absolute path to a UTF-8 Markdown, HTML, or text file to snapshot."
12276
+ }
12277
+ },
12278
+ required: ["title"],
12279
+ additionalProperties: false
12280
+ };
12281
+ constructor(manager, source) {
12282
+ this.manager = manager;
12283
+ this.source = source;
12284
+ }
12285
+ execute = async (args, context) => {
12286
+ const request = normalizeRequest(args);
12287
+ const content = request.content ?? await this.readFileContent(request.filePath);
12288
+ const delivery = await this.manager.createDelivery({
12289
+ title: request.title,
12290
+ summary: request.summary,
12291
+ content,
12292
+ contentType: request.contentType,
12293
+ source: {
12294
+ kind: "agent",
12295
+ agentId: this.source.agentId,
12296
+ sessionId: this.source.sessionId,
12297
+ toolCallId: readOptionalString$1(context?.toolCallId),
12298
+ filePath: request.filePath
12299
+ }
12300
+ });
12301
+ return {
12302
+ ok: true,
12303
+ deliveryId: delivery.id,
12304
+ title: delivery.title
12305
+ };
12306
+ };
12307
+ readFileContent = async (filePath) => {
12308
+ const file = await stat(filePath);
12309
+ if (!file.isFile()) throw new Error("filePath must point to a regular file.");
12310
+ if (file.size > 524288) throw new Error(`filePath must be at most ${MAX_INBOX_DELIVERY_CONTENT_LENGTH} bytes.`);
12311
+ try {
12312
+ return new TextDecoder("utf-8", { fatal: true }).decode(await readFile(filePath));
12313
+ } catch (error) {
12314
+ if (error instanceof TypeError) throw new Error("filePath must contain valid UTF-8 text.");
12315
+ throw error;
12316
+ }
12317
+ };
12318
+ };
12319
+ function createInboxDeliveryTools(manager, source) {
12320
+ return [new DeliverToInboxTool(manager, source)];
12321
+ }
12322
+ //#endregion
12323
+ //#region src/contributions/tool-provider/providers/inbox-delivery-tool.provider.ts
12324
+ var InboxDeliveryToolProvider = class {
12325
+ constructor(manager) {
12326
+ this.manager = manager;
12327
+ }
12328
+ provide = (request) => createInboxDeliveryTools(this.manager, {
12329
+ agentId: request.agentId ?? null,
12330
+ sessionId: request.sessionId ?? null
12331
+ });
12332
+ };
12333
+ //#endregion
11944
12334
  //#region src/contributions/tool-provider/providers/structured-result-tool.provider.ts
11945
12335
  var StructuredResultToolProvider = class {
11946
12336
  provide = (request) => {
@@ -12015,6 +12405,7 @@ var ToolProviderContribution = class {
12015
12405
  return [
12016
12406
  new StructuredResultToolProvider(),
12017
12407
  new ShowContentToolProvider(this.kernel.eventBus),
12408
+ new InboxDeliveryToolProvider(this.kernel.inboxDeliveryManager),
12018
12409
  new CoreToolProvider(runContextService, this.kernel.getGatewayController),
12019
12410
  new MessagingToolProvider(runContextService, this.kernel.channels, this.kernel.automation, this.kernel.extensions),
12020
12411
  new ProjectToolProvider(this.kernel.projectManager),
@@ -12046,6 +12437,11 @@ function resolveKernelProjectStorePath(options) {
12046
12437
  if (homeDir) return resolve(expandHome(homeDir), "projects", "projects.json");
12047
12438
  return resolve(getDataDir(), "projects", "projects.json");
12048
12439
  }
12440
+ function resolveKernelInboxDeliveryStorePath(options) {
12441
+ const homeDir = options.homeDir?.trim();
12442
+ if (homeDir) return resolve(expandHome(homeDir), "inbox", "deliveries.json");
12443
+ return resolve(getDataDir(), "inbox", "deliveries.json");
12444
+ }
12049
12445
  var NextclawKernelControlManager = class {
12050
12446
  runtimeControl = null;
12051
12447
  installRuntimeControl = (runtimeControl) => {
@@ -12074,6 +12470,7 @@ var NextclawKernel = class {
12074
12470
  assetStore;
12075
12471
  mcpManager;
12076
12472
  sessionManager;
12473
+ inboxDeliveryManager;
12077
12474
  panelAppManager;
12078
12475
  preferenceManager;
12079
12476
  projectManager;
@@ -12122,6 +12519,11 @@ var NextclawKernel = class {
12122
12519
  projectManager: this.projectManager,
12123
12520
  sessionSearch: this.sessionSearch
12124
12521
  });
12522
+ this.inboxDeliveryManager = new InboxDeliveryManager({
12523
+ eventBus: this.eventBus,
12524
+ sessionManager: this.sessionManager,
12525
+ storePath: resolveKernelInboxDeliveryStorePath(options)
12526
+ });
12125
12527
  this.panelAppManager = new PanelAppManager({
12126
12528
  configManager: this.configManager,
12127
12529
  eventBus: this.eventBus,
@@ -12760,6 +13162,6 @@ function resolveLegacyEventType(message) {
12760
13162
  return `message.${role || "other"}`;
12761
13163
  }
12762
13164
  //#endregion
12763
- 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, SessionContextCompactionError, SessionContextCompactionManager, 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, getUiContentParamsBootstrapScript, getUnsignedUpdateManifest, hasLlmUsageTelemetry, injectUiContentParamsBootstrap, isContextCompactionProjectionMessage, isContextCompactionTimelineMessage, isContextWindowSnapshot, isPanelAppAgentCapability, isPanelAppError, isPreferenceError, isProjectError, isReplyCapableChannel, isServiceAppError, isSessionContextCompactionError, 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 };
13165
+ 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, InboxDeliveryError, InboxDeliveryManager, LlmProviderManager, LlmUsageManager, LlmUsageStore, MAX_INBOX_DELIVERY_CONTENT_LENGTH, 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, SessionContextCompactionError, SessionContextCompactionManager, 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, getUiContentParamsBootstrapScript, getUnsignedUpdateManifest, hasLlmUsageTelemetry, injectUiContentParamsBootstrap, isContextCompactionProjectionMessage, isContextCompactionTimelineMessage, isContextWindowSnapshot, isInboxDeliveryError, isPanelAppAgentCapability, isPanelAppError, isPreferenceError, isProjectError, isReplyCapableChannel, isServiceAppError, isSessionContextCompactionError, 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 };
12764
13166
 
12765
13167
  //# sourceMappingURL=index.js.map