@nextclaw/kernel 0.5.0 → 0.5.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { n as getUnsignedUpdateManifest, r as serializeUnsignedUpdateManifest, t as UpdateManifestReader } from "./update-manifest.types-C0qPrjGQ.js";
2
2
  import { createRequire } from "node:module";
3
- import { APP_NAME, AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, CLEAR_THINKING_TOKENS, CONTEXT_COMPACTION_METADATA_KEY, ConfigSchema, ContextCompactionService, ContextWindowBudgetService, CronService, CronTool, DEFAULT_PANELS_DIR, DEFAULT_SERVICE_APPS_DIR, DEFAULT_SESSION_SEARCH_LIMIT, DEFAULT_WORKSPACE_REPOSITORY_IDENTITY_RESOLVER, EditFileTool, ExecTool, ExtensionChannelAdapter, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, LiteLLMProvider, MAX_SESSION_SEARCH_LIMIT, MemoryGetTool, MemorySearchTool, MemoryStore, MessageBus, MessageTool, ProviderRegistry, ReadFileTool, RequestedSkillsMetadataReader, SILENT_REPLY_TOKEN, SessionProjectContextResolver, SessionSearchService, SkillsLoader, THINKING_LEVELS, WebFetchTool, WebSearchTool, WriteFileTool, buildCompressingCompactionCheckpoint, buildConfigSchema, buildContextWindowSnapshot, buildReloadPlan, buildSessionRequestToolResult, buildToolCatalogEntries, createAgentProfile, createAssistantStreamDeltaControlMessage, createAssistantStreamResetControlMessage, createCompletedSessionRequest, createFailedSessionRequest, createRunningSessionRequest, createRuntimeChildEnv, createTypingStopControlMessage, diffConfigPaths, ensureDir, evaluateSilentReply, expandHome, findEffectiveAgentProfile, getConfigPath, getDataDir, getDataPath, getSessionsPath, getWorkspacePath, getWorkspacePathFromConfig, isNextclawControlMessage, loadConfig, mergeExtensionConfigView, modelSupportsVision, normalizeAgentProfileId, normalizeInlineSecretRefs, normalizeProviderModelConfig, normalizeToolParams, parseAgentScopedSessionKey, parseThinkingLevel, readCompressedContextCompactionCheckpoint, readOptionalString, readParentSessionId, readSessionProjectRoot, redactConfigObject, removeAgentProfile, resolveConfigSecrets, resolveDefaultAgentProfileId, resolveEffectiveAgentProfiles, resolveNextclawSelfManageGuidePaths, resolveProviderRuntime, resolveSessionWorkspacePath, resolveThinkingLevel, sanitizeOutboundAssistantContent, saveConfig, summarizeSessionRequestTask, toExtensionConfigView, updateAgentProfile } from "@nextclaw/core";
3
+ import { APP_NAME, AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, CLEAR_THINKING_TOKENS, CONTEXT_COMPACTION_METADATA_KEY, ConfigSchema, ContextCompactionService, ContextWindowBudgetService, CronService, CronTool, DEFAULT_PANELS_DIR, DEFAULT_SERVICE_APPS_DIR, DEFAULT_SESSION_SEARCH_LIMIT, DEFAULT_WORKSPACE_REPOSITORY_IDENTITY_RESOLVER, EditFileTool, ExecTool, ExtensionChannelAdapter, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, LiteLLMProvider, MAX_SESSION_SEARCH_LIMIT, MemoryGetTool, MemorySearchTool, MemoryStore, MessageBus, MessageTool, ProviderRegistry, ReadFileTool, RequestedSkillsMetadataReader, SILENT_REPLY_TOKEN, SessionProjectContextResolver, SessionSearchService, SkillsLoader, THINKING_LEVELS, WebFetchTool, WebSearchTool, WriteFileTool, buildCompressingCompactionCheckpoint, buildConfigSchema, buildContextWindowSnapshot, buildReloadPlan, buildSessionRequestToolResult, buildToolCatalogEntries, createAgentProfile, createAssistantStreamDeltaControlMessage, createAssistantStreamResetControlMessage, createCompletedSessionRequest, createFailedSessionRequest, createRunningSessionRequest, createRuntimeChildEnv, createTypingStopControlMessage, diffConfigPaths, ensureDir, evaluateSilentReply, expandHome, findEffectiveAgentProfile, getConfigPath, getDataDir, getDataPath, getSessionsPath, getWorkspacePath, getWorkspacePathFromConfig, isNextclawControlMessage, loadConfig, mergeExtensionConfigView, modelSupportsVision, normalizeAgentProfileId, normalizeInlineSecretRefs, normalizeModelThinkingCapability, normalizeProviderModelConfig, normalizeToolParams, parseAgentScopedSessionKey, parseThinkingLevel, readCompressedContextCompactionCheckpoint, readOptionalString, readParentSessionId, readSessionProjectRoot, redactConfigObject, removeAgentProfile, resolveConfigSecrets, resolveDefaultAgentProfileId, resolveEffectiveAgentProfiles, resolveNextclawSelfManageGuidePaths, resolveProviderRuntime, resolveSessionWorkspacePath, resolveThinkingLevel, sanitizeOutboundAssistantContent, saveConfig, summarizeSessionRequestTask, toExtensionConfigView, updateAgentProfile } from "@nextclaw/core";
4
4
  import { NcpEventType, sanitizeAssistantReplyTags } from "@nextclaw/ncp";
5
5
  import { LocalAssetStore, buildAssetContentPath, buildNcpUserContent, buildOpenAiFunctionTool, ncpMessageToOpenAiMessages, validateToolArgs } from "@nextclaw/ncp-agent-runtime";
