@nextclaw/kernel 0.1.5 → 0.1.7
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 +5 -1
- package/dist/index.js +441 -148
- package/package.json +14 -14
package/dist/index.d.ts
CHANGED
|
@@ -494,6 +494,7 @@ declare class AgentRuntimeManager {
|
|
|
494
494
|
private disposed;
|
|
495
495
|
constructor(params: AgentRuntimeManagerOptions);
|
|
496
496
|
get currentHandle(): AgentRuntimeHandle | null;
|
|
497
|
+
isLiveSessionRunning: (sessionId: string) => boolean;
|
|
497
498
|
connectGatewayController: (gatewayController: GatewayController) => void;
|
|
498
499
|
bootstrap: () => Promise<AgentRuntimeHandle>;
|
|
499
500
|
private readonly materializeAgentSendEnvelope;
|
|
@@ -663,6 +664,7 @@ type NcpAgentSessionReadableStore = AgentSessionStore & {
|
|
|
663
664
|
type NcpSessionApiServiceOptions = {
|
|
664
665
|
eventBus: EventBus;
|
|
665
666
|
getConfig: () => Config;
|
|
667
|
+
isLiveSessionRunning?: (sessionId: string) => boolean;
|
|
666
668
|
ncpAgentSessionStore?: NcpAgentSessionReadableStore;
|
|
667
669
|
sessionManager: SessionManager;
|
|
668
670
|
};
|
|
@@ -679,6 +681,7 @@ declare class NcpSessionApiService implements NcpSessionApi {
|
|
|
679
681
|
getSession: (sessionId: string) => Promise<NcpSessionSummary | null>;
|
|
680
682
|
updateSession: (sessionId: string, patch: NcpSessionPatch) => Promise<NcpSessionSummary | null>;
|
|
681
683
|
deleteSession: (sessionId: string) => Promise<void>;
|
|
684
|
+
private withLiveSessionStatus;
|
|
682
685
|
}
|
|
683
686
|
//#endregion
|
|
684
687
|
//#region src/app/nextclaw-kernel.d.ts
|
|
@@ -722,7 +725,7 @@ declare class NextclawKernel {
|
|
|
722
725
|
private readonly sessionLifecycleEvents;
|
|
723
726
|
private readonly ncpAgentSessionStore;
|
|
724
727
|
private readonly ncpAgentSessionJournalStore;
|
|
725
|
-
private
|
|
728
|
+
private readonly contributions;
|
|
726
729
|
constructor(options?: NextclawKernelOptions);
|
|
727
730
|
start: () => Promise<void>;
|
|
728
731
|
dispose: () => Promise<void>;
|
|
@@ -885,6 +888,7 @@ declare class NextclawNcpToolRegistry implements NcpToolRegistry {
|
|
|
885
888
|
private buildToolExecutionContext;
|
|
886
889
|
private registerDefaultTools;
|
|
887
890
|
private registerMessagingTools;
|
|
891
|
+
private resolveMessageChannels;
|
|
888
892
|
private registerExtensionTools;
|
|
889
893
|
private registerTool;
|
|
890
894
|
private registerAdditionalTools;
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { n as getUnsignedUpdateManifest, r as serializeUnsignedUpdateManifest, t
|
|
|
2
2
|
import { AgentRouteResolver, CONTEXT_COMPACTION_METADATA_KEY, ChannelManager, ChannelManager as ChannelManager$1, CommandRegistry, ContextBuilder, ContextCompactionService, ContextWindowBudgetService, CronService, CronTool, DEFAULT_SESSION_SEARCH_LIMIT, DisposableStore, EditFileTool, ExecTool, ExtensionToolAdapter, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, LiteLLMProvider, MAX_SESSION_SEARCH_LIMIT, MemoryGetTool, MemorySearchTool, MessageBus, MessageTool, ProviderRegistry, ReadFileTool, RequestedSkillsMetadataReader, SessionManager, SessionRequestManager, SessionSearchManager, SessionsHistoryTool, SessionsListTool, Tool, ToolRegistry, WebFetchTool, WebSearchTool, WriteFileTool, buildCompressingCompactionCheckpoint, buildContextWindowSnapshot, buildMinimalSystemExecutionPrompt, buildReloadPlan, buildToolCatalogEntries, createAssistantStreamDeltaControlMessage, createAssistantStreamResetControlMessage, createToolExecutionContext, createTypingStopControlMessage, diffConfigPaths, ensureDir, expandHome, findEffectiveAgentProfile, getConfigPath, getDataDir, getSessionsPath, getWorkspacePath, loadConfig, parseAgentScopedSessionKey, parseThinkingLevel, readCompressedContextCompactionCheckpoint, readParentSessionId, readSessionProjectRoot, resolveConfigSecrets, resolveDefaultAgentProfileId, resolveProviderRuntime, resolveSessionWorkspacePath, resolveThinkingLevel, toDisposable } from "@nextclaw/core";
|
|
3
3
|
import { EventBus, Ingress, createAppEventKey, createTypedKey, eventKeys } from "@nextclaw/shared";
|
|
4
4
|
import { DefaultNcpAgentRuntime, LocalAssetStore, buildAssetContentPath, buildNcpUserContent, buildOpenAiFunctionTool } from "@nextclaw/ncp-agent-runtime";
|
|
5
|
-
import { NcpEventType, readAssistantReasoningNormalizationMode, readAssistantReasoningNormalizationModeFromMetadata, sanitizeAssistantReplyTags, writeAssistantReasoningNormalizationModeToMetadata } from "@nextclaw/ncp";
|
|
5
|
+
import { NcpEventType, consumeNcpRunHandle, createNcpRunHandle, readAssistantReasoningNormalizationMode, readAssistantReasoningNormalizationModeFromMetadata, sanitizeAssistantReplyTags, writeAssistantReasoningNormalizationModeToMetadata } from "@nextclaw/ncp";
|
|
6
6
|
import { DefaultNcpAgentBackend, DefaultNcpAgentConversationStateManager, createAgentClientFromServer } from "@nextclaw/ncp-toolkit";
|
|
7
7
|
import { basename, delimiter, dirname, join, resolve } from "node:path";
|
|
8
8
|
import { access, appendFile, mkdir, readFile, readdir, unlink, writeFile } from "node:fs/promises";
|
|
@@ -625,11 +625,11 @@ function normalizeString(value) {
|
|
|
625
625
|
const trimmed = value.trim();
|
|
626
626
|
return trimmed.length > 0 ? trimmed : null;
|
|
627
627
|
}
|
|
628
|
-
function isRecord$
|
|
628
|
+
function isRecord$5(value) {
|
|
629
629
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
630
630
|
}
|
|
631
631
|
function cloneMetadata(value) {
|
|
632
|
-
return isRecord$
|
|
632
|
+
return isRecord$5(value) ? structuredClone(value) : void 0;
|
|
633
633
|
}
|
|
634
634
|
function readStringArray(value) {
|
|
635
635
|
if (!Array.isArray(value)) return null;
|
|
@@ -1876,7 +1876,7 @@ var LlmUsageManager = class {
|
|
|
1876
1876
|
};
|
|
1877
1877
|
//#endregion
|
|
1878
1878
|
//#region src/tools/session-search.tools.ts
|
|
1879
|
-
function isRecord$
|
|
1879
|
+
function isRecord$4(value) {
|
|
1880
1880
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1881
1881
|
}
|
|
1882
1882
|
function readOptionalInteger(value) {
|
|
@@ -1926,7 +1926,7 @@ var SessionSearchTool = class {
|
|
|
1926
1926
|
return issues;
|
|
1927
1927
|
};
|
|
1928
1928
|
execute = async (args) => {
|
|
1929
|
-
if (!isRecord$
|
|
1929
|
+
if (!isRecord$4(args)) throw new Error("session_search requires an object argument.");
|
|
1930
1930
|
const issues = this.validateArgs(args);
|
|
1931
1931
|
if (issues.length > 0) throw new Error(issues.join(" "));
|
|
1932
1932
|
return this.queryService.search({
|
|
@@ -1939,13 +1939,13 @@ var SessionSearchTool = class {
|
|
|
1939
1939
|
};
|
|
1940
1940
|
//#endregion
|
|
1941
1941
|
//#region src/features/native-runtime/tools/ncp-asset.tools.ts
|
|
1942
|
-
function readOptionalString$
|
|
1942
|
+
function readOptionalString$4(value) {
|
|
1943
1943
|
if (typeof value !== "string") return null;
|
|
1944
1944
|
const trimmed = value.trim();
|
|
1945
1945
|
return trimmed.length > 0 ? trimmed : null;
|
|
1946
1946
|
}
|
|
1947
1947
|
function readOptionalBase64Bytes(value) {
|
|
1948
|
-
const base64 = readOptionalString$
|
|
1948
|
+
const base64 = readOptionalString$4(value);
|
|
1949
1949
|
if (!base64) return null;
|
|
1950
1950
|
try {
|
|
1951
1951
|
return Buffer.from(base64, "base64");
|
|
@@ -1996,18 +1996,18 @@ var AssetPutTool = class {
|
|
|
1996
1996
|
this.contentBasePath = contentBasePath;
|
|
1997
1997
|
}
|
|
1998
1998
|
validateArgs = (args) => {
|
|
1999
|
-
const path = readOptionalString$
|
|
2000
|
-
const bytesBase64 = readOptionalString$
|
|
2001
|
-
const fileName = readOptionalString$
|
|
1999
|
+
const path = readOptionalString$4(args.path);
|
|
2000
|
+
const bytesBase64 = readOptionalString$4(args.bytesBase64);
|
|
2001
|
+
const fileName = readOptionalString$4(args.fileName);
|
|
2002
2002
|
if (path && bytesBase64) return ["Provide either path, or bytesBase64 + fileName, not both."];
|
|
2003
2003
|
if (path) return [];
|
|
2004
2004
|
if (bytesBase64) return fileName ? [] : ["fileName is required when using bytesBase64."];
|
|
2005
2005
|
return ["Provide either path, or bytesBase64 + fileName."];
|
|
2006
2006
|
};
|
|
2007
2007
|
execute = async (args) => {
|
|
2008
|
-
const path = readOptionalString$
|
|
2009
|
-
const fileName = readOptionalString$
|
|
2010
|
-
const mimeType = readOptionalString$
|
|
2008
|
+
const path = readOptionalString$4(args?.path);
|
|
2009
|
+
const fileName = readOptionalString$4(args?.fileName);
|
|
2010
|
+
const mimeType = readOptionalString$4(args?.mimeType);
|
|
2011
2011
|
const bytes = readOptionalBase64Bytes(args?.bytesBase64);
|
|
2012
2012
|
if (path) return {
|
|
2013
2013
|
ok: true,
|
|
@@ -2050,8 +2050,8 @@ var AssetExportTool = class {
|
|
|
2050
2050
|
this.assetStore = assetStore;
|
|
2051
2051
|
}
|
|
2052
2052
|
execute = async (args) => {
|
|
2053
|
-
const assetUri = readOptionalString$
|
|
2054
|
-
const targetPath = readOptionalString$
|
|
2053
|
+
const assetUri = readOptionalString$4(args?.assetUri);
|
|
2054
|
+
const targetPath = readOptionalString$4(args?.targetPath);
|
|
2055
2055
|
if (!assetUri || !targetPath) throw new Error("asset_export requires assetUri and targetPath.");
|
|
2056
2056
|
return {
|
|
2057
2057
|
ok: true,
|
|
@@ -2077,7 +2077,7 @@ var AssetStatTool = class {
|
|
|
2077
2077
|
this.contentBasePath = contentBasePath;
|
|
2078
2078
|
}
|
|
2079
2079
|
execute = async (args) => {
|
|
2080
|
-
const assetUri = readOptionalString$
|
|
2080
|
+
const assetUri = readOptionalString$4(args?.assetUri);
|
|
2081
2081
|
if (!assetUri) throw new Error("asset_stat requires assetUri.");
|
|
2082
2082
|
const record = await this.assetStore.statRecord(assetUri);
|
|
2083
2083
|
if (!record) return {
|
|
@@ -2252,7 +2252,7 @@ function readRequiredString$1(params, key) {
|
|
|
2252
2252
|
if (typeof value !== "string" || value.trim().length === 0) throw new Error(`${key} must be a non-empty string.`);
|
|
2253
2253
|
return value.trim();
|
|
2254
2254
|
}
|
|
2255
|
-
function readOptionalString$
|
|
2255
|
+
function readOptionalString$3(params, key) {
|
|
2256
2256
|
const value = params[key];
|
|
2257
2257
|
if (typeof value !== "string") return;
|
|
2258
2258
|
const trimmed = value.trim();
|
|
@@ -2313,7 +2313,7 @@ var SessionRequestTool = class extends Tool {
|
|
|
2313
2313
|
const target = params.target;
|
|
2314
2314
|
if (!target || typeof target !== "object" || Array.isArray(target)) throw new Error("target must be an object.");
|
|
2315
2315
|
const task = readRequiredString$1(params, "task");
|
|
2316
|
-
const notifyMode = readOptionalString$
|
|
2316
|
+
const notifyMode = readOptionalString$3(params, "notify")?.toLowerCase();
|
|
2317
2317
|
if (notifyMode !== "none" && notifyMode !== "final_reply") throw new Error("notify must be \"none\" or \"final_reply\".");
|
|
2318
2318
|
const { toolCallId, updateToolCallResult } = context;
|
|
2319
2319
|
return this.manager.requestSession({
|
|
@@ -2322,7 +2322,7 @@ var SessionRequestTool = class extends Tool {
|
|
|
2322
2322
|
updateToolCallResult,
|
|
2323
2323
|
targetSessionId: readRequiredString$1(target, "session_id"),
|
|
2324
2324
|
task,
|
|
2325
|
-
title: readOptionalString$
|
|
2325
|
+
title: readOptionalString$3(params, "title"),
|
|
2326
2326
|
notify: notifyMode,
|
|
2327
2327
|
handoffDepth: this.handoffDepth
|
|
2328
2328
|
});
|
|
@@ -2334,13 +2334,13 @@ function readRequiredString(value, key) {
|
|
|
2334
2334
|
if (typeof value !== "string" || value.trim().length === 0) throw new Error(`${key} must be a non-empty string.`);
|
|
2335
2335
|
return value.trim();
|
|
2336
2336
|
}
|
|
2337
|
-
function readOptionalString$
|
|
2337
|
+
function readOptionalString$2(value) {
|
|
2338
2338
|
if (typeof value !== "string") return;
|
|
2339
2339
|
const trimmed = value.trim();
|
|
2340
2340
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
2341
2341
|
}
|
|
2342
2342
|
function readSpawnScope(value) {
|
|
2343
|
-
const normalized = readOptionalString$
|
|
2343
|
+
const normalized = readOptionalString$2(value)?.toLowerCase();
|
|
2344
2344
|
if (!normalized || normalized === "standalone") return "standalone";
|
|
2345
2345
|
if (normalized === "child") return "child";
|
|
2346
2346
|
throw new Error("scope must be \"standalone\" or \"child\".");
|
|
@@ -2348,7 +2348,7 @@ function readSpawnScope(value) {
|
|
|
2348
2348
|
function readSpawnRequestOptions(value) {
|
|
2349
2349
|
if (typeof value === "undefined") return;
|
|
2350
2350
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("request must be an object.");
|
|
2351
|
-
const notifyMode = readOptionalString$
|
|
2351
|
+
const notifyMode = readOptionalString$2(value.notify)?.toLowerCase();
|
|
2352
2352
|
if (notifyMode === "none" || notifyMode === "final_reply") return { notify: notifyMode };
|
|
2353
2353
|
throw new Error("request.notify must be \"none\" or \"final_reply\".");
|
|
2354
2354
|
}
|
|
@@ -2429,10 +2429,10 @@ var SessionSpawnTool = class extends Tool {
|
|
|
2429
2429
|
updateToolCallResult,
|
|
2430
2430
|
sourceSessionMetadata: this.sourceSessionMetadata,
|
|
2431
2431
|
task,
|
|
2432
|
-
title: readOptionalString$
|
|
2433
|
-
agentId: readOptionalString$
|
|
2434
|
-
model: readOptionalString$
|
|
2435
|
-
runtime: readOptionalString$
|
|
2432
|
+
title: readOptionalString$2(rawTitle),
|
|
2433
|
+
agentId: readOptionalString$2(rawAgentId),
|
|
2434
|
+
model: readOptionalString$2(rawModel),
|
|
2435
|
+
runtime: readOptionalString$2(rawRuntime),
|
|
2436
2436
|
handoffDepth: this.handoffDepth,
|
|
2437
2437
|
...parentSessionId ? { parentSessionId } : {},
|
|
2438
2438
|
notify: request.notify
|
|
@@ -2440,11 +2440,11 @@ var SessionSpawnTool = class extends Tool {
|
|
|
2440
2440
|
const session = this.sessionManager.createSession({
|
|
2441
2441
|
sourceSessionId: this.sourceSessionId,
|
|
2442
2442
|
task,
|
|
2443
|
-
title: readOptionalString$
|
|
2443
|
+
title: readOptionalString$2(rawTitle),
|
|
2444
2444
|
sourceSessionMetadata: this.sourceSessionMetadata,
|
|
2445
|
-
agentId: readOptionalString$
|
|
2446
|
-
model: readOptionalString$
|
|
2447
|
-
runtime: readOptionalString$
|
|
2445
|
+
agentId: readOptionalString$2(rawAgentId),
|
|
2446
|
+
model: readOptionalString$2(rawModel),
|
|
2447
|
+
runtime: readOptionalString$2(rawRuntime),
|
|
2448
2448
|
...parentSessionId ? { parentSessionId } : {}
|
|
2449
2449
|
});
|
|
2450
2450
|
this.onSessionUpdated?.(session.sessionId);
|
|
@@ -2469,10 +2469,10 @@ var SessionSpawnTool = class extends Tool {
|
|
|
2469
2469
|
//#endregion
|
|
2470
2470
|
//#region src/features/native-runtime/services/nextclaw-ncp-tool-registry.service.ts
|
|
2471
2471
|
function toToolParams(args) {
|
|
2472
|
-
if (isRecord$
|
|
2472
|
+
if (isRecord$5(args)) return args;
|
|
2473
2473
|
if (typeof args === "string") try {
|
|
2474
2474
|
const parsed = JSON.parse(args);
|
|
2475
|
-
return isRecord$
|
|
2475
|
+
return isRecord$5(parsed) ? parsed : {};
|
|
2476
2476
|
} catch {
|
|
2477
2477
|
return {};
|
|
2478
2478
|
}
|
|
@@ -2611,7 +2611,7 @@ var NextclawNcpToolRegistry = class {
|
|
|
2611
2611
|
registerMessagingTools = (context) => {
|
|
2612
2612
|
const { channel, chatId, metadata } = context;
|
|
2613
2613
|
const accountId = readMetadataAccountId(metadata, {});
|
|
2614
|
-
const messageTool = new MessageTool((message) => this.options.bus.publishOutbound(message));
|
|
2614
|
+
const messageTool = new MessageTool((message) => this.options.bus.publishOutbound(message), { resolveChannels: this.resolveMessageChannels });
|
|
2615
2615
|
messageTool.setContext(channel, chatId, accountId ?? null);
|
|
2616
2616
|
this.registerTool(messageTool);
|
|
2617
2617
|
if (this.options.cronService) {
|
|
@@ -2620,6 +2620,10 @@ var NextclawNcpToolRegistry = class {
|
|
|
2620
2620
|
this.registerTool(cronTool);
|
|
2621
2621
|
}
|
|
2622
2622
|
};
|
|
2623
|
+
resolveMessageChannels = () => {
|
|
2624
|
+
const channels = (this.options.getExtensionRegistry?.())?.channels ?? [];
|
|
2625
|
+
return [...new Set(channels.map((registration) => registration.channel.id.trim()).filter(Boolean))].sort();
|
|
2626
|
+
};
|
|
2623
2627
|
registerExtensionTools = (context) => {
|
|
2624
2628
|
const extensionRegistry = this.options.getExtensionRegistry?.();
|
|
2625
2629
|
if (!extensionRegistry || extensionRegistry.tools.length === 0) return;
|
|
@@ -2664,14 +2668,14 @@ function readAccountIdForHints(metadata, sessionMetadata) {
|
|
|
2664
2668
|
//#endregion
|
|
2665
2669
|
//#region src/features/native-runtime/services/nextclaw-ncp-context-builder.service.ts
|
|
2666
2670
|
const TIME_HINT_TRIGGER_PATTERNS = [/\b(now|right now|current time|what time|today|tonight|tomorrow|yesterday|this morning|this afternoon|this evening|date)\b/i, /(现在|此刻|当前时间|现在几点|几点了|今天|今晚|今早|今晨|明天|昨天|日期)/];
|
|
2667
|
-
function isRecord$
|
|
2671
|
+
function isRecord$3(value) {
|
|
2668
2672
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2669
2673
|
}
|
|
2670
2674
|
function mergeInputMetadata(input) {
|
|
2671
|
-
const messageMetadata = input.messages.slice().reverse().find((message) => isRecord$
|
|
2675
|
+
const messageMetadata = input.messages.slice().reverse().find((message) => isRecord$3(message.metadata))?.metadata;
|
|
2672
2676
|
return {
|
|
2673
|
-
...isRecord$
|
|
2674
|
-
...isRecord$
|
|
2677
|
+
...isRecord$3(messageMetadata) ? structuredClone(messageMetadata) : {},
|
|
2678
|
+
...isRecord$3(input.metadata) ? structuredClone(input.metadata) : {}
|
|
2675
2679
|
};
|
|
2676
2680
|
}
|
|
2677
2681
|
const REQUESTED_SKILLS_METADATA_READER = new RequestedSkillsMetadataReader();
|
|
@@ -2978,12 +2982,12 @@ function createContextWindowUpdatedEvent(params) {
|
|
|
2978
2982
|
}
|
|
2979
2983
|
};
|
|
2980
2984
|
}
|
|
2981
|
-
function isRecord$
|
|
2985
|
+
function isRecord$2(value) {
|
|
2982
2986
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2983
2987
|
}
|
|
2984
2988
|
function resolveNativeReasoningNormalizationMode(params) {
|
|
2985
2989
|
const runtimeEntry = params.config.agents.runtimes.entries.native?.config ?? params.config.ui.ncp.runtimes.native;
|
|
2986
|
-
const runtimeMetadata = isRecord$
|
|
2990
|
+
const runtimeMetadata = isRecord$2(runtimeEntry) ? runtimeEntry : {};
|
|
2987
2991
|
return readAssistantReasoningNormalizationModeFromMetadata(params.sessionMetadata) ?? readAssistantReasoningNormalizationMode(runtimeMetadata.reasoningNormalization) ?? readAssistantReasoningNormalizationMode(runtimeMetadata.reasoning_normalization) ?? readAssistantReasoningNormalizationMode(runtimeMetadata.reasoningNormalizationMode) ?? readAssistantReasoningNormalizationMode(runtimeMetadata.reasoning_normalization_mode) ?? "think-tags";
|
|
2988
2992
|
}
|
|
2989
2993
|
function createSessionSearchTools(params) {
|
|
@@ -3240,7 +3244,7 @@ function createAgentRuntimeSessionRequestDispatcher(options) {
|
|
|
3240
3244
|
}
|
|
3241
3245
|
//#endregion
|
|
3242
3246
|
//#region src/managers/agent-runtime.manager.ts
|
|
3243
|
-
function readOptionalString(value) {
|
|
3247
|
+
function readOptionalString$1(value) {
|
|
3244
3248
|
if (typeof value !== "string") return null;
|
|
3245
3249
|
const trimmed = value.trim();
|
|
3246
3250
|
return trimmed.length > 0 ? trimmed : null;
|
|
@@ -3251,7 +3255,7 @@ function readMessageTask(message) {
|
|
|
3251
3255
|
}
|
|
3252
3256
|
function readSessionMetadataString(metadata, ...keys) {
|
|
3253
3257
|
for (const key of keys) {
|
|
3254
|
-
const value = readOptionalString(metadata?.[key]);
|
|
3258
|
+
const value = readOptionalString$1(metadata?.[key]);
|
|
3255
3259
|
if (value) return value;
|
|
3256
3260
|
}
|
|
3257
3261
|
}
|
|
@@ -3286,6 +3290,7 @@ var AgentRuntimeManager = class {
|
|
|
3286
3290
|
get currentHandle() {
|
|
3287
3291
|
return this.handle;
|
|
3288
3292
|
}
|
|
3293
|
+
isLiveSessionRunning = (sessionId) => this.backend?.isLiveSessionRunning(sessionId) ?? false;
|
|
3289
3294
|
connectGatewayController = (gatewayController) => {
|
|
3290
3295
|
this.assertNotDisposed();
|
|
3291
3296
|
this.gatewayController = gatewayController;
|
|
@@ -3350,7 +3355,7 @@ var AgentRuntimeManager = class {
|
|
|
3350
3355
|
return this.handle;
|
|
3351
3356
|
};
|
|
3352
3357
|
materializeAgentSendEnvelope = (envelope) => {
|
|
3353
|
-
const existingSessionId = readOptionalString(envelope.sessionId) ?? readOptionalString(envelope.message.sessionId);
|
|
3358
|
+
const existingSessionId = readOptionalString$1(envelope.sessionId) ?? readOptionalString$1(envelope.message.sessionId);
|
|
3354
3359
|
if (existingSessionId) return {
|
|
3355
3360
|
...envelope,
|
|
3356
3361
|
sessionId: existingSessionId,
|
|
@@ -3382,35 +3387,39 @@ var AgentRuntimeManager = class {
|
|
|
3382
3387
|
}
|
|
3383
3388
|
};
|
|
3384
3389
|
};
|
|
3385
|
-
createMaterializingAgentClientEndpoint = (backend) =>
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3390
|
+
createMaterializingAgentClientEndpoint = (backend) => {
|
|
3391
|
+
const send = async (envelope) => {
|
|
3392
|
+
const requestEnvelope = this.materializeAgentSendEnvelope(envelope);
|
|
3393
|
+
return await consumeNcpRunHandle(backend.send(requestEnvelope), createNcpRunHandle(requestEnvelope));
|
|
3394
|
+
};
|
|
3395
|
+
return {
|
|
3396
|
+
get manifest() {
|
|
3397
|
+
return backend.manifest;
|
|
3398
|
+
},
|
|
3399
|
+
start: backend.start,
|
|
3400
|
+
stop: backend.stop,
|
|
3401
|
+
subscribe: backend.subscribe,
|
|
3402
|
+
stream: async (payload) => {
|
|
3403
|
+
await consumeAgentEvents(backend.stream(payload));
|
|
3404
|
+
},
|
|
3405
|
+
abort: backend.abort,
|
|
3406
|
+
send,
|
|
3407
|
+
emit: async (event) => {
|
|
3408
|
+
switch (event.type) {
|
|
3409
|
+
case NcpEventType.MessageRequest:
|
|
3410
|
+
await send(event.payload);
|
|
3411
|
+
return;
|
|
3412
|
+
case NcpEventType.MessageStreamRequest:
|
|
3413
|
+
await consumeAgentEvents(backend.stream(event.payload));
|
|
3414
|
+
return;
|
|
3415
|
+
case NcpEventType.MessageAbort:
|
|
3416
|
+
await backend.abort(event.payload);
|
|
3417
|
+
return;
|
|
3418
|
+
default: await backend.emit(event);
|
|
3419
|
+
}
|
|
3411
3420
|
}
|
|
3412
|
-
}
|
|
3413
|
-
}
|
|
3421
|
+
};
|
|
3422
|
+
};
|
|
3414
3423
|
warmDerivedCapabilities = async () => {
|
|
3415
3424
|
this.assertNotDisposed();
|
|
3416
3425
|
this.warmupPromise ??= this.runDerivedCapabilityWarmup();
|
|
@@ -4182,13 +4191,6 @@ function buildUpdatedMetadata(params) {
|
|
|
4182
4191
|
function normalizeSessionId(sessionId) {
|
|
4183
4192
|
return sessionId.trim();
|
|
4184
4193
|
}
|
|
4185
|
-
function toSessionCandidate(record) {
|
|
4186
|
-
const sessionId = normalizeSessionId(record.key);
|
|
4187
|
-
return sessionId ? {
|
|
4188
|
-
...record,
|
|
4189
|
-
sessionId
|
|
4190
|
-
} : null;
|
|
4191
|
-
}
|
|
4192
4194
|
function createSessionListSummary(record) {
|
|
4193
4195
|
return {
|
|
4194
4196
|
sessionId: record.sessionId,
|
|
@@ -4201,9 +4203,6 @@ function createSessionListSummary(record) {
|
|
|
4201
4203
|
metadata: structuredClone(record.metadata)
|
|
4202
4204
|
};
|
|
4203
4205
|
}
|
|
4204
|
-
function readSessionActivityAt$1(record) {
|
|
4205
|
-
return record.lastMessageAt ?? record.created_at;
|
|
4206
|
-
}
|
|
4207
4206
|
var NcpSessionApiService = class {
|
|
4208
4207
|
unsubscribeSessionUpdated = null;
|
|
4209
4208
|
contextWindowPreview;
|
|
@@ -4237,8 +4236,11 @@ var NcpSessionApiService = class {
|
|
|
4237
4236
|
this.options.eventBus.emit(eventKeys.sessionSummaryDelete, { sessionKey: normalizedSessionKey });
|
|
4238
4237
|
};
|
|
4239
4238
|
listSessions = async (options) => {
|
|
4240
|
-
if (this.options.ncpAgentSessionStore?.listSessionSummaries) return applyLimit(await this.options.ncpAgentSessionStore.listSessionSummaries(), options?.limit);
|
|
4241
|
-
return applyLimit(this.options.sessionManager.listSessions().map(
|
|
4239
|
+
if (this.options.ncpAgentSessionStore?.listSessionSummaries) return applyLimit(await this.options.ncpAgentSessionStore.listSessionSummaries(), options?.limit).map(this.withLiveSessionStatus);
|
|
4240
|
+
return applyLimit(this.options.sessionManager.listSessions().map((record) => ({
|
|
4241
|
+
...record,
|
|
4242
|
+
sessionId: normalizeSessionId(record.key)
|
|
4243
|
+
})).filter((record) => Boolean(record)).sort((left, right) => (right.lastMessageAt ?? right.created_at).localeCompare(left.lastMessageAt ?? left.created_at)), options?.limit).map(createSessionListSummary).map(this.withLiveSessionStatus);
|
|
4242
4244
|
};
|
|
4243
4245
|
listSessionMessages = async (sessionId, options) => {
|
|
4244
4246
|
const normalizedSessionId = normalizeSessionId(sessionId);
|
|
@@ -4251,7 +4253,7 @@ var NcpSessionApiService = class {
|
|
|
4251
4253
|
const normalizedSessionId = normalizeSessionId(sessionId);
|
|
4252
4254
|
if (normalizedSessionId && this.options.ncpAgentSessionStore?.getSession) {
|
|
4253
4255
|
const record = await this.options.ncpAgentSessionStore.getSession(normalizedSessionId);
|
|
4254
|
-
if (record) return createNcpSessionSummary({
|
|
4256
|
+
if (record) return this.withLiveSessionStatus(createNcpSessionSummary({
|
|
4255
4257
|
sessionId: normalizedSessionId,
|
|
4256
4258
|
agentId: record.agentId,
|
|
4257
4259
|
messages: record.messages,
|
|
@@ -4265,12 +4267,12 @@ var NcpSessionApiService = class {
|
|
|
4265
4267
|
sessionId: normalizedSessionId,
|
|
4266
4268
|
sessionMessages: record.messages
|
|
4267
4269
|
})
|
|
4268
|
-
});
|
|
4270
|
+
}));
|
|
4269
4271
|
}
|
|
4270
4272
|
const session = normalizedSessionId ? this.options.sessionManager.getIfExists(normalizedSessionId) : null;
|
|
4271
4273
|
if (!session) return null;
|
|
4272
4274
|
const messages = toNcpMessages(normalizedSessionId, session.messages);
|
|
4273
|
-
return createNcpSessionSummary({
|
|
4275
|
+
return this.withLiveSessionStatus(createNcpSessionSummary({
|
|
4274
4276
|
sessionId: normalizedSessionId,
|
|
4275
4277
|
agentId: session.agentId,
|
|
4276
4278
|
messages,
|
|
@@ -4284,7 +4286,7 @@ var NcpSessionApiService = class {
|
|
|
4284
4286
|
sessionId: normalizedSessionId,
|
|
4285
4287
|
sessionMessages: messages
|
|
4286
4288
|
})
|
|
4287
|
-
});
|
|
4289
|
+
}));
|
|
4288
4290
|
};
|
|
4289
4291
|
updateSession = async (sessionId, patch) => {
|
|
4290
4292
|
const normalizedSessionId = normalizeSessionId(sessionId);
|
|
@@ -4321,6 +4323,10 @@ var NcpSessionApiService = class {
|
|
|
4321
4323
|
this.options.sessionManager.delete(normalizedSessionId);
|
|
4322
4324
|
await this.publishSessionChange(normalizedSessionId);
|
|
4323
4325
|
};
|
|
4326
|
+
withLiveSessionStatus = (summary) => this.options.isLiveSessionRunning?.(summary.sessionId) ? {
|
|
4327
|
+
...summary,
|
|
4328
|
+
status: "running"
|
|
4329
|
+
} : summary;
|
|
4324
4330
|
};
|
|
4325
4331
|
//#endregion
|
|
4326
4332
|
//#region src/utils/ncp-session-metadata.utils.ts
|
|
@@ -4351,21 +4357,6 @@ var NcpAgentLegacySessionStore = class {
|
|
|
4351
4357
|
metadata: structuredClone(session.metadata)
|
|
4352
4358
|
};
|
|
4353
4359
|
};
|
|
4354
|
-
getSessionSummary = (sessionId) => {
|
|
4355
|
-
const session = this.getSession(sessionId);
|
|
4356
|
-
if (!session) return null;
|
|
4357
|
-
const lastMessageAt = session.messages.at(-1)?.timestamp;
|
|
4358
|
-
return {
|
|
4359
|
-
sessionId: session.sessionId,
|
|
4360
|
-
...session.agentId ? { agentId: session.agentId } : {},
|
|
4361
|
-
messageCount: session.messages.length,
|
|
4362
|
-
createdAt: session.createdAt,
|
|
4363
|
-
updatedAt: session.updatedAt,
|
|
4364
|
-
...lastMessageAt ? { lastMessageAt } : {},
|
|
4365
|
-
status: "idle",
|
|
4366
|
-
metadata: session.metadata
|
|
4367
|
-
};
|
|
4368
|
-
};
|
|
4369
4360
|
listSessionMessages = (sessionId) => this.getSession(sessionId)?.messages ?? [];
|
|
4370
4361
|
listSessionSummaries = () => this.sessionManager.listSessions().map((record) => ({
|
|
4371
4362
|
sessionId: record.key,
|
|
@@ -4431,7 +4422,6 @@ var NcpAgentSessionStoreAdapter = class {
|
|
|
4431
4422
|
};
|
|
4432
4423
|
}
|
|
4433
4424
|
getSession = async (sessionId) => await this.options.journalStore?.getSession(sessionId) ?? this.legacyStore.getSession(sessionId);
|
|
4434
|
-
getSessionSummary = async (sessionId) => await this.options.journalStore?.getSessionSummary(sessionId) ?? this.legacyStore.getSessionSummary(sessionId);
|
|
4435
4425
|
listSessionMessages = async (sessionId) => {
|
|
4436
4426
|
const journalStore = this.options.journalStore;
|
|
4437
4427
|
return journalStore && await journalStore.hasSession(sessionId) ? await journalStore.listSessionMessages(sessionId) : this.legacyStore.listSessionMessages(sessionId);
|
|
@@ -4505,7 +4495,7 @@ function normalizeNcpAgentId(agentId) {
|
|
|
4505
4495
|
const normalized = agentId?.trim().toLowerCase();
|
|
4506
4496
|
return normalized ? normalized : void 0;
|
|
4507
4497
|
}
|
|
4508
|
-
function isRecord(value) {
|
|
4498
|
+
function isRecord$1(value) {
|
|
4509
4499
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
4510
4500
|
}
|
|
4511
4501
|
function toIsoString(value, fallback) {
|
|
@@ -4563,10 +4553,19 @@ function upsertNcpAgentSessionSummaryEvent(params) {
|
|
|
4563
4553
|
}
|
|
4564
4554
|
async function replayNcpAgentSessionEvents(events) {
|
|
4565
4555
|
const stateManager = new DefaultNcpAgentConversationStateManager();
|
|
4566
|
-
for (const event of events) await stateManager.dispatch(
|
|
4556
|
+
for (const event of events) await stateManager.dispatch(createReplayEvent(event));
|
|
4567
4557
|
const snapshot = stateManager.getSnapshot();
|
|
4568
4558
|
return [...snapshot.messages.map((message) => structuredClone(message)), ...snapshot.streamingMessage ? [structuredClone(snapshot.streamingMessage)] : []];
|
|
4569
4559
|
}
|
|
4560
|
+
function createReplayEvent(event) {
|
|
4561
|
+
const replayEvent = structuredClone(event);
|
|
4562
|
+
if (replayEvent.type === NcpEventType.MessageCompleted) return {
|
|
4563
|
+
type: NcpEventType.MessageSent,
|
|
4564
|
+
payload: replayEvent.payload
|
|
4565
|
+
};
|
|
4566
|
+
if (replayEvent.type === NcpEventType.MessageSent && replayEvent.payload.message.role === "assistant" && (replayEvent.payload.message.status === "pending" || replayEvent.payload.message.status === "streaming")) replayEvent.payload.message.status = "final";
|
|
4567
|
+
return replayEvent;
|
|
4568
|
+
}
|
|
4570
4569
|
function readNcpSessionSummaryActivityAt(summary) {
|
|
4571
4570
|
return summary.lastMessageAt ?? summary.createdAt ?? summary.updatedAt;
|
|
4572
4571
|
}
|
|
@@ -4604,19 +4603,14 @@ function readSummaryLabelFromEvent(event) {
|
|
|
4604
4603
|
if (message?.role !== "user") return null;
|
|
4605
4604
|
return resolveAutoSessionLabel([message]);
|
|
4606
4605
|
}
|
|
4607
|
-
function createCompletedMessageEvent(event) {
|
|
4608
|
-
return {
|
|
4609
|
-
type: NcpEventType.MessageSent,
|
|
4610
|
-
payload: {
|
|
4611
|
-
sessionId: event.payload.sessionId,
|
|
4612
|
-
message: structuredClone(event.payload.message),
|
|
4613
|
-
...event.payload.correlationId ? { correlationId: event.payload.correlationId } : {},
|
|
4614
|
-
metadata: event.payload.metadata
|
|
4615
|
-
}
|
|
4616
|
-
};
|
|
4617
|
-
}
|
|
4618
4606
|
//#endregion
|
|
4619
4607
|
//#region src/stores/ncp-agent-session-journal.store.ts
|
|
4608
|
+
function serializeJournalEntry(entry) {
|
|
4609
|
+
const serialized = JSON.stringify(entry);
|
|
4610
|
+
if (!serialized) throw new Error("ncp agent session journal entry serialization produced empty output");
|
|
4611
|
+
if (!isRecord$1(JSON.parse(serialized))) throw new Error("ncp agent session journal entry serialization produced a non-object entry");
|
|
4612
|
+
return serialized;
|
|
4613
|
+
}
|
|
4620
4614
|
var NcpAgentSessionJournalStore = class {
|
|
4621
4615
|
sessions = /* @__PURE__ */ new Map();
|
|
4622
4616
|
nextSeqBySession = /* @__PURE__ */ new Map();
|
|
@@ -4648,14 +4642,6 @@ var NcpAgentSessionJournalStore = class {
|
|
|
4648
4642
|
this.sessions.set(normalizedSessionId, loaded);
|
|
4649
4643
|
return structuredClone(loaded.record);
|
|
4650
4644
|
};
|
|
4651
|
-
getSessionSummary = async (sessionId) => {
|
|
4652
|
-
const normalizedSessionId = normalizeNcpSessionId(sessionId);
|
|
4653
|
-
if (!normalizedSessionId) return null;
|
|
4654
|
-
const indexed = (await this.loadSummaryIndex()).get(normalizedSessionId);
|
|
4655
|
-
if (indexed) return structuredClone(indexed);
|
|
4656
|
-
const record = await this.getSession(normalizedSessionId);
|
|
4657
|
-
return record ? createNcpAgentSessionSummary(record) : null;
|
|
4658
|
-
};
|
|
4659
4645
|
listSessionSummaries = async () => {
|
|
4660
4646
|
return [...(await this.loadSummaryIndex()).values()].map((summary) => structuredClone(summary)).sort((left, right) => readNcpSessionSummaryActivityAt(right).localeCompare(readNcpSessionSummaryActivityAt(left)));
|
|
4661
4647
|
};
|
|
@@ -4684,7 +4670,7 @@ var NcpAgentSessionJournalStore = class {
|
|
|
4684
4670
|
}
|
|
4685
4671
|
}
|
|
4686
4672
|
}))];
|
|
4687
|
-
await writeFile(this.sessionPath(sessionId), `${entries.map(
|
|
4673
|
+
await writeFile(this.sessionPath(sessionId), `${entries.map(serializeJournalEntry).join("\n")}\n`, "utf-8");
|
|
4688
4674
|
const nextSeq = nextRecord.messages.length + 1;
|
|
4689
4675
|
this.sessions.set(sessionId, {
|
|
4690
4676
|
record: nextRecord,
|
|
@@ -4718,7 +4704,7 @@ var NcpAgentSessionJournalStore = class {
|
|
|
4718
4704
|
const hasJournal = Boolean(existing) || this.nextSeqBySession.has(sessionId);
|
|
4719
4705
|
const nextSeq = this.nextSeqBySession.get(sessionId) ?? existing?.nextSeq ?? 1;
|
|
4720
4706
|
const path = this.sessionPath(sessionId);
|
|
4721
|
-
if (!hasJournal) await
|
|
4707
|
+
if (!hasJournal) await this.appendJournalEntry(path, createNcpAgentSessionJournalMetadataEntry(session));
|
|
4722
4708
|
const entry = {
|
|
4723
4709
|
_type: "event",
|
|
4724
4710
|
version: 1,
|
|
@@ -4726,7 +4712,7 @@ var NcpAgentSessionJournalStore = class {
|
|
|
4726
4712
|
timestamp: updatedAt,
|
|
4727
4713
|
event: structuredClone(event)
|
|
4728
4714
|
};
|
|
4729
|
-
await
|
|
4715
|
+
await this.appendJournalEntry(path, entry);
|
|
4730
4716
|
this.nextSeqBySession.set(sessionId, nextSeq + 1);
|
|
4731
4717
|
this.sessions.delete(sessionId);
|
|
4732
4718
|
await this.upsertSummaryIndexForEvent({
|
|
@@ -4765,18 +4751,24 @@ var NcpAgentSessionJournalStore = class {
|
|
|
4765
4751
|
let updatedAt = createdAt;
|
|
4766
4752
|
let nextSeq = 1;
|
|
4767
4753
|
const events = [];
|
|
4768
|
-
for (const line of raw.split("\n")) {
|
|
4754
|
+
for (const [index, line] of raw.split("\n").entries()) {
|
|
4769
4755
|
if (!line.trim()) continue;
|
|
4770
|
-
|
|
4771
|
-
|
|
4756
|
+
let parsed;
|
|
4757
|
+
try {
|
|
4758
|
+
parsed = JSON.parse(line);
|
|
4759
|
+
} catch (error) {
|
|
4760
|
+
console.warn(`[ncp-agent-session-journal] skipped corrupted journal line ${index + 1}: ${error instanceof Error ? error.message : String(error)}`);
|
|
4761
|
+
continue;
|
|
4762
|
+
}
|
|
4763
|
+
if (!isRecord$1(parsed)) continue;
|
|
4772
4764
|
if (parsed._type === "metadata") {
|
|
4773
|
-
metadata = isRecord(parsed.metadata) ? structuredClone(parsed.metadata) : {};
|
|
4765
|
+
metadata = isRecord$1(parsed.metadata) ? structuredClone(parsed.metadata) : {};
|
|
4774
4766
|
agentId = normalizeNcpAgentId(typeof parsed.agent_id === "string" ? parsed.agent_id : void 0);
|
|
4775
4767
|
createdAt = toIsoString(parsed.created_at, createdAt);
|
|
4776
4768
|
updatedAt = toIsoString(parsed.updated_at, updatedAt);
|
|
4777
4769
|
continue;
|
|
4778
4770
|
}
|
|
4779
|
-
if (parsed._type === "event" && isRecord(parsed.event)) {
|
|
4771
|
+
if (parsed._type === "event" && isRecord$1(parsed.event)) {
|
|
4780
4772
|
const seq = Number(parsed.seq);
|
|
4781
4773
|
nextSeq = Math.max(nextSeq, Number.isFinite(seq) ? Math.trunc(seq) + 1 : nextSeq);
|
|
4782
4774
|
updatedAt = toIsoString(parsed.timestamp, updatedAt);
|
|
@@ -4792,6 +4784,9 @@ var NcpAgentSessionJournalStore = class {
|
|
|
4792
4784
|
events
|
|
4793
4785
|
};
|
|
4794
4786
|
};
|
|
4787
|
+
appendJournalEntry = async (path, entry) => {
|
|
4788
|
+
await appendFile(path, `${serializeJournalEntry(entry)}\n`, "utf-8");
|
|
4789
|
+
};
|
|
4795
4790
|
loadSummaryIndex = async () => {
|
|
4796
4791
|
if (this.summaryIndex) return this.summaryIndex;
|
|
4797
4792
|
try {
|
|
@@ -4860,6 +4855,306 @@ var NcpAgentSessionJournalStore = class {
|
|
|
4860
4855
|
indexPath = () => resolve(this.journalDir, NCP_AGENT_SESSION_JOURNAL_INDEX_FILE);
|
|
4861
4856
|
};
|
|
4862
4857
|
//#endregion
|
|
4858
|
+
//#region src/contributions/session-context-window/index.ts
|
|
4859
|
+
const STREAM_REFRESH_DELAY_MS = 1500;
|
|
4860
|
+
function formatBackgroundError$1(error) {
|
|
4861
|
+
if (error instanceof Error) return error.stack ?? error.message;
|
|
4862
|
+
return String(error);
|
|
4863
|
+
}
|
|
4864
|
+
function readEventSessionId(event) {
|
|
4865
|
+
const payload = "payload" in event ? event.payload : null;
|
|
4866
|
+
if (!payload || typeof payload !== "object") return null;
|
|
4867
|
+
return "sessionId" in payload && typeof payload.sessionId === "string" ? payload.sessionId.trim() || null : null;
|
|
4868
|
+
}
|
|
4869
|
+
function isContextWindow(value) {
|
|
4870
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
4871
|
+
}
|
|
4872
|
+
function createSignature(value) {
|
|
4873
|
+
return JSON.stringify(value);
|
|
4874
|
+
}
|
|
4875
|
+
function shouldRefreshDuringStream(event) {
|
|
4876
|
+
switch (event.type) {
|
|
4877
|
+
case NcpEventType.MessageTextDelta:
|
|
4878
|
+
case NcpEventType.MessageReasoningDelta:
|
|
4879
|
+
case NcpEventType.MessageToolCallArgsDelta:
|
|
4880
|
+
case NcpEventType.MessageToolCallResult: return true;
|
|
4881
|
+
default: return false;
|
|
4882
|
+
}
|
|
4883
|
+
}
|
|
4884
|
+
function shouldRefreshImmediately(event) {
|
|
4885
|
+
switch (event.type) {
|
|
4886
|
+
case NcpEventType.MessageCompleted:
|
|
4887
|
+
case NcpEventType.MessageFailed:
|
|
4888
|
+
case NcpEventType.MessageAbort:
|
|
4889
|
+
case NcpEventType.RunFinished:
|
|
4890
|
+
case NcpEventType.RunError: return true;
|
|
4891
|
+
default: return false;
|
|
4892
|
+
}
|
|
4893
|
+
}
|
|
4894
|
+
var SessionContextWindowContribution = class {
|
|
4895
|
+
unsubscribeNcpEvent = null;
|
|
4896
|
+
stopped = true;
|
|
4897
|
+
pendingTimers = /* @__PURE__ */ new Map();
|
|
4898
|
+
lastPublishedSignatureBySession = /* @__PURE__ */ new Map();
|
|
4899
|
+
constructor(kernel) {
|
|
4900
|
+
this.kernel = kernel;
|
|
4901
|
+
}
|
|
4902
|
+
start = () => {
|
|
4903
|
+
if (this.unsubscribeNcpEvent) return;
|
|
4904
|
+
this.stopped = false;
|
|
4905
|
+
this.unsubscribeNcpEvent = this.kernel.eventBus.on(eventKeys.ncpEvent, this.handleNcpEvent);
|
|
4906
|
+
};
|
|
4907
|
+
dispose = () => {
|
|
4908
|
+
this.unsubscribeNcpEvent?.();
|
|
4909
|
+
this.unsubscribeNcpEvent = null;
|
|
4910
|
+
this.stopped = true;
|
|
4911
|
+
for (const timer of this.pendingTimers.values()) clearTimeout(timer);
|
|
4912
|
+
this.pendingTimers.clear();
|
|
4913
|
+
this.lastPublishedSignatureBySession.clear();
|
|
4914
|
+
};
|
|
4915
|
+
handleNcpEvent = (event) => {
|
|
4916
|
+
const sessionId = readEventSessionId(event);
|
|
4917
|
+
if (!sessionId) return;
|
|
4918
|
+
if (event.type === NcpEventType.ContextWindowUpdated) {
|
|
4919
|
+
this.rememberPublishedContextWindow(sessionId, event.payload.contextWindow);
|
|
4920
|
+
return;
|
|
4921
|
+
}
|
|
4922
|
+
if (shouldRefreshImmediately(event)) {
|
|
4923
|
+
this.refreshNow(sessionId);
|
|
4924
|
+
return;
|
|
4925
|
+
}
|
|
4926
|
+
if (shouldRefreshDuringStream(event)) this.refreshSoon(sessionId);
|
|
4927
|
+
};
|
|
4928
|
+
refreshSoon = (sessionId) => {
|
|
4929
|
+
if (this.pendingTimers.has(sessionId)) return;
|
|
4930
|
+
const timer = setTimeout(() => {
|
|
4931
|
+
this.pendingTimers.delete(sessionId);
|
|
4932
|
+
this.refreshNow(sessionId);
|
|
4933
|
+
}, STREAM_REFRESH_DELAY_MS);
|
|
4934
|
+
this.pendingTimers.set(sessionId, timer);
|
|
4935
|
+
};
|
|
4936
|
+
refreshNow = (sessionId) => {
|
|
4937
|
+
const timer = this.pendingTimers.get(sessionId);
|
|
4938
|
+
if (timer) {
|
|
4939
|
+
clearTimeout(timer);
|
|
4940
|
+
this.pendingTimers.delete(sessionId);
|
|
4941
|
+
}
|
|
4942
|
+
this.publishContextWindow(sessionId).catch((error) => {
|
|
4943
|
+
console.error(`[session-context-window] failed to refresh ${sessionId}: ${formatBackgroundError$1(error)}`);
|
|
4944
|
+
});
|
|
4945
|
+
};
|
|
4946
|
+
publishContextWindow = async (sessionId) => {
|
|
4947
|
+
if (this.stopped) return;
|
|
4948
|
+
const contextWindow = (await this.kernel.ncpSessionApi.getSession(sessionId))?.contextWindow;
|
|
4949
|
+
if (!isContextWindow(contextWindow) || this.stopped) return;
|
|
4950
|
+
const endpoint = this.kernel.agentRuntimeManager.currentHandle?.agentClientEndpoint;
|
|
4951
|
+
if (!endpoint) return;
|
|
4952
|
+
const signature = createSignature(contextWindow);
|
|
4953
|
+
if (this.lastPublishedSignatureBySession.get(sessionId) === signature) return;
|
|
4954
|
+
this.lastPublishedSignatureBySession.set(sessionId, signature);
|
|
4955
|
+
await endpoint.emit({
|
|
4956
|
+
type: NcpEventType.ContextWindowUpdated,
|
|
4957
|
+
payload: {
|
|
4958
|
+
sessionId,
|
|
4959
|
+
contextWindow
|
|
4960
|
+
}
|
|
4961
|
+
});
|
|
4962
|
+
};
|
|
4963
|
+
rememberPublishedContextWindow = (sessionId, contextWindow) => {
|
|
4964
|
+
this.lastPublishedSignatureBySession.set(sessionId, createSignature(contextWindow));
|
|
4965
|
+
};
|
|
4966
|
+
};
|
|
4967
|
+
//#endregion
|
|
4968
|
+
//#region src/contributions/session-activity-preview/utils/session-activity-preview-ncp-event.utils.ts
|
|
4969
|
+
const PREVIEW_TEXT_MAX_LENGTH = 160;
|
|
4970
|
+
function readSessionId(value) {
|
|
4971
|
+
if (typeof value !== "string") return null;
|
|
4972
|
+
const trimmed = value.trim();
|
|
4973
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
4974
|
+
}
|
|
4975
|
+
function compactPreviewText(value) {
|
|
4976
|
+
return value.replace(/\s+/g, " ").trim();
|
|
4977
|
+
}
|
|
4978
|
+
function truncatePreviewText(value) {
|
|
4979
|
+
const compacted = compactPreviewText(value);
|
|
4980
|
+
if (compacted.length <= PREVIEW_TEXT_MAX_LENGTH) return compacted;
|
|
4981
|
+
return compacted.slice(0, PREVIEW_TEXT_MAX_LENGTH - 1).trimEnd();
|
|
4982
|
+
}
|
|
4983
|
+
function readMessagePreviewText(message) {
|
|
4984
|
+
const chunks = [];
|
|
4985
|
+
for (const part of message.parts) if ((part.type === "text" || part.type === "rich-text") && part.text.trim()) chunks.push(part.text);
|
|
4986
|
+
const previewText = truncatePreviewText(chunks.join(" "));
|
|
4987
|
+
return previewText.length > 0 ? previewText : null;
|
|
4988
|
+
}
|
|
4989
|
+
function createProjection(sessionId, preview) {
|
|
4990
|
+
if (!sessionId) return null;
|
|
4991
|
+
return {
|
|
4992
|
+
sessionId,
|
|
4993
|
+
preview
|
|
4994
|
+
};
|
|
4995
|
+
}
|
|
4996
|
+
function formatErrorStatus(error) {
|
|
4997
|
+
if (typeof error === "string" && error.trim()) return `运行出错:${truncatePreviewText(error)}`;
|
|
4998
|
+
if (error && typeof error === "object" && "message" in error) {
|
|
4999
|
+
const message = error.message;
|
|
5000
|
+
if (typeof message === "string" && message.trim()) return `运行出错:${truncatePreviewText(message)}`;
|
|
5001
|
+
}
|
|
5002
|
+
return "运行出错";
|
|
5003
|
+
}
|
|
5004
|
+
function createSessionActivityPreviewFromNcpEvent(event, timestamp) {
|
|
5005
|
+
switch (event.type) {
|
|
5006
|
+
case NcpEventType.RunStarted: return createProjection(readSessionId(event.payload.sessionId), {
|
|
5007
|
+
state: "running",
|
|
5008
|
+
statusText: "正在处理...",
|
|
5009
|
+
timestamp
|
|
5010
|
+
});
|
|
5011
|
+
case NcpEventType.RunFinished: return createProjection(readSessionId(event.payload.sessionId), {
|
|
5012
|
+
state: "completed",
|
|
5013
|
+
timestamp
|
|
5014
|
+
});
|
|
5015
|
+
case NcpEventType.RunError: return createProjection(readSessionId(event.payload.sessionId), {
|
|
5016
|
+
state: "failed",
|
|
5017
|
+
statusText: formatErrorStatus(event.payload.error),
|
|
5018
|
+
timestamp
|
|
5019
|
+
});
|
|
5020
|
+
case NcpEventType.MessageSent: {
|
|
5021
|
+
const text = readMessagePreviewText(event.payload.message);
|
|
5022
|
+
if (!text || event.payload.message.role !== "user") return null;
|
|
5023
|
+
return createProjection(readSessionId(event.payload.sessionId), {
|
|
5024
|
+
state: "running",
|
|
5025
|
+
statusText: text,
|
|
5026
|
+
timestamp: event.payload.message.timestamp || timestamp
|
|
5027
|
+
});
|
|
5028
|
+
}
|
|
5029
|
+
case NcpEventType.MessageCompleted: {
|
|
5030
|
+
const text = readMessagePreviewText(event.payload.message);
|
|
5031
|
+
if (!text || event.payload.message.role !== "assistant") return null;
|
|
5032
|
+
return createProjection(readSessionId(event.payload.sessionId), {
|
|
5033
|
+
state: "completed",
|
|
5034
|
+
replyText: text,
|
|
5035
|
+
timestamp: event.payload.message.timestamp || timestamp
|
|
5036
|
+
});
|
|
5037
|
+
}
|
|
5038
|
+
case NcpEventType.MessageFailed: return createProjection(readSessionId(event.payload.sessionId), {
|
|
5039
|
+
state: "failed",
|
|
5040
|
+
statusText: formatErrorStatus(event.payload.error),
|
|
5041
|
+
timestamp
|
|
5042
|
+
});
|
|
5043
|
+
case NcpEventType.MessageToolCallStart: return createProjection(readSessionId(event.payload.sessionId), {
|
|
5044
|
+
state: "running",
|
|
5045
|
+
statusText: `正在调用工具:${event.payload.toolName}`,
|
|
5046
|
+
timestamp
|
|
5047
|
+
});
|
|
5048
|
+
case NcpEventType.MessageToolCallEnd:
|
|
5049
|
+
case NcpEventType.MessageToolCallResult: return createProjection(readSessionId(event.payload.sessionId), {
|
|
5050
|
+
state: "running",
|
|
5051
|
+
statusText: "工具调用完成",
|
|
5052
|
+
timestamp
|
|
5053
|
+
});
|
|
5054
|
+
default: return null;
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5057
|
+
//#endregion
|
|
5058
|
+
//#region src/contributions/session-activity-preview/utils/session-activity-preview-metadata.utils.ts
|
|
5059
|
+
const SESSION_ACTIVITY_PREVIEW_METADATA_KEY = "last_activity_preview";
|
|
5060
|
+
const SESSION_ACTIVITY_PREVIEW_STATES = new Set([
|
|
5061
|
+
"running",
|
|
5062
|
+
"completed",
|
|
5063
|
+
"failed",
|
|
5064
|
+
"idle"
|
|
5065
|
+
]);
|
|
5066
|
+
function isRecord(value) {
|
|
5067
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
5068
|
+
}
|
|
5069
|
+
function readOptionalString(value) {
|
|
5070
|
+
if (typeof value !== "string") return;
|
|
5071
|
+
const trimmed = value.trim();
|
|
5072
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
5073
|
+
}
|
|
5074
|
+
function readSessionActivityPreviewMetadata(value) {
|
|
5075
|
+
if (!isRecord(value)) return null;
|
|
5076
|
+
const state = value.state;
|
|
5077
|
+
const timestamp = readOptionalString(value.timestamp);
|
|
5078
|
+
if (!SESSION_ACTIVITY_PREVIEW_STATES.has(state) || !timestamp) return null;
|
|
5079
|
+
return {
|
|
5080
|
+
state,
|
|
5081
|
+
timestamp,
|
|
5082
|
+
...readOptionalString(value.statusText) ? { statusText: readOptionalString(value.statusText) } : {},
|
|
5083
|
+
...readOptionalString(value.replyText) ? { replyText: readOptionalString(value.replyText) } : {}
|
|
5084
|
+
};
|
|
5085
|
+
}
|
|
5086
|
+
function compareIsoTimestamp(left, right) {
|
|
5087
|
+
const leftTime = Date.parse(left);
|
|
5088
|
+
const rightTime = Date.parse(right);
|
|
5089
|
+
if (!Number.isFinite(leftTime) || !Number.isFinite(rightTime)) return left.localeCompare(right);
|
|
5090
|
+
return leftTime - rightTime;
|
|
5091
|
+
}
|
|
5092
|
+
function mergeSessionActivityPreview(current, incoming) {
|
|
5093
|
+
if (current && compareIsoTimestamp(incoming.timestamp, current.timestamp) < 0) return current;
|
|
5094
|
+
return {
|
|
5095
|
+
state: incoming.state,
|
|
5096
|
+
timestamp: incoming.timestamp,
|
|
5097
|
+
...incoming.statusText ?? (incoming.state === "completed" ? current?.statusText : void 0) ? { statusText: incoming.statusText ?? current?.statusText } : {},
|
|
5098
|
+
...incoming.replyText ?? current?.replyText ? { replyText: incoming.replyText ?? current?.replyText } : {}
|
|
5099
|
+
};
|
|
5100
|
+
}
|
|
5101
|
+
function areSessionActivityPreviewsEqual(left, right) {
|
|
5102
|
+
return Boolean(left) && left?.state === right.state && left.timestamp === right.timestamp && left.statusText === right.statusText && left.replyText === right.replyText;
|
|
5103
|
+
}
|
|
5104
|
+
function writeSessionActivityPreviewMetadata(metadata, projection) {
|
|
5105
|
+
const current = readSessionActivityPreviewMetadata(metadata?.[SESSION_ACTIVITY_PREVIEW_METADATA_KEY]);
|
|
5106
|
+
const next = mergeSessionActivityPreview(current, projection.preview);
|
|
5107
|
+
if (areSessionActivityPreviewsEqual(current, next)) return null;
|
|
5108
|
+
return {
|
|
5109
|
+
...metadata ?? {},
|
|
5110
|
+
[SESSION_ACTIVITY_PREVIEW_METADATA_KEY]: next
|
|
5111
|
+
};
|
|
5112
|
+
}
|
|
5113
|
+
//#endregion
|
|
5114
|
+
//#region src/contributions/session-activity-preview/index.ts
|
|
5115
|
+
function formatBackgroundError(error) {
|
|
5116
|
+
if (error instanceof Error) return error.stack ?? error.message;
|
|
5117
|
+
return String(error);
|
|
5118
|
+
}
|
|
5119
|
+
function readMetadata(value) {
|
|
5120
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
5121
|
+
return value;
|
|
5122
|
+
}
|
|
5123
|
+
var SessionActivityPreviewContribution = class {
|
|
5124
|
+
unsubscribeNcpEvent = null;
|
|
5125
|
+
stopped = true;
|
|
5126
|
+
constructor(kernel) {
|
|
5127
|
+
this.kernel = kernel;
|
|
5128
|
+
}
|
|
5129
|
+
start = () => {
|
|
5130
|
+
if (this.unsubscribeNcpEvent) return;
|
|
5131
|
+
this.stopped = false;
|
|
5132
|
+
this.unsubscribeNcpEvent = this.kernel.eventBus.on(eventKeys.ncpEvent, (event) => {
|
|
5133
|
+
const projection = createSessionActivityPreviewFromNcpEvent(event, (/* @__PURE__ */ new Date()).toISOString());
|
|
5134
|
+
if (!projection) return;
|
|
5135
|
+
this.updatePreview(projection);
|
|
5136
|
+
});
|
|
5137
|
+
};
|
|
5138
|
+
dispose = () => {
|
|
5139
|
+
this.unsubscribeNcpEvent?.();
|
|
5140
|
+
this.unsubscribeNcpEvent = null;
|
|
5141
|
+
this.stopped = true;
|
|
5142
|
+
};
|
|
5143
|
+
updatePreview = (projection) => {
|
|
5144
|
+
this.persistPreview(projection).catch((error) => {
|
|
5145
|
+
console.error(`[session-activity-preview] failed to update ${projection.sessionId}: ${formatBackgroundError(error)}`);
|
|
5146
|
+
});
|
|
5147
|
+
};
|
|
5148
|
+
persistPreview = async (projection) => {
|
|
5149
|
+
if (this.stopped) return;
|
|
5150
|
+
const summary = await this.kernel.ncpSessionApi.getSession(projection.sessionId);
|
|
5151
|
+
if (!summary || this.stopped) return;
|
|
5152
|
+
const nextMetadata = writeSessionActivityPreviewMetadata(readMetadata(summary.metadata), projection);
|
|
5153
|
+
if (!nextMetadata) return;
|
|
5154
|
+
await this.kernel.ncpSessionApi.updateSession(projection.sessionId, { metadata: nextMetadata });
|
|
5155
|
+
};
|
|
5156
|
+
};
|
|
5157
|
+
//#endregion
|
|
4863
5158
|
//#region src/app/nextclaw-kernel.ts
|
|
4864
5159
|
function resolveKernelSessionsDir(options) {
|
|
4865
5160
|
const homeDir = options.homeDir?.trim();
|
|
@@ -4904,7 +5199,7 @@ var NextclawKernel = class {
|
|
|
4904
5199
|
sessionLifecycleEvents;
|
|
4905
5200
|
ncpAgentSessionStore;
|
|
4906
5201
|
ncpAgentSessionJournalStore;
|
|
4907
|
-
|
|
5202
|
+
contributions;
|
|
4908
5203
|
constructor(options = {}) {
|
|
4909
5204
|
const sessionsDir = resolveKernelSessionsDir(options);
|
|
4910
5205
|
this.sessions = new SessionManager({ sessionsDir });
|
|
@@ -4942,12 +5237,6 @@ var NextclawKernel = class {
|
|
|
4942
5237
|
channels: this.channels,
|
|
4943
5238
|
providerManager: this.llmProviders
|
|
4944
5239
|
});
|
|
4945
|
-
this.ncpSessionApi = new NcpSessionApiService({
|
|
4946
|
-
eventBus: this.eventBus,
|
|
4947
|
-
getConfig: this.configManager.loadConfig,
|
|
4948
|
-
ncpAgentSessionStore: this.ncpAgentSessionStore,
|
|
4949
|
-
sessionManager: this.sessions
|
|
4950
|
-
});
|
|
4951
5240
|
this.agentRuntimeManager = new AgentRuntimeManager({
|
|
4952
5241
|
bus: this.messageBus,
|
|
4953
5242
|
providerManager: this.llmProviders,
|
|
@@ -4964,30 +5253,34 @@ var NextclawKernel = class {
|
|
|
4964
5253
|
llmUsage: this.llmUsage,
|
|
4965
5254
|
onSessionUpdated: this.publishSessionUpdated
|
|
4966
5255
|
});
|
|
5256
|
+
this.ncpSessionApi = new NcpSessionApiService({
|
|
5257
|
+
eventBus: this.eventBus,
|
|
5258
|
+
getConfig: this.configManager.loadConfig,
|
|
5259
|
+
isLiveSessionRunning: this.agentRuntimeManager.isLiveSessionRunning,
|
|
5260
|
+
ncpAgentSessionStore: this.ncpAgentSessionStore,
|
|
5261
|
+
sessionManager: this.sessions
|
|
5262
|
+
});
|
|
4967
5263
|
this.learningLoop = new LearningLoopManager({
|
|
4968
5264
|
eventBus: this.eventBus,
|
|
4969
5265
|
sessionManager: this.sessions,
|
|
4970
5266
|
sessionRequester: this.sessionRequests,
|
|
4971
5267
|
resolveLearningLoopConfig: () => readLearningLoopRuntimeConfig(this.configManager.loadConfig())
|
|
4972
5268
|
});
|
|
5269
|
+
this.contributions = [new SessionActivityPreviewContribution(this), new SessionContextWindowContribution(this)];
|
|
4973
5270
|
}
|
|
4974
5271
|
start = async () => {
|
|
4975
|
-
this.
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
this.startPromise = null;
|
|
4981
|
-
throw error;
|
|
4982
|
-
});
|
|
4983
|
-
return this.startPromise;
|
|
5272
|
+
this.ncpSessionApi.start();
|
|
5273
|
+
this.sessionSearch.start();
|
|
5274
|
+
for (const contribution of this.contributions) contribution.start();
|
|
5275
|
+
this.agentRuntimeManager.bootstrap();
|
|
5276
|
+
this.learningLoop.start();
|
|
4984
5277
|
};
|
|
4985
5278
|
dispose = async () => {
|
|
4986
5279
|
this.learningLoop.dispose();
|
|
5280
|
+
for (const contribution of this.contributions) contribution.dispose();
|
|
4987
5281
|
this.ncpSessionApi.dispose();
|
|
4988
5282
|
await this.agentRuntimeManager.dispose();
|
|
4989
5283
|
await this.sessionSearch.dispose();
|
|
4990
|
-
this.startPromise = null;
|
|
4991
5284
|
};
|
|
4992
5285
|
publishSessionUpdated = (sessionKey) => {
|
|
4993
5286
|
this.sessionLifecycleEvents.publishSessionUpdated(sessionKey);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/kernel",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "NextClaw product kernel skeleton for agents, tasks, sessions, context, tools, skills, providers, automation, and channels.",
|
|
6
6
|
"type": "module",
|
|
@@ -20,19 +20,19 @@
|
|
|
20
20
|
"dist"
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@nextclaw/core": "0.12.
|
|
24
|
-
"@nextclaw/ncp-http-agent-server": "0.3.
|
|
25
|
-
"@nextclaw/mcp": "0.1.
|
|
26
|
-
"@nextclaw/ncp-agent-runtime": "0.3.
|
|
27
|
-
"@nextclaw/ncp-mcp": "0.1.
|
|
28
|
-
"@nextclaw/ncp-toolkit": "0.5.
|
|
29
|
-
"@nextclaw/nextclaw-
|
|
30
|
-
"@nextclaw/nextclaw-ncp-runtime-stdio-client": "0.1.
|
|
31
|
-
"@nextclaw/
|
|
32
|
-
"@nextclaw/openclaw-compat": "1.0.
|
|
33
|
-
"@nextclaw/
|
|
34
|
-
"@nextclaw/
|
|
35
|
-
"@nextclaw/ncp": "0.
|
|
23
|
+
"@nextclaw/core": "0.12.18",
|
|
24
|
+
"@nextclaw/ncp-http-agent-server": "0.3.23",
|
|
25
|
+
"@nextclaw/mcp": "0.1.83",
|
|
26
|
+
"@nextclaw/ncp-agent-runtime": "0.3.21",
|
|
27
|
+
"@nextclaw/ncp-mcp": "0.1.85",
|
|
28
|
+
"@nextclaw/ncp-toolkit": "0.5.16",
|
|
29
|
+
"@nextclaw/nextclaw-hermes-acp-bridge": "0.1.10",
|
|
30
|
+
"@nextclaw/nextclaw-ncp-runtime-stdio-client": "0.1.11",
|
|
31
|
+
"@nextclaw/ncp": "0.5.11",
|
|
32
|
+
"@nextclaw/openclaw-compat": "1.0.18",
|
|
33
|
+
"@nextclaw/runtime": "0.2.50",
|
|
34
|
+
"@nextclaw/shared": "0.1.5",
|
|
35
|
+
"@nextclaw/nextclaw-ncp-runtime-http-client": "0.1.10"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^20.17.6",
|