@nextclaw/kernel 0.5.0 → 0.5.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +40 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +665 -369
- package/dist/index.js.map +1 -1
- package/package.json +14 -14
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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";
|
|
7
7
|
import { EventBus, Ingress, eventKeys, ingressKeys, isRuntimeDefaultModelValue, normalizeRuntimeModelSelectionMode } from "@nextclaw/shared";
|
|
8
8
|
import { catchError, from, lastValueFrom, tap } from "rxjs";
|
|
9
|
-
import { appendFileSync, chmodSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { appendFileSync, chmodSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { basename, delimiter, dirname, extname, isAbsolute, join, normalize, relative, resolve } from "node:path";
|
|
11
|
-
import { spawn } from "node:child_process";
|
|
11
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
12
12
|
import { access, appendFile, mkdir, readFile, readdir, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
14
|
import { BUILTIN_PROVIDER_PLUGINS } from "@nextclaw/runtime";
|
|
@@ -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();
|
|
@@ -1993,7 +2023,11 @@ var CommandRegistry = class {
|
|
|
1993
2023
|
//#region src/features/extension-runtime/services/extension-lifecycle.service.ts
|
|
1994
2024
|
var ExtensionLifecycleService = class {
|
|
1995
2025
|
processes = /* @__PURE__ */ new Map();
|
|
2026
|
+
constructor(options = {}) {
|
|
2027
|
+
this.options = options;
|
|
2028
|
+
}
|
|
1996
2029
|
startAll = (manifests, params) => {
|
|
2030
|
+
this.cleanupOrphanProcesses(manifests);
|
|
1997
2031
|
const started = [];
|
|
1998
2032
|
for (const manifest of manifests) try {
|
|
1999
2033
|
started.push(this.start(manifest, params));
|
|
@@ -2024,6 +2058,7 @@ var ExtensionLifecycleService = class {
|
|
|
2024
2058
|
...manifest.server.env,
|
|
2025
2059
|
NEXTCLAW_EXTENSION_ID: manifest.id,
|
|
2026
2060
|
NEXTCLAW_EXTENSION_ENDPOINT: params.endpoint,
|
|
2061
|
+
NEXTCLAW_EXTENSION_PARENT_PID: String(process.pid),
|
|
2027
2062
|
NEXTCLAW_EXTENSION_TOKEN: params.tokenForExtension(manifest.id)
|
|
2028
2063
|
}, { inheritBaseEnv: true }),
|
|
2029
2064
|
stdio: [
|
|
@@ -2047,6 +2082,86 @@ var ExtensionLifecycleService = class {
|
|
|
2047
2082
|
});
|
|
2048
2083
|
return running;
|
|
2049
2084
|
};
|
|
2085
|
+
cleanupOrphanProcesses = (manifests) => {
|
|
2086
|
+
if (this.options.cleanupOrphanProcesses) {
|
|
2087
|
+
this.options.cleanupOrphanProcesses(manifests);
|
|
2088
|
+
return;
|
|
2089
|
+
}
|
|
2090
|
+
if (process.platform === "win32" || process.env.NEXTCLAW_EXTENSION_ORPHAN_CLEANUP === "0") return;
|
|
2091
|
+
try {
|
|
2092
|
+
const roots = this.resolveManifestRoots(manifests);
|
|
2093
|
+
for (const snapshot of this.listOrphanNodeDistMainProcesses()) {
|
|
2094
|
+
const cwd = this.readProcessCwd(snapshot.pid);
|
|
2095
|
+
if (!cwd || !this.isNextClawChannelExtensionCwd(cwd, roots)) continue;
|
|
2096
|
+
try {
|
|
2097
|
+
process.kill(snapshot.pid, "SIGTERM");
|
|
2098
|
+
console.warn(`Stopped orphan extension process ${snapshot.pid} (${cwd}).`);
|
|
2099
|
+
} catch (error) {
|
|
2100
|
+
console.warn(`Failed to stop orphan extension process ${snapshot.pid}: ${error instanceof Error ? error.message : String(error)}`);
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
} catch (error) {
|
|
2104
|
+
console.warn(`Extension orphan cleanup skipped: ${error instanceof Error ? error.message : String(error)}`);
|
|
2105
|
+
}
|
|
2106
|
+
};
|
|
2107
|
+
resolveManifestRoots = (manifests) => {
|
|
2108
|
+
return new Set(manifests.map((manifest) => this.normalizePath(manifest.rootDir)));
|
|
2109
|
+
};
|
|
2110
|
+
normalizePath = (value) => {
|
|
2111
|
+
try {
|
|
2112
|
+
return realpathSync(value);
|
|
2113
|
+
} catch {
|
|
2114
|
+
return resolve(value);
|
|
2115
|
+
}
|
|
2116
|
+
};
|
|
2117
|
+
listOrphanNodeDistMainProcesses = () => {
|
|
2118
|
+
return execFileSync("ps", ["-axo", "pid=,ppid=,command="], {
|
|
2119
|
+
encoding: "utf8",
|
|
2120
|
+
maxBuffer: 16 * 1024 * 1024
|
|
2121
|
+
}).split("\n").map((line) => {
|
|
2122
|
+
const match = line.trim().match(/^(\d+)\s+(\d+)\s+(.*)$/);
|
|
2123
|
+
if (!match) return null;
|
|
2124
|
+
return {
|
|
2125
|
+
pid: Number(match[1]),
|
|
2126
|
+
ppid: Number(match[2]),
|
|
2127
|
+
command: match[3] ?? ""
|
|
2128
|
+
};
|
|
2129
|
+
}).filter((snapshot) => Boolean(snapshot && snapshot.ppid === 1 && this.isNodeDistMainCommand(snapshot.command)));
|
|
2130
|
+
};
|
|
2131
|
+
isNodeDistMainCommand = (command) => {
|
|
2132
|
+
return /(?:^|\s|\/)node(?:\.exe)?\s+dist\/main\.js(?:\s|$)/.test(command);
|
|
2133
|
+
};
|
|
2134
|
+
readProcessCwd = (pid) => {
|
|
2135
|
+
if (process.platform === "linux") try {
|
|
2136
|
+
return this.normalizePath(readlinkSync(`/proc/${pid}/cwd`));
|
|
2137
|
+
} catch {
|
|
2138
|
+
return null;
|
|
2139
|
+
}
|
|
2140
|
+
try {
|
|
2141
|
+
const cwd = execFileSync("lsof", [
|
|
2142
|
+
"-a",
|
|
2143
|
+
"-p",
|
|
2144
|
+
String(pid),
|
|
2145
|
+
"-d",
|
|
2146
|
+
"cwd",
|
|
2147
|
+
"-Fn"
|
|
2148
|
+
], {
|
|
2149
|
+
encoding: "utf8",
|
|
2150
|
+
stdio: [
|
|
2151
|
+
"ignore",
|
|
2152
|
+
"pipe",
|
|
2153
|
+
"ignore"
|
|
2154
|
+
]
|
|
2155
|
+
}).split("\n").find((line) => line.startsWith("n"))?.slice(1).trim();
|
|
2156
|
+
return cwd ? this.normalizePath(cwd) : null;
|
|
2157
|
+
} catch {
|
|
2158
|
+
return null;
|
|
2159
|
+
}
|
|
2160
|
+
};
|
|
2161
|
+
isNextClawChannelExtensionCwd = (cwd, roots) => {
|
|
2162
|
+
if (roots.has(cwd)) return true;
|
|
2163
|
+
return /(?:^|\/)nextclaw-channel-extension-[^/]+$/.test(cwd) || /\/node_modules\/@nextclaw\/channel-extension-[^/]+$/.test(cwd) || /\/node_modules\/\.nextclaw-[^/]+\/node_modules\/@nextclaw\/channel-extension-[^/]+$/.test(cwd);
|
|
2164
|
+
};
|
|
2050
2165
|
};
|
|
2051
2166
|
//#endregion
|
|
2052
2167
|
//#region src/features/extension-runtime/services/extension-manifest-discovery.service.ts
|
|
@@ -3443,6 +3558,262 @@ function readEventSessionId$1(event) {
|
|
|
3443
3558
|
return "payload" in event && "sessionId" in event.payload ? readOptionalMetadataString(event.payload.sessionId) : void 0;
|
|
3444
3559
|
}
|
|
3445
3560
|
//#endregion
|
|
3561
|
+
//#region src/contributions/session-activity-preview/utils/session-activity-preview-ncp-event.utils.ts
|
|
3562
|
+
const PREVIEW_TEXT_MAX_LENGTH = 160;
|
|
3563
|
+
function readSessionId(value) {
|
|
3564
|
+
if (typeof value !== "string") return null;
|
|
3565
|
+
const trimmed = value.trim();
|
|
3566
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
3567
|
+
}
|
|
3568
|
+
function compactPreviewText(value) {
|
|
3569
|
+
return value.replace(/\s+/g, " ").trim();
|
|
3570
|
+
}
|
|
3571
|
+
function truncatePreviewText(value) {
|
|
3572
|
+
const compacted = compactPreviewText(value);
|
|
3573
|
+
if (compacted.length <= PREVIEW_TEXT_MAX_LENGTH) return compacted;
|
|
3574
|
+
return compacted.slice(0, PREVIEW_TEXT_MAX_LENGTH - 1).trimEnd();
|
|
3575
|
+
}
|
|
3576
|
+
function readSessionActivityPreviewText(message) {
|
|
3577
|
+
const chunks = [];
|
|
3578
|
+
for (const part of message.parts) if ((part.type === "text" || part.type === "rich-text") && part.text.trim()) chunks.push(part.text);
|
|
3579
|
+
const previewText = truncatePreviewText(chunks.join(" "));
|
|
3580
|
+
return previewText.length > 0 ? previewText : null;
|
|
3581
|
+
}
|
|
3582
|
+
function createProjection(sessionId, preview) {
|
|
3583
|
+
if (!sessionId) return null;
|
|
3584
|
+
return {
|
|
3585
|
+
sessionId,
|
|
3586
|
+
preview
|
|
3587
|
+
};
|
|
3588
|
+
}
|
|
3589
|
+
function formatErrorStatus(error) {
|
|
3590
|
+
if (typeof error === "string" && error.trim()) return `运行出错:${truncatePreviewText(error)}`;
|
|
3591
|
+
if (error && typeof error === "object" && "message" in error) {
|
|
3592
|
+
const message = error.message;
|
|
3593
|
+
if (typeof message === "string" && message.trim()) return `运行出错:${truncatePreviewText(message)}`;
|
|
3594
|
+
}
|
|
3595
|
+
return "运行出错";
|
|
3596
|
+
}
|
|
3597
|
+
function readToolCallId(value) {
|
|
3598
|
+
if (typeof value !== "string") return null;
|
|
3599
|
+
const trimmed = value.trim();
|
|
3600
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
3601
|
+
}
|
|
3602
|
+
function formatToolDoneStatus(toolName) {
|
|
3603
|
+
return toolName ? `工具调用完成:${toolName}` : "工具调用完成";
|
|
3604
|
+
}
|
|
3605
|
+
function createSessionActivityPreviewFromNcpEvent(event, timestamp, options = {}) {
|
|
3606
|
+
switch (event.type) {
|
|
3607
|
+
case NcpEventType.RunStarted: return createProjection(readSessionId(event.payload.sessionId), {
|
|
3608
|
+
state: "running",
|
|
3609
|
+
statusText: "正在思考",
|
|
3610
|
+
timestamp
|
|
3611
|
+
});
|
|
3612
|
+
case NcpEventType.RunFinished: return createProjection(readSessionId(event.payload.sessionId), {
|
|
3613
|
+
state: "completed",
|
|
3614
|
+
timestamp
|
|
3615
|
+
});
|
|
3616
|
+
case NcpEventType.RunError: return createProjection(readSessionId(event.payload.sessionId), {
|
|
3617
|
+
state: "failed",
|
|
3618
|
+
statusText: formatErrorStatus(event.payload.error),
|
|
3619
|
+
timestamp
|
|
3620
|
+
});
|
|
3621
|
+
case NcpEventType.MessageSent: {
|
|
3622
|
+
const text = readSessionActivityPreviewText(event.payload.message);
|
|
3623
|
+
if (!text || event.payload.message.role !== "user") return null;
|
|
3624
|
+
return createProjection(readSessionId(event.payload.sessionId), {
|
|
3625
|
+
state: "running",
|
|
3626
|
+
statusText: text,
|
|
3627
|
+
timestamp: event.payload.message.timestamp || timestamp
|
|
3628
|
+
});
|
|
3629
|
+
}
|
|
3630
|
+
case NcpEventType.MessageCompleted: {
|
|
3631
|
+
const text = readSessionActivityPreviewText(event.payload.message);
|
|
3632
|
+
if (!text || event.payload.message.role !== "assistant") return null;
|
|
3633
|
+
return createProjection(readSessionId(event.payload.sessionId), {
|
|
3634
|
+
state: "completed",
|
|
3635
|
+
replyText: text,
|
|
3636
|
+
timestamp: event.payload.message.timestamp || timestamp
|
|
3637
|
+
});
|
|
3638
|
+
}
|
|
3639
|
+
case NcpEventType.MessageFailed: return createProjection(readSessionId(event.payload.sessionId), {
|
|
3640
|
+
state: "failed",
|
|
3641
|
+
statusText: formatErrorStatus(event.payload.error),
|
|
3642
|
+
timestamp
|
|
3643
|
+
});
|
|
3644
|
+
case NcpEventType.MessageToolCallStart: return createProjection(readSessionId(event.payload.sessionId), {
|
|
3645
|
+
state: "running",
|
|
3646
|
+
statusText: `正在调用工具:${event.payload.toolName}`,
|
|
3647
|
+
timestamp
|
|
3648
|
+
});
|
|
3649
|
+
case NcpEventType.MessageToolCallEnd:
|
|
3650
|
+
case NcpEventType.MessageToolCallResult: {
|
|
3651
|
+
const sessionId = readSessionId(event.payload.sessionId);
|
|
3652
|
+
const toolCallId = readToolCallId(event.payload.toolCallId);
|
|
3653
|
+
return createProjection(sessionId, {
|
|
3654
|
+
state: "running",
|
|
3655
|
+
statusText: formatToolDoneStatus(sessionId && toolCallId ? options.readToolName?.(sessionId, toolCallId) ?? null : null),
|
|
3656
|
+
timestamp
|
|
3657
|
+
});
|
|
3658
|
+
}
|
|
3659
|
+
default: return null;
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3662
|
+
//#endregion
|
|
3663
|
+
//#region src/contributions/session-activity-preview/utils/session-activity-preview-metadata.utils.ts
|
|
3664
|
+
const SESSION_ACTIVITY_PREVIEW_METADATA_KEY = "last_activity_preview";
|
|
3665
|
+
const SESSION_ACTIVITY_PREVIEW_STATES = new Set([
|
|
3666
|
+
"running",
|
|
3667
|
+
"completed",
|
|
3668
|
+
"failed",
|
|
3669
|
+
"idle"
|
|
3670
|
+
]);
|
|
3671
|
+
function isRecord$10(value) {
|
|
3672
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3673
|
+
}
|
|
3674
|
+
function readOptionalString$7(value) {
|
|
3675
|
+
if (typeof value !== "string") return;
|
|
3676
|
+
const trimmed = value.trim();
|
|
3677
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
3678
|
+
}
|
|
3679
|
+
function readSessionActivityPreviewMetadata(value) {
|
|
3680
|
+
if (!isRecord$10(value)) return null;
|
|
3681
|
+
const state = value.state;
|
|
3682
|
+
const timestamp = readOptionalString$7(value.timestamp);
|
|
3683
|
+
if (!SESSION_ACTIVITY_PREVIEW_STATES.has(state) || !timestamp) return null;
|
|
3684
|
+
return {
|
|
3685
|
+
state,
|
|
3686
|
+
timestamp,
|
|
3687
|
+
...readOptionalString$7(value.statusText) ? { statusText: readOptionalString$7(value.statusText) } : {},
|
|
3688
|
+
...readOptionalString$7(value.replyText) ? { replyText: readOptionalString$7(value.replyText) } : {}
|
|
3689
|
+
};
|
|
3690
|
+
}
|
|
3691
|
+
function compareIsoTimestamp(left, right) {
|
|
3692
|
+
const leftTime = Date.parse(left);
|
|
3693
|
+
const rightTime = Date.parse(right);
|
|
3694
|
+
if (!Number.isFinite(leftTime) || !Number.isFinite(rightTime)) return left.localeCompare(right);
|
|
3695
|
+
return leftTime - rightTime;
|
|
3696
|
+
}
|
|
3697
|
+
function mergeSessionActivityPreview(current, incoming) {
|
|
3698
|
+
if (current && compareIsoTimestamp(incoming.timestamp, current.timestamp) < 0) return incoming.replyText && !current.replyText ? {
|
|
3699
|
+
...current,
|
|
3700
|
+
replyText: incoming.replyText
|
|
3701
|
+
} : current;
|
|
3702
|
+
return {
|
|
3703
|
+
state: incoming.state,
|
|
3704
|
+
timestamp: incoming.timestamp,
|
|
3705
|
+
...incoming.statusText ?? (incoming.state === "completed" ? current?.statusText : void 0) ? { statusText: incoming.statusText ?? current?.statusText } : {},
|
|
3706
|
+
...incoming.replyText ?? (incoming.state === "completed" ? current?.replyText : void 0) ? { replyText: incoming.replyText ?? current?.replyText } : {}
|
|
3707
|
+
};
|
|
3708
|
+
}
|
|
3709
|
+
function areSessionActivityPreviewsEqual(left, right) {
|
|
3710
|
+
return Boolean(left) && left?.state === right.state && left.timestamp === right.timestamp && left.statusText === right.statusText && left.replyText === right.replyText;
|
|
3711
|
+
}
|
|
3712
|
+
function writeSessionActivityPreviewMetadata(metadata, projection) {
|
|
3713
|
+
const current = readSessionActivityPreviewMetadata(metadata?.[SESSION_ACTIVITY_PREVIEW_METADATA_KEY]);
|
|
3714
|
+
const next = mergeSessionActivityPreview(current, projection.preview);
|
|
3715
|
+
if (areSessionActivityPreviewsEqual(current, next)) return null;
|
|
3716
|
+
return {
|
|
3717
|
+
...metadata ?? {},
|
|
3718
|
+
[SESSION_ACTIVITY_PREVIEW_METADATA_KEY]: next
|
|
3719
|
+
};
|
|
3720
|
+
}
|
|
3721
|
+
//#endregion
|
|
3722
|
+
//#region src/contributions/session-activity-preview/services/session-activity-preview-event.service.ts
|
|
3723
|
+
var SessionActivityPreviewEventService = class {
|
|
3724
|
+
toolNames = /* @__PURE__ */ new Map();
|
|
3725
|
+
constructor(options) {
|
|
3726
|
+
this.options = options;
|
|
3727
|
+
}
|
|
3728
|
+
projectEvent = (event, timestamp) => {
|
|
3729
|
+
this.rememberToolName(event);
|
|
3730
|
+
const projection = createSessionActivityPreviewFromNcpEvent(event, timestamp, { readToolName: this.readToolName });
|
|
3731
|
+
this.clearFinishedRunToolNames(event);
|
|
3732
|
+
return projection;
|
|
3733
|
+
};
|
|
3734
|
+
updatePreview = async (projection) => {
|
|
3735
|
+
const session = await this.options.getSessionRecord(projection.sessionId);
|
|
3736
|
+
const replyText = projection.preview.replyText ?? (projection.preview.state === "completed" ? this.readLatestAssistantPreviewText(session?.messages) : void 0);
|
|
3737
|
+
const nextMetadata = writeSessionActivityPreviewMetadata(session?.metadata, {
|
|
3738
|
+
...projection,
|
|
3739
|
+
preview: {
|
|
3740
|
+
...projection.preview,
|
|
3741
|
+
replyText
|
|
3742
|
+
}
|
|
3743
|
+
});
|
|
3744
|
+
if (nextMetadata) await this.options.updateSessionMetadata(projection.sessionId, nextMetadata);
|
|
3745
|
+
};
|
|
3746
|
+
clear = () => {
|
|
3747
|
+
this.toolNames.clear();
|
|
3748
|
+
};
|
|
3749
|
+
rememberToolName = (event) => {
|
|
3750
|
+
if (event.type !== NcpEventType.MessageToolCallStart) return;
|
|
3751
|
+
const { sessionId, toolCallId, toolName } = event.payload;
|
|
3752
|
+
if (sessionId && toolCallId && toolName) this.toolNames.set(this.createToolNameKey(sessionId, toolCallId), toolName);
|
|
3753
|
+
};
|
|
3754
|
+
readToolName = (sessionId, toolCallId) => this.toolNames.get(this.createToolNameKey(sessionId, toolCallId)) ?? null;
|
|
3755
|
+
clearFinishedRunToolNames = (event) => {
|
|
3756
|
+
if (event.type !== NcpEventType.RunFinished && event.type !== NcpEventType.RunError) return;
|
|
3757
|
+
const sessionId = event.payload.sessionId;
|
|
3758
|
+
for (const key of this.toolNames.keys()) if (key.startsWith(`${sessionId}:`)) this.toolNames.delete(key);
|
|
3759
|
+
};
|
|
3760
|
+
createToolNameKey = (sessionId, toolCallId) => `${sessionId}:${toolCallId}`;
|
|
3761
|
+
readLatestAssistantPreviewText = (messages) => {
|
|
3762
|
+
for (let index = (messages?.length ?? 0) - 1; index >= 0; index -= 1) {
|
|
3763
|
+
const message = messages?.[index];
|
|
3764
|
+
if (message?.role === "assistant") return readSessionActivityPreviewText(message) ?? void 0;
|
|
3765
|
+
}
|
|
3766
|
+
};
|
|
3767
|
+
};
|
|
3768
|
+
//#endregion
|
|
3769
|
+
//#region src/services/session-event-ingestion.service.ts
|
|
3770
|
+
const SESSION_METADATA_PATCH_RUN_METADATA_KIND = "session_metadata_patch";
|
|
3771
|
+
function isDurableSessionEvent(event) {
|
|
3772
|
+
return event.type !== NcpEventType.ContextWindowUpdated;
|
|
3773
|
+
}
|
|
3774
|
+
function readRuntimeSessionMetadataPatch(event) {
|
|
3775
|
+
if (event.type !== NcpEventType.RunMetadata) return null;
|
|
3776
|
+
const metadata = event.payload.metadata;
|
|
3777
|
+
if (metadata.kind !== SESSION_METADATA_PATCH_RUN_METADATA_KIND || !metadata.sessionMetadataPatch || typeof metadata.sessionMetadataPatch !== "object" || Array.isArray(metadata.sessionMetadataPatch)) return null;
|
|
3778
|
+
const patch = metadata.sessionMetadataPatch;
|
|
3779
|
+
return Object.keys(patch).length > 0 ? structuredClone(patch) : null;
|
|
3780
|
+
}
|
|
3781
|
+
var SessionEventIngestionService = class {
|
|
3782
|
+
activityPreview;
|
|
3783
|
+
chains = /* @__PURE__ */ new Map();
|
|
3784
|
+
constructor(options) {
|
|
3785
|
+
this.options = options;
|
|
3786
|
+
this.activityPreview = new SessionActivityPreviewEventService({
|
|
3787
|
+
getSessionRecord: options.getSessionRecord,
|
|
3788
|
+
updateSessionMetadata: options.updateSessionMetadata
|
|
3789
|
+
});
|
|
3790
|
+
}
|
|
3791
|
+
handleEvent = (event) => {
|
|
3792
|
+
const sessionId = readEventSessionId$1(event);
|
|
3793
|
+
if (!sessionId || !isDurableSessionEvent(event)) return;
|
|
3794
|
+
const next = (this.chains.get(sessionId) ?? Promise.resolve()).then(() => this.handleDurableEvent(sessionId, event));
|
|
3795
|
+
this.chains.set(sessionId, next.catch(() => void 0));
|
|
3796
|
+
next.catch((error) => this.options.onError(sessionId, error));
|
|
3797
|
+
};
|
|
3798
|
+
clear = () => {
|
|
3799
|
+
this.chains.clear();
|
|
3800
|
+
this.activityPreview.clear();
|
|
3801
|
+
};
|
|
3802
|
+
handleDurableEvent = async (sessionId, event) => {
|
|
3803
|
+
const preview = this.activityPreview.projectEvent(event, (/* @__PURE__ */ new Date()).toISOString());
|
|
3804
|
+
const metadataPatch = readRuntimeSessionMetadataPatch(event);
|
|
3805
|
+
if (metadataPatch) {
|
|
3806
|
+
await this.options.updateSessionMetadata(sessionId, metadataPatch);
|
|
3807
|
+
return;
|
|
3808
|
+
}
|
|
3809
|
+
await this.options.appendSessionEvent({
|
|
3810
|
+
event,
|
|
3811
|
+
sessionId
|
|
3812
|
+
});
|
|
3813
|
+
if (preview) await this.activityPreview.updatePreview(preview);
|
|
3814
|
+
};
|
|
3815
|
+
};
|
|
3816
|
+
//#endregion
|
|
3446
3817
|
//#region src/services/session-working-dir-resolver.service.ts
|
|
3447
3818
|
var SessionWorkingDirResolver = class {
|
|
3448
3819
|
constructor(agentManager) {
|
|
@@ -3471,10 +3842,6 @@ const SESSION_METADATA_LABEL_KEY = "label";
|
|
|
3471
3842
|
const CHILD_SESSION_PARENT_METADATA_KEY = "parent_session_id";
|
|
3472
3843
|
const CHILD_SESSION_REQUEST_METADATA_KEY = "spawned_by_request_id";
|
|
3473
3844
|
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
3845
|
function readThinkingEffort(metadata) {
|
|
3479
3846
|
return readOptionalMetadataString(metadata?.thinkingEffort) ?? null;
|
|
3480
3847
|
}
|
|
@@ -3542,42 +3909,34 @@ function isSessionSummaryRefreshEvent(event) {
|
|
|
3542
3909
|
default: return false;
|
|
3543
3910
|
}
|
|
3544
3911
|
}
|
|
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
3912
|
var SessionManager = class {
|
|
3553
3913
|
cleanups = [];
|
|
3554
3914
|
contextWindowPreview;
|
|
3915
|
+
eventIngestion;
|
|
3555
3916
|
workingDirResolver;
|
|
3556
3917
|
started = false;
|
|
3557
3918
|
constructor(options) {
|
|
3558
3919
|
this.options = options;
|
|
3920
|
+
this.eventIngestion = new SessionEventIngestionService({
|
|
3921
|
+
appendSessionEvent: (params) => this.appendSessionEvent(params),
|
|
3922
|
+
getSessionRecord: (sessionId) => this.getSessionRecord(sessionId),
|
|
3923
|
+
onError: (sessionId, error) => {
|
|
3924
|
+
const message = error instanceof Error ? error.stack ?? error.message : String(error);
|
|
3925
|
+
console.error(`[session-manager] failed to handle ncp event for ${sessionId}: ${message}`);
|
|
3926
|
+
},
|
|
3927
|
+
updateSessionMetadata: (sessionId, metadata) => this.updateSessionMetadata(sessionId, metadata)
|
|
3928
|
+
});
|
|
3559
3929
|
this.contextWindowPreview = new ContextWindowPreviewManager(options.agentManager);
|
|
3560
3930
|
this.workingDirResolver = new SessionWorkingDirResolver(options.agentManager);
|
|
3561
3931
|
}
|
|
3562
3932
|
start = () => {
|
|
3563
3933
|
if (this.started) return;
|
|
3564
3934
|
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
|
-
}));
|
|
3935
|
+
this.cleanups.push(this.options.eventBus.on(eventKeys.ncpEvent, this.eventIngestion.handleEvent));
|
|
3578
3936
|
};
|
|
3579
3937
|
dispose = () => {
|
|
3580
3938
|
while (this.cleanups.length > 0) this.cleanups.pop()?.();
|
|
3939
|
+
this.eventIngestion.clear();
|
|
3581
3940
|
this.started = false;
|
|
3582
3941
|
};
|
|
3583
3942
|
createSession = async (params) => {
|
|
@@ -3706,6 +4065,10 @@ var SessionManager = class {
|
|
|
3706
4065
|
metadata: structuredClone(record.metadata ?? {}),
|
|
3707
4066
|
model,
|
|
3708
4067
|
projectRoot: readProjectRoot(record.metadata),
|
|
4068
|
+
workingDir: this.workingDirResolver.resolve({
|
|
4069
|
+
agentId: record.agentId,
|
|
4070
|
+
metadata: record.metadata
|
|
4071
|
+
}),
|
|
3709
4072
|
thinkingEffort: readThinkingEffort(record.metadata)
|
|
3710
4073
|
};
|
|
3711
4074
|
};
|
|
@@ -3742,6 +4105,10 @@ var SessionManager = class {
|
|
|
3742
4105
|
metadata: structuredClone(created.metadata ?? {}),
|
|
3743
4106
|
model,
|
|
3744
4107
|
projectRoot,
|
|
4108
|
+
workingDir: this.workingDirResolver.resolve({
|
|
4109
|
+
agentId,
|
|
4110
|
+
metadata: created.metadata
|
|
4111
|
+
}),
|
|
3745
4112
|
thinkingEffort: thinkingEffort ?? null
|
|
3746
4113
|
};
|
|
3747
4114
|
};
|
|
@@ -4009,12 +4376,12 @@ var PanelAppCapabilityGrantStore = class {
|
|
|
4009
4376
|
};
|
|
4010
4377
|
};
|
|
4011
4378
|
function normalizeStoreData$2(value) {
|
|
4012
|
-
if (!isRecord$
|
|
4379
|
+
if (!isRecord$9(value) || value.version !== 1 || !isRecord$9(value.grants)) return structuredClone(EMPTY_GRANTS$2);
|
|
4013
4380
|
const grants = {};
|
|
4014
4381
|
for (const [callerKey, callerValue] of Object.entries(value.grants)) {
|
|
4015
|
-
if (!isRecord$
|
|
4382
|
+
if (!isRecord$9(callerValue) || !isRecord$9(callerValue.capabilities)) continue;
|
|
4016
4383
|
const capabilities = {};
|
|
4017
|
-
for (const [capability, grantValue] of Object.entries(callerValue.capabilities)) if (isRecord$
|
|
4384
|
+
for (const [capability, grantValue] of Object.entries(callerValue.capabilities)) if (isRecord$9(grantValue) && typeof grantValue.grantedAt === "string") capabilities[capability] = { grantedAt: grantValue.grantedAt };
|
|
4018
4385
|
grants[callerKey] = { capabilities };
|
|
4019
4386
|
}
|
|
4020
4387
|
return {
|
|
@@ -4025,7 +4392,7 @@ function normalizeStoreData$2(value) {
|
|
|
4025
4392
|
function getCallerKey(caller) {
|
|
4026
4393
|
return `${caller.surface}:${caller.appId}`;
|
|
4027
4394
|
}
|
|
4028
|
-
function isRecord$
|
|
4395
|
+
function isRecord$9(value) {
|
|
4029
4396
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
4030
4397
|
}
|
|
4031
4398
|
function isMissingFileError$3(error) {
|
|
@@ -4071,19 +4438,19 @@ var PanelAppClientGrantStore = class {
|
|
|
4071
4438
|
};
|
|
4072
4439
|
};
|
|
4073
4440
|
function normalizeStoreData$1(value) {
|
|
4074
|
-
if (!isRecord$
|
|
4441
|
+
if (!isRecord$8(value) || value.version !== 1 || !isRecord$8(value.grants)) return structuredClone(EMPTY_GRANTS$1);
|
|
4075
4442
|
const grants = {};
|
|
4076
|
-
for (const [appId, grant] of Object.entries(value.grants)) if (isRecord$
|
|
4443
|
+
for (const [appId, grant] of Object.entries(value.grants)) if (isRecord$8(grant) && typeof grant.grantedAt === "string") grants[appId] = { grantedAt: grant.grantedAt };
|
|
4077
4444
|
return {
|
|
4078
4445
|
grants,
|
|
4079
4446
|
version: 1
|
|
4080
4447
|
};
|
|
4081
4448
|
}
|
|
4082
|
-
function isRecord$
|
|
4449
|
+
function isRecord$8(value) {
|
|
4083
4450
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
4084
4451
|
}
|
|
4085
4452
|
function isMissingFileError$2(error) {
|
|
4086
|
-
return isRecord$
|
|
4453
|
+
return isRecord$8(error) && error.code === "ENOENT";
|
|
4087
4454
|
}
|
|
4088
4455
|
//#endregion
|
|
4089
4456
|
//#region src/services/agent-run-client.service.ts
|
|
@@ -4302,7 +4669,7 @@ const PANEL_APP_AGENT_MAX_CONTEXT_CHARS = 8e4;
|
|
|
4302
4669
|
function normalizePanelAppGenerateObjectInput(input) {
|
|
4303
4670
|
const peerId = input.peerId.trim();
|
|
4304
4671
|
const prompt = input.prompt.trim();
|
|
4305
|
-
if (!peerId || !prompt || !isRecord$
|
|
4672
|
+
if (!peerId || !prompt || !isRecord$7(input.schema)) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "invalid generateObject request");
|
|
4306
4673
|
if (prompt.length > PANEL_APP_AGENT_MAX_PROMPT_CHARS) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "generateObject prompt is too large");
|
|
4307
4674
|
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
4675
|
return {
|
|
@@ -4443,7 +4810,7 @@ function readStructuredResultEvent(event, resultToolCallId) {
|
|
|
4443
4810
|
};
|
|
4444
4811
|
}
|
|
4445
4812
|
function assertToolResultContent(content) {
|
|
4446
|
-
if (!isRecord$
|
|
4813
|
+
if (!isRecord$7(content) || content.ok !== false || !isRecord$7(content.error)) return;
|
|
4447
4814
|
if (content.error.code === "invalid_tool_arguments") throw new PanelAppError("AGENT_OBJECT_RESULT_SCHEMA_INVALID", "agent object result did not match the schema");
|
|
4448
4815
|
throw new PanelAppError("AGENT_OBJECT_REQUEST_FAILED", typeof content.error.message === "string" ? content.error.message : "agent object request failed");
|
|
4449
4816
|
}
|
|
@@ -4459,7 +4826,7 @@ function stringifyJsonValue(value) {
|
|
|
4459
4826
|
throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "value is not JSON serializable");
|
|
4460
4827
|
}
|
|
4461
4828
|
}
|
|
4462
|
-
function isRecord$
|
|
4829
|
+
function isRecord$7(value) {
|
|
4463
4830
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
4464
4831
|
}
|
|
4465
4832
|
//#endregion
|
|
@@ -4733,14 +5100,14 @@ function parsePanelAppFolderManifest(raw) {
|
|
|
4733
5100
|
} catch (error) {
|
|
4734
5101
|
throw new Error(`panel-app.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
4735
5102
|
}
|
|
4736
|
-
if (!isRecord$
|
|
4737
|
-
const id = readOptionalString$
|
|
5103
|
+
if (!isRecord$6(parsed)) throw new Error("panel-app.json must contain an object.");
|
|
5104
|
+
const id = readOptionalString$6(parsed, "id");
|
|
4738
5105
|
if (id && !PANEL_APP_ID_PATTERN.test(id)) throw new Error("panel app id must be kebab-case.");
|
|
4739
5106
|
return {
|
|
4740
5107
|
id,
|
|
4741
5108
|
title: readRequiredString$4(parsed, "title"),
|
|
4742
|
-
description: readOptionalString$
|
|
4743
|
-
icon: readOptionalString$
|
|
5109
|
+
description: readOptionalString$6(parsed, "description"),
|
|
5110
|
+
icon: readOptionalString$6(parsed, "icon"),
|
|
4744
5111
|
entry: readRequiredString$4(parsed, "entry"),
|
|
4745
5112
|
capabilities: readStringArray$1(parsed.capabilities, "capabilities"),
|
|
4746
5113
|
client: readOptionalBoolean$2(parsed, "client"),
|
|
@@ -4802,11 +5169,11 @@ function parseTokenList(content) {
|
|
|
4802
5169
|
return [...new Set(content.split(/[,\s]+/).map((entry) => entry.trim()).filter(Boolean))];
|
|
4803
5170
|
}
|
|
4804
5171
|
function readRequiredString$4(record, key) {
|
|
4805
|
-
const value = readOptionalString$
|
|
5172
|
+
const value = readOptionalString$6(record, key);
|
|
4806
5173
|
if (!value) throw new Error(`panel app ${key} is required.`);
|
|
4807
5174
|
return value;
|
|
4808
5175
|
}
|
|
4809
|
-
function readOptionalString$
|
|
5176
|
+
function readOptionalString$6(record, key) {
|
|
4810
5177
|
return typeof record[key] === "string" && record[key].trim() ? record[key].trim() : void 0;
|
|
4811
5178
|
}
|
|
4812
5179
|
function readOptionalBoolean$2(record, key) {
|
|
@@ -4820,7 +5187,7 @@ function readStringArray$1(value, key) {
|
|
|
4820
5187
|
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) throw new Error(`panel app ${key} must be a string array.`);
|
|
4821
5188
|
return [...new Set(value.map((entry) => entry.trim()).filter(Boolean))];
|
|
4822
5189
|
}
|
|
4823
|
-
function isRecord$
|
|
5190
|
+
function isRecord$6(value) {
|
|
4824
5191
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
4825
5192
|
}
|
|
4826
5193
|
function normalizeTextValue(value) {
|
|
@@ -5301,27 +5668,146 @@ var PanelAppManager = class {
|
|
|
5301
5668
|
clientGranted: await this.isPanelAppClientGranted(appId, manifest.client),
|
|
5302
5669
|
openCount: state.openCount ?? 0
|
|
5303
5670
|
};
|
|
5304
|
-
if (manifest.description) entry.description = manifest.description;
|
|
5305
|
-
if (manifest.icon) entry.icon = source.kind === "folder" ? resolvePanelAppIconUrl(id, manifest.icon) : manifest.icon;
|
|
5306
|
-
if (state.lastOpenedAt) entry.lastOpenedAt = state.lastOpenedAt;
|
|
5307
|
-
return entry;
|
|
5671
|
+
if (manifest.description) entry.description = manifest.description;
|
|
5672
|
+
if (manifest.icon) entry.icon = source.kind === "folder" ? resolvePanelAppIconUrl(id, manifest.icon) : manifest.icon;
|
|
5673
|
+
if (state.lastOpenedAt) entry.lastOpenedAt = state.lastOpenedAt;
|
|
5674
|
+
return entry;
|
|
5675
|
+
};
|
|
5676
|
+
resolvePanelAppFileName = async (id) => {
|
|
5677
|
+
return (await this.sourceService.resolveSource(this.getPanelsPath(this.getWorkspacePath()), id)).sourceName;
|
|
5678
|
+
};
|
|
5679
|
+
comparePanelApps = (left, right) => resolvePanelAppActivityMs(right) - resolvePanelAppActivityMs(left) || Number(right.favorite) - Number(left.favorite) || left.title.localeCompare(right.title);
|
|
5680
|
+
deleteExpiredBridgeSessions = () => {
|
|
5681
|
+
const now = Date.now();
|
|
5682
|
+
for (const [token, session] of this.bridgeSessions) if (new Date(session.expiresAt).getTime() <= now) this.bridgeSessions.delete(token);
|
|
5683
|
+
};
|
|
5684
|
+
deleteBridgeSessionsByPanelAppId = (panelAppId) => {
|
|
5685
|
+
for (const [token, session] of this.bridgeSessions) if (session.appId === panelAppId) this.bridgeSessions.delete(token);
|
|
5686
|
+
};
|
|
5687
|
+
isPanelAppClientGranted = async (appId, clientDeclared) => {
|
|
5688
|
+
if (!clientDeclared) return false;
|
|
5689
|
+
return await this.createClientGrantStore().isGranted(appId);
|
|
5690
|
+
};
|
|
5691
|
+
isMissingFileError = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
5692
|
+
};
|
|
5693
|
+
//#endregion
|
|
5694
|
+
//#region src/stores/preference.store.ts
|
|
5695
|
+
const PREFERENCE_STORE_VERSION = 1;
|
|
5696
|
+
var PreferenceStore = class {
|
|
5697
|
+
constructor(storePath) {
|
|
5698
|
+
this.storePath = storePath;
|
|
5699
|
+
}
|
|
5700
|
+
get = async (key) => {
|
|
5701
|
+
return (await this.load())[key] ?? null;
|
|
5702
|
+
};
|
|
5703
|
+
set = async (key, value, updatedAt = /* @__PURE__ */ new Date()) => {
|
|
5704
|
+
const entries = await this.load();
|
|
5705
|
+
const entry = {
|
|
5706
|
+
key,
|
|
5707
|
+
value,
|
|
5708
|
+
updatedAt: updatedAt.toISOString()
|
|
5709
|
+
};
|
|
5710
|
+
entries[key] = entry;
|
|
5711
|
+
await this.persist(entries);
|
|
5712
|
+
return entry;
|
|
5713
|
+
};
|
|
5714
|
+
delete = async (key) => {
|
|
5715
|
+
const entries = await this.load();
|
|
5716
|
+
if (!(key in entries)) return false;
|
|
5717
|
+
delete entries[key];
|
|
5718
|
+
await this.persist(entries);
|
|
5719
|
+
return true;
|
|
5720
|
+
};
|
|
5721
|
+
load = async () => {
|
|
5722
|
+
try {
|
|
5723
|
+
const parsed = JSON.parse(await readFile(this.storePath, "utf8"));
|
|
5724
|
+
return this.normalizeStoreFile(parsed).entries;
|
|
5725
|
+
} catch (error) {
|
|
5726
|
+
if (this.isMissingFileError(error) || error instanceof SyntaxError) return {};
|
|
5727
|
+
throw error;
|
|
5728
|
+
}
|
|
5729
|
+
};
|
|
5730
|
+
persist = async (entries) => {
|
|
5731
|
+
const tempPath = `${this.storePath}.${randomUUID()}.tmp`;
|
|
5732
|
+
const storeFile = {
|
|
5733
|
+
version: PREFERENCE_STORE_VERSION,
|
|
5734
|
+
entries
|
|
5735
|
+
};
|
|
5736
|
+
await mkdir(dirname(this.storePath), { recursive: true });
|
|
5737
|
+
try {
|
|
5738
|
+
await writeFile(tempPath, `${JSON.stringify(storeFile, null, 2)}\n`, "utf8");
|
|
5739
|
+
await rename(tempPath, this.storePath);
|
|
5740
|
+
} catch (error) {
|
|
5741
|
+
await rm(tempPath, { force: true }).catch(() => void 0);
|
|
5742
|
+
throw error;
|
|
5743
|
+
}
|
|
5744
|
+
};
|
|
5745
|
+
normalizeStoreFile = (value) => {
|
|
5746
|
+
if (!this.isRecord(value) || !this.isRecord(value.entries)) return {
|
|
5747
|
+
version: PREFERENCE_STORE_VERSION,
|
|
5748
|
+
entries: {}
|
|
5749
|
+
};
|
|
5750
|
+
return {
|
|
5751
|
+
version: PREFERENCE_STORE_VERSION,
|
|
5752
|
+
entries: Object.fromEntries(Object.entries(value.entries).flatMap(([key, entry]) => {
|
|
5753
|
+
if (!this.isRecord(entry) || !this.isJsonValue(entry.value)) return [];
|
|
5754
|
+
return [[key, {
|
|
5755
|
+
key,
|
|
5756
|
+
value: entry.value,
|
|
5757
|
+
updatedAt: typeof entry.updatedAt === "string" ? entry.updatedAt : (/* @__PURE__ */ new Date(0)).toISOString()
|
|
5758
|
+
}]];
|
|
5759
|
+
}))
|
|
5760
|
+
};
|
|
5308
5761
|
};
|
|
5309
|
-
|
|
5310
|
-
|
|
5762
|
+
isJsonValue = (value) => {
|
|
5763
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return true;
|
|
5764
|
+
if (Array.isArray(value)) return value.every((item) => this.isJsonValue(item));
|
|
5765
|
+
if (this.isPlainRecord(value)) return Object.values(value).every((item) => this.isJsonValue(item));
|
|
5766
|
+
return false;
|
|
5311
5767
|
};
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5768
|
+
isRecord = (value) => this.isPlainRecord(value);
|
|
5769
|
+
isPlainRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
5770
|
+
isMissingFileError = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
5771
|
+
};
|
|
5772
|
+
//#endregion
|
|
5773
|
+
//#region src/managers/preference.manager.ts
|
|
5774
|
+
var PreferenceError = class extends Error {
|
|
5775
|
+
constructor(code, message) {
|
|
5776
|
+
super(message);
|
|
5777
|
+
this.code = code;
|
|
5778
|
+
this.name = "PreferenceError";
|
|
5779
|
+
}
|
|
5780
|
+
};
|
|
5781
|
+
function isPreferenceError(error) {
|
|
5782
|
+
return error instanceof PreferenceError;
|
|
5783
|
+
}
|
|
5784
|
+
var PreferenceManager = class {
|
|
5785
|
+
store;
|
|
5786
|
+
constructor(options) {
|
|
5787
|
+
this.store = new PreferenceStore(options.storePath);
|
|
5788
|
+
}
|
|
5789
|
+
getPreference = async (key) => {
|
|
5790
|
+
return await this.store.get(this.normalizeKey(key));
|
|
5316
5791
|
};
|
|
5317
|
-
|
|
5318
|
-
|
|
5792
|
+
setPreference = async (key, value) => {
|
|
5793
|
+
if (value === void 0 || !this.isJsonValue(value)) throw new PreferenceError("PREFERENCE_INVALID_VALUE", "preference value must be JSON serializable");
|
|
5794
|
+
return await this.store.set(this.normalizeKey(key), value);
|
|
5319
5795
|
};
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
return await this.createClientGrantStore().isGranted(appId);
|
|
5796
|
+
deletePreference = async (key) => {
|
|
5797
|
+
return await this.store.delete(this.normalizeKey(key));
|
|
5323
5798
|
};
|
|
5324
|
-
|
|
5799
|
+
normalizeKey = (key) => {
|
|
5800
|
+
const normalized = key.trim();
|
|
5801
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/.test(normalized)) throw new PreferenceError("PREFERENCE_INVALID_KEY", "preference key is invalid");
|
|
5802
|
+
return normalized;
|
|
5803
|
+
};
|
|
5804
|
+
isJsonValue = (value) => {
|
|
5805
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return true;
|
|
5806
|
+
if (Array.isArray(value)) return value.every((item) => this.isJsonValue(item));
|
|
5807
|
+
if (this.isPlainRecord(value)) return Object.values(value).every((item) => this.isJsonValue(item));
|
|
5808
|
+
return false;
|
|
5809
|
+
};
|
|
5810
|
+
isPlainRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
5325
5811
|
};
|
|
5326
5812
|
//#endregion
|
|
5327
5813
|
//#region src/utils/service-action.utils.ts
|
|
@@ -5538,12 +6024,12 @@ var ServiceActionGrantStore = class {
|
|
|
5538
6024
|
};
|
|
5539
6025
|
};
|
|
5540
6026
|
function normalizeStoreData(value) {
|
|
5541
|
-
if (!isRecord$
|
|
6027
|
+
if (!isRecord$5(value) || value.version !== 1 || !isRecord$5(value.grants)) return structuredClone(EMPTY_GRANTS);
|
|
5542
6028
|
const grants = {};
|
|
5543
6029
|
for (const [callerKey, callerValue] of Object.entries(value.grants)) {
|
|
5544
|
-
if (!isRecord$
|
|
6030
|
+
if (!isRecord$5(callerValue) || !isRecord$5(callerValue.actions)) continue;
|
|
5545
6031
|
const actions = {};
|
|
5546
|
-
for (const [actionId, actionValue] of Object.entries(callerValue.actions)) if (isRecord$
|
|
6032
|
+
for (const [actionId, actionValue] of Object.entries(callerValue.actions)) if (isRecord$5(actionValue) && typeof actionValue.grantedAt === "string" && isServiceActionRisk(actionValue.risk)) actions[actionId] = {
|
|
5547
6033
|
grantedAt: actionValue.grantedAt,
|
|
5548
6034
|
risk: actionValue.risk
|
|
5549
6035
|
};
|
|
@@ -5557,11 +6043,11 @@ function normalizeStoreData(value) {
|
|
|
5557
6043
|
function isServiceActionRisk(value) {
|
|
5558
6044
|
return value === "read" || value === "write" || value === "external" || value === "dangerous";
|
|
5559
6045
|
}
|
|
5560
|
-
function isRecord$
|
|
6046
|
+
function isRecord$5(value) {
|
|
5561
6047
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
5562
6048
|
}
|
|
5563
6049
|
function isMissingFileError(error) {
|
|
5564
|
-
return isRecord$
|
|
6050
|
+
return isRecord$5(error) && error.code === "ENOENT";
|
|
5565
6051
|
}
|
|
5566
6052
|
//#endregion
|
|
5567
6053
|
//#region src/utils/service-app-manifest.utils.ts
|
|
@@ -5586,15 +6072,15 @@ function parseServiceAppManifest(raw) {
|
|
|
5586
6072
|
} catch (error) {
|
|
5587
6073
|
throw new Error(`service-app.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
5588
6074
|
}
|
|
5589
|
-
if (!isRecord$
|
|
6075
|
+
if (!isRecord$4(parsed)) throw new Error("service-app.json must contain an object.");
|
|
5590
6076
|
const id = readRequiredString$3(parsed, "id");
|
|
5591
6077
|
if (!SERVICE_APP_ID_PATTERN.test(id)) throw new Error("service app id must be kebab-case.");
|
|
5592
|
-
const protocol = readOptionalString$
|
|
6078
|
+
const protocol = readOptionalString$5(parsed, "protocol") ?? "mcp";
|
|
5593
6079
|
if (protocol !== "mcp") throw new Error("service app protocol must be mcp.");
|
|
5594
6080
|
return {
|
|
5595
6081
|
id,
|
|
5596
6082
|
title: readRequiredString$3(parsed, "title"),
|
|
5597
|
-
description: readOptionalString$
|
|
6083
|
+
description: readOptionalString$5(parsed, "description"),
|
|
5598
6084
|
enabled: readOptionalBoolean$1(parsed, "enabled") ?? true,
|
|
5599
6085
|
protocol,
|
|
5600
6086
|
command: readRequiredString$3(parsed, "command"),
|
|
@@ -5604,31 +6090,31 @@ function parseServiceAppManifest(raw) {
|
|
|
5604
6090
|
}
|
|
5605
6091
|
function readManifestActions(value) {
|
|
5606
6092
|
if (value === void 0) throw new Error("service app actions are required.");
|
|
5607
|
-
if (!isRecord$
|
|
6093
|
+
if (!isRecord$4(value)) throw new Error("service app actions must be an object.");
|
|
5608
6094
|
if (Object.keys(value).length === 0) throw new Error("service app actions cannot be empty.");
|
|
5609
6095
|
const actions = {};
|
|
5610
6096
|
for (const [name, action] of Object.entries(value)) {
|
|
5611
6097
|
if (!name.trim()) throw new Error("service app action name cannot be empty.");
|
|
5612
|
-
if (!isRecord$
|
|
5613
|
-
const risk = readOptionalString$
|
|
6098
|
+
if (!isRecord$4(action)) throw new Error(`service app action ${name} must be an object.`);
|
|
6099
|
+
const risk = readOptionalString$5(action, "risk");
|
|
5614
6100
|
if (risk !== void 0 && !SERVICE_ACTION_RISKS.has(risk)) throw new Error(`service app action ${name} has invalid risk.`);
|
|
5615
6101
|
const inputSchema = action.inputSchema;
|
|
5616
|
-
if (inputSchema !== void 0 && !isRecord$
|
|
6102
|
+
if (inputSchema !== void 0 && !isRecord$4(inputSchema)) throw new Error(`service app action ${name} inputSchema must be an object.`);
|
|
5617
6103
|
actions[name] = {
|
|
5618
6104
|
risk,
|
|
5619
|
-
title: readOptionalString$
|
|
5620
|
-
description: readOptionalString$
|
|
6105
|
+
title: readOptionalString$5(action, "title"),
|
|
6106
|
+
description: readOptionalString$5(action, "description"),
|
|
5621
6107
|
inputSchema
|
|
5622
6108
|
};
|
|
5623
6109
|
}
|
|
5624
6110
|
return actions;
|
|
5625
6111
|
}
|
|
5626
6112
|
function readRequiredString$3(record, key) {
|
|
5627
|
-
const value = readOptionalString$
|
|
6113
|
+
const value = readOptionalString$5(record, key);
|
|
5628
6114
|
if (!value) throw new Error(`service app ${key} is required.`);
|
|
5629
6115
|
return value;
|
|
5630
6116
|
}
|
|
5631
|
-
function readOptionalString$
|
|
6117
|
+
function readOptionalString$5(record, key) {
|
|
5632
6118
|
return typeof record[key] === "string" && record[key].trim() ? record[key].trim() : void 0;
|
|
5633
6119
|
}
|
|
5634
6120
|
function readOptionalBoolean$1(record, key) {
|
|
@@ -5641,7 +6127,7 @@ function readStringArray(value, key) {
|
|
|
5641
6127
|
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) throw new Error(`service app ${key} must be a string array.`);
|
|
5642
6128
|
return value;
|
|
5643
6129
|
}
|
|
5644
|
-
function isRecord$
|
|
6130
|
+
function isRecord$4(value) {
|
|
5645
6131
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
5646
6132
|
}
|
|
5647
6133
|
//#endregion
|
|
@@ -6064,7 +6550,7 @@ function parseFrontmatterBlock(raw) {
|
|
|
6064
6550
|
function parseYamlFrontmatter(raw) {
|
|
6065
6551
|
try {
|
|
6066
6552
|
const parsed = parse(raw);
|
|
6067
|
-
return isRecord$
|
|
6553
|
+
return isRecord$3(parsed) ? parsed : {};
|
|
6068
6554
|
} catch (error) {
|
|
6069
6555
|
const message = error instanceof Error ? error.message : String(error);
|
|
6070
6556
|
throw new Error(`Invalid SKILL.md frontmatter: ${message}`);
|
|
@@ -6076,7 +6562,7 @@ function readString$3(record, ...names) {
|
|
|
6076
6562
|
}
|
|
6077
6563
|
function readLocalizedTextMap(record, ...names) {
|
|
6078
6564
|
const value = readValue(record, names);
|
|
6079
|
-
if (!isRecord$
|
|
6565
|
+
if (!isRecord$3(value)) return;
|
|
6080
6566
|
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
6567
|
return Object.keys(localized).length > 0 ? localized : void 0;
|
|
6082
6568
|
}
|
|
@@ -6100,7 +6586,7 @@ function normalizeFrontmatterKey(raw) {
|
|
|
6100
6586
|
function normalizeLocaleTag(raw) {
|
|
6101
6587
|
return raw.trim().toLowerCase();
|
|
6102
6588
|
}
|
|
6103
|
-
function isRecord$
|
|
6589
|
+
function isRecord$3(value) {
|
|
6104
6590
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6105
6591
|
}
|
|
6106
6592
|
//#endregion
|
|
@@ -6925,9 +7411,6 @@ function readString$2(value) {
|
|
|
6925
7411
|
if (typeof value !== "string") return;
|
|
6926
7412
|
return value.trim() || void 0;
|
|
6927
7413
|
}
|
|
6928
|
-
function resolveEntryIcon(entry) {
|
|
6929
|
-
return entry.icon ?? null;
|
|
6930
|
-
}
|
|
6931
7414
|
function resolveRequestedModel(params) {
|
|
6932
7415
|
const { configuredModel, defaultModel, input, modelSelectionMode, sessionMetadata } = params;
|
|
6933
7416
|
if (modelSelectionMode === "runtime-default") return;
|
|
@@ -6981,25 +7464,28 @@ var BuiltinHttpRuntimeSessionTypeService = class {
|
|
|
6981
7464
|
describeInternal = async () => {
|
|
6982
7465
|
const config = new HttpRuntimeConfigResolver(this.entry.config ?? {}).resolve({ defaultModel: this.defaultModel });
|
|
6983
7466
|
const modelSelectionMode = normalizeRuntimeModelSelectionMode(this.entry.config?.modelSelectionMode);
|
|
7467
|
+
const baseDescriptor = {
|
|
7468
|
+
icon: this.entry.icon ?? null,
|
|
7469
|
+
modelSelectionMode,
|
|
7470
|
+
runtimeDefaultThinking: normalizeModelThinkingCapability(this.entry.config?.runtimeDefaultThinking)
|
|
7471
|
+
};
|
|
6984
7472
|
if (!config.baseUrl) return {
|
|
6985
|
-
|
|
7473
|
+
...baseDescriptor,
|
|
6986
7474
|
ready: false,
|
|
6987
7475
|
reason: "base_url_missing",
|
|
6988
7476
|
reasonMessage: "Configure the runtime entry baseUrl before starting an HTTP runtime session.",
|
|
6989
7477
|
recommendedModel: config.recommendedModel ?? null,
|
|
6990
|
-
modelSelectionMode,
|
|
6991
7478
|
cta: {
|
|
6992
7479
|
kind: "settings",
|
|
6993
7480
|
label: "Configure HTTP Runtime"
|
|
6994
7481
|
}
|
|
6995
7482
|
};
|
|
6996
7483
|
if (!((config.capabilityProbe ?? true) && Boolean(config.healthcheckUrl))) return {
|
|
6997
|
-
|
|
7484
|
+
...baseDescriptor,
|
|
6998
7485
|
ready: true,
|
|
6999
7486
|
reason: null,
|
|
7000
7487
|
reasonMessage: null,
|
|
7001
7488
|
recommendedModel: config.recommendedModel ?? null,
|
|
7002
|
-
modelSelectionMode,
|
|
7003
7489
|
...config.supportedModels ? { supportedModels: config.supportedModels } : {},
|
|
7004
7490
|
cta: null
|
|
7005
7491
|
};
|
|
@@ -7013,28 +7499,26 @@ var BuiltinHttpRuntimeSessionTypeService = class {
|
|
|
7013
7499
|
signal: controller.signal
|
|
7014
7500
|
});
|
|
7015
7501
|
if (!response.ok) return {
|
|
7016
|
-
|
|
7502
|
+
...baseDescriptor,
|
|
7017
7503
|
ready: false,
|
|
7018
7504
|
reason: "healthcheck_failed",
|
|
7019
7505
|
reasonMessage: `HTTP runtime healthcheck returned HTTP ${response.status}.`,
|
|
7020
7506
|
recommendedModel: config.recommendedModel ?? null,
|
|
7021
|
-
modelSelectionMode,
|
|
7022
7507
|
...config.supportedModels ? { supportedModels: config.supportedModels } : {},
|
|
7023
7508
|
cta: null
|
|
7024
7509
|
};
|
|
7025
7510
|
return {
|
|
7026
|
-
|
|
7511
|
+
...baseDescriptor,
|
|
7027
7512
|
ready: true,
|
|
7028
7513
|
reason: null,
|
|
7029
7514
|
reasonMessage: null,
|
|
7030
7515
|
recommendedModel: config.recommendedModel ?? null,
|
|
7031
|
-
modelSelectionMode,
|
|
7032
7516
|
...config.supportedModels ? { supportedModels: config.supportedModels } : {},
|
|
7033
7517
|
cta: null
|
|
7034
7518
|
};
|
|
7035
7519
|
} catch (error) {
|
|
7036
7520
|
return {
|
|
7037
|
-
|
|
7521
|
+
...baseDescriptor,
|
|
7038
7522
|
ready: false,
|
|
7039
7523
|
reason: "healthcheck_unreachable",
|
|
7040
7524
|
reasonMessage: error instanceof Error ? error.message : "Failed to reach the configured HTTP runtime healthcheck.",
|
|
@@ -7067,53 +7551,54 @@ var BuiltinStdioRuntimeSessionTypeService = class {
|
|
|
7067
7551
|
describeInternal = async (describeMode) => {
|
|
7068
7552
|
const resolver = new StdioRuntimeConfigResolver(this.entry.config ?? {});
|
|
7069
7553
|
const modelSelectionMode = normalizeRuntimeModelSelectionMode(this.entry.config?.modelSelectionMode);
|
|
7554
|
+
const baseDescriptor = {
|
|
7555
|
+
icon: this.entry.icon ?? null,
|
|
7556
|
+
modelSelectionMode,
|
|
7557
|
+
runtimeDefaultThinking: normalizeModelThinkingCapability(this.entry.config?.runtimeDefaultThinking)
|
|
7558
|
+
};
|
|
7070
7559
|
try {
|
|
7071
7560
|
const config = resolver.resolve();
|
|
7072
7561
|
if (!await resolveExecutablePath(config.command)) return {
|
|
7073
|
-
|
|
7562
|
+
...baseDescriptor,
|
|
7074
7563
|
ready: false,
|
|
7075
7564
|
reason: "command_missing",
|
|
7076
7565
|
reasonMessage: `Configured stdio command "${config.command}" is not available. Update the runtime entry command or install the required launcher first.`,
|
|
7077
7566
|
cta: {
|
|
7078
7567
|
kind: "settings",
|
|
7079
7568
|
label: "Configure Stdio Runtime"
|
|
7080
|
-
}
|
|
7081
|
-
modelSelectionMode
|
|
7569
|
+
}
|
|
7082
7570
|
};
|
|
7083
7571
|
if (describeMode === "probe") try {
|
|
7084
7572
|
await probeStdioRuntime(config);
|
|
7085
7573
|
} catch (error) {
|
|
7086
7574
|
return {
|
|
7087
|
-
|
|
7575
|
+
...baseDescriptor,
|
|
7088
7576
|
ready: false,
|
|
7089
7577
|
reason: "probe_failed",
|
|
7090
7578
|
reasonMessage: error instanceof Error ? error.message : "Configured stdio runtime could not complete the ACP probe.",
|
|
7091
7579
|
cta: {
|
|
7092
7580
|
kind: "settings",
|
|
7093
7581
|
label: "Repair Stdio Runtime"
|
|
7094
|
-
}
|
|
7095
|
-
modelSelectionMode
|
|
7582
|
+
}
|
|
7096
7583
|
};
|
|
7097
7584
|
}
|
|
7098
7585
|
return {
|
|
7099
|
-
|
|
7586
|
+
...baseDescriptor,
|
|
7100
7587
|
ready: true,
|
|
7101
7588
|
reason: null,
|
|
7102
7589
|
reasonMessage: null,
|
|
7103
|
-
modelSelectionMode,
|
|
7104
7590
|
cta: null
|
|
7105
7591
|
};
|
|
7106
7592
|
} catch (error) {
|
|
7107
7593
|
return {
|
|
7108
|
-
|
|
7594
|
+
...baseDescriptor,
|
|
7109
7595
|
ready: false,
|
|
7110
7596
|
reason: "command_missing",
|
|
7111
7597
|
reasonMessage: error instanceof Error ? error.message : "Configure a stdio command before starting this runtime.",
|
|
7112
7598
|
cta: {
|
|
7113
7599
|
kind: "settings",
|
|
7114
7600
|
label: "Configure Stdio Runtime"
|
|
7115
|
-
}
|
|
7116
|
-
modelSelectionMode
|
|
7601
|
+
}
|
|
7117
7602
|
};
|
|
7118
7603
|
}
|
|
7119
7604
|
};
|
|
@@ -7184,7 +7669,6 @@ var BuiltinNarpRuntimeProviderService = class {
|
|
|
7184
7669
|
const config = readRecord(entry.config) ?? {};
|
|
7185
7670
|
return new StdioRuntimeNcpAgentRuntime({
|
|
7186
7671
|
...new StdioRuntimeConfigResolver(config).resolve(),
|
|
7187
|
-
sessionId: runtimeParams.sessionId,
|
|
7188
7672
|
resolveTools: runtimeParams.resolveTools,
|
|
7189
7673
|
stateManager: runtimeParams.stateManager,
|
|
7190
7674
|
resolveProviderRoute: (input) => buildProviderRoute({
|
|
@@ -7280,13 +7764,13 @@ var ProviderManagerNcpLLMApi = class {
|
|
|
7280
7764
|
};
|
|
7281
7765
|
//#endregion
|
|
7282
7766
|
//#region src/features/native-runtime/tools/ncp-asset.tools.ts
|
|
7283
|
-
function readOptionalString$
|
|
7767
|
+
function readOptionalString$4(value) {
|
|
7284
7768
|
if (typeof value !== "string") return null;
|
|
7285
7769
|
const trimmed = value.trim();
|
|
7286
7770
|
return trimmed.length > 0 ? trimmed : null;
|
|
7287
7771
|
}
|
|
7288
7772
|
function readOptionalBase64Bytes(value) {
|
|
7289
|
-
const base64 = readOptionalString$
|
|
7773
|
+
const base64 = readOptionalString$4(value);
|
|
7290
7774
|
if (!base64) return null;
|
|
7291
7775
|
try {
|
|
7292
7776
|
return Buffer.from(base64, "base64");
|
|
@@ -7337,18 +7821,18 @@ var AssetPutTool = class {
|
|
|
7337
7821
|
this.contentBasePath = contentBasePath;
|
|
7338
7822
|
}
|
|
7339
7823
|
validateArgs = (args) => {
|
|
7340
|
-
const path = readOptionalString$
|
|
7341
|
-
const bytesBase64 = readOptionalString$
|
|
7342
|
-
const fileName = readOptionalString$
|
|
7824
|
+
const path = readOptionalString$4(args.path);
|
|
7825
|
+
const bytesBase64 = readOptionalString$4(args.bytesBase64);
|
|
7826
|
+
const fileName = readOptionalString$4(args.fileName);
|
|
7343
7827
|
if (path && bytesBase64) return ["Provide either path, or bytesBase64 + fileName, not both."];
|
|
7344
7828
|
if (path) return [];
|
|
7345
7829
|
if (bytesBase64) return fileName ? [] : ["fileName is required when using bytesBase64."];
|
|
7346
7830
|
return ["Provide either path, or bytesBase64 + fileName."];
|
|
7347
7831
|
};
|
|
7348
7832
|
execute = async (args) => {
|
|
7349
|
-
const path = readOptionalString$
|
|
7350
|
-
const fileName = readOptionalString$
|
|
7351
|
-
const mimeType = readOptionalString$
|
|
7833
|
+
const path = readOptionalString$4(args?.path);
|
|
7834
|
+
const fileName = readOptionalString$4(args?.fileName);
|
|
7835
|
+
const mimeType = readOptionalString$4(args?.mimeType);
|
|
7352
7836
|
const bytes = readOptionalBase64Bytes(args?.bytesBase64);
|
|
7353
7837
|
if (path) return {
|
|
7354
7838
|
ok: true,
|
|
@@ -7391,8 +7875,8 @@ var AssetExportTool = class {
|
|
|
7391
7875
|
this.assetStore = assetStore;
|
|
7392
7876
|
}
|
|
7393
7877
|
execute = async (args) => {
|
|
7394
|
-
const assetUri = readOptionalString$
|
|
7395
|
-
const targetPath = readOptionalString$
|
|
7878
|
+
const assetUri = readOptionalString$4(args?.assetUri);
|
|
7879
|
+
const targetPath = readOptionalString$4(args?.targetPath);
|
|
7396
7880
|
if (!assetUri || !targetPath) throw new Error("asset_export requires assetUri and targetPath.");
|
|
7397
7881
|
return {
|
|
7398
7882
|
ok: true,
|
|
@@ -7418,7 +7902,7 @@ var AssetStatTool = class {
|
|
|
7418
7902
|
this.contentBasePath = contentBasePath;
|
|
7419
7903
|
}
|
|
7420
7904
|
execute = async (args) => {
|
|
7421
|
-
const assetUri = readOptionalString$
|
|
7905
|
+
const assetUri = readOptionalString$4(args?.assetUri);
|
|
7422
7906
|
if (!assetUri) throw new Error("asset_stat requires assetUri.");
|
|
7423
7907
|
const record = await this.assetStore.statRecord(assetUri);
|
|
7424
7908
|
if (!record) return {
|
|
@@ -7695,7 +8179,8 @@ var NcpAgentRuntimeWrapper = class {
|
|
|
7695
8179
|
runId: spec.runId,
|
|
7696
8180
|
messages,
|
|
7697
8181
|
correlationId: spec.correlationId,
|
|
7698
|
-
metadata: this.buildMetadata(spec)
|
|
8182
|
+
metadata: this.buildMetadata(options.session, spec),
|
|
8183
|
+
executionContext: { cwd: options.session.workingDir }
|
|
7699
8184
|
};
|
|
7700
8185
|
for await (const event of this.getRuntime().run(input, { signal: options.signal })) yield await this.applyEvent(sessionRun, event);
|
|
7701
8186
|
this.currentTools = [];
|
|
@@ -7720,10 +8205,10 @@ var NcpAgentRuntimeWrapper = class {
|
|
|
7720
8205
|
correlationId: spec.correlationId
|
|
7721
8206
|
}
|
|
7722
8207
|
}));
|
|
7723
|
-
buildMetadata = (spec) => ({
|
|
7724
|
-
...
|
|
8208
|
+
buildMetadata = (session, spec) => ({
|
|
8209
|
+
...session.metadata,
|
|
7725
8210
|
agentId: spec.agentId,
|
|
7726
|
-
agentRuntimeId:
|
|
8211
|
+
agentRuntimeId: session.agentRuntimeId,
|
|
7727
8212
|
maxTokens: spec.maxTokens,
|
|
7728
8213
|
model: spec.model,
|
|
7729
8214
|
preferred_model: spec.model,
|
|
@@ -7787,20 +8272,16 @@ var AgentRunRuntimeContribution = class {
|
|
|
7787
8272
|
describeSessionTypeForEntry: provider.describeSessionTypeForEntry,
|
|
7788
8273
|
createRuntime: ({ entry, session }) => {
|
|
7789
8274
|
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
|
-
});
|
|
8275
|
+
return new NcpAgentRuntimeWrapper({ createRuntime: ({ resolveTools, stateManager }) => provider.createRuntimeForEntry({
|
|
8276
|
+
entry,
|
|
8277
|
+
runtimeParams: {
|
|
8278
|
+
...session.agentId ? { agentId: session.agentId } : {},
|
|
8279
|
+
resolveAssetContentPath: (assetUri) => this.kernel.assetStore.resolveContentPath(assetUri),
|
|
8280
|
+
resolveTools,
|
|
8281
|
+
sessionMetadata: session.metadata,
|
|
8282
|
+
stateManager
|
|
8283
|
+
}
|
|
8284
|
+
}) });
|
|
7804
8285
|
}
|
|
7805
8286
|
});
|
|
7806
8287
|
};
|
|
@@ -7966,6 +8447,13 @@ const createToolCallStyleContextProvider = () => staticBlock([
|
|
|
7966
8447
|
"Keep narration brief and value-dense; avoid repeating obvious steps.",
|
|
7967
8448
|
"Use plain human language for narration unless in a technical context."
|
|
7968
8449
|
]);
|
|
8450
|
+
const createInlineInteractiveSurfaceContextProvider = () => staticBlock([
|
|
8451
|
+
"## Inline Interactive Surfaces",
|
|
8452
|
+
"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.",
|
|
8453
|
+
"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.",
|
|
8454
|
+
"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.",
|
|
8455
|
+
"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."
|
|
8456
|
+
]);
|
|
7969
8457
|
const createChatComposerTokensContextProvider = () => staticBlock([
|
|
7970
8458
|
"## Chat Composer Tokens",
|
|
7971
8459
|
"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 +8860,7 @@ var ContextProviderContribution = class {
|
|
|
8372
8860
|
createAssistantIdentityContextProvider(),
|
|
8373
8861
|
new ToolingContextProvider(context),
|
|
8374
8862
|
createToolCallStyleContextProvider(),
|
|
8863
|
+
createInlineInteractiveSurfaceContextProvider(),
|
|
8375
8864
|
createChatComposerTokensContextProvider(),
|
|
8376
8865
|
createSafetyContextProvider(),
|
|
8377
8866
|
createCliQuickReferenceContextProvider(),
|
|
@@ -8639,227 +9128,6 @@ var LearningLoopContribution = class {
|
|
|
8639
9128
|
};
|
|
8640
9129
|
};
|
|
8641
9130
|
//#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
9131
|
//#region src/contributions/tool-provider/providers/asset-tool.provider.ts
|
|
8864
9132
|
var AssetToolProvider = class {
|
|
8865
9133
|
constructor(assetStore) {
|
|
@@ -9402,6 +9670,12 @@ function readPurpose(value) {
|
|
|
9402
9670
|
if (normalized === "read" || normalized === "preview" || normalized === "edit" || normalized === "interact") return normalized;
|
|
9403
9671
|
throw new Error("purpose must be \"read\", \"preview\", \"edit\", or \"interact\".");
|
|
9404
9672
|
}
|
|
9673
|
+
function readPlacement(value) {
|
|
9674
|
+
const normalized = readOptionalString$1(value);
|
|
9675
|
+
if (!normalized) return;
|
|
9676
|
+
if (normalized === "inline" || normalized === "side_panel") return normalized;
|
|
9677
|
+
throw new Error("placement must be \"inline\" or \"side_panel\".");
|
|
9678
|
+
}
|
|
9405
9679
|
function readPayload(params) {
|
|
9406
9680
|
if (!isRecord$1(params.payload)) throw new Error("payload must be an object.");
|
|
9407
9681
|
return params.payload;
|
|
@@ -9423,6 +9697,7 @@ function normalizeShowContentArgs(args) {
|
|
|
9423
9697
|
const payload = readPayload(params);
|
|
9424
9698
|
const title = readOptionalString$1(params.title);
|
|
9425
9699
|
const purpose = readPurpose(params.purpose);
|
|
9700
|
+
const placement = readPlacement(params.placement);
|
|
9426
9701
|
if (type === "file") return {
|
|
9427
9702
|
target: {
|
|
9428
9703
|
type,
|
|
@@ -9433,7 +9708,8 @@ function normalizeShowContentArgs(args) {
|
|
|
9433
9708
|
}
|
|
9434
9709
|
},
|
|
9435
9710
|
title,
|
|
9436
|
-
purpose
|
|
9711
|
+
purpose,
|
|
9712
|
+
placement
|
|
9437
9713
|
};
|
|
9438
9714
|
if (type === "url") return {
|
|
9439
9715
|
target: {
|
|
@@ -9441,7 +9717,8 @@ function normalizeShowContentArgs(args) {
|
|
|
9441
9717
|
payload: { url: readUrl(payload.url) }
|
|
9442
9718
|
},
|
|
9443
9719
|
title,
|
|
9444
|
-
purpose
|
|
9720
|
+
purpose,
|
|
9721
|
+
placement
|
|
9445
9722
|
};
|
|
9446
9723
|
if (type === "panel_app") return {
|
|
9447
9724
|
target: {
|
|
@@ -9449,7 +9726,8 @@ function normalizeShowContentArgs(args) {
|
|
|
9449
9726
|
payload: { appId: readRequiredString(payload.appId, "payload.appId") }
|
|
9450
9727
|
},
|
|
9451
9728
|
title,
|
|
9452
|
-
purpose
|
|
9729
|
+
purpose,
|
|
9730
|
+
placement
|
|
9453
9731
|
};
|
|
9454
9732
|
throw new Error("type must be \"file\", \"url\", or \"panel_app\".");
|
|
9455
9733
|
}
|
|
@@ -9465,12 +9743,19 @@ function createShowContentEventPayload(request, context) {
|
|
|
9465
9743
|
toolCallId,
|
|
9466
9744
|
target: request.target,
|
|
9467
9745
|
title: request.title,
|
|
9468
|
-
purpose: request.purpose
|
|
9746
|
+
purpose: request.purpose,
|
|
9747
|
+
placement: request.placement
|
|
9469
9748
|
};
|
|
9470
9749
|
}
|
|
9471
9750
|
var ShowContentTool = class {
|
|
9472
9751
|
name = SHOW_CONTENT_TOOL_NAME;
|
|
9473
|
-
description =
|
|
9752
|
+
description = [
|
|
9753
|
+
"Show file, URL, or panel app content in the current chat UI.",
|
|
9754
|
+
"placement=\"inline\" embeds a lightweight panel_app as an interactive chat card so the user can try it directly in the conversation.",
|
|
9755
|
+
"placement=\"side_panel\" opens content in the right side panel for larger reading, editing, or sustained workflows.",
|
|
9756
|
+
"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.",
|
|
9757
|
+
"Omit placement only when preserving the existing default side panel behavior is intentional."
|
|
9758
|
+
].join(" ");
|
|
9474
9759
|
parameters = {
|
|
9475
9760
|
type: "object",
|
|
9476
9761
|
properties: {
|
|
@@ -9497,6 +9782,11 @@ var ShowContentTool = class {
|
|
|
9497
9782
|
],
|
|
9498
9783
|
description: "Optional user intent for the content."
|
|
9499
9784
|
},
|
|
9785
|
+
placement: {
|
|
9786
|
+
type: "string",
|
|
9787
|
+
enum: ["inline", "side_panel"],
|
|
9788
|
+
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."
|
|
9789
|
+
},
|
|
9500
9790
|
payload: {
|
|
9501
9791
|
type: "object",
|
|
9502
9792
|
description: "Type-specific fields: file={path,line,column}; url={url}; panel_app={appId}.",
|
|
@@ -9622,6 +9912,11 @@ function resolveKernelAutomationStorePath(options) {
|
|
|
9622
9912
|
if (homeDir) return resolve(expandHome(homeDir), "cron", "jobs.json");
|
|
9623
9913
|
return resolve(getDataDir(), "cron", "jobs.json");
|
|
9624
9914
|
}
|
|
9915
|
+
function resolveKernelPreferenceStorePath(options) {
|
|
9916
|
+
const homeDir = options.homeDir?.trim();
|
|
9917
|
+
if (homeDir) return resolve(expandHome(homeDir), "preferences", "preferences.json");
|
|
9918
|
+
return resolve(getDataDir(), "preferences", "preferences.json");
|
|
9919
|
+
}
|
|
9625
9920
|
var NextclawKernelControlManager = class {
|
|
9626
9921
|
runtimeControl = null;
|
|
9627
9922
|
installRuntimeControl = (runtimeControl) => {
|
|
@@ -9651,6 +9946,7 @@ var NextclawKernel = class {
|
|
|
9651
9946
|
mcpManager;
|
|
9652
9947
|
sessionManager;
|
|
9653
9948
|
panelAppManager;
|
|
9949
|
+
preferenceManager;
|
|
9654
9950
|
serviceAppManager;
|
|
9655
9951
|
extensions;
|
|
9656
9952
|
agentRuntimeManager = new AgentRuntimeManager();
|
|
@@ -9695,6 +9991,7 @@ var NextclawKernel = class {
|
|
|
9695
9991
|
eventBus: this.eventBus,
|
|
9696
9992
|
ingress: this.ingress
|
|
9697
9993
|
});
|
|
9994
|
+
this.preferenceManager = new PreferenceManager({ storePath: resolveKernelPreferenceStorePath(options) });
|
|
9698
9995
|
this.serviceAppManager = new ServiceAppManager({ configManager: this.configManager });
|
|
9699
9996
|
this.extensions = new ExtensionManager({
|
|
9700
9997
|
configManager: this.configManager,
|
|
@@ -9722,7 +10019,6 @@ var NextclawKernel = class {
|
|
|
9722
10019
|
this.agentRunRequestManager = new AgentRunRequestManager(this.agentRuntimeManager, this.agents, this.configManager, this.contextProviderManager, this.eventBus, this.ingress, this.sessionManager, this.sessionRunManager, this.toolProviderManager);
|
|
9723
10020
|
this.contributions = [
|
|
9724
10021
|
new ToolProviderContribution(this),
|
|
9725
|
-
new SessionActivityPreviewContribution(this),
|
|
9726
10022
|
new LearningLoopContribution(this),
|
|
9727
10023
|
new ContextProviderContribution(this),
|
|
9728
10024
|
new AgentRunRuntimeContribution(this),
|
|
@@ -10359,6 +10655,6 @@ function resolveLegacyEventType(message) {
|
|
|
10359
10655
|
return `message.${role || "other"}`;
|
|
10360
10656
|
}
|
|
10361
10657
|
//#endregion
|
|
10362
|
-
export { AccessManager, AgentManager, AgentRunClient, AutomationManager, BuiltinNarpRuntimeProviderService, CONTEXT_COMPACTION_TIMELINE_KIND, ChannelManager, CommandRegistry, ConfigManager, ContextCompactionPreflightService, ContextWindowPreviewManager, DEFAULT_AGENT_RUNTIME_ENTRY_ID, DEFAULT_SERVICE_ACTION_RISK, ExtensionManager, GatewayInboundProcessor, LlmProviderManager, LlmUsageManager, LlmUsageStore, McpManager, McpServiceAppRuntimeService, NARP_HTTP_RUNTIME_KIND, NARP_STDIO_RUNTIME_KIND, NEXTCLAW_TIMELINE_KIND_METADATA_KEY, NcpAgentSessionJournalStore, NextclawKernel, PANEL_APP_AGENT_CAPABILITIES, PanelAppAssetTokenService, PanelAppError, PanelAppManager, ProviderManagerNcpLLMApi, SERVICE_APP_MANIFEST_FILE_NAME, ServiceAppError, ServiceAppManager, SessionManager, SessionRequestManager, SkillManager, UpdateManifestReader, buildAgentRunSendPayload, buildContextCompactionModelInput, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildNextclawNcpRunContext, buildServiceActionId, createAgentRuntimeSessionRequestDispatcher, createAssetTools, createContextCompactionMessageId, createContextWindowSignature, createLlmUsageRecord, describeAgentRuntimeSessionTypes, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, getServiceActionName, getServiceAppManifestPath, getUnsignedUpdateManifest, hasLlmUsageTelemetry, isContextCompactionTimelineMessage, isContextWindowSnapshot, isPanelAppAgentCapability, isPanelAppError, isReplyCapableChannel, isServiceAppError, listExtensionChannelIds, listServiceAppManifestActions, mergeServiceAppRuntimeActions, normalizeAgentRuntimeSessionTypeIcon, normalizeLlmUsageModel, normalizeOptionalString, parseServiceAppManifest, parseSkillFrontmatter, readContextWindowEventSessionId, readLatestContextCompactionCheckpoint, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, readServiceAppManifest, resolveAgentRuntimeEntries, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolveSessionChannelContext, runGatewayInboundLoop, sanitizeLlmUsage, serializeUnsignedUpdateManifest, shouldRefreshContextWindowDuringStream, shouldRefreshContextWindowImmediately, stripSkillFrontmatter, syncSessionThinkingPreference, toNcpMessages, waitForAgentRuntimeSessionReply };
|
|
10658
|
+
export { AccessManager, AgentManager, AgentRunClient, AutomationManager, BuiltinNarpRuntimeProviderService, CONTEXT_COMPACTION_TIMELINE_KIND, ChannelManager, CommandRegistry, ConfigManager, ContextCompactionPreflightService, ContextWindowPreviewManager, DEFAULT_AGENT_RUNTIME_ENTRY_ID, DEFAULT_SERVICE_ACTION_RISK, ExtensionManager, GatewayInboundProcessor, LlmProviderManager, LlmUsageManager, LlmUsageStore, McpManager, McpServiceAppRuntimeService, NARP_HTTP_RUNTIME_KIND, NARP_STDIO_RUNTIME_KIND, NEXTCLAW_TIMELINE_KIND_METADATA_KEY, NcpAgentSessionJournalStore, NextclawKernel, PANEL_APP_AGENT_CAPABILITIES, PanelAppAssetTokenService, PanelAppError, PanelAppManager, PreferenceError, PreferenceManager, ProviderManagerNcpLLMApi, SERVICE_APP_MANIFEST_FILE_NAME, ServiceAppError, ServiceAppManager, SessionManager, SessionRequestManager, SkillManager, UpdateManifestReader, buildAgentRunSendPayload, buildContextCompactionModelInput, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildNextclawNcpRunContext, buildServiceActionId, createAgentRuntimeSessionRequestDispatcher, createAssetTools, createContextCompactionMessageId, createContextWindowSignature, createLlmUsageRecord, describeAgentRuntimeSessionTypes, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, getServiceActionName, getServiceAppManifestPath, getUnsignedUpdateManifest, hasLlmUsageTelemetry, isContextCompactionTimelineMessage, isContextWindowSnapshot, isPanelAppAgentCapability, isPanelAppError, isPreferenceError, isReplyCapableChannel, isServiceAppError, listExtensionChannelIds, listServiceAppManifestActions, mergeServiceAppRuntimeActions, normalizeAgentRuntimeSessionTypeIcon, normalizeLlmUsageModel, normalizeOptionalString, parseServiceAppManifest, parseSkillFrontmatter, readContextWindowEventSessionId, readLatestContextCompactionCheckpoint, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, readServiceAppManifest, resolveAgentRuntimeEntries, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolveSessionChannelContext, runGatewayInboundLoop, sanitizeLlmUsage, serializeUnsignedUpdateManifest, shouldRefreshContextWindowDuringStream, shouldRefreshContextWindowImmediately, stripSkillFrontmatter, syncSessionThinkingPreference, toNcpMessages, waitForAgentRuntimeSessionReply };
|
|
10363
10659
|
|
|
10364
10660
|
//# sourceMappingURL=index.js.map
|