6
6
  import { createHash, createHmac, randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
@@ -703,13 +703,16 @@ var AgentRunRequestManager = class {
703
703
  const contextBlocks = await this.contextProviderManager.buildContext(providerRequest);
704
704
  const tools = await this.toolProviderManager.buildTools(providerRequest);
705
705
  let messageCompletedSeen = false;
706
+ let runtimeFailed = false;
706
707
  lastValueFrom(from(runtime.run(spec, {
707
708
  contextBlocks,
709
+ session,
708
710
  sessionRun,
709
711
  signal: activeRun.signal,
710
712
  tools
711
713
  })).pipe(tap((event) => {
712
714
  const eventsToPublish = [];
715
+ if (event.type === NcpEventType.RunError) runtimeFailed = true;
713
716
  if (event.type === NcpEventType.MessageCompleted) messageCompletedSeen = true;
714
717
  if (event.type === NcpEventType.RunFinished && !messageCompletedSeen) {
715
718
  const message = findCompletedAssistantMessage(sessionRun.getSnapshot().messages, event.payload.messageId);
@@ -730,6 +733,7 @@ var AgentRunRequestManager = class {
730
733
  source: "agent-run-request"
731
734
  });
732
735
  }), catchError(async (error) => {
736
+ runtimeFailed = true;
733
737
  const event = {
734
738
  type: NcpEventType.RunError,
735
739
  payload: {
@@ -744,7 +748,14 @@ var AgentRunRequestManager = class {
744
748
  emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
745
749
  source: "agent-run-request"
746
750
  });
747
- })), { defaultValue: void 0 });
751
+ })), { defaultValue: void 0 }).finally(() => {
752
+ if (!runtimeFailed) return;
753
+ this.agentRuntimeManager.disposeRuntime({
754
+ agentRuntimeId: session.agentRuntimeId,
755
+ session,
756
+ sessionRun
757
+ }).catch(() => void 0);
758
+ });
748
759
  return {
749
760
  sessionId: session.sessionId,
750
761
  userMessageId: message.id,
@@ -855,6 +866,7 @@ async function describeAgentRuntimeSessionTypes(params) {
855
866
  reasonMessage: `Runtime provider unavailable for type "${entry.type}".`,
856
867
  recommendedModel: null,
857
868
  modelSelectionMode: "nextclaw",
869
+ runtimeDefaultThinking: null,
858
870
  cta: {
859
871
  kind: "settings",
860
872
  label: "Configure Runtime"
@@ -873,6 +885,7 @@ async function describeAgentRuntimeSessionTypes(params) {
873
885
  reasonMessage: descriptor?.reasonMessage ?? null,
874
886
  recommendedModel: descriptor?.recommendedModel ?? null,
875
887
  modelSelectionMode: descriptor?.modelSelectionMode ?? "nextclaw",
888
+ runtimeDefaultThinking: descriptor?.runtimeDefaultThinking ?? null,
876
889
  cta: descriptor?.cta ?? null,
877
890
  ...descriptor?.supportedModels ? { supportedModels: descriptor.supportedModels } : {}
878
891
  };
@@ -909,12 +922,8 @@ var AgentRuntimeManager = class {
909
922
  });
910
923
  };
911
924
  getOrCreate = (params) => {
912
- const { agentRuntimeId, session, sessionRun } = params;
913
- const entry = this.getEntry(agentRuntimeId);
914
- const provider = this.getProvider(entry.type);
915
- const reuseScope = this.resolveReuseScope(entry, provider);
916
- const cacheKey = reuseScope === "global" ? entry.id : `${entry.id}:${session.sessionId}`;
917
- const cache = reuseScope === "global" ? this.globalRuntimes : this.sessionRuntimes;
925
+ const { session, sessionRun } = params;
926
+ const { cache, cacheKey, entry, provider } = this.resolveRuntimeCache(params);
918
927
  const existing = cache.get(cacheKey);
919
928
  if (existing) return existing;
920
929
  const agentRuntime = provider.createRuntime({
@@ -925,6 +934,14 @@ var AgentRuntimeManager = class {
925
934
  cache.set(cacheKey, agentRuntime);
926
935
  return agentRuntime;
927
936
  };
937
+ disposeRuntime = async (params) => {
938
+ const { cache, cacheKey } = this.resolveRuntimeCache(params);
939
+ const runtime = cache.get(cacheKey);
940
+ if (!runtime) return false;
941
+ cache.delete(cacheKey);
942
+ await runtime.dispose?.();
943
+ return true;
944
+ };
928
945
  listSessionTypes = async (params) => describeAgentRuntimeSessionTypes({
929
946
  entries: [...this.entries.values()],
930
947
  providers: this.providers,
@@ -954,6 +971,19 @@ var AgentRuntimeManager = class {
954
971
  resolveReuseScope = (entry, provider) => {
955
972
  return entry.config?.reuseScope === "session" || entry.config?.reuseScope === "global" ? entry.config.reuseScope : provider.defaultReuseScope;
956
973
  };
974
+ resolveRuntimeCache = (params) => {
975
+ const { agentRuntimeId, session } = params;
976
+ const entry = this.getEntry(agentRuntimeId);
977
+ const provider = this.getProvider(entry.type);
978
+ const reuseScope = this.resolveReuseScope(entry, provider);
979
+ const cacheKey = reuseScope === "global" ? entry.id : `${entry.id}:${session.sessionId}`;
980
+ return {
981
+ cache: reuseScope === "global" ? this.globalRuntimes : this.sessionRuntimes,
982
+ cacheKey,
983
+ entry,
984
+ provider
985
+ };
986
+ };
957
987
  disposeAllRuntimes = async () => {
958
988
  for (const runtime of [...this.globalRuntimes.values(), ...this.sessionRuntimes.values()]) await runtime.dispose?.();
959
989
  this.globalRuntimes.clear();
@@ -3443,6 +3473,262 @@ function readEventSessionId$1(event) {
3443
3473
  return "payload" in event && "sessionId" in event.payload ? readOptionalMetadataString(event.payload.sessionId) : void 0;
3444
3474
  }
3445
3475
  //#endregion
3476
+ //#region src/contributions/session-activity-preview/utils/session-activity-preview-ncp-event.utils.ts
3477
+ const PREVIEW_TEXT_MAX_LENGTH = 160;
3478
+ function readSessionId(value) {
3479
+ if (typeof value !== "string") return null;
3480
+ const trimmed = value.trim();
3481
+ return trimmed.length > 0 ? trimmed : null;
3482
+ }
3483
+ function compactPreviewText(value) {
3484
+ return value.replace(/\s+/g, " ").trim();
3485
+ }
3486
+ function truncatePreviewText(value) {
3487
+ const compacted = compactPreviewText(value);
3488
+ if (compacted.length <= PREVIEW_TEXT_MAX_LENGTH) return compacted;
3489
+ return compacted.slice(0, PREVIEW_TEXT_MAX_LENGTH - 1).trimEnd();
3490
+ }
3491
+ function readSessionActivityPreviewText(message) {
3492
+ const chunks = [];
3493
+ for (const part of message.parts) if ((part.type === "text" || part.type === "rich-text") && part.text.trim()) chunks.push(part.text);
3494
+ const previewText = truncatePreviewText(chunks.join(" "));
3495
+ return previewText.length > 0 ? previewText : null;
3496
+ }
3497
+ function createProjection(sessionId, preview) {
3498
+ if (!sessionId) return null;
3499
+ return {
3500
+ sessionId,
3501
+ preview
3502
+ };
3503
+ }
3504
+ function formatErrorStatus(error) {
3505
+ if (typeof error === "string" && error.trim()) return `运行出错:${truncatePreviewText(error)}`;
3506
+ if (error && typeof error === "object" && "message" in error) {
3507
+ const message = error.message;
3508
+ if (typeof message === "string" && message.trim()) return `运行出错:${truncatePreviewText(message)}`;
3509
+ }
3510
+ return "运行出错";
3511
+ }
3512
+ function readToolCallId(value) {
3513
+ if (typeof value !== "string") return null;
3514
+ const trimmed = value.trim();
3515
+ return trimmed.length > 0 ? trimmed : null;
3516
+ }
3517
+ function formatToolDoneStatus(toolName) {
3518
+ return toolName ? `工具调用完成:${toolName}` : "工具调用完成";
3519
+ }
3520
+ function createSessionActivityPreviewFromNcpEvent(event, timestamp, options = {}) {
3521
+ switch (event.type) {
3522
+ case NcpEventType.RunStarted: return createProjection(readSessionId(event.payload.sessionId), {
3523
+ state: "running",
3524
+ statusText: "正在思考",
3525
+ timestamp
3526
+ });
3527
+ case NcpEventType.RunFinished: return createProjection(readSessionId(event.payload.sessionId), {
3528
+ state: "completed",
3529
+ timestamp
3530
+ });
3531
+ case NcpEventType.RunError: return createProjection(readSessionId(event.payload.sessionId), {
3532
+ state: "failed",
3533
+ statusText: formatErrorStatus(event.payload.error),
3534
+ timestamp
3535
+ });
3536
+ case NcpEventType.MessageSent: {
3537
+ const text = readSessionActivityPreviewText(event.payload.message);
3538
+ if (!text || event.payload.message.role !== "user") return null;
3539
+ return createProjection(readSessionId(event.payload.sessionId), {
3540
+ state: "running",
3541
+ statusText: text,
3542
+ timestamp: event.payload.message.timestamp || timestamp
3543
+ });
3544
+ }
3545
+ case NcpEventType.MessageCompleted: {
3546
+ const text = readSessionActivityPreviewText(event.payload.message);
3547
+ if (!text || event.payload.message.role !== "assistant") return null;
3548
+ return createProjection(readSessionId(event.payload.sessionId), {
3549
+ state: "completed",
3550
+ replyText: text,
3551
+ timestamp: event.payload.message.timestamp || timestamp
3552
+ });
3553
+ }
3554
+ case NcpEventType.MessageFailed: return createProjection(readSessionId(event.payload.sessionId), {
3555
+ state: "failed",
3556
+ statusText: formatErrorStatus(event.payload.error),
3557
+ timestamp
3558
+ });
3559
+ case NcpEventType.MessageToolCallStart: return createProjection(readSessionId(event.payload.sessionId), {
3560
+ state: "running",
3561
+ statusText: `正在调用工具:${event.payload.toolName}`,
3562
+ timestamp
3563
+ });
3564
+ case NcpEventType.MessageToolCallEnd:
3565
+ case NcpEventType.MessageToolCallResult: {
3566
+ const sessionId = readSessionId(event.payload.sessionId);
3567
+ const toolCallId = readToolCallId(event.payload.toolCallId);
3568
+ return createProjection(sessionId, {
3569
+ state: "running",
3570
+ statusText: formatToolDoneStatus(sessionId && toolCallId ? options.readToolName?.(sessionId, toolCallId) ?? null : null),
3571
+ timestamp
3572
+ });
3573
+ }
3574
+ default: return null;
3575
+ }
3576
+ }
3577
+ //#endregion
3578
+ //#region src/contributions/session-activity-preview/utils/session-activity-preview-metadata.utils.ts
3579
+ const SESSION_ACTIVITY_PREVIEW_METADATA_KEY = "last_activity_preview";
3580
+ const SESSION_ACTIVITY_PREVIEW_STATES = new Set([
3581
+ "running",
3582
+ "completed",
3583
+ "failed",
3584
+ "idle"
3585
+ ]);
3586
+ function isRecord$10(value) {
3587
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3588
+ }
3589
+ function readOptionalString$7(value) {
3590
+ if (typeof value !== "string") return;
3591
+ const trimmed = value.trim();
3592
+ return trimmed.length > 0 ? trimmed : void 0;
3593
+ }
3594
+ function readSessionActivityPreviewMetadata(value) {
3595
+ if (!isRecord$10(value)) return null;
3596
+ const state = value.state;
3597
+ const timestamp = readOptionalString$7(value.timestamp);
3598
+ if (!SESSION_ACTIVITY_PREVIEW_STATES.has(state) || !timestamp) return null;
3599
+ return {
3600
+ state,
3601
+ timestamp,
3602
+ ...readOptionalString$7(value.statusText) ? { statusText: readOptionalString$7(value.statusText) } : {},
3603
+ ...readOptionalString$7(value.replyText) ? { replyText: readOptionalString$7(value.replyText) } : {}
3604
+ };
3605
+ }
3606
+ function compareIsoTimestamp(left, right) {
3607
+ const leftTime = Date.parse(left);
3608
+ const rightTime = Date.parse(right);
3609
+ if (!Number.isFinite(leftTime) || !Number.isFinite(rightTime)) return left.localeCompare(right);
3610
+ return leftTime - rightTime;
3611
+ }
3612
+ function mergeSessionActivityPreview(current, incoming) {
3613
+ if (current && compareIsoTimestamp(incoming.timestamp, current.timestamp) < 0) return incoming.replyText && !current.replyText ? {
3614
+ ...current,
3615
+ replyText: incoming.replyText
3616
+ } : current;
3617
+ return {
3618
+ state: incoming.state,
3619
+ timestamp: incoming.timestamp,
3620
+ ...incoming.statusText ?? (incoming.state === "completed" ? current?.statusText : void 0) ? { statusText: incoming.statusText ?? current?.statusText } : {},
3621
+ ...incoming.replyText ?? (incoming.state === "completed" ? current?.replyText : void 0) ? { replyText: incoming.replyText ?? current?.replyText } : {}
3622
+ };
3623
+ }
3624
+ function areSessionActivityPreviewsEqual(left, right) {
3625
+ return Boolean(left) && left?.state === right.state && left.timestamp === right.timestamp && left.statusText === right.statusText && left.replyText === right.replyText;
3626
+ }
3627
+ function writeSessionActivityPreviewMetadata(metadata, projection) {
3628
+ const current = readSessionActivityPreviewMetadata(metadata?.[SESSION_ACTIVITY_PREVIEW_METADATA_KEY]);
3629
+ const next = mergeSessionActivityPreview(current, projection.preview);
3630
+ if (areSessionActivityPreviewsEqual(current, next)) return null;
3631
+ return {
3632
+ ...metadata ?? {},
3633
+ [SESSION_ACTIVITY_PREVIEW_METADATA_KEY]: next
3634
+ };
3635
+ }
3636
+ //#endregion
3637
+ //#region src/contributions/session-activity-preview/services/session-activity-preview-event.service.ts
3638
+ var SessionActivityPreviewEventService = class {
3639
+ toolNames = /* @__PURE__ */ new Map();
3640
+ constructor(options) {
3641
+ this.options = options;
3642
+ }
3643
+ projectEvent = (event, timestamp) => {
3644
+ this.rememberToolName(event);
3645
+ const projection = createSessionActivityPreviewFromNcpEvent(event, timestamp, { readToolName: this.readToolName });
3646
+ this.clearFinishedRunToolNames(event);
3647
+ return projection;
3648
+ };
3649
+ updatePreview = async (projection) => {
3650
+ const session = await this.options.getSessionRecord(projection.sessionId);
3651
+ const replyText = projection.preview.replyText ?? (projection.preview.state === "completed" ? this.readLatestAssistantPreviewText(session?.messages) : void 0);
3652
+ const nextMetadata = writeSessionActivityPreviewMetadata(session?.metadata, {
3653
+ ...projection,
3654
+ preview: {
3655
+ ...projection.preview,
3656
+ replyText
3657
+ }
3658
+ });
3659
+ if (nextMetadata) await this.options.updateSessionMetadata(projection.sessionId, nextMetadata);
3660
+ };
3661
+ clear = () => {
3662
+ this.toolNames.clear();
3663
+ };
3664
+ rememberToolName = (event) => {
3665
+ if (event.type !== NcpEventType.MessageToolCallStart) return;
3666
+ const { sessionId, toolCallId, toolName } = event.payload;
3667
+ if (sessionId && toolCallId && toolName) this.toolNames.set(this.createToolNameKey(sessionId, toolCallId), toolName);
3668
+ };
3669
+ readToolName = (sessionId, toolCallId) => this.toolNames.get(this.createToolNameKey(sessionId, toolCallId)) ?? null;
3670
+ clearFinishedRunToolNames = (event) => {
3671
+ if (event.type !== NcpEventType.RunFinished && event.type !== NcpEventType.RunError) return;
3672
+ const sessionId = event.payload.sessionId;
3673
+ for (const key of this.toolNames.keys()) if (key.startsWith(`${sessionId}:`)) this.toolNames.delete(key);
3674
+ };
3675
+ createToolNameKey = (sessionId, toolCallId) => `${sessionId}:${toolCallId}`;
3676
+ readLatestAssistantPreviewText = (messages) => {
3677
+ for (let index = (messages?.length ?? 0) - 1; index >= 0; index -= 1) {
3678
+ const message = messages?.[index];
3679
+ if (message?.role === "assistant") return readSessionActivityPreviewText(message) ?? void 0;
3680
+ }
3681
+ };
3682
+ };
3683
+ //#endregion
3684
+ //#region src/services/session-event-ingestion.service.ts
3685
+ const SESSION_METADATA_PATCH_RUN_METADATA_KIND = "session_metadata_patch";
3686
+ function isDurableSessionEvent(event) {
3687
+ return event.type !== NcpEventType.ContextWindowUpdated;
3688
+ }
3689
+ function readRuntimeSessionMetadataPatch(event) {
3690
+ if (event.type !== NcpEventType.RunMetadata) return null;
3691
+ const metadata = event.payload.metadata;
3692
+ if (metadata.kind !== SESSION_METADATA_PATCH_RUN_METADATA_KIND || !metadata.sessionMetadataPatch || typeof metadata.sessionMetadataPatch !== "object" || Array.isArray(metadata.sessionMetadataPatch)) return null;
3693
+ const patch = metadata.sessionMetadataPatch;
3694
+ return Object.keys(patch).length > 0 ? structuredClone(patch) : null;
3695
+ }
3696
+ var SessionEventIngestionService = class {
3697
+ activityPreview;
3698
+ chains = /* @__PURE__ */ new Map();
3699
+ constructor(options) {
3700
+ this.options = options;
3701
+ this.activityPreview = new SessionActivityPreviewEventService({
3702
+ getSessionRecord: options.getSessionRecord,
3703
+ updateSessionMetadata: options.updateSessionMetadata
3704
+ });
3705
+ }
3706
+ handleEvent = (event) => {
3707
+ const sessionId = readEventSessionId$1(event);
3708
+ if (!sessionId || !isDurableSessionEvent(event)) return;
3709
+ const next = (this.chains.get(sessionId) ?? Promise.resolve()).then(() => this.handleDurableEvent(sessionId, event));
3710
+ this.chains.set(sessionId, next.catch(() => void 0));
3711
+ next.catch((error) => this.options.onError(sessionId, error));
3712
+ };
3713
+ clear = () => {
3714
+ this.chains.clear();
3715
+ this.activityPreview.clear();
3716
+ };
3717
+ handleDurableEvent = async (sessionId, event) => {
3718
+ const preview = this.activityPreview.projectEvent(event, (/* @__PURE__ */ new Date()).toISOString());
3719
+ const metadataPatch = readRuntimeSessionMetadataPatch(event);
3720
+ if (metadataPatch) {
3721
+ await this.options.updateSessionMetadata(sessionId, metadataPatch);
3722
+ return;
3723
+ }
3724
+ await this.options.appendSessionEvent({
3725
+ event,
3726
+ sessionId
3727
+ });
3728
+ if (preview) await this.activityPreview.updatePreview(preview);
3729
+ };
3730
+ };
3731
+ //#endregion
3446
3732
  //#region src/services/session-working-dir-resolver.service.ts
3447
3733
  var SessionWorkingDirResolver = class {
3448
3734
  constructor(agentManager) {
@@ -3471,10 +3757,6 @@ const SESSION_METADATA_LABEL_KEY = "label";
3471
3757
  const CHILD_SESSION_PARENT_METADATA_KEY = "parent_session_id";
3472
3758
  const CHILD_SESSION_REQUEST_METADATA_KEY = "spawned_by_request_id";
3473
3759
  const CHILD_SESSION_LIFECYCLE_METADATA_KEY = "session_lifecycle";
3474
- const SESSION_METADATA_PATCH_RUN_METADATA_KIND = "session_metadata_patch";
3475
- function isDurableSessionEvent(event) {
3476
- return event.type !== NcpEventType.ContextWindowUpdated;
3477
- }
3478
3760
  function readThinkingEffort(metadata) {
3479
3761
  return readOptionalMetadataString(metadata?.thinkingEffort) ?? null;
3480
3762
  }
@@ -3542,42 +3824,34 @@ function isSessionSummaryRefreshEvent(event) {
3542
3824
  default: return false;
3543
3825
  }
3544
3826
  }
3545
- function readRuntimeSessionMetadataPatch(event) {
3546
- if (event.type !== NcpEventType.RunMetadata) return null;
3547
- const metadata = event.payload.metadata;
3548
- if (metadata.kind !== SESSION_METADATA_PATCH_RUN_METADATA_KIND || !metadata.sessionMetadataPatch || typeof metadata.sessionMetadataPatch !== "object" || Array.isArray(metadata.sessionMetadataPatch)) return null;
3549
- const patch = metadata.sessionMetadataPatch;
3550
- return Object.keys(patch).length > 0 ? structuredClone(patch) : null;
3551
- }
3552
3827
  var SessionManager = class {
3553
3828
  cleanups = [];
3554
3829
  contextWindowPreview;
3830
+ eventIngestion;
3555
3831
  workingDirResolver;
3556
3832
  started = false;
3557
3833
  constructor(options) {
3558
3834
  this.options = options;
3835
+ this.eventIngestion = new SessionEventIngestionService({
3836
+ appendSessionEvent: (params) => this.appendSessionEvent(params),
3837
+ getSessionRecord: (sessionId) => this.getSessionRecord(sessionId),
3838
+ onError: (sessionId, error) => {
3839
+ const message = error instanceof Error ? error.stack ?? error.message : String(error);
3840
+ console.error(`[session-manager] failed to handle ncp event for ${sessionId}: ${message}`);
3841
+ },
3842
+ updateSessionMetadata: (sessionId, metadata) => this.updateSessionMetadata(sessionId, metadata)
3843
+ });
3559
3844
  this.contextWindowPreview = new ContextWindowPreviewManager(options.agentManager);
3560
3845
  this.workingDirResolver = new SessionWorkingDirResolver(options.agentManager);
3561
3846
  }
3562
3847
  start = () => {
3563
3848
  if (this.started) return;
3564
3849
  this.started = true;
3565
- this.cleanups.push(this.options.eventBus.on(eventKeys.ncpEvent, async (event) => {
3566
- const sessionId = readEventSessionId$1(event);
3567
- if (!sessionId || !isDurableSessionEvent(event)) return;
3568
- const metadataPatch = readRuntimeSessionMetadataPatch(event);
3569
- if (metadataPatch) {
3570
- await this.updateSessionMetadata(sessionId, metadataPatch);
3571
- return;
3572
- }
3573
- await this.appendSessionEvent({
3574
- event,
3575
- sessionId
3576
- });
3577
- }));
3850
+ this.cleanups.push(this.options.eventBus.on(eventKeys.ncpEvent, this.eventIngestion.handleEvent));
3578
3851
  };
3579
3852
  dispose = () => {
3580
3853
  while (this.cleanups.length > 0) this.cleanups.pop()?.();
3854
+ this.eventIngestion.clear();
3581
3855
  this.started = false;
3582
3856
  };
3583
3857
  createSession = async (params) => {
@@ -3706,6 +3980,10 @@ var SessionManager = class {
3706
3980
  metadata: structuredClone(record.metadata ?? {}),
3707
3981
  model,
3708
3982
  projectRoot: readProjectRoot(record.metadata),
3983
+ workingDir: this.workingDirResolver.resolve({
3984
+ agentId: record.agentId,
3985
+ metadata: record.metadata
3986
+ }),
3709
3987
  thinkingEffort: readThinkingEffort(record.metadata)
3710
3988
  };
3711
3989
  };
@@ -3742,6 +4020,10 @@ var SessionManager = class {
3742
4020
  metadata: structuredClone(created.metadata ?? {}),
3743
4021
  model,
3744
4022
  projectRoot,
4023
+ workingDir: this.workingDirResolver.resolve({
4024
+ agentId,
4025
+ metadata: created.metadata
4026
+ }),
3745
4027
  thinkingEffort: thinkingEffort ?? null
3746
4028
  };
3747
4029
  };
@@ -4009,12 +4291,12 @@ var PanelAppCapabilityGrantStore = class {
4009
4291
  };
4010
4292
  };
4011
4293
  function normalizeStoreData$2(value) {
4012
- if (!isRecord$10(value) || value.version !== 1 || !isRecord$10(value.grants)) return structuredClone(EMPTY_GRANTS$2);
4294
+ if (!isRecord$9(value) || value.version !== 1 || !isRecord$9(value.grants)) return structuredClone(EMPTY_GRANTS$2);
4013
4295
  const grants = {};
4014
4296
  for (const [callerKey, callerValue] of Object.entries(value.grants)) {
4015
- if (!isRecord$10(callerValue) || !isRecord$10(callerValue.capabilities)) continue;
4297
+ if (!isRecord$9(callerValue) || !isRecord$9(callerValue.capabilities)) continue;
4016
4298
  const capabilities = {};
4017
- for (const [capability, grantValue] of Object.entries(callerValue.capabilities)) if (isRecord$10(grantValue) && typeof grantValue.grantedAt === "string") capabilities[capability] = { grantedAt: grantValue.grantedAt };
4299
+ for (const [capability, grantValue] of Object.entries(callerValue.capabilities)) if (isRecord$9(grantValue) && typeof grantValue.grantedAt === "string") capabilities[capability] = { grantedAt: grantValue.grantedAt };
4018
4300
  grants[callerKey] = { capabilities };
4019
4301
  }
4020
4302
  return {
@@ -4025,7 +4307,7 @@ function normalizeStoreData$2(value) {
4025
4307
  function getCallerKey(caller) {
4026
4308
  return `${caller.surface}:${caller.appId}`;
4027
4309
  }
4028
- function isRecord$10(value) {
4310
+ function isRecord$9(value) {
4029
4311
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4030
4312
  }
4031
4313
  function isMissingFileError$3(error) {
@@ -4071,19 +4353,19 @@ var PanelAppClientGrantStore = class {
4071
4353
  };
4072
4354
  };
4073
4355
  function normalizeStoreData$1(value) {
4074
- if (!isRecord$9(value) || value.version !== 1 || !isRecord$9(value.grants)) return structuredClone(EMPTY_GRANTS$1);
4356
+ if (!isRecord$8(value) || value.version !== 1 || !isRecord$8(value.grants)) return structuredClone(EMPTY_GRANTS$1);
4075
4357
  const grants = {};
4076
- for (const [appId, grant] of Object.entries(value.grants)) if (isRecord$9(grant) && typeof grant.grantedAt === "string") grants[appId] = { grantedAt: grant.grantedAt };
4358
+ for (const [appId, grant] of Object.entries(value.grants)) if (isRecord$8(grant) && typeof grant.grantedAt === "string") grants[appId] = { grantedAt: grant.grantedAt };
4077
4359
  return {
4078
4360
  grants,
4079
4361
  version: 1
4080
4362
  };
4081
4363
  }
4082
- function isRecord$9(value) {
4364
+ function isRecord$8(value) {
4083
4365
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4084
4366
  }
4085
4367
  function isMissingFileError$2(error) {
4086
- return isRecord$9(error) && error.code === "ENOENT";
4368
+ return isRecord$8(error) && error.code === "ENOENT";
4087
4369
  }
4088
4370
  //#endregion
4089
4371
  //#region src/services/agent-run-client.service.ts
@@ -4302,7 +4584,7 @@ const PANEL_APP_AGENT_MAX_CONTEXT_CHARS = 8e4;
4302
4584
  function normalizePanelAppGenerateObjectInput(input) {
4303
4585
  const peerId = input.peerId.trim();
4304
4586
  const prompt = input.prompt.trim();
4305
- if (!peerId || !prompt || !isRecord$8(input.schema)) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "invalid generateObject request");
4587
+ if (!peerId || !prompt || !isRecord$7(input.schema)) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "invalid generateObject request");
4306
4588
  if (prompt.length > PANEL_APP_AGENT_MAX_PROMPT_CHARS) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "generateObject prompt is too large");
4307
4589
  if (stringifyJsonValue(input.context ?? null).length > PANEL_APP_AGENT_MAX_CONTEXT_CHARS) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "generateObject context is too large");
4308
4590
  return {
@@ -4443,7 +4725,7 @@ function readStructuredResultEvent(event, resultToolCallId) {
4443
4725
  };
4444
4726
  }
4445
4727
  function assertToolResultContent(content) {
4446
- if (!isRecord$8(content) || content.ok !== false || !isRecord$8(content.error)) return;
4728
+ if (!isRecord$7(content) || content.ok !== false || !isRecord$7(content.error)) return;
4447
4729
  if (content.error.code === "invalid_tool_arguments") throw new PanelAppError("AGENT_OBJECT_RESULT_SCHEMA_INVALID", "agent object result did not match the schema");
4448
4730
  throw new PanelAppError("AGENT_OBJECT_REQUEST_FAILED", typeof content.error.message === "string" ? content.error.message : "agent object request failed");
4449
4731
  }
@@ -4459,7 +4741,7 @@ function stringifyJsonValue(value) {
4459
4741
  throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "value is not JSON serializable");
4460
4742
  }
4461
4743
  }
4462
- function isRecord$8(value) {
4744
+ function isRecord$7(value) {
4463
4745
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4464
4746
  }
4465
4747
  //#endregion
@@ -4733,14 +5015,14 @@ function parsePanelAppFolderManifest(raw) {
4733
5015
  } catch (error) {
4734
5016
  throw new Error(`panel-app.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
4735
5017
  }
4736
- if (!isRecord$7(parsed)) throw new Error("panel-app.json must contain an object.");
4737
- const id = readOptionalString$7(parsed, "id");
5018
+ if (!isRecord$6(parsed)) throw new Error("panel-app.json must contain an object.");
5019
+ const id = readOptionalString$6(parsed, "id");
4738
5020
  if (id && !PANEL_APP_ID_PATTERN.test(id)) throw new Error("panel app id must be kebab-case.");
4739
5021
  return {
4740
5022
  id,
4741
5023
  title: readRequiredString$4(parsed, "title"),
4742
- description: readOptionalString$7(parsed, "description"),
4743
- icon: readOptionalString$7(parsed, "icon"),
5024
+ description: readOptionalString$6(parsed, "description"),
5025
+ icon: readOptionalString$6(parsed, "icon"),
4744
5026
  entry: readRequiredString$4(parsed, "entry"),
4745
5027
  capabilities: readStringArray$1(parsed.capabilities, "capabilities"),
4746
5028
  client: readOptionalBoolean$2(parsed, "client"),
@@ -4802,11 +5084,11 @@ function parseTokenList(content) {
4802
5084
  return [...new Set(content.split(/[,\s]+/).map((entry) => entry.trim()).filter(Boolean))];
4803
5085
  }
4804
5086
  function readRequiredString$4(record, key) {
4805
- const value = readOptionalString$7(record, key);
5087
+ const value = readOptionalString$6(record, key);
4806
5088
  if (!value) throw new Error(`panel app ${key} is required.`);
4807
5089
  return value;
4808
5090
  }
4809
- function readOptionalString$7(record, key) {
5091
+ function readOptionalString$6(record, key) {
4810
5092
  return typeof record[key] === "string" && record[key].trim() ? record[key].trim() : void 0;
4811
5093
  }
4812
5094
  function readOptionalBoolean$2(record, key) {
@@ -4820,7 +5102,7 @@ function readStringArray$1(value, key) {
4820
5102
  if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) throw new Error(`panel app ${key} must be a string array.`);
4821
5103
  return [...new Set(value.map((entry) => entry.trim()).filter(Boolean))];
4822
5104
  }
4823
- function isRecord$7(value) {
5105
+ function isRecord$6(value) {
4824
5106
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4825
5107
  }
4826
5108
  function normalizeTextValue(value) {
@@ -5538,12 +5820,12 @@ var ServiceActionGrantStore = class {
5538
5820
  };
5539
5821
  };
5540
5822
  function normalizeStoreData(value) {
5541
- if (!isRecord$6(value) || value.version !== 1 || !isRecord$6(value.grants)) return structuredClone(EMPTY_GRANTS);
5823
+ if (!isRecord$5(value) || value.version !== 1 || !isRecord$5(value.grants)) return structuredClone(EMPTY_GRANTS);
5542
5824
  const grants = {};
5543
5825
  for (const [callerKey, callerValue] of Object.entries(value.grants)) {
5544
- if (!isRecord$6(callerValue) || !isRecord$6(callerValue.actions)) continue;
5826
+ if (!isRecord$5(callerValue) || !isRecord$5(callerValue.actions)) continue;
5545
5827
  const actions = {};
5546
- for (const [actionId, actionValue] of Object.entries(callerValue.actions)) if (isRecord$6(actionValue) && typeof actionValue.grantedAt === "string" && isServiceActionRisk(actionValue.risk)) actions[actionId] = {
5828
+ for (const [actionId, actionValue] of Object.entries(callerValue.actions)) if (isRecord$5(actionValue) && typeof actionValue.grantedAt === "string" && isServiceActionRisk(actionValue.risk)) actions[actionId] = {
5547
5829
  grantedAt: actionValue.grantedAt,
5548
5830
  risk: actionValue.risk
5549
5831
  };
@@ -5557,11 +5839,11 @@ function normalizeStoreData(value) {
5557
5839
  function isServiceActionRisk(value) {
5558
5840
  return value === "read" || value === "write" || value === "external" || value === "dangerous";
5559
5841
  }
5560
- function isRecord$6(value) {
5842
+ function isRecord$5(value) {
5561
5843
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
5562
5844
  }
5563
5845
  function isMissingFileError(error) {
5564
- return isRecord$6(error) && error.code === "ENOENT";
5846
+ return isRecord$5(error) && error.code === "ENOENT";
5565
5847
  }
5566
5848
  //#endregion
5567
5849
  //#region src/utils/service-app-manifest.utils.ts
@@ -5586,15 +5868,15 @@ function parseServiceAppManifest(raw) {
5586
5868
  } catch (error) {
5587
5869
  throw new Error(`service-app.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
5588
5870
  }
5589
- if (!isRecord$5(parsed)) throw new Error("service-app.json must contain an object.");
5871
+ if (!isRecord$4(parsed)) throw new Error("service-app.json must contain an object.");
5590
5872
  const id = readRequiredString$3(parsed, "id");
5591
5873
  if (!SERVICE_APP_ID_PATTERN.test(id)) throw new Error("service app id must be kebab-case.");
5592
- const protocol = readOptionalString$6(parsed, "protocol") ?? "mcp";
5874
+ const protocol = readOptionalString$5(parsed, "protocol") ?? "mcp";
5593
5875
  if (protocol !== "mcp") throw new Error("service app protocol must be mcp.");
5594
5876
  return {
5595
5877
  id,
5596
5878
  title: readRequiredString$3(parsed, "title"),
5597
- description: readOptionalString$6(parsed, "description"),
5879
+ description: readOptionalString$5(parsed, "description"),
5598
5880
  enabled: readOptionalBoolean$1(parsed, "enabled") ?? true,
5599
5881
  protocol,
5600
5882
  command: readRequiredString$3(parsed, "command"),
@@ -5604,31 +5886,31 @@ function parseServiceAppManifest(raw) {
5604
5886
  }
5605
5887
  function readManifestActions(value) {
5606
5888
  if (value === void 0) throw new Error("service app actions are required.");
5607
- if (!isRecord$5(value)) throw new Error("service app actions must be an object.");
5889
+ if (!isRecord$4(value)) throw new Error("service app actions must be an object.");
5608
5890
  if (Object.keys(value).length === 0) throw new Error("service app actions cannot be empty.");
5609
5891
  const actions = {};
5610
5892
  for (const [name, action] of Object.entries(value)) {
5611
5893
  if (!name.trim()) throw new Error("service app action name cannot be empty.");
5612
- if (!isRecord$5(action)) throw new Error(`service app action ${name} must be an object.`);
5613
- const risk = readOptionalString$6(action, "risk");
5894
+ if (!isRecord$4(action)) throw new Error(`service app action ${name} must be an object.`);
5895
+ const risk = readOptionalString$5(action, "risk");
5614
5896
  if (risk !== void 0 && !SERVICE_ACTION_RISKS.has(risk)) throw new Error(`service app action ${name} has invalid risk.`);
5615
5897
  const inputSchema = action.inputSchema;
5616
- if (inputSchema !== void 0 && !isRecord$5(inputSchema)) throw new Error(`service app action ${name} inputSchema must be an object.`);
5898
+ if (inputSchema !== void 0 && !isRecord$4(inputSchema)) throw new Error(`service app action ${name} inputSchema must be an object.`);
5617
5899
  actions[name] = {
5618
5900
  risk,
5619
- title: readOptionalString$6(action, "title"),
5620
- description: readOptionalString$6(action, "description"),
5901
+ title: readOptionalString$5(action, "title"),
5902
+ description: readOptionalString$5(action, "description"),
5621
5903
  inputSchema
5622
5904
  };
5623
5905
  }
5624
5906
  return actions;
5625
5907
  }
5626
5908
  function readRequiredString$3(record, key) {
5627
- const value = readOptionalString$6(record, key);
5909
+ const value = readOptionalString$5(record, key);
5628
5910
  if (!value) throw new Error(`service app ${key} is required.`);
5629
5911
  return value;
5630
5912
  }
5631
- function readOptionalString$6(record, key) {
5913
+ function readOptionalString$5(record, key) {
5632
5914
  return typeof record[key] === "string" && record[key].trim() ? record[key].trim() : void 0;
5633
5915
  }
5634
5916
  function readOptionalBoolean$1(record, key) {
@@ -5641,7 +5923,7 @@ function readStringArray(value, key) {
5641
5923
  if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) throw new Error(`service app ${key} must be a string array.`);
5642
5924
  return value;
5643
5925
  }
5644
- function isRecord$5(value) {
5926
+ function isRecord$4(value) {
5645
5927
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
5646
5928
  }
5647
5929
  //#endregion
@@ -6064,7 +6346,7 @@ function parseFrontmatterBlock(raw) {
6064
6346
  function parseYamlFrontmatter(raw) {
6065
6347
  try {
6066
6348
  const parsed = parse(raw);
6067
- return isRecord$4(parsed) ? parsed : {};
6349
+ return isRecord$3(parsed) ? parsed : {};
6068
6350
  } catch (error) {
6069
6351
  const message = error instanceof Error ? error.message : String(error);
6070
6352
  throw new Error(`Invalid SKILL.md frontmatter: ${message}`);
@@ -6076,7 +6358,7 @@ function readString$3(record, ...names) {
6076
6358
  }
6077
6359
  function readLocalizedTextMap(record, ...names) {
6078
6360
  const value = readValue(record, names);
6079
- if (!isRecord$4(value)) return;
6361
+ if (!isRecord$3(value)) return;
6080
6362
  const localized = Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string" && entry[1].trim().length > 0).map(([locale, text]) => [normalizeLocaleTag(locale), text.trim()]));
6081
6363
  return Object.keys(localized).length > 0 ? localized : void 0;
6082
6364
  }
@@ -6100,7 +6382,7 @@ function normalizeFrontmatterKey(raw) {
6100
6382
  function normalizeLocaleTag(raw) {
6101
6383
  return raw.trim().toLowerCase();
6102
6384
  }
6103
- function isRecord$4(value) {
6385
+ function isRecord$3(value) {
6104
6386
  return typeof value === "object" && value !== null && !Array.isArray(value);
6105
6387
  }
6106
6388
  //#endregion
@@ -6925,9 +7207,6 @@ function readString$2(value) {
6925
7207
  if (typeof value !== "string") return;
6926
7208
  return value.trim() || void 0;
6927
7209
  }
6928
- function resolveEntryIcon(entry) {
6929
- return entry.icon ?? null;
6930
- }
6931
7210
  function resolveRequestedModel(params) {
6932
7211
  const { configuredModel, defaultModel, input, modelSelectionMode, sessionMetadata } = params;
6933
7212
  if (modelSelectionMode === "runtime-default") return;
@@ -6981,25 +7260,28 @@ var BuiltinHttpRuntimeSessionTypeService = class {
6981
7260
  describeInternal = async () => {
6982
7261
  const config = new HttpRuntimeConfigResolver(this.entry.config ?? {}).resolve({ defaultModel: this.defaultModel });
6983
7262
  const modelSelectionMode = normalizeRuntimeModelSelectionMode(this.entry.config?.modelSelectionMode);
7263
+ const baseDescriptor = {
7264
+ icon: this.entry.icon ?? null,
7265
+ modelSelectionMode,
7266
+ runtimeDefaultThinking: normalizeModelThinkingCapability(this.entry.config?.runtimeDefaultThinking)
7267
+ };
6984
7268
  if (!config.baseUrl) return {
6985
- icon: resolveEntryIcon(this.entry),
7269
+ ...baseDescriptor,
6986
7270
  ready: false,
6987
7271
  reason: "base_url_missing",
6988
7272
  reasonMessage: "Configure the runtime entry baseUrl before starting an HTTP runtime session.",
6989
7273
  recommendedModel: config.recommendedModel ?? null,
6990
- modelSelectionMode,
6991
7274
  cta: {
6992
7275
  kind: "settings",
6993
7276
  label: "Configure HTTP Runtime"
6994
7277
  }
6995
7278
  };
6996
7279
  if (!((config.capabilityProbe ?? true) && Boolean(config.healthcheckUrl))) return {
6997
- icon: resolveEntryIcon(this.entry),
7280
+ ...baseDescriptor,
6998
7281
  ready: true,
6999
7282
  reason: null,
7000
7283
  reasonMessage: null,
7001
7284
  recommendedModel: config.recommendedModel ?? null,
7002
- modelSelectionMode,
7003
7285
  ...config.supportedModels ? { supportedModels: config.supportedModels } : {},
7004
7286
  cta: null
7005
7287
  };
@@ -7013,28 +7295,26 @@ var BuiltinHttpRuntimeSessionTypeService = class {
7013
7295
  signal: controller.signal
7014
7296
  });
7015
7297
  if (!response.ok) return {
7016
- icon: resolveEntryIcon(this.entry),
7298
+ ...baseDescriptor,
7017
7299
  ready: false,
7018
7300
  reason: "healthcheck_failed",
7019
7301
  reasonMessage: `HTTP runtime healthcheck returned HTTP ${response.status}.`,
7020
7302
  recommendedModel: config.recommendedModel ?? null,
7021
- modelSelectionMode,
7022
7303
  ...config.supportedModels ? { supportedModels: config.supportedModels } : {},
7023
7304
  cta: null
7024
7305
  };
7025
7306
  return {
7026
- icon: resolveEntryIcon(this.entry),
7307
+ ...baseDescriptor,
7027
7308
  ready: true,
7028
7309
  reason: null,
7029
7310
  reasonMessage: null,
7030
7311
  recommendedModel: config.recommendedModel ?? null,
7031
- modelSelectionMode,
7032
7312
  ...config.supportedModels ? { supportedModels: config.supportedModels } : {},
7033
7313
  cta: null
7034
7314
  };
7035
7315
  } catch (error) {
7036
7316
  return {
7037
- icon: resolveEntryIcon(this.entry),
7317
+ ...baseDescriptor,
7038
7318
  ready: false,
7039
7319
  reason: "healthcheck_unreachable",
7040
7320
  reasonMessage: error instanceof Error ? error.message : "Failed to reach the configured HTTP runtime healthcheck.",
@@ -7067,53 +7347,54 @@ var BuiltinStdioRuntimeSessionTypeService = class {
7067
7347
  describeInternal = async (describeMode) => {
7068
7348
  const resolver = new StdioRuntimeConfigResolver(this.entry.config ?? {});
7069
7349
  const modelSelectionMode = normalizeRuntimeModelSelectionMode(this.entry.config?.modelSelectionMode);
7350
+ const baseDescriptor = {
7351
+ icon: this.entry.icon ?? null,
7352
+ modelSelectionMode,
7353
+ runtimeDefaultThinking: normalizeModelThinkingCapability(this.entry.config?.runtimeDefaultThinking)
7354
+ };
7070
7355
  try {
7071
7356
  const config = resolver.resolve();
7072
7357
  if (!await resolveExecutablePath(config.command)) return {
7073
- icon: resolveEntryIcon(this.entry),
7358
+ ...baseDescriptor,
7074
7359
  ready: false,
7075
7360
  reason: "command_missing",
7076
7361
  reasonMessage: `Configured stdio command "${config.command}" is not available. Update the runtime entry command or install the required launcher first.`,
7077
7362
  cta: {
7078
7363
  kind: "settings",
7079
7364
  label: "Configure Stdio Runtime"
7080
- },
7081
- modelSelectionMode
7365
+ }
7082
7366
  };
7083
7367
  if (describeMode === "probe") try {
7084
7368
  await probeStdioRuntime(config);
7085
7369
  } catch (error) {
7086
7370
  return {
7087
- icon: resolveEntryIcon(this.entry),
7371
+ ...baseDescriptor,
7088
7372
  ready: false,
7089
7373
  reason: "probe_failed",
7090
7374
  reasonMessage: error instanceof Error ? error.message : "Configured stdio runtime could not complete the ACP probe.",
7091
7375
  cta: {
7092
7376
  kind: "settings",
7093
7377
  label: "Repair Stdio Runtime"
7094
- },
7095
- modelSelectionMode
7378
+ }
7096
7379
  };
7097
7380
  }
7098
7381
  return {
7099
- icon: resolveEntryIcon(this.entry),
7382
+ ...baseDescriptor,
7100
7383
  ready: true,
7101
7384
  reason: null,
7102
7385
  reasonMessage: null,
7103
- modelSelectionMode,
7104
7386
  cta: null
7105
7387
  };
7106
7388
  } catch (error) {
7107
7389
  return {
7108
- icon: resolveEntryIcon(this.entry),
7390
+ ...baseDescriptor,
7109
7391
  ready: false,
7110
7392
  reason: "command_missing",
7111
7393
  reasonMessage: error instanceof Error ? error.message : "Configure a stdio command before starting this runtime.",
7112
7394
  cta: {
7113
7395
  kind: "settings",
7114
7396
  label: "Configure Stdio Runtime"
7115
- },
7116
- modelSelectionMode
7397
+ }
7117
7398
  };
7118
7399
  }
7119
7400
  };
@@ -7184,7 +7465,6 @@ var BuiltinNarpRuntimeProviderService = class {
7184
7465
  const config = readRecord(entry.config) ?? {};
7185
7466
  return new StdioRuntimeNcpAgentRuntime({
7186
7467
  ...new StdioRuntimeConfigResolver(config).resolve(),
7187
- sessionId: runtimeParams.sessionId,
7188
7468
  resolveTools: runtimeParams.resolveTools,
7189
7469
  stateManager: runtimeParams.stateManager,
7190
7470
  resolveProviderRoute: (input) => buildProviderRoute({
@@ -7280,13 +7560,13 @@ var ProviderManagerNcpLLMApi = class {
7280
7560
  };
7281
7561
  //#endregion
7282
7562
  //#region src/features/native-runtime/tools/ncp-asset.tools.ts
7283
- function readOptionalString$5(value) {
7563
+ function readOptionalString$4(value) {
7284
7564
  if (typeof value !== "string") return null;
7285
7565
  const trimmed = value.trim();
7286
7566
  return trimmed.length > 0 ? trimmed : null;
7287
7567
  }
7288
7568
  function readOptionalBase64Bytes(value) {
7289
- const base64 = readOptionalString$5(value);
7569
+ const base64 = readOptionalString$4(value);
7290
7570
  if (!base64) return null;
7291
7571
  try {
7292
7572
  return Buffer.from(base64, "base64");
@@ -7337,18 +7617,18 @@ var AssetPutTool = class {
7337
7617
  this.contentBasePath = contentBasePath;
7338
7618
  }
7339
7619
  validateArgs = (args) => {
7340
- const path = readOptionalString$5(args.path);
7341
- const bytesBase64 = readOptionalString$5(args.bytesBase64);
7342
- const fileName = readOptionalString$5(args.fileName);
7620
+ const path = readOptionalString$4(args.path);
7621
+ const bytesBase64 = readOptionalString$4(args.bytesBase64);
7622
+ const fileName = readOptionalString$4(args.fileName);
7343
7623
  if (path && bytesBase64) return ["Provide either path, or bytesBase64 + fileName, not both."];
7344
7624
  if (path) return [];
7345
7625
  if (bytesBase64) return fileName ? [] : ["fileName is required when using bytesBase64."];
7346
7626
  return ["Provide either path, or bytesBase64 + fileName."];
7347
7627
  };
7348
7628
  execute = async (args) => {
7349
- const path = readOptionalString$5(args?.path);
7350
- const fileName = readOptionalString$5(args?.fileName);
7351
- const mimeType = readOptionalString$5(args?.mimeType);
7629
+ const path = readOptionalString$4(args?.path);
7630
+ const fileName = readOptionalString$4(args?.fileName);
7631
+ const mimeType = readOptionalString$4(args?.mimeType);
7352
7632
  const bytes = readOptionalBase64Bytes(args?.bytesBase64);
7353
7633
  if (path) return {
7354
7634
  ok: true,
@@ -7391,8 +7671,8 @@ var AssetExportTool = class {
7391
7671
  this.assetStore = assetStore;
7392
7672
  }
7393
7673
  execute = async (args) => {
7394
- const assetUri = readOptionalString$5(args?.assetUri);
7395
- const targetPath = readOptionalString$5(args?.targetPath);
7674
+ const assetUri = readOptionalString$4(args?.assetUri);
7675
+ const targetPath = readOptionalString$4(args?.targetPath);
7396
7676
  if (!assetUri || !targetPath) throw new Error("asset_export requires assetUri and targetPath.");
7397
7677
  return {
7398
7678
  ok: true,
@@ -7418,7 +7698,7 @@ var AssetStatTool = class {
7418
7698
  this.contentBasePath = contentBasePath;
7419
7699
  }
7420
7700
  execute = async (args) => {
7421
- const assetUri = readOptionalString$5(args?.assetUri);
7701
+ const assetUri = readOptionalString$4(args?.assetUri);
7422
7702
  if (!assetUri) throw new Error("asset_stat requires assetUri.");
7423
7703
  const record = await this.assetStore.statRecord(assetUri);
7424
7704
  if (!record) return {
@@ -7695,7 +7975,8 @@ var NcpAgentRuntimeWrapper = class {
7695
7975
  runId: spec.runId,
7696
7976
  messages,
7697
7977
  correlationId: spec.correlationId,
7698
- metadata: this.buildMetadata(spec)
7978
+ metadata: this.buildMetadata(options.session, spec),
7979
+ executionContext: { cwd: options.session.workingDir }
7699
7980
  };
7700
7981
  for await (const event of this.getRuntime().run(input, { signal: options.signal })) yield await this.applyEvent(sessionRun, event);
7701
7982
  this.currentTools = [];
@@ -7720,10 +8001,10 @@ var NcpAgentRuntimeWrapper = class {
7720
8001
  correlationId: spec.correlationId
7721
8002
  }
7722
8003
  }));
7723
- buildMetadata = (spec) => ({
7724
- ...this.params.session.metadata,
8004
+ buildMetadata = (session, spec) => ({
8005
+ ...session.metadata,
7725
8006
  agentId: spec.agentId,
7726
- agentRuntimeId: this.params.session.agentRuntimeId,
8007
+ agentRuntimeId: session.agentRuntimeId,
7727
8008
  maxTokens: spec.maxTokens,
7728
8009
  model: spec.model,
7729
8010
  preferred_model: spec.model,
@@ -7787,20 +8068,16 @@ var AgentRunRuntimeContribution = class {
7787
8068
  describeSessionTypeForEntry: provider.describeSessionTypeForEntry,
7788
8069
  createRuntime: ({ entry, session }) => {
7789
8070
  if (!provider.createRuntimeForEntry) throw new Error(`Agent runtime provider does not support entries: ${provider.kind}`);
7790
- return new NcpAgentRuntimeWrapper({
7791
- session,
7792
- createRuntime: ({ resolveTools, stateManager }) => provider.createRuntimeForEntry({
7793
- entry,
7794
- runtimeParams: {
7795
- sessionId: session.sessionId,
7796
- ...session.agentId ? { agentId: session.agentId } : {},
7797
- resolveAssetContentPath: (assetUri) => this.kernel.assetStore.resolveContentPath(assetUri),
7798
- resolveTools,
7799
- sessionMetadata: session.metadata,
7800
- stateManager
7801
- }
7802
- })
7803
- });
8071
+ return new NcpAgentRuntimeWrapper({ createRuntime: ({ resolveTools, stateManager }) => provider.createRuntimeForEntry({
8072
+ entry,
8073
+ runtimeParams: {
8074
+ ...session.agentId ? { agentId: session.agentId } : {},
8075
+ resolveAssetContentPath: (assetUri) => this.kernel.assetStore.resolveContentPath(assetUri),
8076
+ resolveTools,
8077
+ sessionMetadata: session.metadata,
8078
+ stateManager
8079
+ }
8080
+ }) });
7804
8081
  }
7805
8082
  });
7806
8083
  };
@@ -7966,6 +8243,13 @@ const createToolCallStyleContextProvider = () => staticBlock([
7966
8243
  "Keep narration brief and value-dense; avoid repeating obvious steps.",
7967
8244
  "Use plain human language for narration unless in a technical context."
7968
8245
  ]);
8246
+ const createInlineInteractiveSurfaceContextProvider = () => staticBlock([
8247
+ "## Inline Interactive Surfaces",
8248
+ "Do not make every UI an inline card. Choose inline only when the intended result is a compact, immediately usable card or short interaction; use the side panel for normal Panel Apps, long reading, rich editing, file browsing, large tables, multi-page workflows, or sustained workspaces.",
8249
+ "When you choose an inline card, call `show_content` with `type=\"panel_app\"` and `placement=\"inline\"` so the user can try it directly in the chat. Do not wait for the user to say \"inline\" or \"placement\" after you have already chosen the card form.",
8250
+ "A Panel Card must be designed card-first: prefer a landscape composition where width carries the main information and the card is wider than it is tall; collapse to one column only in narrow containers. Core value must be visible in the first 220-420px, with no horizontal scrolling, no reliance on document-level internal scrolling, compact controls, at most one primary action, clear loading/empty/error states, and an obvious expand path for details.",
8251
+ "Typical Panel Card fits: weather cards, calculators, timers, checklists, pickers, compact forms, previews, and small dashboards. If the UI needs more space than a card, choose `placement=\"side_panel\"` instead. Inline hosts may pass `nextclawDisplayMode=card` and `nextclawPlacement=inline`; use those hints to render a compact card layout instead of a full page."
8252
+ ]);
7969
8253
  const createChatComposerTokensContextProvider = () => staticBlock([
7970
8254
  "## Chat Composer Tokens",
7971
8255
  "When a user message contains tokens like `$weather` or `$web-search`, treat each `$<skill-spec>` token as a user-visible marker that the corresponding skill was explicitly selected in the chat composer.",
@@ -8372,6 +8656,7 @@ var ContextProviderContribution = class {
8372
8656
  createAssistantIdentityContextProvider(),
8373
8657
  new ToolingContextProvider(context),
8374
8658
  createToolCallStyleContextProvider(),
8659
+ createInlineInteractiveSurfaceContextProvider(),
8375
8660
  createChatComposerTokensContextProvider(),
8376
8661
  createSafetyContextProvider(),
8377
8662
  createCliQuickReferenceContextProvider(),
@@ -8639,227 +8924,6 @@ var LearningLoopContribution = class {
8639
8924
  };
8640
8925
  };
8641
8926
  //#endregion
8642
- //#region src/contributions/session-activity-preview/utils/session-activity-preview-ncp-event.utils.ts
8643
- const PREVIEW_TEXT_MAX_LENGTH = 160;
8644
- function readSessionId(value) {
8645
- if (typeof value !== "string") return null;
8646
- const trimmed = value.trim();
8647
- return trimmed.length > 0 ? trimmed : null;
8648
- }
8649
- function compactPreviewText(value) {
8650
- return value.replace(/\s+/g, " ").trim();
8651
- }
8652
- function truncatePreviewText(value) {
8653
- const compacted = compactPreviewText(value);
8654
- if (compacted.length <= PREVIEW_TEXT_MAX_LENGTH) return compacted;
8655
- return compacted.slice(0, PREVIEW_TEXT_MAX_LENGTH - 1).trimEnd();
8656
- }
8657
- function readMessagePreviewText(message) {
8658
- const chunks = [];
8659
- for (const part of message.parts) if ((part.type === "text" || part.type === "rich-text") && part.text.trim()) chunks.push(part.text);
8660
- const previewText = truncatePreviewText(chunks.join(" "));
8661
- return previewText.length > 0 ? previewText : null;
8662
- }
8663
- function createProjection(sessionId, preview) {
8664
- if (!sessionId) return null;
8665
- return {
8666
- sessionId,
8667
- preview
8668
- };
8669
- }
8670
- function formatErrorStatus(error) {
8671
- if (typeof error === "string" && error.trim()) return `运行出错:${truncatePreviewText(error)}`;
8672
- if (error && typeof error === "object" && "message" in error) {
8673
- const message = error.message;
8674
- if (typeof message === "string" && message.trim()) return `运行出错:${truncatePreviewText(message)}`;
8675
- }
8676
- return "运行出错";
8677
- }
8678
- function readToolCallId(value) {
8679
- if (typeof value !== "string") return null;
8680
- const trimmed = value.trim();
8681
- return trimmed.length > 0 ? trimmed : null;
8682
- }
8683
- function formatToolDoneStatus(toolName) {
8684
- return toolName ? `工具调用完成:${toolName}` : "工具调用完成";
8685
- }
8686
- function createSessionActivityPreviewFromNcpEvent(event, timestamp, options = {}) {
8687
- switch (event.type) {
8688
- case NcpEventType.RunStarted: return createProjection(readSessionId(event.payload.sessionId), {
8689
- state: "running",
8690
- statusText: "正在思考",
8691
- timestamp
8692
- });
8693
- case NcpEventType.RunFinished: return createProjection(readSessionId(event.payload.sessionId), {
8694
- state: "completed",
8695
- timestamp
8696
- });
8697
- case NcpEventType.RunError: return createProjection(readSessionId(event.payload.sessionId), {
8698
- state: "failed",
8699
- statusText: formatErrorStatus(event.payload.error),
8700
- timestamp
8701
- });
8702
- case NcpEventType.MessageSent: {
8703
- const text = readMessagePreviewText(event.payload.message);
8704
- if (!text || event.payload.message.role !== "user") return null;
8705
- return createProjection(readSessionId(event.payload.sessionId), {
8706
- state: "running",
8707
- statusText: text,
8708
- timestamp: event.payload.message.timestamp || timestamp
8709
- });
8710
- }
8711
- case NcpEventType.MessageCompleted: {
8712
- const text = readMessagePreviewText(event.payload.message);
8713
- if (!text || event.payload.message.role !== "assistant") return null;
8714
- return createProjection(readSessionId(event.payload.sessionId), {
8715
- state: "completed",
8716
- replyText: text,
8717
- timestamp: event.payload.message.timestamp || timestamp
8718
- });
8719
- }
8720
- case NcpEventType.MessageFailed: return createProjection(readSessionId(event.payload.sessionId), {
8721
- state: "failed",
8722
- statusText: formatErrorStatus(event.payload.error),
8723
- timestamp
8724
- });
8725
- case NcpEventType.MessageToolCallStart: return createProjection(readSessionId(event.payload.sessionId), {
8726
- state: "running",
8727
- statusText: `正在调用工具:${event.payload.toolName}`,
8728
- timestamp
8729
- });
8730
- case NcpEventType.MessageToolCallEnd:
8731
- case NcpEventType.MessageToolCallResult: {
8732
- const sessionId = readSessionId(event.payload.sessionId);
8733
- const toolCallId = readToolCallId(event.payload.toolCallId);
8734
- return createProjection(sessionId, {
8735
- state: "running",
8736
- statusText: formatToolDoneStatus(sessionId && toolCallId ? options.readToolName?.(sessionId, toolCallId) ?? null : null),
8737
- timestamp
8738
- });
8739
- }
8740
- default: return null;
8741
- }
8742
- }
8743
- //#endregion
8744
- //#region src/contributions/session-activity-preview/utils/session-activity-preview-metadata.utils.ts
8745
- const SESSION_ACTIVITY_PREVIEW_METADATA_KEY = "last_activity_preview";
8746
- const SESSION_ACTIVITY_PREVIEW_STATES = new Set([
8747
- "running",
8748
- "completed",
8749
- "failed",
8750
- "idle"
8751
- ]);
8752
- function isRecord$3(value) {
8753
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
8754
- }
8755
- function readOptionalString$4(value) {
8756
- if (typeof value !== "string") return;
8757
- const trimmed = value.trim();
8758
- return trimmed.length > 0 ? trimmed : void 0;
8759
- }
8760
- function readSessionActivityPreviewMetadata(value) {
8761
- if (!isRecord$3(value)) return null;
8762
- const state = value.state;
8763
- const timestamp = readOptionalString$4(value.timestamp);
8764
- if (!SESSION_ACTIVITY_PREVIEW_STATES.has(state) || !timestamp) return null;
8765
- return {
8766
- state,
8767
- timestamp,
8768
- ...readOptionalString$4(value.statusText) ? { statusText: readOptionalString$4(value.statusText) } : {},
8769
- ...readOptionalString$4(value.replyText) ? { replyText: readOptionalString$4(value.replyText) } : {}
8770
- };
8771
- }
8772
- function compareIsoTimestamp(left, right) {
8773
- const leftTime = Date.parse(left);
8774
- const rightTime = Date.parse(right);
8775
- if (!Number.isFinite(leftTime) || !Number.isFinite(rightTime)) return left.localeCompare(right);
8776
- return leftTime - rightTime;
8777
- }
8778
- function mergeSessionActivityPreview(current, incoming) {
8779
- if (current && compareIsoTimestamp(incoming.timestamp, current.timestamp) < 0) return incoming.replyText && !current.replyText ? {
8780
- ...current,
8781
- replyText: incoming.replyText
8782
- } : current;
8783
- return {
8784
- state: incoming.state,
8785
- timestamp: incoming.timestamp,
8786
- ...incoming.statusText ?? (incoming.state === "completed" ? current?.statusText : void 0) ? { statusText: incoming.statusText ?? current?.statusText } : {},
8787
- ...incoming.replyText ?? current?.replyText ? { replyText: incoming.replyText ?? current?.replyText } : {}
8788
- };
8789
- }
8790
- function areSessionActivityPreviewsEqual(left, right) {
8791
- return Boolean(left) && left?.state === right.state && left.timestamp === right.timestamp && left.statusText === right.statusText && left.replyText === right.replyText;
8792
- }
8793
- function writeSessionActivityPreviewMetadata(metadata, projection) {
8794
- const current = readSessionActivityPreviewMetadata(metadata?.[SESSION_ACTIVITY_PREVIEW_METADATA_KEY]);
8795
- const next = mergeSessionActivityPreview(current, projection.preview);
8796
- if (areSessionActivityPreviewsEqual(current, next)) return null;
8797
- return {
8798
- ...metadata ?? {},
8799
- [SESSION_ACTIVITY_PREVIEW_METADATA_KEY]: next
8800
- };
8801
- }
8802
- //#endregion
8803
- //#region src/contributions/session-activity-preview/index.ts
8804
- var SessionActivityPreviewContribution = class {
8805
- unsubscribeNcpEvent = null;
8806
- metadataWriteChains = /* @__PURE__ */ new Map();
8807
- toolNames = /* @__PURE__ */ new Map();
8808
- constructor(kernel) {
8809
- this.kernel = kernel;
8810
- }
8811
- start = () => {
8812
- if (this.unsubscribeNcpEvent) return;
8813
- this.unsubscribeNcpEvent = this.kernel.eventBus.on(eventKeys.ncpEvent, (event) => {
8814
- this.rememberToolName(event);
8815
- const projection = createSessionActivityPreviewFromNcpEvent(event, (/* @__PURE__ */ new Date()).toISOString(), { readToolName: this.readToolName });
8816
- this.clearFinishedRunToolNames(event);
8817
- if (!projection) return;
8818
- this.updatePreview(projection);
8819
- });
8820
- };
8821
- dispose = () => {
8822
- this.unsubscribeNcpEvent?.();
8823
- this.unsubscribeNcpEvent = null;
8824
- this.metadataWriteChains.clear();
8825
- this.toolNames.clear();
8826
- };
8827
- rememberToolName = (event) => {
8828
- if (event.type !== NcpEventType.MessageToolCallStart) return;
8829
- const sessionId = event.payload.sessionId.trim();
8830
- const toolCallId = event.payload.toolCallId.trim();
8831
- const toolName = event.payload.toolName.trim();
8832
- if (!sessionId || !toolCallId || !toolName) return;
8833
- this.toolNames.set(this.createToolNameKey(sessionId, toolCallId), toolName);
8834
- };
8835
- readToolName = (sessionId, toolCallId) => this.toolNames.get(this.createToolNameKey(sessionId, toolCallId)) ?? null;
8836
- clearFinishedRunToolNames = (event) => {
8837
- if (event.type !== NcpEventType.RunFinished && event.type !== NcpEventType.RunError) return;
8838
- const sessionId = this.readNonEmptyString(event.payload.sessionId);
8839
- if (!sessionId) return;
8840
- for (const key of this.toolNames.keys()) if (key.startsWith(`${sessionId}:`)) this.toolNames.delete(key);
8841
- };
8842
- readNonEmptyString = (value) => {
8843
- if (typeof value !== "string") return null;
8844
- const trimmed = value.trim();
8845
- return trimmed.length > 0 ? trimmed : null;
8846
- };
8847
- createToolNameKey = (sessionId, toolCallId) => `${sessionId}:${toolCallId}`;
8848
- updatePreview = (projection) => {
8849
- const next = (this.metadataWriteChains.get(projection.sessionId) ?? Promise.resolve()).then(() => this.updatePreviewMetadata(projection));
8850
- this.metadataWriteChains.set(projection.sessionId, next.catch(() => void 0));
8851
- next.catch((error) => {
8852
- const message = error instanceof Error ? error.stack ?? error.message : String(error);
8853
- console.error(`[session-activity-preview] failed to update ${projection.sessionId}: ${message}`);
8854
- });
8855
- };
8856
- updatePreviewMetadata = async (projection) => {
8857
- const nextMetadata = writeSessionActivityPreviewMetadata((await this.kernel.sessionManager.getSessionRecord(projection.sessionId))?.metadata, projection);
8858
- if (!nextMetadata) return;
8859
- await this.kernel.sessionManager.updateSessionMetadata(projection.sessionId, nextMetadata);
8860
- };
8861
- };
8862
- //#endregion
8863
8927
  //#region src/contributions/tool-provider/providers/asset-tool.provider.ts
8864
8928
  var AssetToolProvider = class {
8865
8929
  constructor(assetStore) {
@@ -9402,6 +9466,12 @@ function readPurpose(value) {
9402
9466
  if (normalized === "read" || normalized === "preview" || normalized === "edit" || normalized === "interact") return normalized;
9403
9467
  throw new Error("purpose must be \"read\", \"preview\", \"edit\", or \"interact\".");
9404
9468
  }
9469
+ function readPlacement(value) {
9470
+ const normalized = readOptionalString$1(value);
9471
+ if (!normalized) return;
9472
+ if (normalized === "inline" || normalized === "side_panel") return normalized;
9473
+ throw new Error("placement must be \"inline\" or \"side_panel\".");
9474
+ }
9405
9475
  function readPayload(params) {
9406
9476
  if (!isRecord$1(params.payload)) throw new Error("payload must be an object.");
9407
9477
  return params.payload;
@@ -9423,6 +9493,7 @@ function normalizeShowContentArgs(args) {
9423
9493
  const payload = readPayload(params);
9424
9494
  const title = readOptionalString$1(params.title);
9425
9495
  const purpose = readPurpose(params.purpose);
9496
+ const placement = readPlacement(params.placement);
9426
9497
  if (type === "file") return {
9427
9498
  target: {
9428
9499
  type,
@@ -9433,7 +9504,8 @@ function normalizeShowContentArgs(args) {
9433
9504
  }
9434
9505
  },
9435
9506
  title,
9436
- purpose
9507
+ purpose,
9508
+ placement
9437
9509
  };
9438
9510
  if (type === "url") return {
9439
9511
  target: {
@@ -9441,7 +9513,8 @@ function normalizeShowContentArgs(args) {
9441
9513
  payload: { url: readUrl(payload.url) }
9442
9514
  },
9443
9515
  title,
9444
- purpose
9516
+ purpose,
9517
+ placement
9445
9518
  };
9446
9519
  if (type === "panel_app") return {
9447
9520
  target: {
@@ -9449,7 +9522,8 @@ function normalizeShowContentArgs(args) {
9449
9522
  payload: { appId: readRequiredString(payload.appId, "payload.appId") }
9450
9523
  },
9451
9524
  title,
9452
- purpose
9525
+ purpose,
9526
+ placement
9453
9527
  };
9454
9528
  throw new Error("type must be \"file\", \"url\", or \"panel_app\".");
9455
9529
  }
@@ -9465,12 +9539,19 @@ function createShowContentEventPayload(request, context) {
9465
9539
  toolCallId,
9466
9540
  target: request.target,
9467
9541
  title: request.title,
9468
- purpose: request.purpose
9542
+ purpose: request.purpose,
9543
+ placement: request.placement
9469
9544
  };
9470
9545
  }
9471
9546
  var ShowContentTool = class {
9472
9547
  name = SHOW_CONTENT_TOOL_NAME;
9473
- description = "Show file, URL, or panel app content in the current chat UI.";
9548
+ description = [
9549
+ "Show file, URL, or panel app content in the current chat UI.",
9550
+ "placement=\"inline\" embeds a lightweight panel_app as an interactive chat card so the user can try it directly in the conversation.",
9551
+ "placement=\"side_panel\" opens content in the right side panel for larger reading, editing, or sustained workflows.",
9552
+ "Choose the placement yourself: after creating a lightweight Panel App such as a weather card, calculator, timer, checklist, picker, form, preview, or small dashboard, call this tool with placement=\"inline\" without waiting for the user to ask to see it.",
9553
+ "Omit placement only when preserving the existing default side panel behavior is intentional."
9554
+ ].join(" ");
9474
9555
  parameters = {
9475
9556
  type: "object",
9476
9557
  properties: {
@@ -9497,6 +9578,11 @@ var ShowContentTool = class {
9497
9578
  ],
9498
9579
  description: "Optional user intent for the content."
9499
9580
  },
9581
+ placement: {
9582
+ type: "string",
9583
+ enum: ["inline", "side_panel"],
9584
+ description: "Optional display placement. \"inline\" embeds a lightweight panel app as an interactive chat card; \"side_panel\" opens it in the right panel for larger workflows. Choose the appropriate placement yourself; defaults to the existing side panel behavior when omitted."
9585
+ },
9500
9586
  payload: {
9501
9587
  type: "object",
9502
9588
  description: "Type-specific fields: file={path,line,column}; url={url}; panel_app={appId}.",
@@ -9722,7 +9808,6 @@ var NextclawKernel = class {
9722
9808
  this.agentRunRequestManager = new AgentRunRequestManager(this.agentRuntimeManager, this.agents, this.configManager, this.contextProviderManager, this.eventBus, this.ingress, this.sessionManager, this.sessionRunManager, this.toolProviderManager);
9723
9809
  this.contributions = [
9724
9810
  new ToolProviderContribution(this),
9725
- new SessionActivityPreviewContribution(this),
9726
9811
  new LearningLoopContribution(this),
9727
9812
  new ContextProviderContribution(this),
9728
9813
  new AgentRunRuntimeContribution(this),