@nextclaw/kernel 0.3.4-beta.0 → 0.4.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
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
3
  import { NcpEventType, sanitizeAssistantReplyTags } from "@nextclaw/ncp";
4
- import { AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, CLEAR_THINKING_TOKENS, CONTEXT_COMPACTION_METADATA_KEY, ChannelManager, ChannelManager as ChannelManager$1, ConfigSchema, ContextBuilder, ContextCompactionService, ContextWindowBudgetService, CronService, CronTool, DEFAULT_PANELS_DIR, DEFAULT_SERVICE_APPS_DIR, DEFAULT_SESSION_SEARCH_LIMIT, EditFileTool, ExecTool, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, LiteLLMProvider, MAX_SESSION_SEARCH_LIMIT, MemoryGetTool, MemorySearchTool, MessageBus, MessageTool, ProviderRegistry, ReadFileTool, RequestedSkillsMetadataReader, SessionSearchManager, SkillsLoader, THINKING_LEVELS, WebFetchTool, WebSearchTool, WriteFileTool, buildCompressingCompactionCheckpoint, buildConfigSchema, buildContextWindowSnapshot, buildMinimalSystemExecutionPrompt, buildReloadPlan, buildSessionRequestToolResult, buildToolCatalogEntries, createAssistantStreamDeltaControlMessage, createAssistantStreamResetControlMessage, createCompletedSessionRequest, createFailedSessionRequest, createRunningSessionRequest, createTypingStopControlMessage, diffConfigPaths, ensureDir, expandHome, findEffectiveAgentProfile, getConfigPath, getDataDir, getDataPath, getSessionsPath, getWorkspacePath, getWorkspacePathFromConfig, loadConfig, mergeExtensionConfigView, modelSupportsVision, normalizeInlineSecretRefs, normalizeProviderModelConfig, normalizeToolParams, parseAgentScopedSessionKey, parseThinkingLevel, readCompressedContextCompactionCheckpoint, readOptionalString, readParentSessionId, readSessionProjectRoot, redactConfigObject, resolveConfigSecrets, resolveDefaultAgentProfileId, resolveProviderRuntime, resolveSessionWorkspacePath, resolveThinkingLevel, saveConfig, summarizeSessionRequestTask, toDisposable, toExtensionConfigView } from "@nextclaw/core";
4
+ import { AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, CLEAR_THINKING_TOKENS, CONTEXT_COMPACTION_METADATA_KEY, ChannelManager, ChannelManager as ChannelManager$1, ConfigSchema, ContextBuilder, ContextCompactionService, ContextWindowBudgetService, CronService, CronTool, DEFAULT_PANELS_DIR, DEFAULT_SERVICE_APPS_DIR, DEFAULT_SESSION_SEARCH_LIMIT, EditFileTool, ExecTool, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, LiteLLMProvider, MAX_SESSION_SEARCH_LIMIT, MemoryGetTool, MemorySearchTool, MessageBus, MessageTool, ProviderRegistry, ReadFileTool, RequestedSkillsMetadataReader, SessionSearchManager, SkillsLoader, THINKING_LEVELS, WebFetchTool, WebSearchTool, WriteFileTool, buildCompressingCompactionCheckpoint, buildConfigSchema, buildContextWindowSnapshot, buildMinimalSystemExecutionPrompt, buildReloadPlan, buildSessionRequestToolResult, buildToolCatalogEntries, createAssistantStreamDeltaControlMessage, createAssistantStreamResetControlMessage, createCompletedSessionRequest, createFailedSessionRequest, createRunningSessionRequest, createRuntimeChildEnv, createTypingStopControlMessage, diffConfigPaths, ensureDir, expandHome, findEffectiveAgentProfile, getConfigPath, getDataDir, getDataPath, getSessionsPath, getWorkspacePath, getWorkspacePathFromConfig, loadConfig, mergeExtensionConfigView, modelSupportsVision, normalizeInlineSecretRefs, normalizeProviderModelConfig, normalizeToolParams, parseAgentScopedSessionKey, parseThinkingLevel, readCompressedContextCompactionCheckpoint, readOptionalString, readParentSessionId, readSessionProjectRoot, redactConfigObject, resolveConfigSecrets, resolveDefaultAgentProfileId, resolveProviderRuntime, resolveSessionWorkspacePath, resolveThinkingLevel, saveConfig, summarizeSessionRequestTask, toDisposable, toExtensionConfigView } from "@nextclaw/core";
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 } from "@nextclaw/shared";
@@ -148,6 +148,12 @@ function toLegacyMessages(messages, options = {}) {
148
148
  //#region src/features/context-compaction/utils/context-compaction-timeline-message.utils.ts
149
149
  const NEXTCLAW_TIMELINE_KIND_METADATA_KEY = "nextclaw_timeline_kind";
150
150
  const CONTEXT_COMPACTION_TIMELINE_KIND = "context_compaction";
151
+ function readCheckpointTimelineText(checkpoint) {
152
+ return checkpoint.status === "compressing" ? "正在压缩较早上下文" : "较早上下文已自动压缩";
153
+ }
154
+ function createContextCompactionMessageId() {
155
+ return `context-compaction-message-${randomUUID()}`;
156
+ }
151
157
  function readTimelineMetadata(message) {
152
158
  const rawMetadata = message?.ncp_metadata;
153
159
  if (!rawMetadata || typeof rawMetadata !== "object" || Array.isArray(rawMetadata)) return null;
@@ -161,13 +167,14 @@ function readTimelineMetadata(message) {
161
167
  };
162
168
  }
163
169
  function buildTimelineMessage(checkpoint) {
170
+ const text = readCheckpointTimelineText(checkpoint);
164
171
  return {
165
172
  role: "service",
166
- content: checkpoint.status === "compressing" ? "正在压缩较早上下文" : "较早上下文已自动压缩",
173
+ content: text,
167
174
  timestamp: checkpoint.updatedAt,
168
175
  ncp_parts: [{
169
176
  type: "text",
170
- text: checkpoint.status === "compressing" ? "正在压缩较早上下文" : "较早上下文已自动压缩"
177
+ text
171
178
  }],
172
179
  ncp_metadata: {
173
180
  [NEXTCLAW_TIMELINE_KIND_METADATA_KEY]: CONTEXT_COMPACTION_TIMELINE_KIND,
@@ -176,10 +183,10 @@ function buildTimelineMessage(checkpoint) {
176
183
  };
177
184
  }
178
185
  function buildContextCompactionTimelineNcpMessage(params) {
179
- const { checkpoint, sessionId } = params;
180
- const text = checkpoint.status === "compressing" ? "正在压缩较早上下文" : "较早上下文已自动压缩";
186
+ const { checkpoint, messageId, sessionId } = params;
187
+ const text = readCheckpointTimelineText(checkpoint);
181
188
  return {
182
- id: `${sessionId}:service:context-compaction:${checkpoint.id}`,
189
+ id: messageId,
183
190
  sessionId,
184
191
  role: "service",
185
192
  status: "final",
@@ -233,34 +240,31 @@ function isContextCompactionTimelineMessage(message) {
233
240
  }
234
241
  //#endregion
235
242
  //#region src/features/context-compaction/utils/context-compaction-projection.utils.ts
236
- function readCompactionSummary(message) {
243
+ function readCompactionCheckpoint(message) {
237
244
  const metadata = message.metadata;
238
- if (metadata?.["nextclaw_timeline_kind"] !== "context_compaction") return null;
239
- return readCompressedContextCompactionCheckpoint(metadata.checkpoint)?.summary ?? null;
245
+ return metadata?.["nextclaw_timeline_kind"] === "context_compaction" ? readCompressedContextCompactionCheckpoint(metadata.checkpoint) : null;
246
+ }
247
+ function readLatestContextCompactionCheckpoint(sessionMessages) {
248
+ return sessionMessages.reduce((checkpoint, message) => {
249
+ const candidateCheckpoint = readCompactionCheckpoint(message);
250
+ return candidateCheckpoint && (!checkpoint || Date.parse(candidateCheckpoint.updatedAt) >= Date.parse(checkpoint.updatedAt)) ? candidateCheckpoint : checkpoint;
251
+ }, null);
240
252
  }
241
253
  function projectNcpMessagesWithContextCompaction(params) {
242
254
  const { sessionId, sessionMessages } = params;
243
- let checkpointIndex = -1;
244
- let summary = "";
245
- for (let index = sessionMessages.length - 1; index >= 0; index -= 1) {
246
- const candidateSummary = readCompactionSummary(sessionMessages[index]);
247
- if (!candidateSummary) continue;
248
- checkpointIndex = index;
249
- summary = candidateSummary;
250
- break;
251
- }
252
- if (checkpointIndex < 0) return sessionMessages.filter((message) => !readCompactionSummary(message)).map((message) => structuredClone(message));
255
+ const checkpoint = readLatestContextCompactionCheckpoint(sessionMessages);
256
+ if (!checkpoint) return sessionMessages.filter((message) => !readCompactionCheckpoint(message)).map((message) => structuredClone(message));
253
257
  return [{
254
- id: `${sessionId}:context-compaction-summary:${sessionMessages[checkpointIndex]?.id ?? "latest"}`,
258
+ id: `${sessionId}:context-compaction-summary:${checkpoint.id}:${checkpoint.updatedAt}`,
255
259
  sessionId,
256
260
  role: "user",
257
261
  status: "final",
258
- timestamp: sessionMessages[checkpointIndex]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
262
+ timestamp: checkpoint.updatedAt,
259
263
  parts: [{
260
264
  type: "text",
261
- text: summary
265
+ text: checkpoint.summary
262
266
  }]
263
- }, ...sessionMessages.slice(checkpointIndex + 1).filter((message) => !readCompactionSummary(message)).map((message) => structuredClone(message))];
267
+ }, ...sessionMessages.filter((message) => !readCompactionCheckpoint(message) && Date.parse(message.timestamp) > Date.parse(checkpoint.updatedAt)).sort((left, right) => Date.parse(left.timestamp) - Date.parse(right.timestamp)).map((message) => structuredClone(message))];
264
268
  }
265
269
  //#endregion
266
270
  //#region src/features/context-compaction/services/context-compaction-preflight.service.ts
@@ -311,23 +315,6 @@ function buildContextWindowSnapshotFromBudget(params) {
311
315
  compactedUsedContextTokens: checkpoint ? budget.estimatedTokens : void 0
312
316
  });
313
317
  }
314
- function buildContextWindowSnapshotForMessages(contextWindowBudgetService, params) {
315
- const { checkpoint, contextTokens, reservedContextTokens, sessionId, sessionMessages } = params;
316
- const modelCandidateMessages = sessionMessages.filter((message) => !isContextCompactionTimelineMessage(message));
317
- const messages = toLegacyMessages(checkpoint ? projectNcpMessagesWithContextCompaction({
318
- sessionId,
319
- sessionMessages
320
- }) : modelCandidateMessages);
321
- return buildContextWindowSnapshotFromBudget({
322
- budget: contextWindowBudgetService.evaluate({
323
- messages,
324
- contextTokens,
325
- reservedContextTokens
326
- }),
327
- checkpoint,
328
- totalContextTokens: contextTokens
329
- });
330
- }
331
318
  var ContextCompactionPreflightService = class {
332
319
  compactionService = new ContextCompactionService();
333
320
  contextWindowBudgetService = new ContextWindowBudgetService();
@@ -341,13 +328,19 @@ var ContextCompactionPreflightService = class {
341
328
  requestMetadata,
342
329
  storedAgentId
343
330
  });
344
- const existingCheckpoint = readCompressedContextCompactionCheckpoint(storedMetadata[CONTEXT_COMPACTION_METADATA_KEY]);
345
- return buildContextWindowSnapshotForMessages(this.contextWindowBudgetService, {
346
- checkpoint: existingCheckpoint,
347
- contextTokens: profile.contextTokens,
348
- reservedContextTokens: profile.reservedContextTokens,
331
+ const existingCheckpoint = readCompressedContextCompactionCheckpoint(storedMetadata[CONTEXT_COMPACTION_METADATA_KEY]) ?? readLatestContextCompactionCheckpoint(sessionMessages);
332
+ const projectedMessages = existingCheckpoint ? projectNcpMessagesWithContextCompaction({
349
333
  sessionId,
350
334
  sessionMessages
335
+ }) : sessionMessages.filter((message) => !isContextCompactionTimelineMessage(message));
336
+ return buildContextWindowSnapshotFromBudget({
337
+ budget: this.contextWindowBudgetService.evaluate({
338
+ messages: toLegacyMessages(projectedMessages),
339
+ contextTokens: profile.contextTokens,
340
+ reservedContextTokens: profile.reservedContextTokens
341
+ }),
342
+ checkpoint: existingCheckpoint,
343
+ totalContextTokens: profile.contextTokens
351
344
  });
352
345
  };
353
346
  begin = (params) => {
@@ -362,26 +355,27 @@ var ContextCompactionPreflightService = class {
362
355
  inputMessages,
363
356
  sessionMessages
364
357
  });
365
- const modelCandidateMessages = ncpMessages.filter((message) => !isContextCompactionTimelineMessage(message));
366
- const existingCheckpoint = readCompressedContextCompactionCheckpoint(storedMetadata[CONTEXT_COMPACTION_METADATA_KEY]);
358
+ const existingCheckpoint = readCompressedContextCompactionCheckpoint(storedMetadata[CONTEXT_COMPACTION_METADATA_KEY]) ?? readLatestContextCompactionCheckpoint(ncpMessages);
367
359
  const messages = toLegacyMessages(existingCheckpoint ? projectNcpMessagesWithContextCompaction({
368
360
  sessionId,
369
361
  sessionMessages: ncpMessages
370
- }) : modelCandidateMessages);
362
+ }) : ncpMessages.filter((message) => !isContextCompactionTimelineMessage(message)));
371
363
  const budget = this.contextWindowBudgetService.evaluate({
372
364
  messages,
373
365
  contextTokens,
374
366
  reservedContextTokens
375
367
  });
