@nextclaw/kernel 0.1.5 → 0.1.6

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 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 startPromise;
728
+ private readonly contributions;
726
729
  constructor(options?: NextclawKernelOptions);
727
730
  start: () => Promise<void>;
728
731
  dispose: () => Promise<void>;
package/dist/index.js CHANGED
@@ -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$4(value) {
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$4(value) ? structuredClone(value) : void 0;
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$3(value) {
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$3(args)) throw new Error("session_search requires an object argument.");
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$3(value) {
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$3(value);
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$3(args.path);
2000
- const bytesBase64 = readOptionalString$3(args.bytesBase64);
2001
- const fileName = readOptionalString$3(args.fileName);
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$3(args?.path);
2009
- const fileName = readOptionalString$3(args?.fileName);
2010
- const mimeType = readOptionalString$3(args?.mimeType);
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$3(args?.assetUri);
2054
- const targetPath = readOptionalString$3(args?.targetPath);
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$3(args?.assetUri);
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$2(params, key) {
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$2(params, "notify")?.toLowerCase();
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$2(params, "title"),
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$1(value) {
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$1(value)?.toLowerCase();
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$1(value.notify)?.toLowerCase();
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$1(rawTitle),
2433
- agentId: readOptionalString$1(rawAgentId),
2434
- model: readOptionalString$1(rawModel),
2435
- runtime: readOptionalString$1(rawRuntime),
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$1(rawTitle),
2443
+ title: readOptionalString$2(rawTitle),
2444
2444
  sourceSessionMetadata: this.sourceSessionMetadata,
2445
- agentId: readOptionalString$1(rawAgentId),
2446
- model: readOptionalString$1(rawModel),
2447
- runtime: readOptionalString$1(rawRuntime),
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$4(args)) return args;
2472
+ if (isRecord$5(args)) return args;
2473
2473
  if (typeof args === "string") try {
2474
2474
  const parsed = JSON.parse(args);
2475
- return isRecord$4(parsed) ? parsed : {};
2475
+ return isRecord$5(parsed) ? parsed : {};
2476
2476
  } catch {
2477
2477
  return {};
2478
2478
  }
@@ -2664,14 +2664,14 @@ function readAccountIdForHints(metadata, sessionMetadata) {
2664
2664
  //#endregion
2665
2665
  //#region src/features/native-runtime/services/nextclaw-ncp-context-builder.service.ts
2666
2666
  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$2(value) {
2667
+ function isRecord$3(value) {
2668
2668
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2669
2669
  }
2670
2670
  function mergeInputMetadata(input) {
2671
- const messageMetadata = input.messages.slice().reverse().find((message) => isRecord$2(message.metadata))?.metadata;
2671
+ const messageMetadata = input.messages.slice().reverse().find((message) => isRecord$3(message.metadata))?.metadata;
2672
2672
  return {
2673
- ...isRecord$2(messageMetadata) ? structuredClone(messageMetadata) : {},
2674
- ...isRecord$2(input.metadata) ? structuredClone(input.metadata) : {}
2673
+ ...isRecord$3(messageMetadata) ? structuredClone(messageMetadata) : {},
2674
+ ...isRecord$3(input.metadata) ? structuredClone(input.metadata) : {}
2675
2675
  };
2676
2676
  }
2677
2677
  const REQUESTED_SKILLS_METADATA_READER = new RequestedSkillsMetadataReader();
@@ -2978,12 +2978,12 @@ function createContextWindowUpdatedEvent(params) {
2978
2978
  }
2979
2979
  };
2980
2980
  }
2981
- function isRecord$1(value) {
2981
+ function isRecord$2(value) {
2982
2982
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2983
2983
  }
2984
2984
  function resolveNativeReasoningNormalizationMode(params) {
2985
2985
  const runtimeEntry = params.config.agents.runtimes.entries.native?.config ?? params.config.ui.ncp.runtimes.native;
2986
- const runtimeMetadata = isRecord$1(runtimeEntry) ? runtimeEntry : {};
2986
+ const runtimeMetadata = isRecord$2(runtimeEntry) ? runtimeEntry : {};
2987
2987
  return readAssistantReasoningNormalizationModeFromMetadata(params.sessionMetadata) ?? readAssistantReasoningNormalizationMode(runtimeMetadata.reasoningNormalization) ?? readAssistantReasoningNormalizationMode(runtimeMetadata.reasoning_normalization) ?? readAssistantReasoningNormalizationMode(runtimeMetadata.reasoningNormalizationMode) ?? readAssistantReasoningNormalizationMode(runtimeMetadata.reasoning_normalization_mode) ?? "think-tags";
2988
2988
  }
2989
2989
  function createSessionSearchTools(params) {
@@ -3240,7 +3240,7 @@ function createAgentRuntimeSessionRequestDispatcher(options) {
3240
3240
  }
3241
3241
  //#endregion
3242
3242
  //#region src/managers/agent-runtime.manager.ts
3243
- function readOptionalString(value) {
3243
+ function readOptionalString$1(value) {
3244
3244
  if (typeof value !== "string") return null;
3245
3245
  const trimmed = value.trim();
3246
3246
  return trimmed.length > 0 ? trimmed : null;
@@ -3251,7 +3251,7 @@ function readMessageTask(message) {
3251
3251
  }
3252
3252
  function readSessionMetadataString(metadata, ...keys) {
3253
3253
  for (const key of keys) {
3254
- const value = readOptionalString(metadata?.[key]);
3254
+ const value = readOptionalString$1(metadata?.[key]);
3255
3255
  if (value) return value;
3256
3256
  }
3257
3257
  }
@@ -3286,6 +3286,7 @@ var AgentRuntimeManager = class {
3286
3286
  get currentHandle() {
3287
3287
  return this.handle;
3288
3288
  }
3289
+ isLiveSessionRunning = (sessionId) => this.backend?.isLiveSessionRunning(sessionId) ?? false;
3289
3290
  connectGatewayController = (gatewayController) => {
3290
3291
  this.assertNotDisposed();
3291
3292
  this.gatewayController = gatewayController;
@@ -3350,7 +3351,7 @@ var AgentRuntimeManager = class {
3350
3351
  return this.handle;
3351
3352
  };
3352
3353
  materializeAgentSendEnvelope = (envelope) => {
3353
- const existingSessionId = readOptionalString(envelope.sessionId) ?? readOptionalString(envelope.message.sessionId);
3354
+ const existingSessionId = readOptionalString$1(envelope.sessionId) ?? readOptionalString$1(envelope.message.sessionId);
3354
3355
  if (existingSessionId) return {
3355
3356
  ...envelope,
3356
3357
  sessionId: existingSessionId,
@@ -4182,13 +4183,6 @@ function buildUpdatedMetadata(params) {
4182
4183
  function normalizeSessionId(sessionId) {
4183
4184
  return sessionId.trim();
4184
4185
  }
4185
- function toSessionCandidate(record) {
4186
- const sessionId = normalizeSessionId(record.key);
4187
- return sessionId ? {
4188
- ...record,
4189
- sessionId
4190
- } : null;
4191
- }
4192
4186
  function createSessionListSummary(record) {
4193
4187
  return {
4194
4188
  sessionId: record.sessionId,
@@ -4201,9 +4195,6 @@ function createSessionListSummary(record) {
4201
4195
  metadata: structuredClone(record.metadata)
4202
4196
  };
4203
4197
  }
4204
- function readSessionActivityAt$1(record) {
4205
- return record.lastMessageAt ?? record.created_at;
4206
- }
4207
4198
  var NcpSessionApiService = class {
4208
4199
  unsubscribeSessionUpdated = null;
4209
4200
  contextWindowPreview;
@@ -4237,8 +4228,11 @@ var NcpSessionApiService = class {
4237
4228
  this.options.eventBus.emit(eventKeys.sessionSummaryDelete, { sessionKey: normalizedSessionKey });
4238
4229
  };
4239
4230
  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(toSessionCandidate).filter((record) => Boolean(record)).sort((left, right) => readSessionActivityAt$1(right).localeCompare(readSessionActivityAt$1(left))), options?.limit).map(createSessionListSummary);
4231
+ if (this.options.ncpAgentSessionStore?.listSessionSummaries) return applyLimit(await this.options.ncpAgentSessionStore.listSessionSummaries(), options?.limit).map(this.withLiveSessionStatus);
4232
+ return applyLimit(this.options.sessionManager.listSessions().map((record) => ({
4233
+ ...record,
4234
+ sessionId: normalizeSessionId(record.key)
4235
+ })).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
4236
  };
4243
4237
  listSessionMessages = async (sessionId, options) => {
4244
4238
  const normalizedSessionId = normalizeSessionId(sessionId);
@@ -4251,7 +4245,7 @@ var NcpSessionApiService = class {
4251
4245
  const normalizedSessionId = normalizeSessionId(sessionId);
4252
4246
  if (normalizedSessionId && this.options.ncpAgentSessionStore?.getSession) {
4253
4247
  const record = await this.options.ncpAgentSessionStore.getSession(normalizedSessionId);
4254
- if (record) return createNcpSessionSummary({
4248
+ if (record) return this.withLiveSessionStatus(createNcpSessionSummary({
4255
4249
  sessionId: normalizedSessionId,
4256
4250
  agentId: record.agentId,
4257
4251
  messages: record.messages,
@@ -4265,12 +4259,12 @@ var NcpSessionApiService = class {
4265
4259
  sessionId: normalizedSessionId,
4266
4260
  sessionMessages: record.messages
4267
4261
  })
4268
- });
4262
+ }));
4269
4263
  }
4270
4264
  const session = normalizedSessionId ? this.options.sessionManager.getIfExists(normalizedSessionId) : null;
4271
4265
  if (!session) return null;
4272
4266
  const messages = toNcpMessages(normalizedSessionId, session.messages);
4273
- return createNcpSessionSummary({
4267
+ return this.withLiveSessionStatus(createNcpSessionSummary({
4274
4268
  sessionId: normalizedSessionId,
4275
4269
  agentId: session.agentId,
4276
4270
  messages,
@@ -4284,7 +4278,7 @@ var NcpSessionApiService = class {
4284
4278
  sessionId: normalizedSessionId,
4285
4279
  sessionMessages: messages
4286
4280
  })
4287
- });
4281
+ }));
4288
4282
  };
4289
4283
  updateSession = async (sessionId, patch) => {
4290
4284
  const normalizedSessionId = normalizeSessionId(sessionId);
@@ -4321,6 +4315,10 @@ var NcpSessionApiService = class {
4321
4315
  this.options.sessionManager.delete(normalizedSessionId);
4322
4316
  await this.publishSessionChange(normalizedSessionId);
4323
4317
  };
4318
+ withLiveSessionStatus = (summary) => this.options.isLiveSessionRunning?.(summary.sessionId) ? {
4319
+ ...summary,
4320
+ status: "running"
4321
+ } : summary;
4324
4322
  };
4325
4323
  //#endregion
4326
4324
  //#region src/utils/ncp-session-metadata.utils.ts
@@ -4505,7 +4503,7 @@ function normalizeNcpAgentId(agentId) {
4505
4503
  const normalized = agentId?.trim().toLowerCase();
4506
4504
  return normalized ? normalized : void 0;
4507
4505
  }
4508
- function isRecord(value) {
4506
+ function isRecord$1(value) {
4509
4507
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4510
4508
  }
4511
4509
  function toIsoString(value, fallback) {
@@ -4768,15 +4766,15 @@ var NcpAgentSessionJournalStore = class {
4768
4766
  for (const line of raw.split("\n")) {
4769
4767
  if (!line.trim()) continue;
4770
4768
  const parsed = JSON.parse(line);
4771
- if (!isRecord(parsed)) continue;
4769
+ if (!isRecord$1(parsed)) continue;
4772
4770
  if (parsed._type === "metadata") {
4773
- metadata = isRecord(parsed.metadata) ? structuredClone(parsed.metadata) : {};
4771
+ metadata = isRecord$1(parsed.metadata) ? structuredClone(parsed.metadata) : {};
4774
4772
  agentId = normalizeNcpAgentId(typeof parsed.agent_id === "string" ? parsed.agent_id : void 0);
4775
4773
  createdAt = toIsoString(parsed.created_at, createdAt);
4776
4774
  updatedAt = toIsoString(parsed.updated_at, updatedAt);
4777
4775
  continue;
4778
4776
  }
4779
- if (parsed._type === "event" && isRecord(parsed.event)) {
4777
+ if (parsed._type === "event" && isRecord$1(parsed.event)) {
4780
4778
  const seq = Number(parsed.seq);
4781
4779
  nextSeq = Math.max(nextSeq, Number.isFinite(seq) ? Math.trunc(seq) + 1 : nextSeq);
4782
4780
  updatedAt = toIsoString(parsed.timestamp, updatedAt);
@@ -4860,6 +4858,196 @@ var NcpAgentSessionJournalStore = class {
4860
4858
  indexPath = () => resolve(this.journalDir, NCP_AGENT_SESSION_JOURNAL_INDEX_FILE);
4861
4859
  };
4862
4860
  //#endregion
4861
+ //#region src/contributions/session-activity-preview/utils/session-activity-preview-ncp-event.utils.ts
4862
+ const PREVIEW_TEXT_MAX_LENGTH = 160;
4863
+ function readSessionId(value) {
4864
+ if (typeof value !== "string") return null;
4865
+ const trimmed = value.trim();
4866
+ return trimmed.length > 0 ? trimmed : null;
4867
+ }
4868
+ function compactPreviewText(value) {
4869
+ return value.replace(/\s+/g, " ").trim();
4870
+ }
4871
+ function truncatePreviewText(value) {
4872
+ const compacted = compactPreviewText(value);
4873
+ if (compacted.length <= PREVIEW_TEXT_MAX_LENGTH) return compacted;
4874
+ return compacted.slice(0, PREVIEW_TEXT_MAX_LENGTH - 1).trimEnd();
4875
+ }
4876
+ function readMessagePreviewText(message) {
4877
+ const chunks = [];
4878
+ for (const part of message.parts) if ((part.type === "text" || part.type === "rich-text") && part.text.trim()) chunks.push(part.text);
4879
+ const previewText = truncatePreviewText(chunks.join(" "));
4880
+ return previewText.length > 0 ? previewText : null;
4881
+ }
4882
+ function createProjection(sessionId, preview) {
4883
+ if (!sessionId) return null;
4884
+ return {
4885
+ sessionId,
4886
+ preview
4887
+ };
4888
+ }
4889
+ function formatErrorStatus(error) {
4890
+ if (typeof error === "string" && error.trim()) return `运行出错:${truncatePreviewText(error)}`;
4891
+ if (error && typeof error === "object" && "message" in error) {
4892
+ const message = error.message;
4893
+ if (typeof message === "string" && message.trim()) return `运行出错:${truncatePreviewText(message)}`;
4894
+ }
4895
+ return "运行出错";
4896
+ }
4897
+ function createSessionActivityPreviewFromNcpEvent(event, timestamp) {
4898
+ switch (event.type) {
4899
+ case NcpEventType.RunStarted: return createProjection(readSessionId(event.payload.sessionId), {
4900
+ state: "running",
4901
+ statusText: "正在处理...",
4902
+ timestamp
4903
+ });
4904
+ case NcpEventType.RunFinished: return createProjection(readSessionId(event.payload.sessionId), {
4905
+ state: "completed",
4906
+ timestamp
4907
+ });
4908
+ case NcpEventType.RunError: return createProjection(readSessionId(event.payload.sessionId), {
4909
+ state: "failed",
4910
+ statusText: formatErrorStatus(event.payload.error),
4911
+ timestamp
4912
+ });
4913
+ case NcpEventType.MessageSent: {
4914
+ const text = readMessagePreviewText(event.payload.message);
4915
+ if (!text || event.payload.message.role !== "user") return null;
4916
+ return createProjection(readSessionId(event.payload.sessionId), {
4917
+ state: "running",
4918
+ statusText: text,
4919
+ timestamp: event.payload.message.timestamp || timestamp
4920
+ });
4921
+ }
4922
+ case NcpEventType.MessageCompleted: {
4923
+ const text = readMessagePreviewText(event.payload.message);
4924
+ if (!text || event.payload.message.role !== "assistant") return null;
4925
+ return createProjection(readSessionId(event.payload.sessionId), {
4926
+ state: "completed",
4927
+ replyText: text,
4928
+ timestamp: event.payload.message.timestamp || timestamp
4929
+ });
4930
+ }
4931
+ case NcpEventType.MessageFailed: return createProjection(readSessionId(event.payload.sessionId), {
4932
+ state: "failed",
4933
+ statusText: formatErrorStatus(event.payload.error),
4934
+ timestamp
4935
+ });
4936
+ case NcpEventType.MessageToolCallStart: return createProjection(readSessionId(event.payload.sessionId), {
4937
+ state: "running",
4938
+ statusText: `正在调用工具:${event.payload.toolName}`,
4939
+ timestamp
4940
+ });
4941
+ case NcpEventType.MessageToolCallEnd:
4942
+ case NcpEventType.MessageToolCallResult: return createProjection(readSessionId(event.payload.sessionId), {
4943
+ state: "running",
4944
+ statusText: "工具调用完成",
4945
+ timestamp
4946
+ });
4947
+ default: return null;
4948
+ }
4949
+ }
4950
+ //#endregion
4951
+ //#region src/contributions/session-activity-preview/utils/session-activity-preview-metadata.utils.ts
4952
+ const SESSION_ACTIVITY_PREVIEW_METADATA_KEY = "last_activity_preview";
4953
+ const SESSION_ACTIVITY_PREVIEW_STATES = new Set([
4954
+ "running",
4955
+ "completed",
4956
+ "failed",
4957
+ "idle"
4958
+ ]);
4959
+ function isRecord(value) {
4960
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4961
+ }
4962
+ function readOptionalString(value) {
4963
+ if (typeof value !== "string") return;
4964
+ const trimmed = value.trim();
4965
+ return trimmed.length > 0 ? trimmed : void 0;
4966
+ }
4967
+ function readSessionActivityPreviewMetadata(value) {
4968
+ if (!isRecord(value)) return null;
4969
+ const state = value.state;
4970
+ const timestamp = readOptionalString(value.timestamp);
4971
+ if (!SESSION_ACTIVITY_PREVIEW_STATES.has(state) || !timestamp) return null;
4972
+ return {
4973
+ state,
4974
+ timestamp,
4975
+ ...readOptionalString(value.statusText) ? { statusText: readOptionalString(value.statusText) } : {},
4976
+ ...readOptionalString(value.replyText) ? { replyText: readOptionalString(value.replyText) } : {}
4977
+ };
4978
+ }
4979
+ function compareIsoTimestamp(left, right) {
4980
+ const leftTime = Date.parse(left);
4981
+ const rightTime = Date.parse(right);
4982
+ if (!Number.isFinite(leftTime) || !Number.isFinite(rightTime)) return left.localeCompare(right);
4983
+ return leftTime - rightTime;
4984
+ }
4985
+ function mergeSessionActivityPreview(current, incoming) {
4986
+ if (current && compareIsoTimestamp(incoming.timestamp, current.timestamp) < 0) return current;
4987
+ return {
4988
+ state: incoming.state,
4989
+ timestamp: incoming.timestamp,
4990
+ ...incoming.statusText ?? (incoming.state === "completed" ? current?.statusText : void 0) ? { statusText: incoming.statusText ?? current?.statusText } : {},
4991
+ ...incoming.replyText ?? current?.replyText ? { replyText: incoming.replyText ?? current?.replyText } : {}
4992
+ };
4993
+ }
4994
+ function areSessionActivityPreviewsEqual(left, right) {
4995
+ return Boolean(left) && left?.state === right.state && left.timestamp === right.timestamp && left.statusText === right.statusText && left.replyText === right.replyText;
4996
+ }
4997
+ function writeSessionActivityPreviewMetadata(metadata, projection) {
4998
+ const current = readSessionActivityPreviewMetadata(metadata?.[SESSION_ACTIVITY_PREVIEW_METADATA_KEY]);
4999
+ const next = mergeSessionActivityPreview(current, projection.preview);
5000
+ if (areSessionActivityPreviewsEqual(current, next)) return null;
5001
+ return {
5002
+ ...metadata ?? {},
5003
+ [SESSION_ACTIVITY_PREVIEW_METADATA_KEY]: next
5004
+ };
5005
+ }
5006
+ //#endregion
5007
+ //#region src/contributions/session-activity-preview/index.ts
5008
+ function formatBackgroundError(error) {
5009
+ if (error instanceof Error) return error.stack ?? error.message;
5010
+ return String(error);
5011
+ }
5012
+ function readMetadata(value) {
5013
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
5014
+ return value;
5015
+ }
5016
+ var SessionActivityPreviewContribution = class {
5017
+ unsubscribeNcpEvent = null;
5018
+ stopped = true;
5019
+ constructor(kernel) {
5020
+ this.kernel = kernel;
5021
+ }
5022
+ start = () => {
5023
+ if (this.unsubscribeNcpEvent) return;
5024
+ this.stopped = false;
5025
+ this.unsubscribeNcpEvent = this.kernel.eventBus.on(eventKeys.ncpEvent, (event) => {
5026
+ const projection = createSessionActivityPreviewFromNcpEvent(event, (/* @__PURE__ */ new Date()).toISOString());
5027
+ if (!projection) return;
5028
+ this.updatePreview(projection);
5029
+ });
5030
+ };
5031
+ dispose = () => {
5032
+ this.unsubscribeNcpEvent?.();
5033
+ this.unsubscribeNcpEvent = null;
5034
+ this.stopped = true;
5035
+ };
5036
+ updatePreview = (projection) => {
5037
+ this.persistPreview(projection).catch((error) => {
5038
+ console.error(`[session-activity-preview] failed to update ${projection.sessionId}: ${formatBackgroundError(error)}`);
5039
+ });
5040
+ };
5041
+ persistPreview = async (projection) => {
5042
+ if (this.stopped) return;
5043
+ const summary = await this.kernel.ncpSessionApi.getSession(projection.sessionId);
5044
+ if (!summary || this.stopped) return;
5045
+ const nextMetadata = writeSessionActivityPreviewMetadata(readMetadata(summary.metadata), projection);
5046
+ if (!nextMetadata) return;
5047
+ await this.kernel.ncpSessionApi.updateSession(projection.sessionId, { metadata: nextMetadata });
5048
+ };
5049
+ };
5050
+ //#endregion
4863
5051
  //#region src/app/nextclaw-kernel.ts
4864
5052
  function resolveKernelSessionsDir(options) {
4865
5053
  const homeDir = options.homeDir?.trim();
@@ -4904,7 +5092,7 @@ var NextclawKernel = class {
4904
5092
  sessionLifecycleEvents;
4905
5093
  ncpAgentSessionStore;
4906
5094
  ncpAgentSessionJournalStore;
4907
- startPromise = null;
5095
+ contributions;
4908
5096
  constructor(options = {}) {
4909
5097
  const sessionsDir = resolveKernelSessionsDir(options);
4910
5098
  this.sessions = new SessionManager({ sessionsDir });
@@ -4942,12 +5130,6 @@ var NextclawKernel = class {
4942
5130
  channels: this.channels,
4943
5131
  providerManager: this.llmProviders
4944
5132
  });
4945
- this.ncpSessionApi = new NcpSessionApiService({
4946
- eventBus: this.eventBus,
4947
- getConfig: this.configManager.loadConfig,
4948
- ncpAgentSessionStore: this.ncpAgentSessionStore,
4949
- sessionManager: this.sessions
4950
- });
4951
5133
  this.agentRuntimeManager = new AgentRuntimeManager({
4952
5134
  bus: this.messageBus,
4953
5135
  providerManager: this.llmProviders,
@@ -4964,30 +5146,34 @@ var NextclawKernel = class {
4964
5146
  llmUsage: this.llmUsage,
4965
5147
  onSessionUpdated: this.publishSessionUpdated
4966
5148
  });
5149
+ this.ncpSessionApi = new NcpSessionApiService({
5150
+ eventBus: this.eventBus,
5151
+ getConfig: this.configManager.loadConfig,
5152
+ isLiveSessionRunning: this.agentRuntimeManager.isLiveSessionRunning,
5153
+ ncpAgentSessionStore: this.ncpAgentSessionStore,
5154
+ sessionManager: this.sessions
5155
+ });
4967
5156
  this.learningLoop = new LearningLoopManager({
4968
5157
  eventBus: this.eventBus,
4969
5158
  sessionManager: this.sessions,
4970
5159
  sessionRequester: this.sessionRequests,
4971
5160
  resolveLearningLoopConfig: () => readLearningLoopRuntimeConfig(this.configManager.loadConfig())
4972
5161
  });
5162
+ this.contributions = [new SessionActivityPreviewContribution(this)];
4973
5163
  }
4974
5164
  start = async () => {
4975
- this.startPromise ??= Promise.resolve().then(() => {
4976
- this.ncpSessionApi.start();
4977
- }).then(() => this.sessionSearch.start()).then(() => this.agentRuntimeManager.bootstrap()).then(() => {
4978
- this.learningLoop.start();
4979
- }).catch((error) => {
4980
- this.startPromise = null;
4981
- throw error;
4982
- });
4983
- return this.startPromise;
5165
+ this.ncpSessionApi.start();
5166
+ this.sessionSearch.start();
5167
+ for (const contribution of this.contributions) contribution.start();
5168
+ this.agentRuntimeManager.bootstrap();
5169
+ this.learningLoop.start();
4984
5170
  };
4985
5171
  dispose = async () => {
4986
5172
  this.learningLoop.dispose();
5173
+ for (const contribution of this.contributions) contribution.dispose();
4987
5174
  this.ncpSessionApi.dispose();
4988
5175
  await this.agentRuntimeManager.dispose();
4989
5176
  await this.sessionSearch.dispose();
4990
- this.startPromise = null;
4991
5177
  };
4992
5178
  publishSessionUpdated = (sessionKey) => {
4993
5179
  this.sessionLifecycleEvents.publishSessionUpdated(sessionKey);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/kernel",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
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.16",
24
- "@nextclaw/ncp-http-agent-server": "0.3.21",
25
- "@nextclaw/mcp": "0.1.81",
26
- "@nextclaw/ncp-agent-runtime": "0.3.19",
27
- "@nextclaw/ncp-mcp": "0.1.83",
28
- "@nextclaw/ncp-toolkit": "0.5.14",
29
- "@nextclaw/nextclaw-ncp-runtime-http-client": "0.1.8",
30
- "@nextclaw/nextclaw-ncp-runtime-stdio-client": "0.1.9",
31
- "@nextclaw/nextclaw-hermes-acp-bridge": "0.1.8",
32
- "@nextclaw/openclaw-compat": "1.0.16",
33
- "@nextclaw/shared": "0.1.3",
34
- "@nextclaw/runtime": "0.2.48",
35
- "@nextclaw/ncp": "0.5.9"
23
+ "@nextclaw/core": "0.12.17",
24
+ "@nextclaw/mcp": "0.1.82",
25
+ "@nextclaw/ncp-agent-runtime": "0.3.20",
26
+ "@nextclaw/ncp-http-agent-server": "0.3.22",
27
+ "@nextclaw/nextclaw-hermes-acp-bridge": "0.1.9",
28
+ "@nextclaw/ncp-toolkit": "0.5.15",
29
+ "@nextclaw/nextclaw-ncp-runtime-http-client": "0.1.9",
30
+ "@nextclaw/openclaw-compat": "1.0.17",
31
+ "@nextclaw/runtime": "0.2.49",
32
+ "@nextclaw/nextclaw-ncp-runtime-stdio-client": "0.1.10",
33
+ "@nextclaw/shared": "0.1.4",
34
+ "@nextclaw/ncp": "0.5.10",
35
+ "@nextclaw/ncp-mcp": "0.1.84"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "^20.17.6",