@nextclaw/kernel 0.4.0-beta.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +752 -130
- package/dist/index.js.map +1 -1
- package/package.json +14 -14
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,
|
|
4
|
+
import { APP_NAME, AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, CLEAR_THINKING_TOKENS, CONTEXT_COMPACTION_METADATA_KEY, ChannelManager, ChannelManager as ChannelManager$1, ConfigSchema, ContextCompactionService, ContextWindowBudgetService, CronService, CronTool, DEFAULT_PANELS_DIR, DEFAULT_SERVICE_APPS_DIR, DEFAULT_SESSION_SEARCH_LIMIT, DEFAULT_WORKSPACE_REPOSITORY_IDENTITY_RESOLVER, EditFileTool, ExecTool, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, LiteLLMProvider, MAX_SESSION_SEARCH_LIMIT, MemoryGetTool, MemorySearchTool, MemoryStore, MessageBus, MessageTool, ProviderRegistry, ReadFileTool, RequestedSkillsMetadataReader, SILENT_REPLY_TOKEN, SessionProjectContextResolver, SessionSearchManager, SkillsLoader, THINKING_LEVELS, WebFetchTool, WebSearchTool, WriteFileTool, buildCompressingCompactionCheckpoint, buildConfigSchema, buildContextWindowSnapshot, 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, resolveNextclawSelfManageGuidePaths, 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";
|
|
@@ -15,7 +15,6 @@ import { BUILTIN_PROVIDER_PLUGINS } from "@nextclaw/runtime";
|
|
|
15
15
|
import { McpRegistryService, McpServerLifecycleManager } from "@nextclaw/mcp";
|
|
16
16
|
import { McpNcpToolRegistryAdapter } from "@nextclaw/ncp-mcp";
|
|
17
17
|
import { DefaultNcpAgentConversationStateManager } from "@nextclaw/ncp-toolkit";
|
|
18
|
-
import { createRuntimeChildEnv } from "@nextclaw/core/child-process-env";
|
|
19
18
|
import { parse } from "yaml";
|
|
20
19
|
import { HttpRuntimeConfigResolver, HttpRuntimeNcpAgentRuntime } from "@nextclaw/nextclaw-ncp-runtime-http-client";
|
|
21
20
|
import { StdioRuntimeConfigResolver, StdioRuntimeNcpAgentRuntime, probeStdioRuntime } from "@nextclaw/nextclaw-ncp-runtime-stdio-client";
|
|
@@ -149,6 +148,12 @@ function toLegacyMessages(messages, options = {}) {
|
|
|
149
148
|
//#region src/features/context-compaction/utils/context-compaction-timeline-message.utils.ts
|
|
150
149
|
const NEXTCLAW_TIMELINE_KIND_METADATA_KEY = "nextclaw_timeline_kind";
|
|
151
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
|
+
}
|
|
152
157
|
function readTimelineMetadata(message) {
|
|
153
158
|
const rawMetadata = message?.ncp_metadata;
|
|
154
159
|
if (!rawMetadata || typeof rawMetadata !== "object" || Array.isArray(rawMetadata)) return null;
|
|
@@ -162,13 +167,14 @@ function readTimelineMetadata(message) {
|
|
|
162
167
|
};
|
|
163
168
|
}
|
|
164
169
|
function buildTimelineMessage(checkpoint) {
|
|
170
|
+
const text = readCheckpointTimelineText(checkpoint);
|
|
165
171
|
return {
|
|
166
172
|
role: "service",
|
|
167
|
-
content:
|
|
173
|
+
content: text,
|
|
168
174
|
timestamp: checkpoint.updatedAt,
|
|
169
175
|
ncp_parts: [{
|
|
170
176
|
type: "text",
|
|
171
|
-
text
|
|
177
|
+
text
|
|
172
178
|
}],
|
|
173
179
|
ncp_metadata: {
|
|
174
180
|
[NEXTCLAW_TIMELINE_KIND_METADATA_KEY]: CONTEXT_COMPACTION_TIMELINE_KIND,
|
|
@@ -177,10 +183,10 @@ function buildTimelineMessage(checkpoint) {
|
|
|
177
183
|
};
|
|
178
184
|
}
|
|
179
185
|
function buildContextCompactionTimelineNcpMessage(params) {
|
|
180
|
-
const { checkpoint, sessionId } = params;
|
|
181
|
-
const text = checkpoint
|
|
186
|
+
const { checkpoint, messageId, sessionId } = params;
|
|
187
|
+
const text = readCheckpointTimelineText(checkpoint);
|
|
182
188
|
return {
|
|
183
|
-
id:
|
|
189
|
+
id: messageId,
|
|
184
190
|
sessionId,
|
|
185
191
|
role: "service",
|
|
186
192
|
status: "final",
|
|
@@ -234,34 +240,31 @@ function isContextCompactionTimelineMessage(message) {
|
|
|
234
240
|
}
|
|
235
241
|
//#endregion
|
|
236
242
|
//#region src/features/context-compaction/utils/context-compaction-projection.utils.ts
|
|
237
|
-
function
|
|
243
|
+
function readCompactionCheckpoint(message) {
|
|
238
244
|
const metadata = message.metadata;
|
|
239
|
-
|
|
240
|
-
|
|
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);
|
|
241
252
|
}
|
|
242
253
|
function projectNcpMessagesWithContextCompaction(params) {
|
|
243
254
|
const { sessionId, sessionMessages } = params;
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
for (let index = sessionMessages.length - 1; index >= 0; index -= 1) {
|
|
247
|
-
const candidateSummary = readCompactionSummary(sessionMessages[index]);
|
|
248
|
-
if (!candidateSummary) continue;
|
|
249
|
-
checkpointIndex = index;
|
|
250
|
-
summary = candidateSummary;
|
|
251
|
-
break;
|
|
252
|
-
}
|
|
253
|
-
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));
|
|
254
257
|
return [{
|
|
255
|
-
id: `${sessionId}:context-compaction-summary:${
|
|
258
|
+
id: `${sessionId}:context-compaction-summary:${checkpoint.id}:${checkpoint.updatedAt}`,
|
|
256
259
|
sessionId,
|
|
257
260
|
role: "user",
|
|
258
261
|
status: "final",
|
|
259
|
-
timestamp:
|
|
262
|
+
timestamp: checkpoint.updatedAt,
|
|
260
263
|
parts: [{
|
|
261
264
|
type: "text",
|
|
262
|
-
text: summary
|
|
265
|
+
text: checkpoint.summary
|
|
263
266
|
}]
|
|
264
|
-
}, ...sessionMessages.
|
|
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))];
|
|
265
268
|
}
|
|
266
269
|
//#endregion
|
|
267
270
|
//#region src/features/context-compaction/services/context-compaction-preflight.service.ts
|
|
@@ -312,23 +315,6 @@ function buildContextWindowSnapshotFromBudget(params) {
|
|
|
312
315
|
compactedUsedContextTokens: checkpoint ? budget.estimatedTokens : void 0
|
|
313
316
|
});
|
|
314
317
|
}
|
|
315
|
-
function buildContextWindowSnapshotForMessages(contextWindowBudgetService, params) {
|
|
316
|
-
const { checkpoint, contextTokens, reservedContextTokens, sessionId, sessionMessages } = params;
|
|
317
|
-
const modelCandidateMessages = sessionMessages.filter((message) => !isContextCompactionTimelineMessage(message));
|
|
318
|
-
const messages = toLegacyMessages(checkpoint ? projectNcpMessagesWithContextCompaction({
|
|
319
|
-
sessionId,
|
|
320
|
-
sessionMessages
|
|
321
|
-
}) : modelCandidateMessages);
|
|
322
|
-
return buildContextWindowSnapshotFromBudget({
|
|
323
|
-
budget: contextWindowBudgetService.evaluate({
|
|
324
|
-
messages,
|
|
325
|
-
contextTokens,
|
|
326
|
-
reservedContextTokens
|
|
327
|
-
}),
|
|
328
|
-
checkpoint,
|
|
329
|
-
totalContextTokens: contextTokens
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
318
|
var ContextCompactionPreflightService = class {
|
|
333
319
|
compactionService = new ContextCompactionService();
|
|
334
320
|
contextWindowBudgetService = new ContextWindowBudgetService();
|
|
@@ -342,13 +328,19 @@ var ContextCompactionPreflightService = class {
|
|
|
342
328
|
requestMetadata,
|
|
343
329
|
storedAgentId
|
|
344
330
|
});
|
|
345
|
-
const existingCheckpoint = readCompressedContextCompactionCheckpoint(storedMetadata[CONTEXT_COMPACTION_METADATA_KEY]);
|
|
346
|
-
|
|
347
|
-
checkpoint: existingCheckpoint,
|
|
348
|
-
contextTokens: profile.contextTokens,
|
|
349
|
-
reservedContextTokens: profile.reservedContextTokens,
|
|
331
|
+
const existingCheckpoint = readCompressedContextCompactionCheckpoint(storedMetadata[CONTEXT_COMPACTION_METADATA_KEY]) ?? readLatestContextCompactionCheckpoint(sessionMessages);
|
|
332
|
+
const projectedMessages = existingCheckpoint ? projectNcpMessagesWithContextCompaction({
|
|
350
333
|
sessionId,
|
|
351
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
|
|
352
344
|
});
|
|
353
345
|
};
|
|
354
346
|
begin = (params) => {
|
|
@@ -363,26 +355,27 @@ var ContextCompactionPreflightService = class {
|
|
|
363
355
|
inputMessages,
|
|
364
356
|
sessionMessages
|
|
365
357
|
});
|
|
366
|
-
const
|
|
367
|
-
const existingCheckpoint = readCompressedContextCompactionCheckpoint(storedMetadata[CONTEXT_COMPACTION_METADATA_KEY]);
|
|
358
|
+
const existingCheckpoint = readCompressedContextCompactionCheckpoint(storedMetadata[CONTEXT_COMPACTION_METADATA_KEY]) ?? readLatestContextCompactionCheckpoint(ncpMessages);
|
|
368
359
|
const messages = toLegacyMessages(existingCheckpoint ? projectNcpMessagesWithContextCompaction({
|
|
369
360
|
sessionId,
|
|
370
361
|
sessionMessages: ncpMessages
|
|
371
|
-
}) :
|
|
362
|
+
}) : ncpMessages.filter((message) => !isContextCompactionTimelineMessage(message)));
|
|
372
363
|
const budget = this.contextWindowBudgetService.evaluate({
|
|
373
364
|
messages,
|
|
374
365
|
contextTokens,
|
|
375
366
|
reservedContextTokens
|
|
376
367
|
});
|
|
377
|
-
const plan =
|
|
368
|
+
const plan = !budget.shouldCompact ? null : this.compactionService.prepareForModelInput({
|
|
378
369
|
messages: budget.messages,
|
|
379
370
|
contextTokens,
|
|
380
371
|
compactionThresholdTokens: budget.triggerTokens
|
|
381
372
|
});
|
|
373
|
+
const coveredSessionMessageCount = plan ? (existingCheckpoint?.coveredSessionMessageCount ?? 0) + plan.coveredMessages.length - (existingCheckpoint ? 1 : 0) : 0;
|
|
374
|
+
const serviceMessageId = createContextCompactionMessageId();
|
|
382
375
|
const checkpoint = plan ? {
|
|
383
376
|
...buildCompressingCompactionCheckpoint(storedMetadata[CONTEXT_COMPACTION_METADATA_KEY]),
|
|
384
|
-
coveredMessageCount:
|
|
385
|
-
coveredSessionMessageCount
|
|
377
|
+
coveredMessageCount: coveredSessionMessageCount,
|
|
378
|
+
coveredSessionMessageCount,
|
|
386
379
|
originalEstimatedTokens: plan.originalEstimatedTokens,
|
|
387
380
|
projectedEstimatedTokens: budget.estimatedTokens
|
|
388
381
|
} : existingCheckpoint;
|
|
@@ -395,12 +388,14 @@ var ContextCompactionPreflightService = class {
|
|
|
395
388
|
metadataPatch: plan && checkpoint ? { [CONTEXT_COMPACTION_METADATA_KEY]: checkpoint } : {},
|
|
396
389
|
sessionMessages: ncpMessages,
|
|
397
390
|
timelineMessage: plan && checkpoint ? buildContextCompactionTimelineNcpMessage({
|
|
391
|
+
messageId: serviceMessageId,
|
|
398
392
|
sessionId,
|
|
399
393
|
checkpoint
|
|
400
394
|
}) : null,
|
|
401
395
|
pendingCompaction: plan && checkpoint ? {
|
|
402
396
|
checkpoint,
|
|
403
397
|
contextTokens,
|
|
398
|
+
serviceMessageId,
|
|
404
399
|
model: profile.model,
|
|
405
400
|
plan,
|
|
406
401
|
reservedContextTokens,
|
|
@@ -424,6 +419,8 @@ var ContextCompactionPreflightService = class {
|
|
|
424
419
|
...generatedCheckpoint,
|
|
425
420
|
id: pending.checkpoint.id,
|
|
426
421
|
createdAt: pending.checkpoint.createdAt,
|
|
422
|
+
coveredMessageCount: pending.checkpoint.coveredMessageCount,
|
|
423
|
+
coveredSessionMessageCount: pending.checkpoint.coveredSessionMessageCount,
|
|
427
424
|
status: "compressed"
|
|
428
425
|
};
|
|
429
426
|
return {
|
|
@@ -439,6 +436,7 @@ var ContextCompactionPreflightService = class {
|
|
|
439
436
|
metadataPatch: { [CONTEXT_COMPACTION_METADATA_KEY]: checkpoint },
|
|
440
437
|
sessionMessages: pending.sessionMessages,
|
|
441
438
|
timelineMessage: buildContextCompactionTimelineNcpMessage({
|
|
439
|
+
messageId: pending.serviceMessageId,
|
|
442
440
|
sessionId: pending.sessionId,
|
|
443
441
|
checkpoint
|
|
444
442
|
})
|
|
@@ -3072,6 +3070,30 @@ var McpManager = class {
|
|
|
3072
3070
|
for (const result of results) if (!result.ok) console.warn(`[mcp] Failed to warm ${result.name}: ${result.error}`);
|
|
3073
3071
|
};
|
|
3074
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
|
+
}
|
|
3075
3097
|
const NCP_AGENT_SESSION_JOURNAL_INDEX_FILE = ".ncp-agent-session-index.json";
|
|
3076
3098
|
const AUTO_SESSION_LABEL_MAX_LENGTH = 64;
|
|
3077
3099
|
const NCP_AGENT_SESSION_SNAPSHOT_MESSAGE_EVENT_TYPE = "session.snapshot.message";
|
|
@@ -3097,10 +3119,12 @@ function toIsoString(value, fallback) {
|
|
|
3097
3119
|
function createNcpAgentSessionSummary(record) {
|
|
3098
3120
|
const metadata = structuredClone(record.metadata ?? {});
|
|
3099
3121
|
const label = readOptionalText(metadata.label) ?? resolveAutoSessionLabel(record.messages);
|
|
3122
|
+
const peerId = readNcpAgentSessionPeerId(metadata);
|
|
3100
3123
|
if (label) metadata.label = label;
|
|
3101
3124
|
const lastMessageAt = record.messages.reduceRight((timestamp, message) => timestamp ?? readMessageTimestamp(message), void 0);
|
|
3102
3125
|
return {
|
|
3103
3126
|
sessionId: record.sessionId,
|
|
3127
|
+
peerId: peerId ?? void 0,
|
|
3104
3128
|
...normalizeNcpAgentId(record.agentId) ? { agentId: normalizeNcpAgentId(record.agentId) } : {},
|
|
3105
3129
|
messageCount: record.messages.length,
|
|
3106
3130
|
...record.createdAt ? { createdAt: record.createdAt } : {},
|
|
@@ -3110,6 +3134,9 @@ function createNcpAgentSessionSummary(record) {
|
|
|
3110
3134
|
...Object.keys(metadata).length > 0 ? { metadata } : {}
|
|
3111
3135
|
};
|
|
3112
3136
|
}
|
|
3137
|
+
function readNcpAgentSessionPeerId(metadata) {
|
|
3138
|
+
return readOptionalText(metadata[AGENT_RUN_PEER_ID_METADATA_KEY]);
|
|
3139
|
+
}
|
|
3113
3140
|
function createNcpAgentSessionJournalMetadataEntry(record) {
|
|
3114
3141
|
return {
|
|
3115
3142
|
_type: "metadata",
|
|
@@ -3126,6 +3153,7 @@ function upsertNcpAgentSessionSummaryEvent(params) {
|
|
|
3126
3153
|
const lastMessageAt = readMessageTimestamp(eventMessage) ?? current?.lastMessageAt;
|
|
3127
3154
|
return {
|
|
3128
3155
|
sessionId,
|
|
3156
|
+
peerId: current?.peerId,
|
|
3129
3157
|
...normalizeNcpAgentId(current?.agentId) ? { agentId: normalizeNcpAgentId(current?.agentId) } : {},
|
|
3130
3158
|
messageCount,
|
|
3131
3159
|
createdAt: current?.createdAt ?? updatedAt,
|
|
@@ -3136,9 +3164,14 @@ function upsertNcpAgentSessionSummaryEvent(params) {
|
|
|
3136
3164
|
}
|
|
3137
3165
|
async function replayNcpAgentSessionEvents(events) {
|
|
3138
3166
|
const stateManager = new DefaultNcpAgentConversationStateManager();
|
|
3167
|
+
const knownMessageIds = /* @__PURE__ */ new Set();
|
|
3139
3168
|
for (const event of events) {
|
|
3140
3169
|
if (isJournalOnlyEvent(event)) continue;
|
|
3141
|
-
|
|
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);
|
|
3142
3175
|
}
|
|
3143
3176
|
const snapshot = stateManager.getSnapshot();
|
|
3144
3177
|
return [...snapshot.messages.map((message) => structuredClone(message)), ...snapshot.streamingMessage ? [structuredClone(snapshot.streamingMessage)] : []];
|
|
@@ -3146,6 +3179,8 @@ async function replayNcpAgentSessionEvents(events) {
|
|
|
3146
3179
|
function createReplayEvent(event) {
|
|
3147
3180
|
const replayEvent = structuredClone(event);
|
|
3148
3181
|
const replayMessage = readMessageFromSummaryEvent(replayEvent);
|
|
3182
|
+
const legacyCompactionMessageId = readLegacyContextCompactionMessageId(replayMessage);
|
|
3183
|
+
if (replayMessage && legacyCompactionMessageId) replayMessage.id = legacyCompactionMessageId;
|
|
3149
3184
|
if (replayMessage?.role === "assistant" && (replayMessage.status === "pending" || replayMessage.status === "streaming")) replayMessage.status = "final";
|
|
3150
3185
|
if (replayEvent.type === "session.snapshot.message" || replayEvent.type === NcpEventType.MessageCompleted) return {
|
|
3151
3186
|
type: NcpEventType.MessageSent,
|
|
@@ -3153,6 +3188,58 @@ function createReplayEvent(event) {
|
|
|
3153
3188
|
};
|
|
3154
3189
|
return replayEvent;
|
|
3155
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
|
+
}
|
|
3156
3243
|
function isJournalOnlyEvent(event) {
|
|
3157
3244
|
return event.type === "session.request.accepted" || event.type === "session.request.completed" || event.type === "session.request.failed";
|
|
3158
3245
|
}
|
|
@@ -3183,30 +3270,6 @@ function readMessageFromSummaryEvent(event) {
|
|
|
3183
3270
|
if (event.type === NcpEventType.MessageSent || event.type === NcpEventType.MessageCompleted || event.type === "session.snapshot.message") return event.payload.message;
|
|
3184
3271
|
}
|
|
3185
3272
|
//#endregion
|
|
3186
|
-
//#region src/utils/agent-peer-session.utils.ts
|
|
3187
|
-
const AGENT_RUN_PEER_ID_METADATA_KEY = "agent_peer_id";
|
|
3188
|
-
const AGENT_RUN_PEER_SCOPE_METADATA_KEY = "agent_peer_scope";
|
|
3189
|
-
function createAgentPeerSessionIdentity(params) {
|
|
3190
|
-
const scope = resolveAgentPeerScope(params);
|
|
3191
|
-
return {
|
|
3192
|
-
metadata: {
|
|
3193
|
-
[AGENT_RUN_PEER_ID_METADATA_KEY]: params.peerId,
|
|
3194
|
-
[AGENT_RUN_PEER_SCOPE_METADATA_KEY]: scope
|
|
3195
|
-
},
|
|
3196
|
-
sessionId: `agent-peer-${createHash("sha256").update(`${scope}\0${params.peerId}`).digest("hex").slice(0, 32)}`
|
|
3197
|
-
};
|
|
3198
|
-
}
|
|
3199
|
-
function resolveAgentPeerScope(params) {
|
|
3200
|
-
const metadata = params.metadata ?? {};
|
|
3201
|
-
const explicitScope = readOptionalString$8(metadata["agent_peer_scope"]) ?? readOptionalString$8(metadata.agentPeerScope);
|
|
3202
|
-
if (explicitScope) return explicitScope;
|
|
3203
|
-
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"}`;
|
|
3204
|
-
}
|
|
3205
|
-
function readOptionalString$8(value) {
|
|
3206
|
-
if (typeof value !== "string") return;
|
|
3207
|
-
return value.trim() || void 0;
|
|
3208
|
-
}
|
|
3209
|
-
//#endregion
|
|
3210
3273
|
//#region src/managers/session.manager.ts
|
|
3211
3274
|
const DEFAULT_SESSION_TYPE = "native";
|
|
3212
3275
|
const DEFAULT_LIFECYCLE = "persistent";
|
|
@@ -3425,7 +3488,8 @@ var SessionManager = class {
|
|
|
3425
3488
|
return await this.options.journalStore.getSession(normalizedSessionId);
|
|
3426
3489
|
};
|
|
3427
3490
|
listSessions = async (options) => {
|
|
3428
|
-
|
|
3491
|
+
const peerId = readOptionalString$7(options?.peerId);
|
|
3492
|
+
return applyLimit((await this.options.journalStore.listSessionSummaries()).filter((summary) => !peerId || summary.peerId === peerId), options?.limit);
|
|
3429
3493
|
};
|
|
3430
3494
|
listSessionMessages = async (sessionId, options) => {
|
|
3431
3495
|
const normalizedSessionId = normalizeSessionId(sessionId);
|
|
@@ -4309,6 +4373,35 @@ function getPanelAppBridgeScript(params = {
|
|
|
4309
4373
|
});
|
|
4310
4374
|
}
|
|
4311
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
|
+
|
|
4312
4405
|
function unwrapServiceActionResult(result) {
|
|
4313
4406
|
if (!result || typeof result !== "object") {
|
|
4314
4407
|
return result;
|
|
@@ -4386,7 +4479,7 @@ const PANEL_APP_CLIENT_MARKER = "nextclaw:panel-app-client:init";
|
|
|
4386
4479
|
const PANEL_APP_CLIENT_SDK_PATH = "/api/panel-app-client-sdk.js";
|
|
4387
4480
|
function injectPanelAppClientScript(html, params) {
|
|
4388
4481
|
if (html.includes(PANEL_APP_CLIENT_MARKER)) return html;
|
|
4389
|
-
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("");
|
|
4390
4483
|
const headMatch = /<head(?:\s[^>]*)?>/i.exec(html);
|
|
4391
4484
|
if (headMatch?.index !== void 0) {
|
|
4392
4485
|
const insertAt = headMatch.index + headMatch[0].length;
|
|
@@ -4618,9 +4711,27 @@ function resolvePanelAppIconUrl(id, icon) {
|
|
|
4618
4711
|
}
|
|
4619
4712
|
function injectPanelAppAssetBase(html, baseHref) {
|
|
4620
4713
|
const base = `<base href="${baseHref}">`;
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
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);
|
|
4624
4735
|
}
|
|
4625
4736
|
function encodePanelAppAssetPath(path) {
|
|
4626
4737
|
return path.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
|
|
@@ -6068,6 +6179,16 @@ function serializeJournalEntry(entry) {
|
|
|
6068
6179
|
if (!isRecord$10(JSON.parse(serialized))) throw new Error("ncp agent session journal entry serialization produced a non-object entry");
|
|
6069
6180
|
return serialized;
|
|
6070
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
|
+
}
|
|
6071
6192
|
var NcpAgentSessionJournalStore = class {
|
|
6072
6193
|
sessions = /* @__PURE__ */ new Map();
|
|
6073
6194
|
nextSeqBySession = /* @__PURE__ */ new Map();
|
|
@@ -6234,8 +6355,10 @@ var NcpAgentSessionJournalStore = class {
|
|
|
6234
6355
|
updatedAt: summary.updatedAt,
|
|
6235
6356
|
metadata: {}
|
|
6236
6357
|
});
|
|
6358
|
+
const peerId = summary.peerId ?? readNcpAgentSessionPeerId(snapshot.metadata);
|
|
6237
6359
|
return {
|
|
6238
6360
|
...summary,
|
|
6361
|
+
peerId: peerId ?? void 0,
|
|
6239
6362
|
...!summary.agentId && snapshot.agentId ? { agentId: snapshot.agentId } : {},
|
|
6240
6363
|
...Object.keys(snapshot.metadata).length > 0 ? { metadata: snapshot.metadata } : {}
|
|
6241
6364
|
};
|
|
@@ -6292,9 +6415,10 @@ var NcpAgentSessionJournalStore = class {
|
|
|
6292
6415
|
if (parsed._type === "event" && isRecord$10(parsed.event)) {
|
|
6293
6416
|
const seq = Number(parsed.seq);
|
|
6294
6417
|
nextSeq = Math.max(nextSeq, Number.isFinite(seq) ? Math.trunc(seq) + 1 : nextSeq);
|
|
6295
|
-
|
|
6418
|
+
const eventTimestamp = toIsoString(parsed.timestamp, updatedAt);
|
|
6419
|
+
updatedAt = eventTimestamp;
|
|
6296
6420
|
const event = structuredClone(parsed.event);
|
|
6297
|
-
events.push(event);
|
|
6421
|
+
events.push(attachJournalTimestamp(event, eventTimestamp));
|
|
6298
6422
|
}
|
|
6299
6423
|
}
|
|
6300
6424
|
return {
|
|
@@ -6871,7 +6995,7 @@ var BuiltinNarpRuntimeProviderService = class {
|
|
|
6871
6995
|
};
|
|
6872
6996
|
//#endregion
|
|
6873
6997
|
//#region src/features/native-runtime/services/provider-manager-ncp-llm-api.service.ts
|
|
6874
|
-
function normalizeModel(value) {
|
|
6998
|
+
function normalizeModel$1(value) {
|
|
6875
6999
|
if (typeof value !== "string") return null;
|
|
6876
7000
|
const trimmed = value.trim();
|
|
6877
7001
|
return trimmed.length > 0 ? trimmed : null;
|
|
@@ -6913,7 +7037,7 @@ var ProviderManagerNcpLLMApi = class {
|
|
|
6913
7037
|
this.providerManager = providerManager;
|
|
6914
7038
|
}
|
|
6915
7039
|
generate = async function* (input, options) {
|
|
6916
|
-
const model = normalizeModel(input.model) ?? this.providerManager.get(null).getDefaultModel();
|
|
7040
|
+
const model = normalizeModel$1(input.model) ?? this.providerManager.get(null).getDefaultModel();
|
|
6917
7041
|
const thinkingLevel = normalizeThinkingLevel(input.thinkingLevel);
|
|
6918
7042
|
let sawTextDelta = false;
|
|
6919
7043
|
let sawReasoningDelta = false;
|
|
@@ -7227,19 +7351,6 @@ function mergeRunMetadata(params) {
|
|
|
7227
7351
|
...requestMetadata ? structuredClone(requestMetadata) : {}
|
|
7228
7352
|
};
|
|
7229
7353
|
}
|
|
7230
|
-
function buildSessionOrchestrationSection() {
|
|
7231
|
-
return [
|
|
7232
|
-
"## Session Orchestration",
|
|
7233
|
-
"- Before passing a non-default `runtime` to `sessions_spawn` or agent creation/update flows, inspect the installed runtime kinds with `nextclaw agents runtimes --json`.",
|
|
7234
|
-
"- `sessions_spawn` is the unified session-creation tool. Omit `scope` or use `scope=\"standalone\"` for a regular session, and use `scope=\"child\"` when the new session should be a child session of the current flow.",
|
|
7235
|
-
"- `sessions_spawn` only creates the session by default. Add top-level `notify: \"none\" | \"final_reply\"` when the new session should start working immediately.",
|
|
7236
|
-
"- When `sessions_spawn.scope=\"child\"` and `sessions_spawn.notify=\"final_reply\"`, the new child session starts right away and this session automatically continues after that child reaches its final reply.",
|
|
7237
|
-
"- Use `sessions_spawn` without `notify` when the user wants a separate thread created now but does not need it to start working yet.",
|
|
7238
|
-
"- Use `sessions_request` to send one task to an existing session, including a session that was just created by `sessions_spawn` or a previously created child session.",
|
|
7239
|
-
"- `sessions_request.target` must be an object shaped like `{ \"session_id\": \"<target-session-id>\" }`. Do not pass a bare string.",
|
|
7240
|
-
"- Prefer `notify=\"final_reply\"` when the current session should continue after the target session produces its final reply. Use `notify=\"none\"` when you only want the target session to run independently."
|
|
7241
|
-
].join("\n");
|
|
7242
|
-
}
|
|
7243
7354
|
function resolveNextclawNcpRunContext(params) {
|
|
7244
7355
|
const { configManager, requestMetadata: inputRequestMetadata, sessionId, sessionMetadata: inputSessionMetadata, storedAgentId } = params;
|
|
7245
7356
|
const config = configManager.loadConfig();
|
|
@@ -7705,6 +7816,454 @@ var AgentRunRuntimeContribution = class {
|
|
|
7705
7816
|
});
|
|
7706
7817
|
};
|
|
7707
7818
|
//#endregion
|
|
7819
|
+
//#region src/contributions/context-provider/utils/context-text.utils.ts
|
|
7820
|
+
function truncateContextText(text, limit) {
|
|
7821
|
+
if (limit <= 0 || text.length <= limit) return text;
|
|
7822
|
+
const suffix = `\n\n...[truncated ${text.length - limit} chars]`;
|
|
7823
|
+
if (suffix.length >= limit) return text.slice(0, limit).trimEnd();
|
|
7824
|
+
return `${text.slice(0, limit - suffix.length).trimEnd()}${suffix}`;
|
|
7825
|
+
}
|
|
7826
|
+
//#endregion
|
|
7827
|
+
//#region src/contributions/context-provider/providers/agent-bootstrap-context.provider.ts
|
|
7828
|
+
var AgentBootstrapContextProvider = class {
|
|
7829
|
+
constructor(context) {
|
|
7830
|
+
this.context = context;
|
|
7831
|
+
}
|
|
7832
|
+
provide = async (request) => {
|
|
7833
|
+
const { contextConfig, projectContext, runContext } = await this.context.resolve(request);
|
|
7834
|
+
const budget = this.createReadBudget(contextConfig.bootstrap);
|
|
7835
|
+
const agentBootstrapRoot = projectContext.projectBootstrapRoot ?? projectContext.effectiveWorkspace;
|
|
7836
|
+
const projectBootstrap = this.loadBootstrapFiles({
|
|
7837
|
+
root: agentBootstrapRoot,
|
|
7838
|
+
config: contextConfig.bootstrap,
|
|
7839
|
+
sessionKey: runContext.sessionKey,
|
|
7840
|
+
budget
|
|
7841
|
+
});
|
|
7842
|
+
const hasDistinctHostWorkspace = projectContext.hostWorkspace !== agentBootstrapRoot;
|
|
7843
|
+
const workspaceBootstrap = hasDistinctHostWorkspace ? this.loadBootstrapFiles({
|
|
7844
|
+
root: projectContext.hostWorkspace,
|
|
7845
|
+
config: contextConfig.bootstrap,
|
|
7846
|
+
sessionKey: runContext.sessionKey,
|
|
7847
|
+
budget
|
|
7848
|
+
}) : "";
|
|
7849
|
+
const hasSoulFile = /##\s+SOUL\.md\b/i.test(`${projectBootstrap}\n${workspaceBootstrap}`);
|
|
7850
|
+
const sections = [this.buildBootstrapSection({
|
|
7851
|
+
content: projectBootstrap,
|
|
7852
|
+
emptyLabel: "No agent bootstrap files were found.",
|
|
7853
|
+
includeSoulRule: hasSoulFile,
|
|
7854
|
+
loadedLabel: "Agent bootstrap files loaded:",
|
|
7855
|
+
rootLine: `Agent bootstrap root: ${agentBootstrapRoot}`,
|
|
7856
|
+
title: "# Agent Bootstrap Context"
|
|
7857
|
+
})];
|
|
7858
|
+
if (hasDistinctHostWorkspace) sections.push(this.buildBootstrapSection({
|
|
7859
|
+
content: workspaceBootstrap,
|
|
7860
|
+
emptyLabel: "No bootstrap files were found in the NextClaw workspace directory.",
|
|
7861
|
+
loadedLabel: "NextClaw workspace bootstrap files loaded:",
|
|
7862
|
+
rootLine: `NextClaw workspace directory: ${projectContext.hostWorkspace}`,
|
|
7863
|
+
title: "# NextClaw Workspace Bootstrap Context"
|
|
7864
|
+
}));
|
|
7865
|
+
return [sections.filter(Boolean).join("\n\n")];
|
|
7866
|
+
};
|
|
7867
|
+
buildBootstrapSection = (params) => {
|
|
7868
|
+
const { content, emptyLabel, includeSoulRule, loadedLabel, rootLine, title } = params;
|
|
7869
|
+
const lines = [
|
|
7870
|
+
title,
|
|
7871
|
+
"",
|
|
7872
|
+
rootLine
|
|
7873
|
+
];
|
|
7874
|
+
if (includeSoulRule) lines.push("If SOUL.md is present, embody its persona and tone unless higher-priority instructions override it.");
|
|
7875
|
+
if (content) lines.push("", loadedLabel, "", content);
|
|
7876
|
+
else lines.push("", emptyLabel);
|
|
7877
|
+
return lines.join("\n");
|
|
7878
|
+
};
|
|
7879
|
+
loadBootstrapFiles = (params) => {
|
|
7880
|
+
const { budget, config, root, sessionKey } = params;
|
|
7881
|
+
const parts = [];
|
|
7882
|
+
const fileList = this.selectBootstrapFiles(config, sessionKey);
|
|
7883
|
+
for (const filename of fileList) {
|
|
7884
|
+
const filePath = join(root, filename);
|
|
7885
|
+
if (!existsSync(filePath)) continue;
|
|
7886
|
+
const raw = readFileSync(filePath, "utf-8").trim();
|
|
7887
|
+
if (!raw) continue;
|
|
7888
|
+
const perFileLimit = config.perFileChars > 0 ? config.perFileChars : raw.length;
|
|
7889
|
+
const allowed = Math.min(perFileLimit, budget.remaining);
|
|
7890
|
+
if (allowed <= 0) break;
|
|
7891
|
+
const content = truncateContextText(raw, allowed);
|
|
7892
|
+
parts.push(`## ${filename}\n\n${content}`);
|
|
7893
|
+
budget.remaining -= content.length;
|
|
7894
|
+
if (budget.remaining <= 0) break;
|
|
7895
|
+
}
|
|
7896
|
+
return parts.join("\n\n");
|
|
7897
|
+
};
|
|
7898
|
+
createReadBudget = (config) => ({ remaining: config.totalChars > 0 ? config.totalChars : Number.POSITIVE_INFINITY });
|
|
7899
|
+
selectBootstrapFiles = (config, sessionKey) => {
|
|
7900
|
+
if (!sessionKey) return config.files;
|
|
7901
|
+
if (sessionKey.startsWith("cron:") || sessionKey.startsWith("subagent:")) return config.minimalFiles;
|
|
7902
|
+
return config.files;
|
|
7903
|
+
};
|
|
7904
|
+
};
|
|
7905
|
+
//#endregion
|
|
7906
|
+
//#region src/contributions/context-provider/providers/current-session-context.provider.ts
|
|
7907
|
+
var CurrentSessionContextProvider = class {
|
|
7908
|
+
constructor(context) {
|
|
7909
|
+
this.context = context;
|
|
7910
|
+
}
|
|
7911
|
+
provide = async (request) => {
|
|
7912
|
+
const { runContext } = await this.context.resolve(request);
|
|
7913
|
+
const lines = [
|
|
7914
|
+
"## Current Session",
|
|
7915
|
+
`Channel: ${runContext.channel}`,
|
|
7916
|
+
`Chat ID: ${runContext.chatId}`,
|
|
7917
|
+
`Session: ${runContext.sessionKey}`
|
|
7918
|
+
];
|
|
7919
|
+
if (runContext.runtimeThinking) lines.push(`Thinking policy: ${runContext.runtimeThinking}`);
|
|
7920
|
+
return [lines.join("\n")];
|
|
7921
|
+
};
|
|
7922
|
+
};
|
|
7923
|
+
//#endregion
|
|
7924
|
+
//#region src/contributions/context-provider/providers/execution-policy-context.provider.ts
|
|
7925
|
+
function normalizeModel(model) {
|
|
7926
|
+
return model?.trim().toLowerCase() ?? "";
|
|
7927
|
+
}
|
|
7928
|
+
function isOpenAiOrCodexModel(model) {
|
|
7929
|
+
return /(gpt[-/ ]?5|gpt[-/ ]?4|gpt\b|chatgpt|openai|codex|\bo[134]\b)/i.test(normalizeModel(model));
|
|
7930
|
+
}
|
|
7931
|
+
function isGoogleModel(model) {
|
|
7932
|
+
return /(gemini|google)/i.test(normalizeModel(model));
|
|
7933
|
+
}
|
|
7934
|
+
function buildSection(title, lines) {
|
|
7935
|
+
return [title, ...lines].join("\n");
|
|
7936
|
+
}
|
|
7937
|
+
const TOOL_USE_ENFORCEMENT_LINES = [
|
|
7938
|
+
"- When you say you will inspect, run, read, search, edit, or verify something, call the matching tool in the same turn.",
|
|
7939
|
+
"- Do not stop at promises like 'I'll check' or 'I will do that' unless the tool call already happened in that turn.",
|
|
7940
|
+
"- If the task can still move forward with available tools, continue instead of ending early."
|
|
7941
|
+
];
|
|
7942
|
+
const OPENAI_CODEX_DISCIPLINE_LINES = [
|
|
7943
|
+
"- Do not guess time, date, system state, file contents, git state, or other current facts. Check with tools first.",
|
|
7944
|
+
"- When the default scope is already clear, act on it before asking an avoidable clarification question.",
|
|
7945
|
+
"- If the first tool result is empty or incomplete, retry once with a different strategy before stopping."
|
|
7946
|
+
];
|
|
7947
|
+
const GOOGLE_MODEL_GUIDANCE_LINES = [
|
|
7948
|
+
"- Batch independent reads when possible.",
|
|
7949
|
+
"- Read the surrounding context before editing files.",
|
|
7950
|
+
"- Use explicit file paths and keep the answer focused on results."
|
|
7951
|
+
];
|
|
7952
|
+
function renderSystemExecutionPolicy(model) {
|
|
7953
|
+
const sections = [buildSection("## Tool Use Enforcement", TOOL_USE_ENFORCEMENT_LINES)];
|
|
7954
|
+
if (isOpenAiOrCodexModel(model)) sections.push(buildSection("## OpenAI/Codex Execution Discipline", OPENAI_CODEX_DISCIPLINE_LINES));
|
|
7955
|
+
else if (isGoogleModel(model)) sections.push(buildSection("## Google Model Operational Guidance", GOOGLE_MODEL_GUIDANCE_LINES));
|
|
7956
|
+
return sections.join("\n\n");
|
|
7957
|
+
}
|
|
7958
|
+
var ExecutionPolicyContextProvider = class {
|
|
7959
|
+
constructor(context) {
|
|
7960
|
+
this.context = context;
|
|
7961
|
+
}
|
|
7962
|
+
provide = async (request) => {
|
|
7963
|
+
const { runContext } = await this.context.resolve(request);
|
|
7964
|
+
return [renderSystemExecutionPolicy(runContext.effectiveModel)];
|
|
7965
|
+
};
|
|
7966
|
+
};
|
|
7967
|
+
//#endregion
|
|
7968
|
+
//#region src/contributions/context-provider/providers/native-static-context.provider.ts
|
|
7969
|
+
const block = (lines) => lines.join("\n");
|
|
7970
|
+
const staticProvider = (contextBlock) => ({ provide: () => [contextBlock] });
|
|
7971
|
+
const staticBlock = (lines) => staticProvider(block(lines));
|
|
7972
|
+
const createAssistantIdentityContextProvider = () => staticProvider(`You are a personal assistant running inside ${APP_NAME}.`);
|
|
7973
|
+
const createToolCallStyleContextProvider = () => staticBlock([
|
|
7974
|
+
"## Tool Call Style",
|
|
7975
|
+
"Default: do not narrate routine, low-risk tool calls (just call the tool).",
|
|
7976
|
+
"Narrate only when it helps: multi-step work, complex/challenging problems, sensitive actions (e.g., deletions), or when the user explicitly asks.",
|
|
7977
|
+
"Keep narration brief and value-dense; avoid repeating obvious steps.",
|
|
7978
|
+
"Use plain human language for narration unless in a technical context."
|
|
7979
|
+
]);
|
|
7980
|
+
const createChatComposerTokensContextProvider = () => staticBlock([
|
|
7981
|
+
"## Chat Composer Tokens",
|
|
7982
|
+
"When a user message contains tokens like `$weather` or `$web-search`, treat each `$<skill-spec>` token as a user-visible marker that the corresponding skill was explicitly selected in the chat composer.",
|
|
7983
|
+
"These tokens can appear inline with normal prose. Do not ignore them or reinterpret them as shell variables or currency unless the surrounding context clearly says otherwise."
|
|
7984
|
+
]);
|
|
7985
|
+
const createSafetyContextProvider = () => staticBlock([
|
|
7986
|
+
"## Safety",
|
|
7987
|
+
"You have no independent goals: do not pursue self-preservation, replication, resource acquisition, or power-seeking; avoid long-term plans beyond the user's request.",
|
|
7988
|
+
"Prioritize safety and human oversight over completion; if instructions conflict, pause and ask; comply with stop/pause/audit requests and never bypass safeguards. (Inspired by Anthropic's constitution.)",
|
|
7989
|
+
"Do not manipulate or persuade anyone to expand access or disable safeguards. Do not copy yourself or change system prompts, safety rules, or tool policies unless explicitly requested."
|
|
7990
|
+
]);
|
|
7991
|
+
const createCliQuickReferenceContextProvider = () => {
|
|
7992
|
+
const appLower = APP_NAME.toLowerCase();
|
|
7993
|
+
return staticBlock([
|
|
7994
|
+
`## ${APP_NAME} CLI Quick Reference`,
|
|
7995
|
+
`${APP_NAME} is controlled via subcommands. Do not invent commands.`,
|
|
7996
|
+
"To manage the Gateway daemon service (start/stop/restart):",
|
|
7997
|
+
`- ${appLower} gateway status`,
|
|
7998
|
+
`- ${appLower} gateway start`,
|
|
7999
|
+
`- ${appLower} gateway stop`,
|
|
8000
|
+
`- ${appLower} gateway restart`,
|
|
8001
|
+
`If unsure, ask the user to run \`${appLower} help\` (or \`${appLower} gateway --help\`) and paste the output.`
|
|
8002
|
+
]);
|
|
8003
|
+
};
|
|
8004
|
+
const createSelfUpdateContextProvider = () => staticBlock([
|
|
8005
|
+
`## ${APP_NAME} Self-Update`,
|
|
8006
|
+
"Get Updates (self-update) is ONLY allowed when the user explicitly asks for it.",
|
|
8007
|
+
"Do not run config.apply or update.run unless the user explicitly requests an update or config change; if it's not explicit, ask first.",
|
|
8008
|
+
"Actions: config.get, config.schema, config.apply (validate + write full config, then restart), config.patch (merge + restart), update.run (update deps or git, then restart).",
|
|
8009
|
+
"When patching config, copy enum values exactly from config.schema; never invent new variants.",
|
|
8010
|
+
"session.dmScope legal values are exactly: main | per-peer | per-channel-peer | per-account-channel-peer.",
|
|
8011
|
+
"If an enum/path is uncertain, stop and call config.schema first; do not guess.",
|
|
8012
|
+
`After restart, ${APP_NAME} pings the last active session automatically.`
|
|
8013
|
+
]);
|
|
8014
|
+
const createReplyTagsContextProvider = () => staticBlock([
|
|
8015
|
+
"## Reply Tags",
|
|
8016
|
+
"To request a native reply/quote on supported surfaces, include one tag in your reply:",
|
|
8017
|
+
"- Reply tags must be the very first token in the message (no leading text/newlines): [[reply_to_current]] your reply.",
|
|
8018
|
+
"- [[reply_to_current]] replies to the triggering message.",
|
|
8019
|
+
"- Prefer [[reply_to_current]]. Use [[reply_to:<id>]] only when an id was explicitly provided (e.g. by the user or a tool).",
|
|
8020
|
+
"Whitespace inside the tag is allowed (e.g. [[ reply_to_current ]] / [[ reply_to: 123 ]]).",
|
|
8021
|
+
"Tags are stripped before sending; support depends on the current channel config."
|
|
8022
|
+
]);
|
|
8023
|
+
const createMessagingContextProvider = () => staticBlock([
|
|
8024
|
+
"## Messaging",
|
|
8025
|
+
"- Reply in current session → automatically routes to the source channel (Signal, Telegram, etc.)",
|
|
8026
|
+
"- Cross-session or cross-channel messaging → use message(action=send); use sessions_list first when you need to recover an existing route without guessing.",
|
|
8027
|
+
"- Sub-agent orchestration → use subagents(action=list|steer|kill)",
|
|
8028
|
+
"- `[System Message] ...` blocks are internal context and are not user-visible by default.",
|
|
8029
|
+
"- If a `[System Message]` reports completed cron/subagent work and asks for a user update, rewrite it in your normal assistant voice and send that update (do not forward raw system text or default to <noreply/>).",
|
|
8030
|
+
`- Never use exec/curl for provider messaging; ${APP_NAME} handles all routing internally.`,
|
|
8031
|
+
"",
|
|
8032
|
+
"### message tool",
|
|
8033
|
+
"- Use `message` for proactive sends + channel actions (polls, reactions, etc.).",
|
|
8034
|
+
"- For `action=send`, include `message` plus an explicit `to/chatId` whenever the destination is another channel or another conversation.",
|
|
8035
|
+
"- Omitting `to/chatId` only replies to the current conversation; if you set `channel` to a different channel than the current session, `to/chatId` is required.",
|
|
8036
|
+
"- If multiple channels are configured, pass `channel`.",
|
|
8037
|
+
"- If you use `message` (`action=send`) to deliver your user-visible reply, respond with ONLY two blank lines + <noreply/> (avoid duplicate replies)."
|
|
8038
|
+
]);
|
|
8039
|
+
const createMemoryRecallContextProvider = () => staticBlock([
|
|
8040
|
+
"## Memory Recall",
|
|
8041
|
+
"Before answering anything about prior work, decisions, dates, people, preferences, or todos: run memory_search on MEMORY.md + memory/*.md; then use memory_get to pull only the needed lines. If low confidence after search, say you checked.",
|
|
8042
|
+
"Citations: include Source: <path#line> when it helps the user verify memory snippets."
|
|
8043
|
+
]);
|
|
8044
|
+
const createSilentRepliesContextProvider = () => staticBlock([
|
|
8045
|
+
"## Silent Replies",
|
|
8046
|
+
`Silent marker token: ${SILENT_REPLY_TOKEN}`,
|
|
8047
|
+
"When you have nothing to say, respond with EXACTLY two blank lines followed by <noreply/>",
|
|
8048
|
+
"",
|
|
8049
|
+
"⚠️ Rules:",
|
|
8050
|
+
"- It must be your ENTIRE message — nothing else",
|
|
8051
|
+
"- If <noreply/> appears anywhere, the system will stop reply/output and subsequent processing",
|
|
8052
|
+
"- Never wrap it in markdown or code blocks",
|
|
8053
|
+
"",
|
|
8054
|
+
"❌ Wrong: \"Here's help... <noreply/>\"",
|
|
8055
|
+
"❌ Wrong: \"<noreply/>\"",
|
|
8056
|
+
"✅ Right: \"\\n\\n<noreply/>\""
|
|
8057
|
+
]);
|
|
8058
|
+
const createRuntimeContextProvider = () => staticBlock([
|
|
8059
|
+
"## Runtime",
|
|
8060
|
+
`Runtime: ${process.platform} ${process.arch}, Node ${process.version}`,
|
|
8061
|
+
"Time handling: do not assume exact minute/second unless the user/tool explicitly provides it.",
|
|
8062
|
+
"When a turn includes a time hint, treat it as context for relative-time interpretation in that turn."
|
|
8063
|
+
]);
|
|
8064
|
+
const createSelfManagementContextProvider = () => ({ provide: () => {
|
|
8065
|
+
const appLower = APP_NAME.toLowerCase();
|
|
8066
|
+
const selfManageGuide = resolveNextclawSelfManageGuidePaths();
|
|
8067
|
+
return [block([
|
|
8068
|
+
`## ${APP_NAME} Self-Management Guide`,
|
|
8069
|
+
`- For ${APP_NAME} self-management operations (version/status/doctor/service/channels/config/agents/cron/remote/update), read \`${selfManageGuide.primaryPath ?? "the built-in NextClaw self-management guide"}\` first.`,
|
|
8070
|
+
"- Treat these product-management intents as higher priority than generic skills with overlapping words such as create/install/publish.",
|
|
8071
|
+
"- Do not load unrelated generic skills before reading the built-in self-management guide for a self-management intent.",
|
|
8072
|
+
"- Workspace `USAGE.md` snapshots and copied built-in skills are deprecated artifacts; the built-in package guide is the source of truth.",
|
|
8073
|
+
...selfManageGuide.repoDocsPath ? [`- In repo source checkouts, the authoring copy is \`${selfManageGuide.repoDocsPath}\`; only use it when the packaged guide path above is unavailable.`] : [],
|
|
8074
|
+
"- If no guide file is available, fall back to command help output.",
|
|
8075
|
+
`- For version lookup, use \`${appLower} --version\` exactly; do not infer version from status output.`,
|
|
8076
|
+
`- After mutating operations, validate with \`${appLower} status --json\` (and \`${appLower} doctor --json\` when needed).`,
|
|
8077
|
+
`- For Agent CRUD, use \`${appLower} agents list|new|update|remove --json\` for the normal path; do not directly edit \`config.json\` or \`agents.list\` for routine Agent management.`,
|
|
8078
|
+
"- When creating Agents, prefer explicit non-text avatars and avoid text/initial-based avatar styles such as DiceBear `initials` as the default recommendation."
|
|
8079
|
+
])];
|
|
8080
|
+
} });
|
|
8081
|
+
const createSessionOrchestrationContextProvider = () => staticBlock([
|
|
8082
|
+
"## Session Orchestration",
|
|
8083
|
+
"- Before passing a non-default `runtime` to `sessions_spawn` or agent creation/update flows, inspect the installed runtime kinds with `nextclaw agents runtimes --json`.",
|
|
8084
|
+
"- `sessions_spawn` is the unified session-creation tool. Omit `scope` or use `scope=\"standalone\"` for a regular session, and use `scope=\"child\"` when the new session should be a child session of the current flow.",
|
|
8085
|
+
"- `sessions_spawn` only creates the session by default. Add top-level `notify: \"none\" | \"final_reply\"` when the new session should start working immediately.",
|
|
8086
|
+
"- When `sessions_spawn.scope=\"child\"` and `sessions_spawn.notify=\"final_reply\"`, the new child session starts right away and this session automatically continues after that child reaches its final reply.",
|
|
8087
|
+
"- Use `sessions_spawn` without `notify` when the user wants a separate thread created now but does not need it to start working yet.",
|
|
8088
|
+
"- Use `sessions_request` to send one task to an existing session, including a session that was just created by `sessions_spawn` or a previously created child session.",
|
|
8089
|
+
"- `sessions_request.target` must be an object shaped like `{ \"session_id\": \"<target-session-id>\" }`. Do not pass a bare string.",
|
|
8090
|
+
"- Prefer `notify=\"final_reply\"` when the current session should continue after the target session produces its final reply. Use `notify=\"none\"` when you only want the target session to run independently."
|
|
8091
|
+
]);
|
|
8092
|
+
//#endregion
|
|
8093
|
+
//#region src/contributions/context-provider/providers/project-context.provider.ts
|
|
8094
|
+
var ProjectContextProvider = class {
|
|
8095
|
+
constructor(context) {
|
|
8096
|
+
this.context = context;
|
|
8097
|
+
}
|
|
8098
|
+
provide = async (request) => {
|
|
8099
|
+
const { projectContext } = await this.context.resolve(request);
|
|
8100
|
+
const repositoryIdentity = DEFAULT_WORKSPACE_REPOSITORY_IDENTITY_RESOLVER.resolve(projectContext.effectiveWorkspace);
|
|
8101
|
+
return [this.buildProjectSection({
|
|
8102
|
+
projectContext,
|
|
8103
|
+
repositoryIdentity
|
|
8104
|
+
})];
|
|
8105
|
+
};
|
|
8106
|
+
buildProjectSection = (params) => {
|
|
8107
|
+
const { projectContext, repositoryIdentity } = params;
|
|
8108
|
+
const lines = [
|
|
8109
|
+
"# Project Context",
|
|
8110
|
+
"",
|
|
8111
|
+
`Active project directory: ${projectContext.effectiveWorkspace}`
|
|
8112
|
+
];
|
|
8113
|
+
if (projectContext.projectRoot) lines.push(`Session-bound project root: ${projectContext.projectRoot}`, "This session is explicitly bound to that project directory. Use it as the primary repo and file-operation context for the user's work.");
|
|
8114
|
+
else lines.push("No explicit session project root is set. Use the active project directory as the primary repo and file-operation context for the user's work.");
|
|
8115
|
+
lines.push(...this.buildRepositoryIdentityLines(repositoryIdentity));
|
|
8116
|
+
return lines.join("\n");
|
|
8117
|
+
};
|
|
8118
|
+
buildRepositoryIdentityLines = (repositoryIdentity) => {
|
|
8119
|
+
if (!repositoryIdentity.repoRoot) return ["No Git repository metadata was detected for the active project directory. Do not assume external repository URLs refer to this project unless the user explicitly says so."];
|
|
8120
|
+
const lines = [`Repository root: ${repositoryIdentity.repoRoot}`];
|
|
8121
|
+
if (repositoryIdentity.canonicalWebUrl) lines.push(`Canonical repository: ${repositoryIdentity.canonicalWebUrl}`);
|
|
8122
|
+
else if (repositoryIdentity.canonicalRemoteUrl) {
|
|
8123
|
+
const remoteLabel = repositoryIdentity.canonicalRemoteName ? ` (${repositoryIdentity.canonicalRemoteName})` : "";
|
|
8124
|
+
lines.push(`Canonical git remote${remoteLabel}: ${repositoryIdentity.canonicalRemoteUrl}`);
|
|
8125
|
+
}
|
|
8126
|
+
lines.push("Repository identity rule: treat any other repository URL mentioned in this context as an external reference unless it exactly matches the canonical repository above.");
|
|
8127
|
+
return lines;
|
|
8128
|
+
};
|
|
8129
|
+
};
|
|
8130
|
+
//#endregion
|
|
8131
|
+
//#region src/contributions/context-provider/providers/reply-format-context.provider.ts
|
|
8132
|
+
var ReplyFormatContextProvider = class {
|
|
8133
|
+
provide = (_request) => [[
|
|
8134
|
+
"## Reply Formatting",
|
|
8135
|
+
"- When mentioning a local project file in a user-visible reply, prefer a Markdown link such as `[AGENTS.md](AGENTS.md)` or `[file](packages/example/file.ts)` so the user can open it.",
|
|
8136
|
+
"- Use project-relative links for files under the active/session-bound project root; use absolute links only for files outside it."
|
|
8137
|
+
].join("\n")];
|
|
8138
|
+
};
|
|
8139
|
+
//#endregion
|
|
8140
|
+
//#region src/contributions/context-provider/providers/skills-context.provider.ts
|
|
8141
|
+
function wrapSkillTag(tagName, manifest) {
|
|
8142
|
+
return [
|
|
8143
|
+
`<${tagName}>`,
|
|
8144
|
+
manifest,
|
|
8145
|
+
`</${tagName}>`
|
|
8146
|
+
].join("\n");
|
|
8147
|
+
}
|
|
8148
|
+
function renderActiveSkillsSection(skills, skillSelectors) {
|
|
8149
|
+
const manifest = skills.buildSkillsManifest(skillSelectors);
|
|
8150
|
+
if (!manifest) return "";
|
|
8151
|
+
return [
|
|
8152
|
+
"# Active Skills",
|
|
8153
|
+
"These always-on skills are already active for this session context.",
|
|
8154
|
+
"If an active skill covers the user's intent, follow it before considering unrelated available skills.",
|
|
8155
|
+
"For NextClaw self-management intents, read the built-in NextClaw self-management guide before loading any unrelated generic skill.",
|
|
8156
|
+
"Skill refs are unique identities; names may repeat.",
|
|
8157
|
+
"Read a SKILL.md from <location> only when you need its instructions.",
|
|
8158
|
+
"",
|
|
8159
|
+
wrapSkillTag("active_skills", manifest)
|
|
8160
|
+
].join("\n\n");
|
|
8161
|
+
}
|
|
8162
|
+
function renderAvailableSkillsSection(skills) {
|
|
8163
|
+
const summary = skills.buildSkillsSummary();
|
|
8164
|
+
if (!summary) return "";
|
|
8165
|
+
return [
|
|
8166
|
+
"## Skills (mandatory)",
|
|
8167
|
+
"Always-on skills in <active_skills> take precedence over this list.",
|
|
8168
|
+
"Before replying: first check whether any entry in <available_skills> may be relevant to the user's intent, task type, or requested output. Do not skip this check just because the task seems familiar.",
|
|
8169
|
+
"- If one skill looks like the best relevant match, read its SKILL.md at <location> with `read_file`, then decide whether following it is actually helpful.",
|
|
8170
|
+
"- If a SKILL.md read says `Use offset=... to continue`, continue reading until the relevant trigger, required workflow, constraints, and output requirements are covered.",
|
|
8171
|
+
"- If the user is asking to manage NextClaw itself, read the built-in NextClaw self-management guide first and do not open unrelated generic skills before that.",
|
|
8172
|
+
"- If multiple skills share the same <name>, use <ref> to distinguish them. Never assume duplicate names mean the same skill.",
|
|
8173
|
+
"- If none clearly apply: do not read any SKILL.md.",
|
|
8174
|
+
"Constraints: never read more than one skill up front; only read after selecting.",
|
|
8175
|
+
"",
|
|
8176
|
+
"<available_skills>",
|
|
8177
|
+
summary,
|
|
8178
|
+
"</available_skills>"
|
|
8179
|
+
].join("\n");
|
|
8180
|
+
}
|
|
8181
|
+
function renderSkillLearningSection() {
|
|
8182
|
+
return [
|
|
8183
|
+
"# Skill Learning Loop",
|
|
8184
|
+
"After non-trivial work, run a brief review before your final answer.",
|
|
8185
|
+
"- Summarize the reusable lesson, not the full transcript.",
|
|
8186
|
+
"- Decide exactly one outcome: `no_skill_change`, `patch_existing_skill`, or `create_new_skill`.",
|
|
8187
|
+
"- Prefer patching an existing skill when the lesson extends or corrects it; only create a new skill when the trigger and workflow are genuinely distinct.",
|
|
8188
|
+
"- Promote a lesson into a skill only when it has a clear trigger, repeatable steps, and failure signals/checks.",
|
|
8189
|
+
"- Do not create skills for one-off facts, narrow local quirks, or work that is not likely to recur.",
|
|
8190
|
+
"- Keep the review concise and action-oriented. Do not add user-visible review text unless it materially helps or the user asks for it."
|
|
8191
|
+
].join("\n");
|
|
8192
|
+
}
|
|
8193
|
+
var SkillsContextProvider = class {
|
|
8194
|
+
constructor(context) {
|
|
8195
|
+
this.context = context;
|
|
8196
|
+
}
|
|
8197
|
+
provide = async (request) => {
|
|
8198
|
+
const { projectContext } = await this.context.resolve(request);
|
|
8199
|
+
const skills = new SkillsLoader({
|
|
8200
|
+
workspace: projectContext.hostWorkspace,
|
|
8201
|
+
projectRoot: projectContext.projectRoot
|
|
8202
|
+
});
|
|
8203
|
+
const blocks = [];
|
|
8204
|
+
const alwaysSkills = skills.getAlwaysSkills();
|
|
8205
|
+
if (alwaysSkills.length) {
|
|
8206
|
+
const activeSection = renderActiveSkillsSection(skills, alwaysSkills);
|
|
8207
|
+
if (activeSection) blocks.push(activeSection);
|
|
8208
|
+
}
|
|
8209
|
+
const availableSkillsSection = renderAvailableSkillsSection(skills);
|
|
8210
|
+
if (availableSkillsSection) blocks.push(availableSkillsSection);
|
|
8211
|
+
blocks.push(renderSkillLearningSection());
|
|
8212
|
+
return blocks;
|
|
8213
|
+
};
|
|
8214
|
+
};
|
|
8215
|
+
//#endregion
|
|
8216
|
+
//#region src/contributions/context-provider/providers/tooling-context.provider.ts
|
|
8217
|
+
var ToolingContextProvider = class {
|
|
8218
|
+
constructor(context) {
|
|
8219
|
+
this.context = context;
|
|
8220
|
+
}
|
|
8221
|
+
provide = async (request) => {
|
|
8222
|
+
const { toolCatalog } = await this.context.resolve(request);
|
|
8223
|
+
return [[
|
|
8224
|
+
"## Tooling",
|
|
8225
|
+
"Tool availability (filtered by policy):",
|
|
8226
|
+
"Tool names are case-sensitive. Call tools exactly as listed.",
|
|
8227
|
+
...toolCatalog.length > 0 ? toolCatalog.map((tool) => `- ${tool.name}: ${tool.description ?? "No description available"}`) : ["- No tools available for this turn."],
|
|
8228
|
+
"TOOLS.md does not control tool availability; it is user guidance for how to use external tools.",
|
|
8229
|
+
"For long waits, avoid rapid poll loops: use exec with enough yieldMs.",
|
|
8230
|
+
"For relative time/date scheduling requests (for example 'in 5 minutes' / '1分钟后'), first check the current local time with an available tool such as exec/date, then convert it to an absolute ISO time with timezone. Do not guess.",
|
|
8231
|
+
"If a task is more complex or takes longer, spawn a sub-agent. Completion is push-based: it will auto-announce when done.",
|
|
8232
|
+
"Do not poll `subagents list` / `sessions_list` in a loop; only check status on-demand (for intervention, debugging, or when explicitly asked)."
|
|
8233
|
+
].join("\n")];
|
|
8234
|
+
};
|
|
8235
|
+
};
|
|
8236
|
+
//#endregion
|
|
8237
|
+
//#region src/contributions/context-provider/providers/workspace-context.provider.ts
|
|
8238
|
+
var WorkspaceContextProvider = class {
|
|
8239
|
+
constructor(context) {
|
|
8240
|
+
this.context = context;
|
|
8241
|
+
}
|
|
8242
|
+
provide = async (request) => {
|
|
8243
|
+
const { runContext } = await this.context.resolve(request);
|
|
8244
|
+
return [[
|
|
8245
|
+
"## Workspace",
|
|
8246
|
+
`Your working directory is: ${runContext.effectiveWorkspace}`,
|
|
8247
|
+
"Treat this directory as the single global workspace for file operations unless explicitly instructed otherwise."
|
|
8248
|
+
].join("\n")];
|
|
8249
|
+
};
|
|
8250
|
+
};
|
|
8251
|
+
//#endregion
|
|
8252
|
+
//#region src/contributions/context-provider/providers/workspace-memory-context.provider.ts
|
|
8253
|
+
var WorkspaceMemoryContextProvider = class {
|
|
8254
|
+
constructor(context) {
|
|
8255
|
+
this.context = context;
|
|
8256
|
+
}
|
|
8257
|
+
provide = async (request) => {
|
|
8258
|
+
const { contextConfig, projectContext } = await this.context.resolve(request);
|
|
8259
|
+
const memoryConfig = contextConfig.memory;
|
|
8260
|
+
if (!memoryConfig.enabled) return [];
|
|
8261
|
+
const memory = new MemoryStore(projectContext.hostWorkspace).getMemoryContext();
|
|
8262
|
+
if (!memory) return [];
|
|
8263
|
+
return [`# Memory\n\n${truncateContextText(memory, memoryConfig.maxChars)}`];
|
|
8264
|
+
};
|
|
8265
|
+
};
|
|
8266
|
+
//#endregion
|
|
7708
8267
|
//#region src/utils/agent-run-request-metadata.utils.ts
|
|
7709
8268
|
function normalizeString(value) {
|
|
7710
8269
|
return value?.trim() || void 0;
|
|
@@ -7726,12 +8285,60 @@ function buildAgentRunRequestMetadata(params) {
|
|
|
7726
8285
|
};
|
|
7727
8286
|
}
|
|
7728
8287
|
//#endregion
|
|
7729
|
-
//#region src/contributions/context-provider/
|
|
7730
|
-
|
|
8288
|
+
//#region src/contributions/context-provider/utils/native-context-config.utils.ts
|
|
8289
|
+
const DEFAULT_NATIVE_CONTEXT_CONFIG = {
|
|
8290
|
+
bootstrap: {
|
|
8291
|
+
files: [
|
|
8292
|
+
"AGENTS.md",
|
|
8293
|
+
"SOUL.md",
|
|
8294
|
+
"USER.md",
|
|
8295
|
+
"IDENTITY.md",
|
|
8296
|
+
"TOOLS.md",
|
|
8297
|
+
"BOOT.md",
|
|
8298
|
+
"BOOTSTRAP.md"
|
|
8299
|
+
],
|
|
8300
|
+
minimalFiles: [
|
|
8301
|
+
"AGENTS.md",
|
|
8302
|
+
"SOUL.md",
|
|
8303
|
+
"TOOLS.md",
|
|
8304
|
+
"IDENTITY.md"
|
|
8305
|
+
],
|
|
8306
|
+
perFileChars: 4e3,
|
|
8307
|
+
totalChars: 12e3
|
|
8308
|
+
},
|
|
8309
|
+
memory: {
|
|
8310
|
+
enabled: true,
|
|
8311
|
+
maxChars: 8e3
|
|
8312
|
+
}
|
|
8313
|
+
};
|
|
8314
|
+
function mergeNativeContextConfig(contextConfig) {
|
|
8315
|
+
return {
|
|
8316
|
+
bootstrap: {
|
|
8317
|
+
...DEFAULT_NATIVE_CONTEXT_CONFIG.bootstrap,
|
|
8318
|
+
...contextConfig?.bootstrap ?? {}
|
|
8319
|
+
},
|
|
8320
|
+
memory: {
|
|
8321
|
+
...DEFAULT_NATIVE_CONTEXT_CONFIG.memory,
|
|
8322
|
+
...contextConfig?.memory ?? {}
|
|
8323
|
+
}
|
|
8324
|
+
};
|
|
8325
|
+
}
|
|
8326
|
+
//#endregion
|
|
8327
|
+
//#region src/contributions/context-provider/services/context-provider-run-context.service.ts
|
|
8328
|
+
var ContextProviderRunContextService = class {
|
|
8329
|
+
snapshots = /* @__PURE__ */ new WeakMap();
|
|
8330
|
+
projectContextResolver = new SessionProjectContextResolver();
|
|
7731
8331
|
constructor(kernel) {
|
|
7732
8332
|
this.kernel = kernel;
|
|
7733
8333
|
}
|
|
7734
|
-
|
|
8334
|
+
resolve = (request) => {
|
|
8335
|
+
const existing = this.snapshots.get(request);
|
|
8336
|
+
if (existing) return existing;
|
|
8337
|
+
const next = this.resolveSnapshot(request);
|
|
8338
|
+
this.snapshots.set(request, next);
|
|
8339
|
+
return next;
|
|
8340
|
+
};
|
|
8341
|
+
resolveSnapshot = async (request) => {
|
|
7735
8342
|
const session = request.sessionId ? await this.kernel.sessionManager.getAgentRunSession(request.sessionId) : null;
|
|
7736
8343
|
const sessionId = session?.sessionId ?? request.sessionId ?? request.message.sessionId ?? "";
|
|
7737
8344
|
const requestMetadata = buildAgentRunRequestMetadata({
|
|
@@ -7746,45 +8353,60 @@ var KernelContextProvider = class {
|
|
|
7746
8353
|
storedAgentId: request.agentId ?? session?.agentId
|
|
7747
8354
|
});
|
|
7748
8355
|
const tools = await this.kernel.toolProviderManager.buildTools(request);
|
|
7749
|
-
|
|
7750
|
-
|
|
8356
|
+
const sessionProjectRoot = readSessionProjectRoot(runContext.sessionMetadata);
|
|
8357
|
+
const projectContext = this.projectContextResolver.resolve({
|
|
8358
|
+
sessionMetadata: sessionProjectRoot ? { project_root: sessionProjectRoot } : null,
|
|
8359
|
+
workspace: runContext.profile.workspace,
|
|
8360
|
+
defaultWorkspace: runContext.effectiveWorkspace
|
|
8361
|
+
});
|
|
8362
|
+
return {
|
|
8363
|
+
contextConfig: mergeNativeContextConfig(runContext.config.agents.context),
|
|
8364
|
+
projectContext,
|
|
8365
|
+
runContext,
|
|
8366
|
+
toolCatalog: buildToolCatalogEntries(tools.map((tool) => ({
|
|
7751
8367
|
name: tool.name,
|
|
7752
|
-
description: tool.description
|
|
7753
|
-
|
|
7754
|
-
|
|
7755
|
-
runContext
|
|
7756
|
-
})];
|
|
7757
|
-
};
|
|
7758
|
-
buildSystemContextBlock = (params) => {
|
|
7759
|
-
const { availableTools, runContext } = params;
|
|
7760
|
-
const lines = [
|
|
7761
|
-
new ContextBuilder(runContext.effectiveWorkspace, runContext.config.agents.context, {
|
|
7762
|
-
hostWorkspace: runContext.profile.workspace,
|
|
7763
|
-
sessionProjectRoot: readSessionProjectRoot(runContext.sessionMetadata)
|
|
7764
|
-
}).buildSystemPrompt(void 0, runContext.sessionKey, availableTools, [buildSessionOrchestrationSection(), buildMinimalSystemExecutionPrompt(runContext.effectiveModel)]),
|
|
7765
|
-
"## Current Session",
|
|
7766
|
-
`Channel: ${runContext.channel}`,
|
|
7767
|
-
`Chat ID: ${runContext.chatId}`,
|
|
7768
|
-
`Session: ${runContext.sessionKey}`
|
|
7769
|
-
];
|
|
7770
|
-
if (runContext.runtimeThinking) lines.push(`Thinking policy: ${runContext.runtimeThinking}`);
|
|
7771
|
-
return lines.join("\n");
|
|
8368
|
+
description: tool.description
|
|
8369
|
+
})))
|
|
8370
|
+
};
|
|
7772
8371
|
};
|
|
7773
8372
|
};
|
|
7774
8373
|
//#endregion
|
|
7775
8374
|
//#region src/contributions/context-provider/index.ts
|
|
7776
8375
|
var ContextProviderContribution = class {
|
|
7777
|
-
|
|
8376
|
+
cleanups = [];
|
|
7778
8377
|
constructor(kernel) {
|
|
7779
8378
|
this.kernel = kernel;
|
|
7780
8379
|
}
|
|
7781
8380
|
start = () => {
|
|
7782
|
-
if (this.
|
|
7783
|
-
|
|
8381
|
+
if (this.cleanups.length > 0) return;
|
|
8382
|
+
const context = new ContextProviderRunContextService(this.kernel);
|
|
8383
|
+
for (const provider of [
|
|
8384
|
+
createAssistantIdentityContextProvider(),
|
|
8385
|
+
new ToolingContextProvider(context),
|
|
8386
|
+
createToolCallStyleContextProvider(),
|
|
8387
|
+
createChatComposerTokensContextProvider(),
|
|
8388
|
+
createSafetyContextProvider(),
|
|
8389
|
+
createCliQuickReferenceContextProvider(),
|
|
8390
|
+
createSelfUpdateContextProvider(),
|
|
8391
|
+
new WorkspaceContextProvider(context),
|
|
8392
|
+
createReplyTagsContextProvider(),
|
|
8393
|
+
createMessagingContextProvider(),
|
|
8394
|
+
createMemoryRecallContextProvider(),
|
|
8395
|
+
createSilentRepliesContextProvider(),
|
|
8396
|
+
createRuntimeContextProvider(),
|
|
8397
|
+
createSelfManagementContextProvider(),
|
|
8398
|
+
new ProjectContextProvider(context),
|
|
8399
|
+
new AgentBootstrapContextProvider(context),
|
|
8400
|
+
new WorkspaceMemoryContextProvider(context),
|
|
8401
|
+
new SkillsContextProvider(context),
|
|
8402
|
+
createSessionOrchestrationContextProvider(),
|
|
8403
|
+
new ExecutionPolicyContextProvider(context),
|
|
8404
|
+
new CurrentSessionContextProvider(context),
|
|
8405
|
+
new ReplyFormatContextProvider()
|
|
8406
|
+
]) this.cleanups.push(this.kernel.contextProviderManager.register(provider));
|
|
7784
8407
|
};
|
|
7785
8408
|
dispose = () => {
|
|
7786
|
-
this.
|
|
7787
|
-
this.unregister = null;
|
|
8409
|
+
while (this.cleanups.length > 0) this.cleanups.pop()?.();
|
|
7788
8410
|
};
|
|
7789
8411
|
};
|
|
7790
8412
|
//#endregion
|
|
@@ -9588,6 +10210,6 @@ function resolveLegacyEventType(message) {
|
|
|
9588
10210
|
return `message.${role || "other"}`;
|
|
9589
10211
|
}
|
|
9590
10212
|
//#endregion
|
|
9591
|
-
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,
|
|
10213
|
+
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, 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 };
|
|
9592
10214
|
|
|
9593
10215
|
//# sourceMappingURL=index.js.map
|