376
- const plan = existingCheckpoint || !budget.shouldCompact ? null : this.compactionService.prepareForModelInput({
368
+ const plan = !budget.shouldCompact ? null : this.compactionService.prepareForModelInput({
377
369
  messages: budget.messages,
378
370
  contextTokens,
379
371
  compactionThresholdTokens: budget.triggerTokens
380
372
  });
373
+ const coveredSessionMessageCount = plan ? (existingCheckpoint?.coveredSessionMessageCount ?? 0) + plan.coveredMessages.length - (existingCheckpoint ? 1 : 0) : 0;
374
+ const serviceMessageId = createContextCompactionMessageId();
381
375
  const checkpoint = plan ? {
382
376
  ...buildCompressingCompactionCheckpoint(storedMetadata[CONTEXT_COMPACTION_METADATA_KEY]),
383
- coveredMessageCount: plan.coveredMessages.length,
384
- coveredSessionMessageCount: plan.coveredMessages.length,
377
+ coveredMessageCount: coveredSessionMessageCount,
378
+ coveredSessionMessageCount,
385
379
  originalEstimatedTokens: plan.originalEstimatedTokens,
386
380
  projectedEstimatedTokens: budget.estimatedTokens
387
381
  } : existingCheckpoint;
@@ -394,12 +388,14 @@ var ContextCompactionPreflightService = class {
394
388
  metadataPatch: plan && checkpoint ? { [CONTEXT_COMPACTION_METADATA_KEY]: checkpoint } : {},
395
389
  sessionMessages: ncpMessages,
396
390
  timelineMessage: plan && checkpoint ? buildContextCompactionTimelineNcpMessage({
391
+ messageId: serviceMessageId,
397
392
  sessionId,
398
393
  checkpoint
399
394
  }) : null,
400
395
  pendingCompaction: plan && checkpoint ? {
401
396
  checkpoint,
402
397
  contextTokens,
398
+ serviceMessageId,
403
399
  model: profile.model,
404
400
  plan,
405
401
  reservedContextTokens,
@@ -423,6 +419,8 @@ var ContextCompactionPreflightService = class {
423
419
  ...generatedCheckpoint,
424
420
  id: pending.checkpoint.id,
425
421
  createdAt: pending.checkpoint.createdAt,
422
+ coveredMessageCount: pending.checkpoint.coveredMessageCount,
423
+ coveredSessionMessageCount: pending.checkpoint.coveredSessionMessageCount,
426
424
  status: "compressed"
427
425
  };
428
426
  return {
@@ -438,6 +436,7 @@ var ContextCompactionPreflightService = class {
438
436
  metadataPatch: { [CONTEXT_COMPACTION_METADATA_KEY]: checkpoint },
439
437
  sessionMessages: pending.sessionMessages,
440
438
  timelineMessage: buildContextCompactionTimelineNcpMessage({
439
+ messageId: pending.serviceMessageId,
441
440
  sessionId: pending.sessionId,
442
441
  checkpoint
443
442
  })
@@ -3071,6 +3070,30 @@ var McpManager = class {
3071
3070
  for (const result of results) if (!result.ok) console.warn(`[mcp] Failed to warm ${result.name}: ${result.error}`);
3072
3071
  };
3073
3072
  };
3073
+ //#endregion
3074
+ //#region src/utils/agent-peer-session.utils.ts
3075
+ const AGENT_RUN_PEER_ID_METADATA_KEY = "agent_peer_id";
3076
+ const AGENT_RUN_PEER_SCOPE_METADATA_KEY = "agent_peer_scope";
3077
+ function createAgentPeerSessionIdentity(params) {
3078
+ const scope = resolveAgentPeerScope(params);
3079
+ return {
3080
+ metadata: {
3081
+ [AGENT_RUN_PEER_ID_METADATA_KEY]: params.peerId,
3082
+ [AGENT_RUN_PEER_SCOPE_METADATA_KEY]: scope
3083
+ },
3084
+ sessionId: `agent-peer-${createHash("sha256").update(`${scope}\0${params.peerId}`).digest("hex").slice(0, 32)}`
3085
+ };
3086
+ }
3087
+ function resolveAgentPeerScope(params) {
3088
+ const metadata = params.metadata ?? {};
3089
+ const explicitScope = readOptionalString$8(metadata["agent_peer_scope"]) ?? readOptionalString$8(metadata.agentPeerScope);
3090
+ if (explicitScope) return explicitScope;
3091
+ return `agent:${readOptionalString$8(params.agentId) ?? BUILTIN_MAIN_AGENT_ID}:${readOptionalString$8(params.channel) ?? readOptionalString$8(metadata.channel) ?? "agent-run"}:${readOptionalString$8(metadata.accountId) ?? readOptionalString$8(metadata.account_id) ?? "default"}`;
3092
+ }
3093
+ function readOptionalString$8(value) {
3094
+ if (typeof value !== "string") return;
3095
+ return value.trim() || void 0;
3096
+ }
3074
3097
  const NCP_AGENT_SESSION_JOURNAL_INDEX_FILE = ".ncp-agent-session-index.json";
3075
3098
  const AUTO_SESSION_LABEL_MAX_LENGTH = 64;
3076
3099
  const NCP_AGENT_SESSION_SNAPSHOT_MESSAGE_EVENT_TYPE = "session.snapshot.message";
@@ -3096,10 +3119,12 @@ function toIsoString(value, fallback) {
3096
3119
  function createNcpAgentSessionSummary(record) {
3097
3120
  const metadata = structuredClone(record.metadata ?? {});
3098
3121
  const label = readOptionalText(metadata.label) ?? resolveAutoSessionLabel(record.messages);
3122
+ const peerId = readNcpAgentSessionPeerId(metadata);
3099
3123
  if (label) metadata.label = label;
3100
3124
  const lastMessageAt = record.messages.reduceRight((timestamp, message) => timestamp ?? readMessageTimestamp(message), void 0);
3101
3125
  return {
3102
3126
  sessionId: record.sessionId,
3127
+ peerId: peerId ?? void 0,
3103
3128
  ...normalizeNcpAgentId(record.agentId) ? { agentId: normalizeNcpAgentId(record.agentId) } : {},
3104
3129
  messageCount: record.messages.length,
3105
3130
  ...record.createdAt ? { createdAt: record.createdAt } : {},
@@ -3109,6 +3134,9 @@ function createNcpAgentSessionSummary(record) {
3109
3134
  ...Object.keys(metadata).length > 0 ? { metadata } : {}
3110
3135
  };
3111
3136
  }
3137
+ function readNcpAgentSessionPeerId(metadata) {
3138
+ return readOptionalText(metadata[AGENT_RUN_PEER_ID_METADATA_KEY]);
3139
+ }
3112
3140
  function createNcpAgentSessionJournalMetadataEntry(record) {
3113
3141
  return {
3114
3142
  _type: "metadata",
@@ -3125,6 +3153,7 @@ function upsertNcpAgentSessionSummaryEvent(params) {
3125
3153
  const lastMessageAt = readMessageTimestamp(eventMessage) ?? current?.lastMessageAt;
3126
3154
  return {
3127
3155
  sessionId,
3156
+ peerId: current?.peerId,
3128
3157
  ...normalizeNcpAgentId(current?.agentId) ? { agentId: normalizeNcpAgentId(current?.agentId) } : {},
3129
3158
  messageCount,
3130
3159
  createdAt: current?.createdAt ?? updatedAt,
@@ -3135,9 +3164,14 @@ function upsertNcpAgentSessionSummaryEvent(params) {
3135
3164
  }
3136
3165
  async function replayNcpAgentSessionEvents(events) {
3137
3166
  const stateManager = new DefaultNcpAgentConversationStateManager();
3167
+ const knownMessageIds = /* @__PURE__ */ new Set();
3138
3168
  for (const event of events) {
3139
3169
  if (isJournalOnlyEvent(event)) continue;
3140
- await stateManager.dispatch(createReplayEvent(event));
3170
+ const replayEvent = createReplayEvent(event);
3171
+ const bootstrapEvent = createReplayStreamingBootstrapEvent(replayEvent, knownMessageIds);
3172
+ if (bootstrapEvent) await stateManager.dispatch(bootstrapEvent);
3173
+ rememberReplayMessageId(replayEvent, knownMessageIds);
3174
+ await stateManager.dispatch(replayEvent);
3141
3175
  }
3142
3176
  const snapshot = stateManager.getSnapshot();
3143
3177
  return [...snapshot.messages.map((message) => structuredClone(message)), ...snapshot.streamingMessage ? [structuredClone(snapshot.streamingMessage)] : []];
@@ -3145,6 +3179,8 @@ async function replayNcpAgentSessionEvents(events) {
3145
3179
  function createReplayEvent(event) {
3146
3180
  const replayEvent = structuredClone(event);
3147
3181
  const replayMessage = readMessageFromSummaryEvent(replayEvent);
3182
+ const legacyCompactionMessageId = readLegacyContextCompactionMessageId(replayMessage);
3183
+ if (replayMessage && legacyCompactionMessageId) replayMessage.id = legacyCompactionMessageId;
3148
3184
  if (replayMessage?.role === "assistant" && (replayMessage.status === "pending" || replayMessage.status === "streaming")) replayMessage.status = "final";
3149
3185
  if (replayEvent.type === "session.snapshot.message" || replayEvent.type === NcpEventType.MessageCompleted) return {
3150
3186
  type: NcpEventType.MessageSent,
@@ -3152,6 +3188,58 @@ function createReplayEvent(event) {
3152
3188
  };
3153
3189
  return replayEvent;
3154
3190
  }
3191
+ function readLegacyContextCompactionMessageId(message) {
3192
+ const checkpoint = isRecord$10(message?.metadata?.checkpoint) ? message.metadata.checkpoint : null;
3193
+ const checkpointId = typeof checkpoint?.id === "string" ? checkpoint.id : "";
3194
+ const coveredCount = checkpoint?.coveredSessionMessageCount;
3195
+ const legacyId = `${message?.sessionId}:service:context-compaction:${checkpointId}`;
3196
+ return typeof coveredCount === "number" && message?.id === legacyId ? `${legacyId}:${coveredCount}` : null;
3197
+ }
3198
+ function createReplayStreamingBootstrapEvent(event, knownMessageIds) {
3199
+ const messageId = readStreamingMessageId(event);
3200
+ if (!messageId || knownMessageIds.has(messageId)) return null;
3201
+ knownMessageIds.add(messageId);
3202
+ return {
3203
+ type: NcpEventType.MessageSent,
3204
+ payload: {
3205
+ sessionId: readEventSessionId$2(event),
3206
+ message: {
3207
+ id: messageId,
3208
+ sessionId: readEventSessionId$2(event),
3209
+ role: "assistant",
3210
+ status: "streaming",
3211
+ parts: [],
3212
+ timestamp: readReplayPayloadTimestamp(event) ?? (/* @__PURE__ */ new Date()).toISOString()
3213
+ }
3214
+ }
3215
+ };
3216
+ }
3217
+ function rememberReplayMessageId(event, knownMessageIds) {
3218
+ const message = readMessageFromSummaryEvent(event);
3219
+ if (message?.id) knownMessageIds.add(message.id);
3220
+ }
3221
+ function readEventSessionId$2(event) {
3222
+ const sessionId = ("payload" in event && isRecord$10(event.payload) ? event.payload : null)?.sessionId;
3223
+ return typeof sessionId === "string" ? sessionId : "";
3224
+ }
3225
+ function readReplayPayloadTimestamp(event) {
3226
+ const payload = "payload" in event && isRecord$10(event.payload) ? event.payload : null;
3227
+ const timestamp = typeof payload?.timestamp === "string" ? payload.timestamp : "";
3228
+ return Number.isFinite(Date.parse(timestamp)) ? new Date(timestamp).toISOString() : null;
3229
+ }
3230
+ function readStreamingMessageId(event) {
3231
+ switch (event.type) {
3232
+ case NcpEventType.MessageTextStart:
3233
+ case NcpEventType.MessageTextDelta:
3234
+ case NcpEventType.MessageTextEnd:
3235
+ case NcpEventType.MessageReasoningStart:
3236
+ case NcpEventType.MessageReasoningDelta:
3237
+ case NcpEventType.MessageReasoningEnd:
3238
+ case NcpEventType.MessageToolCallStart:
3239
+ case NcpEventType.MessageToolCallArgsDelta: return event.payload.messageId?.trim() || null;
3240
+ default: return null;
3241
+ }
3242
+ }
3155
3243
  function isJournalOnlyEvent(event) {
3156
3244
  return event.type === "session.request.accepted" || event.type === "session.request.completed" || event.type === "session.request.failed";
3157
3245
  }
@@ -3182,30 +3270,6 @@ function readMessageFromSummaryEvent(event) {
3182
3270
  if (event.type === NcpEventType.MessageSent || event.type === NcpEventType.MessageCompleted || event.type === "session.snapshot.message") return event.payload.message;
3183
3271
  }
3184
3272
  //#endregion
3185
- //#region src/utils/agent-peer-session.utils.ts
3186
- const AGENT_RUN_PEER_ID_METADATA_KEY = "agent_peer_id";
3187
- const AGENT_RUN_PEER_SCOPE_METADATA_KEY = "agent_peer_scope";
3188
- function createAgentPeerSessionIdentity(params) {
3189
- const scope = resolveAgentPeerScope(params);
3190
- return {
3191
- metadata: {
3192
- [AGENT_RUN_PEER_ID_METADATA_KEY]: params.peerId,
3193
- [AGENT_RUN_PEER_SCOPE_METADATA_KEY]: scope
3194
- },
3195
- sessionId: `agent-peer-${createHash("sha256").update(`${scope}\0${params.peerId}`).digest("hex").slice(0, 32)}`
3196
- };
3197
- }
3198
- function resolveAgentPeerScope(params) {
3199
- const metadata = params.metadata ?? {};
3200
- const explicitScope = readOptionalString$8(metadata["agent_peer_scope"]) ?? readOptionalString$8(metadata.agentPeerScope);
3201
- if (explicitScope) return explicitScope;
3202
- return `agent:${readOptionalString$8(params.agentId) ?? BUILTIN_MAIN_AGENT_ID}:${readOptionalString$8(params.channel) ?? readOptionalString$8(metadata.channel) ?? "agent-run"}:${readOptionalString$8(metadata.accountId) ?? readOptionalString$8(metadata.account_id) ?? "default"}`;
3203
- }
3204
- function readOptionalString$8(value) {
3205
- if (typeof value !== "string") return;
3206
- return value.trim() || void 0;
3207
- }
3208
- //#endregion
3209
3273
  //#region src/managers/session.manager.ts
3210
3274
  const DEFAULT_SESSION_TYPE = "native";
3211
3275
  const DEFAULT_LIFECYCLE = "persistent";
@@ -3424,7 +3488,8 @@ var SessionManager = class {
3424
3488
  return await this.options.journalStore.getSession(normalizedSessionId);
3425
3489
  };
3426
3490
  listSessions = async (options) => {
3427
- return applyLimit(await this.options.journalStore.listSessionSummaries(), options?.limit);
3491
+ const peerId = readOptionalString$7(options?.peerId);
3492
+ return applyLimit((await this.options.journalStore.listSessionSummaries()).filter((summary) => !peerId || summary.peerId === peerId), options?.limit);
3428
3493
  };
3429
3494
  listSessionMessages = async (sessionId, options) => {
3430
3495
  const normalizedSessionId = normalizeSessionId(sessionId);
@@ -4308,6 +4373,35 @@ function getPanelAppBridgeScript(params = {
4308
4373
  });
4309
4374
  }
4310
4375
 
4376
+ function resolveApiFetchUrl(input) {
4377
+ const raw = typeof input === "string" || input instanceof URL ? input.toString() : input?.url;
4378
+ if (typeof raw !== "string") {
4379
+ return null;
4380
+ }
4381
+ try {
4382
+ const url = new URL(raw, window.location.href);
4383
+ return url.origin === window.location.origin && url.pathname.startsWith("/api/") ? url : null;
4384
+ } catch {
4385
+ return null;
4386
+ }
4387
+ }
4388
+
4389
+ function createFetchInitWithRuntimeToken(input, init) {
4390
+ if (!resolveApiFetchUrl(input)) {
4391
+ return init;
4392
+ }
4393
+ const headers = new Headers(init?.headers || (typeof input === "object" && input ? input.headers : undefined));
4394
+ if (!headers.has("x-nextclaw-panel-bridge-session")) {
4395
+ headers.set("x-nextclaw-panel-bridge-session", runtimeToken);
4396
+ }
4397
+ return { ...init, headers };
4398
+ }
4399
+
4400
+ const nativeFetch = window.fetch?.bind(window);
4401
+ if (nativeFetch) {
4402
+ window.fetch = (input, init) => nativeFetch(input, createFetchInitWithRuntimeToken(input, init));
4403
+ }
4404
+
4311
4405
  function unwrapServiceActionResult(result) {
4312
4406
  if (!result || typeof result !== "object") {
4313
4407
  return result;
@@ -4385,7 +4479,7 @@ const PANEL_APP_CLIENT_MARKER = "nextclaw:panel-app-client:init";
4385
4479
  const PANEL_APP_CLIENT_SDK_PATH = "/api/panel-app-client-sdk.js";
4386
4480
  function injectPanelAppClientScript(html, params) {
4387
4481
  if (html.includes(PANEL_APP_CLIENT_MARKER)) return html;
4388
- const script = [`<script src="${PANEL_APP_CLIENT_SDK_PATH}"><\/script>`, `<script>${getPanelAppClientInitScript(params)}<\/script>`].join("");
4482
+ const script = [`<script src="${PANEL_APP_CLIENT_SDK_PATH}" crossorigin="anonymous"><\/script>`, `<script>${getPanelAppClientInitScript(params)}<\/script>`].join("");
4389
4483
  const headMatch = /<head(?:\s[^>]*)?>/i.exec(html);
4390
4484
  if (headMatch?.index !== void 0) {
4391
4485
  const insertAt = headMatch.index + headMatch[0].length;
@@ -4401,13 +4495,18 @@ function getPanelAppClientInitScript(params) {
4401
4495
  console.error("[NextClaw] Panel App client SDK failed to load.");
4402
4496
  return;
4403
4497
  }
4498
+ if (typeof window.createNextClawAppClient !== "function") {
4499
+ console.error("[NextClaw] Panel App client projection failed to load.");
4500
+ return;
4501
+ }
4404
4502
  const existing = window.nextclaw && typeof window.nextclaw === "object" ? window.nextclaw : {};
4405
- const client = new window.NextClawClient({
4503
+ const hostClient = new window.NextClawClient({
4406
4504
  baseUrl: window.location.origin,
4407
4505
  headers: {
4408
4506
  "x-nextclaw-panel-bridge-session": ${JSON.stringify(params.runtimeToken)}
4409
4507
  }
4410
4508
  });
4509
+ const client = window.createNextClawAppClient(hostClient);
4411
4510
  Object.defineProperty(window, "nextclaw", {
4412
4511
  configurable: true,
4413
4512
  value: {
@@ -4612,9 +4711,27 @@ function resolvePanelAppIconUrl(id, icon) {
4612
4711
  }
4613
4712
  function injectPanelAppAssetBase(html, baseHref) {
4614
4713
  const base = `<base href="${baseHref}">`;
4615
- if (/<base\b/i.test(html)) return html;
4616
- if (/<head[^>]*>/i.test(html)) return html.replace(/<head[^>]*>/i, (head) => `${head}${base}`);
4617
- return `${base}${html}`;
4714
+ return injectLocalScriptCrossOrigin((() => {
4715
+ if (/<base\b/i.test(html)) return html;
4716
+ if (/<head[^>]*>/i.test(html)) return html.replace(/<head[^>]*>/i, (head) => `${head}${base}`);
4717
+ return `${base}${html}`;
4718
+ })());
4719
+ }
4720
+ function injectLocalScriptCrossOrigin(html) {
4721
+ return html.replace(/<script\b(?=[^>]*\bsrc\s*=)(?![^>]*\bcrossorigin\b)[^>]*>/gi, (tag) => {
4722
+ const src = extractScriptSrc(tag);
4723
+ if (!src || !isLocalScriptSrc(src)) return tag;
4724
+ return `${tag.slice(0, -1)} crossorigin="anonymous">`;
4725
+ });
4726
+ }
4727
+ function extractScriptSrc(tag) {
4728
+ const match = /\bsrc\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i.exec(tag);
4729
+ return match?.[1] ?? match?.[2] ?? match?.[3];
4730
+ }
4731
+ function isLocalScriptSrc(src) {
4732
+ const value = src.trim();
4733
+ if (!value || value.startsWith("//")) return false;
4734
+ return !/^(?:https?|data|blob|javascript):/i.test(value);
4618
4735
  }
4619
4736
  function encodePanelAppAssetPath(path) {
4620
4737
  return path.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
@@ -5123,7 +5240,7 @@ var McpServiceAppRuntimeService = class {
5123
5240
  command: manifest.command,
5124
5241
  args: manifest.args,
5125
5242
  cwd: app.dirPath,
5126
- env: {},
5243
+ env: createRuntimeChildEnv(process.env),
5127
5244
  stderr: "pipe"
5128
5245
  },
5129
5246
  scope: {
@@ -5402,7 +5519,7 @@ var ServiceAppManager = class {
5402
5519
  return record;
5403
5520
  };
5404
5521
  listServiceActions = async (params = {}) => {
5405
- const actions = (params.appId ? [await this.requireServiceApp(params.appId)] : await this.listValidServiceApps()).flatMap(({ manifest, record }) => this.listManifestActions(record, manifest));
5522
+ const actions = (params.appId ? [await this.requireServiceApp(params.appId)] : await this.listValidServiceApps()).flatMap(({ manifest, record }) => listServiceAppManifestActions(record, manifest));
5406
5523
  return await Promise.all(actions.map(async (action) => await this.withGrantState(action, params)));
5407
5524
  };
5408
5525
  discoverServiceAppActions = async (appId) => {
@@ -5497,7 +5614,7 @@ var ServiceAppManager = class {
5497
5614
  };
5498
5615
  requireServiceAction = async (actionId) => {
5499
5616
  const { manifest, record } = await this.requireServiceAppForAction(actionId);
5500
- const action = this.listManifestActions(record, manifest).find((entry) => entry.id === actionId);
5617
+ const action = listServiceAppManifestActions(record, manifest).find((entry) => entry.id === actionId);
5501
5618
  if (!action) throw new ServiceAppError("SERVICE_APP_ACTION_NOT_FOUND", "service action not found");
5502
5619
  return action;
5503
5620
  };
@@ -5506,7 +5623,6 @@ var ServiceAppManager = class {
5506
5623
  if (!appId) throw new ServiceAppError("SERVICE_APP_INVALID_ACTION", "service action id is invalid");
5507
5624
  return await this.requireServiceApp(appId);
5508
5625
  };
5509
- listManifestActions = (record, manifest) => listServiceAppManifestActions(record, manifest);
5510
5626
  requireServiceApp = async (appId) => {
5511
5627
  const dirPath = join(this.getServiceAppsPath(this.getWorkspacePath()), appId);
5512
5628
  try {
@@ -5562,9 +5678,10 @@ var ServiceAppManager = class {
5562
5678
  };
5563
5679
  toServiceAppRecord = (dirPath, manifest) => {
5564
5680
  const runtimeStatus = this.runtimeService.getStatus(manifest.id);
5565
- const record = {
5681
+ return {
5566
5682
  id: manifest.id,
5567
5683
  title: manifest.title,
5684
+ description: manifest.description,
5568
5685
  dirPath,
5569
5686
  manifestPath: getServiceAppManifestPath(dirPath),
5570
5687
  command: manifest.command,
@@ -5572,14 +5689,12 @@ var ServiceAppManager = class {
5572
5689
  cwd: dirPath,
5573
5690
  enabled: manifest.enabled,
5574
5691
  protocol: manifest.protocol,
5575
- status: manifest.enabled ? runtimeStatus.status : "stopped"
5692
+ status: manifest.enabled ? runtimeStatus.status : "stopped",
5693
+ lastError: runtimeStatus.lastError,
5694
+ lastStartedAt: runtimeStatus.lastStartedAt,
5695
+ lastReadyAt: runtimeStatus.lastReadyAt,
5696
+ lastFailedAt: runtimeStatus.lastFailedAt
5576
5697
  };
5577
- if (manifest.description) record.description = manifest.description;
5578
- if (runtimeStatus.lastError) record.lastError = runtimeStatus.lastError;
5579
- if (runtimeStatus.lastStartedAt) record.lastStartedAt = runtimeStatus.lastStartedAt;
5580
- if (runtimeStatus.lastReadyAt) record.lastReadyAt = runtimeStatus.lastReadyAt;
5581
- if (runtimeStatus.lastFailedAt) record.lastFailedAt = runtimeStatus.lastFailedAt;
5582
- return record;
5583
5698
  };
5584
5699
  assertCaller = (caller) => {
5585
5700
  if (caller.surface !== "panel-app" || !caller.appId.trim()) throw new ServiceAppError("SERVICE_APP_INVALID_CALLER", "service action caller is invalid");
@@ -5599,7 +5714,7 @@ var ServiceAppManager = class {
5599
5714
  throw new ServiceAppError("SERVICE_APP_READ_FAILED", error instanceof Error ? error.message : String(error));
5600
5715
  }
5601
5716
  };
5602
- isMissingFileError = (error) => Boolean(error) && typeof error === "object" && error.code === "ENOENT";
5717
+ isMissingFileError = (error) => typeof error === "object" && error !== null && error.code === "ENOENT";
5603
5718
  };
5604
5719
  function toTitle(value) {
5605
5720
  return basename(value).replace(/[-_]+/g, " ").trim() || value;
@@ -6064,6 +6179,16 @@ function serializeJournalEntry(entry) {
6064
6179
  if (!isRecord$10(JSON.parse(serialized))) throw new Error("ncp agent session journal entry serialization produced a non-object entry");
6065
6180
  return serialized;
6066
6181
  }
6182
+ function attachJournalTimestamp(event, timestamp) {
6183
+ if (!("payload" in event) || !isRecord$10(event.payload)) return event;
6184
+ return {
6185
+ ...event,
6186
+ payload: {
6187
+ ...event.payload,
6188
+ timestamp
6189
+ }
6190
+ };
6191
+ }
6067
6192
  var NcpAgentSessionJournalStore = class {
6068
6193
  sessions = /* @__PURE__ */ new Map();
6069
6194
  nextSeqBySession = /* @__PURE__ */ new Map();
@@ -6230,8 +6355,10 @@ var NcpAgentSessionJournalStore = class {
6230
6355
  updatedAt: summary.updatedAt,
6231
6356
  metadata: {}
6232
6357
  });
6358
+ const peerId = summary.peerId ?? readNcpAgentSessionPeerId(snapshot.metadata);
6233
6359
  return {
6234
6360
  ...summary,
6361
+ peerId: peerId ?? void 0,
6235
6362
  ...!summary.agentId && snapshot.agentId ? { agentId: snapshot.agentId } : {},
6236
6363
  ...Object.keys(snapshot.metadata).length > 0 ? { metadata: snapshot.metadata } : {}
6237
6364
  };
@@ -6288,9 +6415,10 @@ var NcpAgentSessionJournalStore = class {
6288
6415
  if (parsed._type === "event" && isRecord$10(parsed.event)) {
6289
6416
  const seq = Number(parsed.seq);
6290
6417
  nextSeq = Math.max(nextSeq, Number.isFinite(seq) ? Math.trunc(seq) + 1 : nextSeq);
6291
- updatedAt = toIsoString(parsed.timestamp, updatedAt);
6418
+ const eventTimestamp = toIsoString(parsed.timestamp, updatedAt);
6419
+ updatedAt = eventTimestamp;
6292
6420
  const event = structuredClone(parsed.event);
6293
- events.push(event);
6421
+ events.push(attachJournalTimestamp(event, eventTimestamp));
6294
6422
  }
6295
6423
  }
6296
6424
  return {
@@ -9584,6 +9712,6 @@ function resolveLegacyEventType(message) {
9584
9712
  return `message.${role || "other"}`;
9585
9713
  }
9586
9714
  //#endregion
9587
- export { AccessManager, AgentManager, AgentRunClient, AgentRuntimeRegistry, 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, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildServiceActionId, buildSessionOrchestrationSection, createAgentRuntimeSessionRequestDispatcher, createAssetTools, createContextWindowSignature, createLlmUsageRecord, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, getServiceActionName, getServiceAppManifestPath, getUnsignedUpdateManifest, hasLlmUsageTelemetry, isContextCompactionTimelineMessage, isContextWindowSnapshot, isPanelAppAgentCapability, isPanelAppError, isReplyCapableChannel, isServiceAppError, listExtensionChannelIds, listServiceAppManifestActions, mergeServiceAppRuntimeActions, normalizeAgentRuntimeSessionTypeIcon, normalizeLlmUsageModel, normalizeOptionalString, parseServiceAppManifest, parseSkillFrontmatter, projectNcpMessagesWithContextCompaction, readContextWindowEventSessionId, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, readServiceAppManifest, resolveAgentRuntimeEntries, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolveNextclawNcpRunContext, resolveSessionChannelContext, runGatewayInboundLoop, sanitizeLlmUsage, serializeUnsignedUpdateManifest, shouldRefreshContextWindowDuringStream, shouldRefreshContextWindowImmediately, stripSkillFrontmatter, syncSessionThinkingPreference, toNcpMessages, upsertContextCompactionTimelineMessage, waitForAgentRuntimeSessionReply };
9715
+ export { AccessManager, AgentManager, AgentRunClient, AgentRuntimeRegistry, 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, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildServiceActionId, buildSessionOrchestrationSection, createAgentRuntimeSessionRequestDispatcher, createAssetTools, createContextCompactionMessageId, createContextWindowSignature, createLlmUsageRecord, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, getServiceActionName, getServiceAppManifestPath, getUnsignedUpdateManifest, hasLlmUsageTelemetry, isContextCompactionTimelineMessage, isContextWindowSnapshot, isPanelAppAgentCapability, isPanelAppError, isReplyCapableChannel, isServiceAppError, listExtensionChannelIds, listServiceAppManifestActions, mergeServiceAppRuntimeActions, normalizeAgentRuntimeSessionTypeIcon, normalizeLlmUsageModel, normalizeOptionalString, parseServiceAppManifest, parseSkillFrontmatter, projectNcpMessagesWithContextCompaction, readContextWindowEventSessionId, readLatestContextCompactionCheckpoint, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, readServiceAppManifest, resolveAgentRuntimeEntries, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolveNextclawNcpRunContext, resolveSessionChannelContext, runGatewayInboundLoop, sanitizeLlmUsage, serializeUnsignedUpdateManifest, shouldRefreshContextWindowDuringStream, shouldRefreshContextWindowImmediately, stripSkillFrontmatter, syncSessionThinkingPreference, toNcpMessages, upsertContextCompactionTimelineMessage, waitForAgentRuntimeSessionReply };
9588
9716
 
9589
9717
  //# sourceMappingURL=index.js.map