@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.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +435 -350
- package/dist/index.js.map +1 -1
- package/package.json +14 -14
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 {
|
|
913
|
-
const entry = this.
|
|
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,
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
4737
|
-
const id = readOptionalString$
|
|
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$
|
|
4743
|
-
icon: readOptionalString$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
5613
|
-
const risk = readOptionalString$
|
|
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$
|
|
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$
|
|
5620
|
-
description: readOptionalString$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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$
|
|
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$
|
|
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$
|
|
7341
|
-
const bytesBase64 = readOptionalString$
|
|
7342
|
-
const fileName = readOptionalString$
|
|
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$
|
|
7350
|
-
const fileName = readOptionalString$
|
|
7351
|
-
const mimeType = readOptionalString$
|
|
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$
|
|
7395
|
-
const targetPath = readOptionalString$
|
|
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$
|
|
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
|
-
...
|
|
8004
|
+
buildMetadata = (session, spec) => ({
|
|
8005
|
+
...session.metadata,
|
|
7725
8006
|
agentId: spec.agentId,
|
|
7726
|
-
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
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
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 =
|
|
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),
|