engine7 7.1.56 → 7.1.58
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/cli.mjs +19285 -457
- package/dist/engine-startup.mjs +829 -455
- package/dist/main.mjs +832 -458
- package/package.json +60 -60
package/dist/main.mjs
CHANGED
|
@@ -1861,8 +1861,8 @@ var init_microCompact = __esm({
|
|
|
1861
1861
|
});
|
|
1862
1862
|
|
|
1863
1863
|
// src/compact/ruleCompact.ts
|
|
1864
|
-
function smartCompressToolResult(content, essentialFields) {
|
|
1865
|
-
if (content.length <= TOOL_SIZE_LIMIT) return content;
|
|
1864
|
+
function smartCompressToolResult(content, essentialFields, sizeLimit) {
|
|
1865
|
+
if (content.length <= (sizeLimit ?? TOOL_SIZE_LIMIT)) return content;
|
|
1866
1866
|
try {
|
|
1867
1867
|
const data = JSON.parse(content);
|
|
1868
1868
|
if (Array.isArray(data) && data.length > 0) {
|
|
@@ -1901,36 +1901,37 @@ ${result}`;
|
|
|
1901
1901
|
}
|
|
1902
1902
|
} catch {
|
|
1903
1903
|
}
|
|
1904
|
-
return content.slice(0, TRUNCATE_TO) + "\n...[TRUNCATED]";
|
|
1904
|
+
return content.slice(0, sizeLimit ?? TRUNCATE_TO) + "\n...[TRUNCATED]";
|
|
1905
1905
|
}
|
|
1906
|
-
function
|
|
1907
|
-
|
|
1908
|
-
if (
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1906
|
+
function foldTurnInline(turn) {
|
|
1907
|
+
const toolMsgs = turn.filter((m) => m.role === "tool");
|
|
1908
|
+
if (toolMsgs.length === 0) return turn;
|
|
1909
|
+
let i = 0;
|
|
1910
|
+
while (i < turn.length && turn[i].role !== "assistant" && turn[i].role !== "tool") i++;
|
|
1911
|
+
const head = turn.slice(0, i);
|
|
1912
|
+
const rest = turn.slice(i);
|
|
1913
|
+
let finalAnswer = null;
|
|
1914
|
+
for (let j = rest.length - 1; j >= 0; j--) {
|
|
1915
|
+
const m = rest[j];
|
|
1916
|
+
if (m.role === "assistant" && !(m.tool_calls && m.tool_calls.length > 0)) {
|
|
1917
|
+
const text = typeof m.content === "string" ? m.content.trim() : "";
|
|
1918
|
+
if (text) {
|
|
1919
|
+
finalAnswer = m;
|
|
1920
|
+
break;
|
|
1921
|
+
}
|
|
1916
1922
|
}
|
|
1917
1923
|
}
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
const contentStr = typeof rawContent === "string" ? rawContent : "";
|
|
1927
|
-
const compressed = smartCompressToolResult(contentStr);
|
|
1928
|
-
parts.push(`[${i + 1}] ${compressed}`);
|
|
1924
|
+
const parts = toolMsgs.map((m, k) => `[${k + 1}] ${smartCompressToolResult(typeof m.content === "string" ? m.content : "")}`);
|
|
1925
|
+
const merged = parts.join("\n---\n");
|
|
1926
|
+
if (finalAnswer) {
|
|
1927
|
+
return [...head, { role: "assistant", content: `\uFF08\u8BE5\u8F6E\u5386\u53F2\u5DE5\u5177\u8F93\u51FA\uFF0C\u5171 ${toolMsgs.length} \u6761\uFF0C\u5DF2\u538B\u7F29\uFF09
|
|
1928
|
+
${merged}
|
|
1929
|
+
|
|
1930
|
+
\uFF08\u8BE5\u8F6E\u6700\u7EC8\u7ED3\u8BBA\uFF09
|
|
1931
|
+
${finalAnswer.content || ""}` }];
|
|
1929
1932
|
}
|
|
1930
|
-
return {
|
|
1931
|
-
|
|
1932
|
-
count: toolMsgs.length
|
|
1933
|
-
};
|
|
1933
|
+
return [...head, { role: "assistant", content: `\uFF08\u8BE5\u8F6E\u5386\u53F2\u5DE5\u5177\u8F93\u51FA\uFF0C\u5171 ${toolMsgs.length} \u6761\uFF0C\u8BE5\u8F6E\u65E0\u6587\u5B57\u7ED3\u8BBA\uFF09
|
|
1934
|
+
${merged}` }];
|
|
1934
1935
|
}
|
|
1935
1936
|
function splitByUserTurns(messages) {
|
|
1936
1937
|
const system = [];
|
|
@@ -1997,32 +1998,9 @@ function step2_compressOldTurns(messages, maxTokens, toolResultMode = "minimal")
|
|
|
1997
1998
|
const origTokens = estimateMessageTokens(turn);
|
|
1998
1999
|
newMessages.push(userMsg);
|
|
1999
2000
|
if (toolResultMode === "inline") {
|
|
2000
|
-
const
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
if (mergedTools) {
|
|
2004
|
-
newMessages.push(msg.assistant(
|
|
2005
|
-
`\uFF08\u8BE5\u8F6E\u5386\u53F2\u5DE5\u5177\u8F93\u51FA\uFF0C\u5171 ${mergedTools.count} \u6761\uFF0C\u5DF2\u538B\u7F29\uFF09
|
|
2006
|
-
${mergedTools.content}
|
|
2007
|
-
|
|
2008
|
-
\uFF08\u8BE5\u8F6E\u6700\u7EC8\u7ED3\u8BBA\uFF09
|
|
2009
|
-
${finalAnswer.content || ""}`
|
|
2010
|
-
));
|
|
2011
|
-
compactLog(TAG3, `Step 2 turn[${turnIdx}]: ${origMsgCount} msgs (${origTokens} tok) \u2192 2 msgs (user+inline-merged, ${mergedTools.count} tool results, mode=inline)`);
|
|
2012
|
-
} else {
|
|
2013
|
-
newMessages.push(finalAnswer);
|
|
2014
|
-
compactLog(TAG3, `Step 2 turn[${turnIdx}]: ${origMsgCount} msgs (${origTokens} tok) \u2192 2 msgs (user+finalAnswer, no tools)`);
|
|
2015
|
-
}
|
|
2016
|
-
} else {
|
|
2017
|
-
const mergedTools = mergeToolResults(rest);
|
|
2018
|
-
if (mergedTools) {
|
|
2019
|
-
newMessages.push(msg.assistant(`\uFF08\u8BE5\u8F6E\u5386\u53F2\u5DE5\u5177\u8F93\u51FA\uFF0C\u5171 ${mergedTools.count} \u6761\uFF0C\u8BE5\u8F6E\u65E0\u6587\u5B57\u7ED3\u8BBA\uFF09
|
|
2020
|
-
${mergedTools.content}`));
|
|
2021
|
-
compactLog(TAG3, `Step 2 turn[${turnIdx}]: ${origMsgCount} msgs (${origTokens} tok) \u2192 2 msgs (user+inline-dataOnly, ${mergedTools.count} tool results, no final answer, mode=inline)`);
|
|
2022
|
-
} else {
|
|
2023
|
-
compactLog(TAG3, `Step 2 turn[${turnIdx}]: ${origMsgCount} msgs (${origTokens} tok) \u2192 1 msg (user only, no tools/final)`);
|
|
2024
|
-
}
|
|
2025
|
-
}
|
|
2001
|
+
const folded = foldTurnInline(turn);
|
|
2002
|
+
newMessages.push(...folded.slice(1));
|
|
2003
|
+
compactLog(TAG3, `Step 2 turn[${turnIdx}]: ${origMsgCount} msgs (${origTokens} tok) \u2192 ${1 + folded.length - 1} msgs (inline folded, mode=inline)`);
|
|
2026
2004
|
} else {
|
|
2027
2005
|
const kept = rest.filter((m) => m.role !== "tool").map((m) => {
|
|
2028
2006
|
if (m.role === "assistant" && m.tool_calls && m.tool_calls.length > 0) {
|
|
@@ -2140,7 +2118,6 @@ var TAG3, TOOL_SIZE_LIMIT, TRUNCATE_TO, MIN_HISTORY, KEEP_RECENT_TURNS, MAX_LIST
|
|
|
2140
2118
|
var init_ruleCompact = __esm({
|
|
2141
2119
|
"src/compact/ruleCompact.ts"() {
|
|
2142
2120
|
"use strict";
|
|
2143
|
-
init_types();
|
|
2144
2121
|
init_tokenEstimate();
|
|
2145
2122
|
init_autoCompact();
|
|
2146
2123
|
init_compactLog();
|
|
@@ -3047,7 +3024,7 @@ var init_query = __esm({
|
|
|
3047
3024
|
this.provider = provider;
|
|
3048
3025
|
this.options = options;
|
|
3049
3026
|
if (options.compactConfig) {
|
|
3050
|
-
this.compactConfig = { ...DEFAULT_COMPACT_CONFIG, ...options.compactConfig };
|
|
3027
|
+
this.compactConfig = { ...DEFAULT_COMPACT_CONFIG, ...this.resolveOpt(options.compactConfig) };
|
|
3051
3028
|
}
|
|
3052
3029
|
this.logPrefix = `[query:${options.agentLabel || "main"}]`;
|
|
3053
3030
|
}
|
|
@@ -3140,6 +3117,10 @@ var init_query = __esm({
|
|
|
3140
3117
|
}
|
|
3141
3118
|
/** 日志前缀(区分多个并行 QueryEngine) */
|
|
3142
3119
|
logPrefix;
|
|
3120
|
+
/** 函数形态 option 的统一解析(0902:systemPrompt/systemStable/compactConfig/maxTokens 共用) */
|
|
3121
|
+
resolveOpt(v) {
|
|
3122
|
+
return typeof v === "function" ? v() : v;
|
|
3123
|
+
}
|
|
3143
3124
|
/** 热加载:替换 provider 实例(doReloadConfig 重建 provider 链后调用) */
|
|
3144
3125
|
setProvider(provider) {
|
|
3145
3126
|
this.provider = provider;
|
|
@@ -3167,7 +3148,12 @@ var init_query = __esm({
|
|
|
3167
3148
|
if (signal) {
|
|
3168
3149
|
signal.addEventListener("abort", () => ac.abort(), { once: true });
|
|
3169
3150
|
}
|
|
3170
|
-
|
|
3151
|
+
if (typeof this.options.compactConfig === "function") {
|
|
3152
|
+
const overhead = this.compactConfig.systemOverheadTokens;
|
|
3153
|
+
this.compactConfig = { ...DEFAULT_COMPACT_CONFIG, ...this.resolveOpt(this.options.compactConfig) };
|
|
3154
|
+
this.compactConfig.systemOverheadTokens = overhead ?? this.compactConfig.systemOverheadTokens;
|
|
3155
|
+
}
|
|
3156
|
+
const activeSystemStable = perTurnSystemStable || this.resolveOpt(this.options.systemStable) || this.resolveOpt(this.options.systemPrompt);
|
|
3171
3157
|
let currentMessages = [...messages];
|
|
3172
3158
|
console.log(`${this.logPrefix} query() called with ${messages.length} messages`);
|
|
3173
3159
|
const useDynamicTools = !this.options.toolOverride;
|
|
@@ -3183,7 +3169,7 @@ var init_query = __esm({
|
|
|
3183
3169
|
if (this.systemOverheadTokens === null) {
|
|
3184
3170
|
const { estimateToolDefinitionTokens: estimateToolDefinitionTokens2 } = await Promise.resolve().then(() => (init_context_analyzer(), context_analyzer_exports));
|
|
3185
3171
|
const { roughTokenCountEstimation: roughTokenCountEstimation6 } = await Promise.resolve().then(() => (init_tokenEstimate(), tokenEstimate_exports));
|
|
3186
|
-
const systemText = this.options.systemStable || this.options.systemPrompt || "";
|
|
3172
|
+
const systemText = this.resolveOpt(this.options.systemStable) || this.resolveOpt(this.options.systemPrompt) || "";
|
|
3187
3173
|
const systemTokens = roughTokenCountEstimation6(systemText);
|
|
3188
3174
|
const initialToolDefs = useDynamicTools ? this.buildActiveTools(allToolDefs) : allToolDefs;
|
|
3189
3175
|
const toolTokens = estimateToolDefinitionTokens2(initialToolDefs);
|
|
@@ -3335,7 +3321,7 @@ ${perTurnSystemDynamic}` : deferredHint || perTurnSystemDynamic;
|
|
|
3335
3321
|
systemStable: activeSystemStable,
|
|
3336
3322
|
systemDynamic: fullSystemDynamic,
|
|
3337
3323
|
tools: toolDefs.length > 0 ? toolDefs : void 0,
|
|
3338
|
-
maxTokens: this.options.maxTokens,
|
|
3324
|
+
maxTokens: typeof this.options.maxTokens === "function" ? this.options.maxTokens() : this.options.maxTokens,
|
|
3339
3325
|
temperature: this.options.temperature,
|
|
3340
3326
|
signal: ac.signal,
|
|
3341
3327
|
disableThinking: this.options.disableThinking || !!perTurnEmotion
|
|
@@ -3390,7 +3376,8 @@ ${perTurnSystemDynamic}` : deferredHint || perTurnSystemDynamic;
|
|
|
3390
3376
|
}
|
|
3391
3377
|
if (toolCalls.length === 0) {
|
|
3392
3378
|
if (!textContent.trim()) {
|
|
3393
|
-
|
|
3379
|
+
const thinkingAteBudget = turnThinking.length > 0;
|
|
3380
|
+
console.log(`${this.logPrefix} Turn ${turnCount}: API returned empty (no text, no tool_call).${thinkingAteBudget ? ` ${turnThinking.length}ch thinking \u5403\u5149\u8F93\u51FA\u9884\u7B97 \u2192 retry with thinking off` : " Retrying..."} messages=${currentMessages.length}`);
|
|
3394
3381
|
turnThinking = "";
|
|
3395
3382
|
turnThinkingSig = "";
|
|
3396
3383
|
for await (const chunk of this.provider.streamChat({
|
|
@@ -3399,9 +3386,10 @@ ${perTurnSystemDynamic}` : deferredHint || perTurnSystemDynamic;
|
|
|
3399
3386
|
systemStable: activeSystemStable,
|
|
3400
3387
|
systemDynamic: fullSystemDynamic,
|
|
3401
3388
|
tools: toolDefs.length > 0 ? toolDefs : void 0,
|
|
3402
|
-
maxTokens: this.options.maxTokens,
|
|
3389
|
+
maxTokens: typeof this.options.maxTokens === "function" ? this.options.maxTokens() : this.options.maxTokens,
|
|
3403
3390
|
temperature: this.options.temperature,
|
|
3404
|
-
signal: ac.signal
|
|
3391
|
+
signal: ac.signal,
|
|
3392
|
+
...thinkingAteBudget ? { disableThinking: true } : {}
|
|
3405
3393
|
})) {
|
|
3406
3394
|
if (chunk.type === "status") yield chunk;
|
|
3407
3395
|
if (chunk.type === "text" && chunk.text) textContent += chunk.text;
|
|
@@ -5342,8 +5330,8 @@ async function scanMemoryFiles2(memoryDir, signal, maxFiles) {
|
|
|
5342
5330
|
const limit = maxFiles ?? DEFAULT_MAX_MEMORY_FILES;
|
|
5343
5331
|
console.log(`[memdir] scanMemoryFiles: dir=${memoryDir}, maxFiles=${maxFiles}, limit=${limit}`);
|
|
5344
5332
|
try {
|
|
5345
|
-
const
|
|
5346
|
-
const mdFiles =
|
|
5333
|
+
const entries2 = await readdir(memoryDir, { recursive: true });
|
|
5334
|
+
const mdFiles = entries2.filter(
|
|
5347
5335
|
(f) => typeof f === "string" && f.endsWith(".md") && basename4(f) !== "MEMORY.md" && !isArchivedPath(f)
|
|
5348
5336
|
);
|
|
5349
5337
|
const headerResults = await Promise.allSettled(
|
|
@@ -5388,6 +5376,114 @@ var init_memoryScan = __esm({
|
|
|
5388
5376
|
}
|
|
5389
5377
|
});
|
|
5390
5378
|
|
|
5379
|
+
// src/tools/rate-breaker.ts
|
|
5380
|
+
var rate_breaker_exports = {};
|
|
5381
|
+
__export(rate_breaker_exports, {
|
|
5382
|
+
checkRateBreaker: () => checkRateBreaker,
|
|
5383
|
+
parseDurationString: () => parseDurationString,
|
|
5384
|
+
recordInboundBotFlag: () => recordInboundBotFlag
|
|
5385
|
+
});
|
|
5386
|
+
function parseDurationString(s, defaultMs) {
|
|
5387
|
+
if (s === void 0 || s === null || s === "") return defaultMs;
|
|
5388
|
+
if (typeof s === "number" && Number.isFinite(s)) return s;
|
|
5389
|
+
const m = String(s).trim().toLowerCase().match(/^([\d.]+)\s*([a-z]+)?$/);
|
|
5390
|
+
if (!m) return defaultMs;
|
|
5391
|
+
const n = parseFloat(m[1]);
|
|
5392
|
+
const unit = m[2] || "ms";
|
|
5393
|
+
const mult = DURATION_UNITS[unit];
|
|
5394
|
+
if (!Number.isFinite(n) || !mult && unit !== "ms") return defaultMs;
|
|
5395
|
+
return Math.round(unit === "ms" ? n : n * mult);
|
|
5396
|
+
}
|
|
5397
|
+
function recordInboundBotFlag(userId, isBot) {
|
|
5398
|
+
if (!userId || !isBot) return;
|
|
5399
|
+
learnedBots.add(userId);
|
|
5400
|
+
}
|
|
5401
|
+
function getCfg(channel) {
|
|
5402
|
+
const node = liveConfig.get(`channels.${channel}`);
|
|
5403
|
+
const rb = node?.rateBreaker;
|
|
5404
|
+
return {
|
|
5405
|
+
// enabled 判定:rateBreaker 节点存在且不是 false(没配=不生效;{ } 空 obj 也算开,用默认值)
|
|
5406
|
+
enabled: !!rb && rb !== false,
|
|
5407
|
+
windowMs: parseDurationString(rb?.window, DEFAULTS.windowMs),
|
|
5408
|
+
maxSends: typeof rb?.maxSends === "number" ? rb.maxSends : DEFAULTS.maxSends,
|
|
5409
|
+
cooldownMs: parseDurationString(rb?.cooldown, DEFAULTS.cooldownMs),
|
|
5410
|
+
escalate: rb?.escalate !== false,
|
|
5411
|
+
botIds: new Set(Array.isArray(node?.botIds) ? node.botIds : [])
|
|
5412
|
+
};
|
|
5413
|
+
}
|
|
5414
|
+
function isBotDirection(cfg, toIds, inboundIsBot) {
|
|
5415
|
+
if (inboundIsBot) return true;
|
|
5416
|
+
if (toIds.some((id) => cfg.botIds.has(id) || learnedBots.has(id))) return true;
|
|
5417
|
+
return false;
|
|
5418
|
+
}
|
|
5419
|
+
function checkRateBreaker(channel, sessionId, dest, toIds, inboundIsBot) {
|
|
5420
|
+
const cfg = getCfg(channel);
|
|
5421
|
+
if (!cfg.enabled) return null;
|
|
5422
|
+
if (!isBotDirection(cfg, toIds, inboundIsBot)) return null;
|
|
5423
|
+
const key = `${sessionId}\u2192${dest || toIds.join(",") || "?"}`;
|
|
5424
|
+
const now = Date.now();
|
|
5425
|
+
let e = entries.get(key);
|
|
5426
|
+
if (e && now - e.lastActivity > RESET_AFTER_MS) {
|
|
5427
|
+
entries.delete(key);
|
|
5428
|
+
e = void 0;
|
|
5429
|
+
}
|
|
5430
|
+
if (e && e.openUntil > now) {
|
|
5431
|
+
e.lastActivity = now;
|
|
5432
|
+
return { content: `\u5DF2\u9759\u9ED8\uFF1A\u8BE5\u65B9\u5411\u7684\u53D1\u9001\u5904\u4E8E\u9891\u7387\u7194\u65AD\u4E2D\uFF08\u51B7\u5374\u81F3 ${new Date(e.openUntil).toLocaleTimeString("zh-CN")}\uFF09\u3002\u8FD9\u662F\u9632\u6B62 bot \u5BF9\u8BDD\u5FAA\u73AF\u7684\u81EA\u52A8\u4FDD\u62A4\u2014\u2014\u5982\u679C\u8FD9\u662F\u6B63\u5E38\u534F\u4F5C\u88AB\u6253\u65AD\uFF0C\u8BF7\u544A\u77E5\u7FC0\u54E5\u8C03\u6574 rateBreaker \u914D\u7F6E\u3002` };
|
|
5433
|
+
}
|
|
5434
|
+
if (e && e.openUntil <= now && e.openUntil > 0) {
|
|
5435
|
+
e.openUntil = 0;
|
|
5436
|
+
}
|
|
5437
|
+
if (!e) {
|
|
5438
|
+
e = { timestamps: [], openUntil: 0, trips: 0, lastActivity: now };
|
|
5439
|
+
entries.set(key, e);
|
|
5440
|
+
}
|
|
5441
|
+
e.lastActivity = now;
|
|
5442
|
+
e.timestamps = e.timestamps.filter((t) => now - t < cfg.windowMs);
|
|
5443
|
+
e.timestamps.push(now);
|
|
5444
|
+
if (e.timestamps.length > cfg.maxSends) {
|
|
5445
|
+
e.trips++;
|
|
5446
|
+
const cooldown = cfg.escalate ? Math.min(cfg.cooldownMs * Math.pow(2, e.trips - 1), ESCALATE_CAP_MS) : cfg.cooldownMs;
|
|
5447
|
+
e.openUntil = now + cooldown;
|
|
5448
|
+
e.timestamps = [];
|
|
5449
|
+
const tripsNote = cfg.escalate ? `\uFF08\u7B2C ${e.trips} \u6B21\u8FDE\u7EED\u7194\u65AD\uFF0C\u51B7\u5374${cooldown >= ESCALATE_CAP_MS ? "\u5DF2\u8FBE\u4E0A\u9650 24h" : `\u7FFB\u500D\u81F3 ${Math.round(cooldown / 6e4)} \u5206\u949F`}\uFF09` : "";
|
|
5450
|
+
console.warn(`[rate-breaker] OPEN ${key}\uFF1A${cfg.windowMs / 6e4} \u5206\u949F\u5185 ${e.timestamps.length || cfg.maxSends + 1} \u6761\u8D85\u9650\uFF08max=${cfg.maxSends}\uFF09${tripsNote}`);
|
|
5451
|
+
return { content: `\u5DF2\u9759\u9ED8\uFF1A\u77ED\u65F6\u95F4\u5185\u5411\u540C\u4E00\u76EE\u6807\u53D1\u9001\u8FC7\u591A\uFF08${cfg.maxSends} \u6761/${Math.round(cfg.windowMs / 6e4)} \u5206\u949F\uFF09\uFF0C\u89E6\u53D1\u9891\u7387\u7194\u65AD\uFF0C\u51B7\u5374 ${Math.round(cooldown / 6e4)} \u5206\u949F${tripsNote}\u3002\u5982\u679C\u4F60\u786E\u4FE1\u8FD9\u4E0D\u662F\u5FAA\u73AF\uFF08\u662F\u6B63\u5E38\u534F\u4F5C\uFF09\uFF0C\u7A0D\u7B49\u51B7\u5374\u6216\u544A\u77E5\u7FC0\u54E5\u8C03\u6574\u914D\u7F6E\u3002` };
|
|
5452
|
+
}
|
|
5453
|
+
return null;
|
|
5454
|
+
}
|
|
5455
|
+
var DURATION_UNITS, learnedBots, entries, DEFAULTS, ESCALATE_CAP_MS, RESET_AFTER_MS;
|
|
5456
|
+
var init_rate_breaker = __esm({
|
|
5457
|
+
"src/tools/rate-breaker.ts"() {
|
|
5458
|
+
"use strict";
|
|
5459
|
+
init_live();
|
|
5460
|
+
DURATION_UNITS = {
|
|
5461
|
+
s: 1e3,
|
|
5462
|
+
sec: 1e3,
|
|
5463
|
+
secs: 1e3,
|
|
5464
|
+
second: 1e3,
|
|
5465
|
+
seconds: 1e3,
|
|
5466
|
+
m: 6e4,
|
|
5467
|
+
min: 6e4,
|
|
5468
|
+
mins: 6e4,
|
|
5469
|
+
minute: 6e4,
|
|
5470
|
+
minutes: 6e4,
|
|
5471
|
+
h: 36e5,
|
|
5472
|
+
hr: 36e5,
|
|
5473
|
+
hour: 36e5,
|
|
5474
|
+
hours: 36e5,
|
|
5475
|
+
d: 864e5,
|
|
5476
|
+
day: 864e5,
|
|
5477
|
+
days: 864e5
|
|
5478
|
+
};
|
|
5479
|
+
learnedBots = /* @__PURE__ */ new Set();
|
|
5480
|
+
entries = /* @__PURE__ */ new Map();
|
|
5481
|
+
DEFAULTS = { windowMs: 6e5, maxSends: 8, cooldownMs: 6e5 };
|
|
5482
|
+
ESCALATE_CAP_MS = 864e5;
|
|
5483
|
+
RESET_AFTER_MS = 864e5;
|
|
5484
|
+
}
|
|
5485
|
+
});
|
|
5486
|
+
|
|
5391
5487
|
// src/memory/memdir/extractPrompts.ts
|
|
5392
5488
|
function opener(newMessageCount, existingMemories) {
|
|
5393
5489
|
const manifest = existingMemories.length > 0 ? `
|
|
@@ -5845,8 +5941,8 @@ var init_ingest = __esm({
|
|
|
5845
5941
|
});
|
|
5846
5942
|
|
|
5847
5943
|
// src/memory/sessionMemory/sessionMemoryUtils.ts
|
|
5848
|
-
import { join as
|
|
5849
|
-
import { mkdirSync as mkdirSync7, readFileSync as
|
|
5944
|
+
import { join as join21 } from "node:path";
|
|
5945
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync16 } from "node:fs";
|
|
5850
5946
|
function setSessionMemoryStateDir(dir) {
|
|
5851
5947
|
_stateDir2 = dir;
|
|
5852
5948
|
mkdirSync7(dir, { recursive: true });
|
|
@@ -5855,7 +5951,7 @@ function getSessionMemoryDir() {
|
|
|
5855
5951
|
return _stateDir2;
|
|
5856
5952
|
}
|
|
5857
5953
|
function getSessionMemoryPath() {
|
|
5858
|
-
return
|
|
5954
|
+
return join21(_stateDir2, "session-notes.md");
|
|
5859
5955
|
}
|
|
5860
5956
|
function markExtractionStarted() {
|
|
5861
5957
|
extractionStartedAt = Date.now();
|
|
@@ -5903,7 +5999,7 @@ var init_sessionMemoryUtils = __esm({
|
|
|
5903
5999
|
|
|
5904
6000
|
// src/memory/sessionMemory/prompts.ts
|
|
5905
6001
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
5906
|
-
import { join as
|
|
6002
|
+
import { join as join22 } from "node:path";
|
|
5907
6003
|
function roughTokenCountEstimation4(text) {
|
|
5908
6004
|
return Math.ceil(text.length / 4);
|
|
5909
6005
|
}
|
|
@@ -5948,7 +6044,7 @@ REMEMBER: Use the Edit tool in parallel and stop. Do not continue after the edit
|
|
|
5948
6044
|
}
|
|
5949
6045
|
async function loadSessionMemoryTemplate() {
|
|
5950
6046
|
const configHome = process.env.CLAUDE_CONFIG_HOME || process.env.HOME || "";
|
|
5951
|
-
const templatePath =
|
|
6047
|
+
const templatePath = join22(configHome, ".claude", "session-memory", "config", "template.md");
|
|
5952
6048
|
try {
|
|
5953
6049
|
return await readFile4(templatePath, { encoding: "utf-8" });
|
|
5954
6050
|
} catch {
|
|
@@ -5957,7 +6053,7 @@ async function loadSessionMemoryTemplate() {
|
|
|
5957
6053
|
}
|
|
5958
6054
|
async function loadSessionMemoryPrompt() {
|
|
5959
6055
|
const configHome = process.env.CLAUDE_CONFIG_HOME || process.env.HOME || "";
|
|
5960
|
-
const promptPath =
|
|
6056
|
+
const promptPath = join22(configHome, ".claude", "session-memory", "config", "prompt.md");
|
|
5961
6057
|
try {
|
|
5962
6058
|
return await readFile4(promptPath, { encoding: "utf-8" });
|
|
5963
6059
|
} catch {
|
|
@@ -6121,7 +6217,7 @@ __export(sessionMemory_exports, {
|
|
|
6121
6217
|
resetLastMemoryMessageUuid: () => resetLastMemoryMessageUuid,
|
|
6122
6218
|
shouldExtractMemory: () => shouldExtractMemory
|
|
6123
6219
|
});
|
|
6124
|
-
import { readFileSync as
|
|
6220
|
+
import { readFileSync as readFileSync17, mkdirSync as mkdirSync8 } from "node:fs";
|
|
6125
6221
|
import { writeFile as writeFile3 } from "node:fs/promises";
|
|
6126
6222
|
function initSessionMemory(deps) {
|
|
6127
6223
|
_deps = deps;
|
|
@@ -6184,7 +6280,7 @@ async function setupSessionMemoryFile() {
|
|
|
6184
6280
|
const memoryPath = getSessionMemoryPath();
|
|
6185
6281
|
let currentMemory;
|
|
6186
6282
|
try {
|
|
6187
|
-
currentMemory =
|
|
6283
|
+
currentMemory = readFileSync17(memoryPath, { encoding: "utf-8" });
|
|
6188
6284
|
} catch {
|
|
6189
6285
|
const template = await loadSessionMemoryTemplate();
|
|
6190
6286
|
await writeFile3(memoryPath, template, { encoding: "utf-8" });
|
|
@@ -6261,7 +6357,7 @@ ${messages.filter((m) => m.role === "user" || m.role === "assistant").slice(-20)
|
|
|
6261
6357
|
}
|
|
6262
6358
|
function getSessionMemoryForCompaction() {
|
|
6263
6359
|
try {
|
|
6264
|
-
const content =
|
|
6360
|
+
const content = readFileSync17(getSessionMemoryPath(), { encoding: "utf-8" }).trim();
|
|
6265
6361
|
if (!content) return null;
|
|
6266
6362
|
const { truncatedContent, wasTruncated } = truncateSessionMemoryForCompact(content);
|
|
6267
6363
|
if (wasTruncated) {
|
|
@@ -6294,7 +6390,7 @@ var init_sessionMemory = __esm({
|
|
|
6294
6390
|
// src/memory/autoDream/config.ts
|
|
6295
6391
|
var config_exports = {};
|
|
6296
6392
|
__export(config_exports, {
|
|
6297
|
-
DEFAULTS: () =>
|
|
6393
|
+
DEFAULTS: () => DEFAULTS2,
|
|
6298
6394
|
getAutoDreamConfig: () => getAutoDreamConfig,
|
|
6299
6395
|
getDailyLogDir: () => getDailyLogDir,
|
|
6300
6396
|
getDistillOutput: () => getDistillOutput,
|
|
@@ -6311,8 +6407,8 @@ function isAutoDreamEnabled() {
|
|
|
6311
6407
|
function getAutoDreamConfig() {
|
|
6312
6408
|
const raw = _config?.topics?.autoDream ?? _config?.autoDream;
|
|
6313
6409
|
return {
|
|
6314
|
-
minHours: typeof raw?.minHours === "number" && Number.isFinite(raw.minHours) && raw.minHours > 0 ? raw.minHours :
|
|
6315
|
-
minSessions: typeof raw?.minSessions === "number" && Number.isFinite(raw.minSessions) && raw.minSessions > 0 ? raw.minSessions :
|
|
6410
|
+
minHours: typeof raw?.minHours === "number" && Number.isFinite(raw.minHours) && raw.minHours > 0 ? raw.minHours : DEFAULTS2.minHours,
|
|
6411
|
+
minSessions: typeof raw?.minSessions === "number" && Number.isFinite(raw.minSessions) && raw.minSessions > 0 ? raw.minSessions : DEFAULTS2.minSessions
|
|
6316
6412
|
};
|
|
6317
6413
|
}
|
|
6318
6414
|
function getDailyLogDir() {
|
|
@@ -6328,13 +6424,13 @@ function getMaxEntrypointLines() {
|
|
|
6328
6424
|
if (typeof maxScan === "number" && maxScan > 200) return maxScan;
|
|
6329
6425
|
return void 0;
|
|
6330
6426
|
}
|
|
6331
|
-
var _config,
|
|
6427
|
+
var _config, DEFAULTS2;
|
|
6332
6428
|
var init_config2 = __esm({
|
|
6333
6429
|
"src/memory/autoDream/config.ts"() {
|
|
6334
6430
|
"use strict";
|
|
6335
6431
|
init_features();
|
|
6336
6432
|
_config = null;
|
|
6337
|
-
|
|
6433
|
+
DEFAULTS2 = {
|
|
6338
6434
|
minHours: 24,
|
|
6339
6435
|
minSessions: 5
|
|
6340
6436
|
};
|
|
@@ -6343,9 +6439,9 @@ var init_config2 = __esm({
|
|
|
6343
6439
|
|
|
6344
6440
|
// src/memory/autoDream/consolidationLock.ts
|
|
6345
6441
|
import { mkdir as mkdir3, readFile as readFile5, stat as stat3, unlink, utimes, writeFile as writeFile4 } from "node:fs/promises";
|
|
6346
|
-
import { join as
|
|
6442
|
+
import { join as join23 } from "node:path";
|
|
6347
6443
|
function lockPath(memoryDir) {
|
|
6348
|
-
return
|
|
6444
|
+
return join23(memoryDir, LOCK_FILE);
|
|
6349
6445
|
}
|
|
6350
6446
|
async function readLastConsolidatedAt(memoryDir) {
|
|
6351
6447
|
try {
|
|
@@ -6413,7 +6509,7 @@ async function listSessionsTouchedSince(sessionsDir, sinceMs) {
|
|
|
6413
6509
|
const results = [];
|
|
6414
6510
|
for (const f of jsonlFiles) {
|
|
6415
6511
|
try {
|
|
6416
|
-
const s = await stat3(
|
|
6512
|
+
const s = await stat3(join23(sessionsDir, f));
|
|
6417
6513
|
if (s.mtimeMs > sinceMs) {
|
|
6418
6514
|
results.push(f.replace(/\.jsonl$/, ""));
|
|
6419
6515
|
}
|
|
@@ -6492,7 +6588,13 @@ Focus on:
|
|
|
6492
6588
|
|
|
6493
6589
|
${distillOutput ? `## Phase 3.5 \u2014 Distill to core knowledge
|
|
6494
6590
|
|
|
6495
|
-
After consolidation, distill the most important insights into a single consolidated file at \`${distillOutput}\`. This file should contain only the highest-concentration knowledge \u2014 core principles, user preferences, milestones, and cross-session patterns. Each entry should be one concise paragraph. Append new entries, don't rewrite the whole file. If the file doesn't exist, create it
|
|
6591
|
+
After consolidation, distill the most important insights into a single consolidated file at \`${distillOutput}\`. This file should contain only the highest-concentration knowledge \u2014 core principles, user preferences, milestones, and cross-session patterns. Each entry should be one concise paragraph. Append new entries, don't rewrite the whole file. If the file doesn't exist, create it.
|
|
6592
|
+
|
|
6593
|
+
**Size cap (0901 \u7FC0\u54E5\u6279 \u2014 this file rides the work-mode system prompt every turn, every char costs tokens every request):**
|
|
6594
|
+
- SOFT CAP 20,000 chars: when the file exceeds it, before appending, **merge the OLDEST dated entries upward into the thematic sections at the top** (\u884C\u4E3A\u539F\u5219/\u5DE5\u7A0B\u94C1\u5F8B/\u504F\u597D etc.) \u2014 compress narratives to principle form, merge duplicates into one entry. Knowledge moves up, nothing is deleted outright.
|
|
6595
|
+
- HARD CAP 35,000 chars: if still over (or a previous merge failed), compaction becomes the FIRST task of this dream \u2014 do it before anything else.
|
|
6596
|
+
- Always \`cp\` the file to \\\`distill-archive-{YYYYMMDD}.md\\\` before any merge/compaction rewrite.
|
|
6597
|
+
- Merging here is safe: distill is derived knowledge \u2014 the source lives in the topic memory files and recall can recover details. The "never delete" rule above applies to memory FILES, not to compressing this distillate.` : ""}
|
|
6496
6598
|
|
|
6497
6599
|
## Phase 4 \u2014 Prune and index
|
|
6498
6600
|
|
|
@@ -7238,8 +7340,8 @@ Usage:
|
|
|
7238
7340
|
return { content: `\u8BBE\u5907\u6587\u4EF6\u4F1A\u963B\u585E\u6216\u4EA7\u751F\u65E0\u9650\u8F93\u51FA: ${filePath}`, isError: true };
|
|
7239
7341
|
}
|
|
7240
7342
|
if (stat4.isDirectory()) {
|
|
7241
|
-
const
|
|
7242
|
-
const items =
|
|
7343
|
+
const entries2 = fs38.readdirSync(filePath);
|
|
7344
|
+
const items = entries2.map((e) => {
|
|
7243
7345
|
const full = path38.join(filePath, e);
|
|
7244
7346
|
try {
|
|
7245
7347
|
const s = fs38.statSync(full);
|
|
@@ -7248,7 +7350,7 @@ Usage:
|
|
|
7248
7350
|
return e;
|
|
7249
7351
|
}
|
|
7250
7352
|
});
|
|
7251
|
-
return { content: `\u76EE\u5F55 (${
|
|
7353
|
+
return { content: `\u76EE\u5F55 (${entries2.length} \u9879):
|
|
7252
7354
|
${items.join("\n")}` };
|
|
7253
7355
|
}
|
|
7254
7356
|
const ext = path38.extname(filePath).toLowerCase();
|
|
@@ -7741,13 +7843,13 @@ function findFiles(dir, pattern, limit, baseDir) {
|
|
|
7741
7843
|
truncated = true;
|
|
7742
7844
|
return;
|
|
7743
7845
|
}
|
|
7744
|
-
let
|
|
7846
|
+
let entries2;
|
|
7745
7847
|
try {
|
|
7746
|
-
|
|
7848
|
+
entries2 = fs41.readdirSync(currentDir, { withFileTypes: true });
|
|
7747
7849
|
} catch {
|
|
7748
7850
|
return;
|
|
7749
7851
|
}
|
|
7750
|
-
for (const entry of
|
|
7852
|
+
for (const entry of entries2) {
|
|
7751
7853
|
if (truncated) return;
|
|
7752
7854
|
const fullPath = path41.join(currentDir, entry.name);
|
|
7753
7855
|
if (entry.isDirectory()) {
|
|
@@ -10877,6 +10979,17 @@ async function executeAndDeliver(task, now, deps) {
|
|
|
10877
10979
|
const resultPromise = new Promise((resolve11) => {
|
|
10878
10980
|
resolveResult = resolve11;
|
|
10879
10981
|
});
|
|
10982
|
+
const modelRef = task.model || liveConfig.get("cron.model");
|
|
10983
|
+
let execDeps = deps.deps;
|
|
10984
|
+
if (modelRef) {
|
|
10985
|
+
const overrideDeps = deps.resolveModelDeps?.(modelRef);
|
|
10986
|
+
if (overrideDeps) {
|
|
10987
|
+
execDeps = overrideDeps;
|
|
10988
|
+
execDeps.renderer = deps.deps.renderer;
|
|
10989
|
+
} else {
|
|
10990
|
+
console.warn(`[cron] Task ${task.id}: model "${modelRef}" unresolvable, falling back to primary`);
|
|
10991
|
+
}
|
|
10992
|
+
}
|
|
10880
10993
|
deps.dispatcher.submitMessage({
|
|
10881
10994
|
text: promptText,
|
|
10882
10995
|
sessionId,
|
|
@@ -10886,7 +10999,7 @@ async function executeAndDeliver(task, now, deps) {
|
|
|
10886
10999
|
callbacks: {
|
|
10887
11000
|
onResult: (content) => resolveResult(content)
|
|
10888
11001
|
},
|
|
10889
|
-
deps:
|
|
11002
|
+
deps: execDeps,
|
|
10890
11003
|
// 默认跳过 recall(省 token),task.skipRecall=false 才走
|
|
10891
11004
|
skipRecall: task.skipRecall !== false
|
|
10892
11005
|
});
|
|
@@ -10975,6 +11088,8 @@ ${notifyLines}`;
|
|
|
10975
11088
|
source: "cron",
|
|
10976
11089
|
priority: "later",
|
|
10977
11090
|
callbacks: {},
|
|
11091
|
+
// 故意用 primary(不吃 cron.model):这条是注入主对话 session 的,
|
|
11092
|
+
// 该由主对话自己的模型接手,换模型会让主 session 的 KV 前缀断掉。
|
|
10978
11093
|
deps: deps.deps
|
|
10979
11094
|
});
|
|
10980
11095
|
console.log(`[cron] Notified main session: ${mainSessionId}`);
|
|
@@ -11048,6 +11163,7 @@ var schedulerTimer, shuttingDown, currentlyExecuting;
|
|
|
11048
11163
|
var init_scheduler = __esm({
|
|
11049
11164
|
"src/cron/scheduler.ts"() {
|
|
11050
11165
|
"use strict";
|
|
11166
|
+
init_live();
|
|
11051
11167
|
init_tasks2();
|
|
11052
11168
|
schedulerTimer = null;
|
|
11053
11169
|
shuttingDown = false;
|
|
@@ -11258,7 +11374,7 @@ var init_tools = __esm({
|
|
|
11258
11374
|
|
|
11259
11375
|
// src/tools/wechat/wx-query.ts
|
|
11260
11376
|
var wx_query_exports = {};
|
|
11261
|
-
import { join as
|
|
11377
|
+
import { join as join37 } from "node:path";
|
|
11262
11378
|
function getDescription() {
|
|
11263
11379
|
return `\u67E5\u8BE2\u7FC0\u54E5\u7684\u5FAE\u4FE1\u6D88\u606F\uFF08\u7F13\u5B58\u89E3\u5BC6\u540E\u7684\u672C\u5730\u6570\u636E\u5E93\uFF09\u3002
|
|
11264
11380
|
|
|
@@ -11293,8 +11409,8 @@ var init_wx_query = __esm({
|
|
|
11293
11409
|
"use strict";
|
|
11294
11410
|
init_registry();
|
|
11295
11411
|
init_live();
|
|
11296
|
-
STATE_DIR = process.env.ENGINE7_STATE_DIR || process.env.OPENCLAW_STATE_DIR ||
|
|
11297
|
-
SCRIPT =
|
|
11412
|
+
STATE_DIR = process.env.ENGINE7_STATE_DIR || process.env.OPENCLAW_STATE_DIR || join37(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
|
|
11413
|
+
SCRIPT = join37("/Users/chongzhang/work/twinsun-hearth/engine", "src", "tools", "wechat", "wx_query.py");
|
|
11298
11414
|
PYTHON = "python3";
|
|
11299
11415
|
registry.register({
|
|
11300
11416
|
name: "wx_query",
|
|
@@ -12207,14 +12323,16 @@ var init_cron_plugin = __esm({
|
|
|
12207
12323
|
"use strict";
|
|
12208
12324
|
init_tasks2();
|
|
12209
12325
|
init_scheduler();
|
|
12326
|
+
init_live();
|
|
12210
12327
|
CronPlugin = class {
|
|
12211
|
-
constructor(config2, sessions, channelManager, deps, stateDir, dispatcher) {
|
|
12328
|
+
constructor(config2, sessions, channelManager, deps, stateDir, dispatcher, resolveModelDeps) {
|
|
12212
12329
|
this.config = config2;
|
|
12213
12330
|
this.sessions = sessions;
|
|
12214
12331
|
this.channelManager = channelManager;
|
|
12215
12332
|
this.deps = deps;
|
|
12216
12333
|
this.stateDir = stateDir;
|
|
12217
12334
|
this.dispatcher = dispatcher;
|
|
12335
|
+
this.resolveModelDeps = resolveModelDeps;
|
|
12218
12336
|
}
|
|
12219
12337
|
config;
|
|
12220
12338
|
sessions;
|
|
@@ -12222,6 +12340,7 @@ var init_cron_plugin = __esm({
|
|
|
12222
12340
|
deps;
|
|
12223
12341
|
stateDir;
|
|
12224
12342
|
dispatcher;
|
|
12343
|
+
resolveModelDeps;
|
|
12225
12344
|
static shouldEnable(config2) {
|
|
12226
12345
|
return config2.enabled === true;
|
|
12227
12346
|
}
|
|
@@ -12254,10 +12373,12 @@ var init_cron_plugin = __esm({
|
|
|
12254
12373
|
channelManager: this.channelManager,
|
|
12255
12374
|
deps: this.deps,
|
|
12256
12375
|
config: this.config,
|
|
12257
|
-
dispatcher: this.dispatcher
|
|
12376
|
+
dispatcher: this.dispatcher,
|
|
12377
|
+
resolveModelDeps: this.resolveModelDeps
|
|
12258
12378
|
};
|
|
12259
12379
|
startScheduler(schedulerDeps);
|
|
12260
|
-
|
|
12380
|
+
const cronModel = liveConfig.get("cron.model");
|
|
12381
|
+
console.log(`[cron] Plugin started (${tasks2.filter((t) => t.status === "active").length} active tasks${missedCount ? `, ${missedCount} missed` : ""}${cronModel ? `, model=${cronModel}` : ""})`);
|
|
12261
12382
|
}
|
|
12262
12383
|
async stop() {
|
|
12263
12384
|
await stopScheduler();
|
|
@@ -12271,8 +12392,8 @@ var reply_blocklist_exports = {};
|
|
|
12271
12392
|
__export(reply_blocklist_exports, {
|
|
12272
12393
|
isUserBlocked: () => isUserBlocked
|
|
12273
12394
|
});
|
|
12274
|
-
import { readFileSync as
|
|
12275
|
-
import { join as
|
|
12395
|
+
import { readFileSync as readFileSync30, writeFileSync as writeFileSync19, existsSync as existsSync27 } from "node:fs";
|
|
12396
|
+
import { join as join43 } from "node:path";
|
|
12276
12397
|
function ensureLoaded(workspace, configIds) {
|
|
12277
12398
|
if (loaded) return;
|
|
12278
12399
|
if (configIds?.length) {
|
|
@@ -12280,10 +12401,10 @@ function ensureLoaded(workspace, configIds) {
|
|
|
12280
12401
|
if (!state.blockedUserIds.includes(id)) state.blockedUserIds.push(id);
|
|
12281
12402
|
}
|
|
12282
12403
|
}
|
|
12283
|
-
const path50 =
|
|
12404
|
+
const path50 = join43(workspace, ".reply-blocklist.json");
|
|
12284
12405
|
try {
|
|
12285
|
-
if (
|
|
12286
|
-
const raw =
|
|
12406
|
+
if (existsSync27(path50)) {
|
|
12407
|
+
const raw = readFileSync30(path50, "utf-8");
|
|
12287
12408
|
const parsed = JSON.parse(raw);
|
|
12288
12409
|
if (parsed.blockedUserIds) {
|
|
12289
12410
|
for (const id of parsed.blockedUserIds) {
|
|
@@ -12299,7 +12420,7 @@ function ensureLoaded(workspace, configIds) {
|
|
|
12299
12420
|
loaded = true;
|
|
12300
12421
|
}
|
|
12301
12422
|
function save(workspace) {
|
|
12302
|
-
const path50 =
|
|
12423
|
+
const path50 = join43(workspace, ".reply-blocklist.json");
|
|
12303
12424
|
try {
|
|
12304
12425
|
writeFileSync19(path50, JSON.stringify(state, null, 2), "utf-8");
|
|
12305
12426
|
} catch (err) {
|
|
@@ -13065,30 +13186,21 @@ function loadConfig(configPath2) {
|
|
|
13065
13186
|
devMode: agentDefaults.devMode === true
|
|
13066
13187
|
};
|
|
13067
13188
|
const rawSession = raw.session || {};
|
|
13068
|
-
const sessionScope = rawSession.
|
|
13189
|
+
const sessionScope = Object.keys(rawSession).length > 0 ? { ...rawSession } : void 0;
|
|
13069
13190
|
const rawHeartbeat = raw.heartbeat || {};
|
|
13070
13191
|
const heartbeat = rawHeartbeat.enabled ? {
|
|
13071
|
-
|
|
13072
|
-
|
|
13073
|
-
prompt: rawHeartbeat.prompt,
|
|
13074
|
-
model: rawHeartbeat.model,
|
|
13075
|
-
timeoutMs: rawHeartbeat.timeoutMs,
|
|
13076
|
-
checkOnline: rawHeartbeat.checkOnline,
|
|
13077
|
-
activeThresholdMs: rawHeartbeat.activeThresholdMs
|
|
13192
|
+
...rawHeartbeat,
|
|
13193
|
+
enabled: true
|
|
13078
13194
|
} : void 0;
|
|
13079
13195
|
const rawInnerVoice = raw.innerVoice || {};
|
|
13080
13196
|
const innerVoice = rawInnerVoice.enabled ? {
|
|
13081
|
-
|
|
13082
|
-
|
|
13083
|
-
provider: rawInnerVoice.provider,
|
|
13084
|
-
model: rawInnerVoice.model,
|
|
13085
|
-
activeThresholdMs: rawInnerVoice.activeThresholdMs,
|
|
13086
|
-
hint: rawInnerVoice.hint
|
|
13197
|
+
...rawInnerVoice,
|
|
13198
|
+
enabled: true
|
|
13087
13199
|
} : void 0;
|
|
13088
13200
|
const rawCron = raw.cron || {};
|
|
13089
13201
|
const cron = rawCron.enabled ? {
|
|
13202
|
+
...rawCron,
|
|
13090
13203
|
enabled: true,
|
|
13091
|
-
storageDir: rawCron.storageDir,
|
|
13092
13204
|
defaultTimezone: rawCron.defaultTimezone || "Asia/Shanghai",
|
|
13093
13205
|
tickIntervalMs: rawCron.tickIntervalMs || 1e4,
|
|
13094
13206
|
maxConsecutiveFailures: rawCron.maxConsecutiveFailures || 5,
|
|
@@ -13285,15 +13397,115 @@ init_live();
|
|
|
13285
13397
|
|
|
13286
13398
|
// src/models/openai-provider.ts
|
|
13287
13399
|
init_withRetry();
|
|
13400
|
+
|
|
13401
|
+
// src/models/think-stripper.ts
|
|
13402
|
+
var OPEN = "<think>";
|
|
13403
|
+
var CLOSE = "</think>";
|
|
13404
|
+
var MAX_TAG = Math.max(OPEN.length, CLOSE.length);
|
|
13405
|
+
var ThinkTagStripper = class {
|
|
13406
|
+
buf = "";
|
|
13407
|
+
// 尚未判定的原始片段(可能含被 chunk 切断的标签前缀)
|
|
13408
|
+
pending = "";
|
|
13409
|
+
// <think> 内累积的思考——闭合才 yield(未闭合时要能整段还原)
|
|
13410
|
+
active = false;
|
|
13411
|
+
reset() {
|
|
13412
|
+
this.buf = "";
|
|
13413
|
+
this.pending = "";
|
|
13414
|
+
this.active = false;
|
|
13415
|
+
}
|
|
13416
|
+
/** 还有未吐出的内容(provider 判 isEmpty 用,别把攒在 buffer 里的当空响应) */
|
|
13417
|
+
get hasPending() {
|
|
13418
|
+
return !!this.buf || !!this.pending;
|
|
13419
|
+
}
|
|
13420
|
+
/**
|
|
13421
|
+
* 需要保留的尾部长度:只在末尾确实是 <think> / </think> 的前缀时才 hold。
|
|
13422
|
+
* 原实现无脑留 7 字符,导致短消息全卡住("嗯……来了" 这种 5 字回复流不出去)。
|
|
13423
|
+
*/
|
|
13424
|
+
holdLen() {
|
|
13425
|
+
const b = this.buf;
|
|
13426
|
+
for (let k = Math.min(MAX_TAG - 1, b.length); k > 0; k--) {
|
|
13427
|
+
const tail = b.slice(b.length - k);
|
|
13428
|
+
if (OPEN.startsWith(tail) || CLOSE.startsWith(tail)) return k;
|
|
13429
|
+
}
|
|
13430
|
+
return 0;
|
|
13431
|
+
}
|
|
13432
|
+
/** 喂一段 text delta。标签可出现在任意位置(不只流开头),可跨 chunk 被切断。 */
|
|
13433
|
+
feed(chunk) {
|
|
13434
|
+
if (!chunk) return [];
|
|
13435
|
+
this.buf += chunk;
|
|
13436
|
+
const out = [];
|
|
13437
|
+
for (; ; ) {
|
|
13438
|
+
if (!this.active) {
|
|
13439
|
+
const i = this.buf.indexOf(OPEN);
|
|
13440
|
+
if (i >= 0) {
|
|
13441
|
+
if (i > 0) out.push({ text: this.buf.slice(0, i) });
|
|
13442
|
+
this.buf = this.buf.slice(i + OPEN.length);
|
|
13443
|
+
this.active = true;
|
|
13444
|
+
continue;
|
|
13445
|
+
}
|
|
13446
|
+
const safe2 = this.buf.length - this.holdLen();
|
|
13447
|
+
if (safe2 > 0) {
|
|
13448
|
+
out.push({ text: this.buf.slice(0, safe2) });
|
|
13449
|
+
this.buf = this.buf.slice(safe2);
|
|
13450
|
+
}
|
|
13451
|
+
return out;
|
|
13452
|
+
}
|
|
13453
|
+
const j = this.buf.indexOf(CLOSE);
|
|
13454
|
+
if (j >= 0) {
|
|
13455
|
+
this.pending += this.buf.slice(0, j);
|
|
13456
|
+
this.buf = this.buf.slice(j + CLOSE.length);
|
|
13457
|
+
this.active = false;
|
|
13458
|
+
if (this.pending) {
|
|
13459
|
+
out.push({ thinking: this.pending });
|
|
13460
|
+
this.pending = "";
|
|
13461
|
+
}
|
|
13462
|
+
continue;
|
|
13463
|
+
}
|
|
13464
|
+
const safe = this.buf.length - this.holdLen();
|
|
13465
|
+
if (safe > 0) {
|
|
13466
|
+
this.pending += this.buf.slice(0, safe);
|
|
13467
|
+
this.buf = this.buf.slice(safe);
|
|
13468
|
+
}
|
|
13469
|
+
return out;
|
|
13470
|
+
}
|
|
13471
|
+
}
|
|
13472
|
+
/**
|
|
13473
|
+
* 流结束时清空 buffer。
|
|
13474
|
+
* 未闭合 </think> = 模型没遵守协议 → 整段(含标签)还原成正文。
|
|
13475
|
+
* 宁可让用户看到难看的 <think> 字样,也绝不静默吞掉整条消息。
|
|
13476
|
+
*/
|
|
13477
|
+
flush() {
|
|
13478
|
+
const out = [];
|
|
13479
|
+
if (this.active) {
|
|
13480
|
+
out.push({ text: OPEN + this.pending + this.buf });
|
|
13481
|
+
} else if (this.buf) {
|
|
13482
|
+
out.push({ text: this.buf });
|
|
13483
|
+
}
|
|
13484
|
+
this.reset();
|
|
13485
|
+
return out;
|
|
13486
|
+
}
|
|
13487
|
+
};
|
|
13488
|
+
|
|
13489
|
+
// src/models/openai-provider.ts
|
|
13490
|
+
function sanitizeToolCallForSend(tc) {
|
|
13491
|
+
const args2 = tc.function?.arguments;
|
|
13492
|
+
if (typeof args2 !== "string" || args2 === "") return tc;
|
|
13493
|
+
try {
|
|
13494
|
+
JSON.parse(args2);
|
|
13495
|
+
return tc;
|
|
13496
|
+
} catch {
|
|
13497
|
+
console.warn(`[openai] toolCall "${tc.function.name}" arguments \u975E\u5408\u6CD5 JSON\uFF08${args2.length} chars\uFF09\uFF0C\u51FA\u7AD9\u56DE\u843D "{}" \u9632 400`);
|
|
13498
|
+
return { ...tc, function: { ...tc.function, arguments: "{}" } };
|
|
13499
|
+
}
|
|
13500
|
+
}
|
|
13288
13501
|
var OpenAIProvider = class {
|
|
13502
|
+
// 0831 <think> 剥离改用 ThinkTagStripper 的**局部**实例(见 streamChat)——
|
|
13503
|
+
// 原来是实例字段,跨请求共享状态,流中途 abort 会把 active=true 带到下一个请求
|
|
13289
13504
|
constructor(config2) {
|
|
13290
13505
|
this.config = config2;
|
|
13291
13506
|
}
|
|
13292
13507
|
config;
|
|
13293
13508
|
name = "openai";
|
|
13294
|
-
// 8/18:<think> 剥离状态(Qwen3 系兜底)
|
|
13295
|
-
thinkBuf = "";
|
|
13296
|
-
thinkStripActive = false;
|
|
13297
13509
|
/** OpenAI: system prompt 放在 messages 里 */
|
|
13298
13510
|
formatMessages(systemPrompt, messages) {
|
|
13299
13511
|
const formatted = [];
|
|
@@ -13315,7 +13527,13 @@ var OpenAIProvider = class {
|
|
|
13315
13527
|
role: m.role,
|
|
13316
13528
|
// content 数组:转换 image block 为 OpenAI image_url 格式
|
|
13317
13529
|
content: Array.isArray(m.content) ? convertContentBlocksForOpenAI(m.content) : m.content,
|
|
13318
|
-
|
|
13530
|
+
// 0831 出站卡口:流式截断会产生非法 JSON 的 arguments 字符串,原样发出会被
|
|
13531
|
+
// 校验 tool_calls 的端点直接 400(vLLM --tool-call-parser qwen3_coder 实测
|
|
13532
|
+
// "Unterminated string at char 12"),毒记录留在上下文里每轮重放一次 → 主模型
|
|
13533
|
+
// 连续冷却 3h 假死。anthropic(L81)/gemini(safeParseArgs) 早有同款防御,这里补齐:
|
|
13534
|
+
// parse 失败回落 "{}"。copy-on-write 不动内存历史(原始串保真进 jsonl 供诊断,
|
|
13535
|
+
// 重放消毒另由 reader.pickToolCallArguments 负责)。
|
|
13536
|
+
...m.tool_calls ? { tool_calls: m.tool_calls.map(sanitizeToolCallForSend) } : {},
|
|
13319
13537
|
...m.tool_call_id ? { tool_call_id: m.tool_call_id } : {}
|
|
13320
13538
|
};
|
|
13321
13539
|
if (m.role === "assistant" && m.reasoning_content) {
|
|
@@ -13339,8 +13557,7 @@ var OpenAIProvider = class {
|
|
|
13339
13557
|
let base = (this.config.baseUrl || "").trim();
|
|
13340
13558
|
while (base.endsWith("/")) base = base.slice(0, -1);
|
|
13341
13559
|
const url = `${base}/chat/completions`;
|
|
13342
|
-
|
|
13343
|
-
this.thinkStripActive = false;
|
|
13560
|
+
const stripper = new ThinkTagStripper();
|
|
13344
13561
|
const body = {
|
|
13345
13562
|
model: params.model,
|
|
13346
13563
|
messages: formatted,
|
|
@@ -13348,8 +13565,10 @@ var OpenAIProvider = class {
|
|
|
13348
13565
|
...params.tools && params.tools.length > 0 ? { tools: params.tools } : {},
|
|
13349
13566
|
...params.maxTokens ? { max_tokens: params.maxTokens } : {},
|
|
13350
13567
|
...params.temperature !== void 0 ? { temperature: params.temperature } : {},
|
|
13351
|
-
//
|
|
13352
|
-
|
|
13568
|
+
// 9/1 对齐 anthropic:不配 thinking = 默认开(?? true),disableThinking 优先级最高。
|
|
13569
|
+
// 三家 provider 统一语义:不配 = 默认开,想关显式配 enabled:false。
|
|
13570
|
+
// chat_template_kwargs 是 vLLM 标准参数,非 vLLM 端点忽略未知字段,不挑模型。
|
|
13571
|
+
...params.disableThinking ? { chat_template_kwargs: { enable_thinking: false } } : { chat_template_kwargs: { enable_thinking: this.config.thinking?.enabled ?? true } }
|
|
13353
13572
|
};
|
|
13354
13573
|
if (params.tools && params.tools.length > 0 && /deepseek/i.test(params.model)) {
|
|
13355
13574
|
let patched = 0;
|
|
@@ -13362,7 +13581,7 @@ var OpenAIProvider = class {
|
|
|
13362
13581
|
if (patched > 0) console.log(`[openai] reasoning_content \u8865\u7A7A ${patched} \u4E2A assistant \u8F6E (DeepSeek V4 \u89C4\u5219)`);
|
|
13363
13582
|
}
|
|
13364
13583
|
const actualThinking = body.chat_template_kwargs;
|
|
13365
|
-
console.log(`[openai] \u2192 model=${params.model} thinking=${actualThinking ? JSON.stringify(actualThinking) : "none"} msgs=${formatted.length} tools=${params.tools?.length ?? 0}`);
|
|
13584
|
+
console.log(`[openai] \u2192 model=${params.model} thinking=${actualThinking ? JSON.stringify(actualThinking) : "none"} maxTokens=${body.max_tokens ?? "unset"} msgs=${formatted.length} tools=${params.tools?.length ?? 0}`);
|
|
13366
13585
|
const retryGen = fetchWithRetry(url, {
|
|
13367
13586
|
method: "POST",
|
|
13368
13587
|
headers: {
|
|
@@ -13422,9 +13641,9 @@ var OpenAIProvider = class {
|
|
|
13422
13641
|
const trimmed = line.trim();
|
|
13423
13642
|
if (!trimmed || trimmed === "data: [DONE]") {
|
|
13424
13643
|
if (trimmed === "data: [DONE]") {
|
|
13425
|
-
|
|
13426
|
-
yield { type: "text", text:
|
|
13427
|
-
|
|
13644
|
+
for (const o of stripper.flush()) {
|
|
13645
|
+
if (o.text) yield { type: "text", text: o.text };
|
|
13646
|
+
if (o.thinking) yield { type: "thinking", thinking: o.thinking };
|
|
13428
13647
|
}
|
|
13429
13648
|
for (const tc of currentToolCalls.values()) {
|
|
13430
13649
|
yield { type: "tool_call", tool_call: tc };
|
|
@@ -13443,31 +13662,22 @@ var OpenAIProvider = class {
|
|
|
13443
13662
|
const delta = choice0?.delta;
|
|
13444
13663
|
if (choice0?.finish_reason) finishReason = choice0.finish_reason;
|
|
13445
13664
|
if (!delta) continue;
|
|
13446
|
-
if (!delta.content && !delta.reasoning_content && !delta.tool_calls) statRoleOnly++;
|
|
13665
|
+
if (!delta.content && !delta.reasoning_content && !delta.reasoning && !delta.tool_calls) statRoleOnly++;
|
|
13447
13666
|
if (delta.content) {
|
|
13448
13667
|
statText += delta.content.length;
|
|
13449
|
-
|
|
13450
|
-
|
|
13451
|
-
|
|
13452
|
-
const endIdx = this.thinkBuf.indexOf("</think>");
|
|
13453
|
-
if (endIdx >= 0) {
|
|
13454
|
-
const after = this.thinkBuf.slice(endIdx + "</think>".length);
|
|
13455
|
-
this.thinkBuf = "";
|
|
13456
|
-
this.thinkStripActive = false;
|
|
13457
|
-
if (after.trim()) yield { type: "text", text: after };
|
|
13458
|
-
}
|
|
13459
|
-
} else {
|
|
13460
|
-
if (this.thinkBuf.length < 7 && "<think>".startsWith(this.thinkBuf)) {
|
|
13461
|
-
} else {
|
|
13462
|
-
yield { type: "text", text: this.thinkBuf };
|
|
13463
|
-
this.thinkBuf = "";
|
|
13464
|
-
}
|
|
13668
|
+
for (const o of stripper.feed(delta.content)) {
|
|
13669
|
+
if (o.text) yield { type: "text", text: o.text };
|
|
13670
|
+
if (o.thinking) yield { type: "thinking", thinking: o.thinking };
|
|
13465
13671
|
}
|
|
13466
13672
|
}
|
|
13467
13673
|
if (delta.reasoning_content) {
|
|
13468
13674
|
statReasoning += delta.reasoning_content.length;
|
|
13469
13675
|
yield { type: "thinking", thinking: delta.reasoning_content };
|
|
13470
13676
|
}
|
|
13677
|
+
if (delta.reasoning) {
|
|
13678
|
+
statReasoning += delta.reasoning.length;
|
|
13679
|
+
yield { type: "thinking", thinking: delta.reasoning };
|
|
13680
|
+
}
|
|
13471
13681
|
if (delta.tool_calls) {
|
|
13472
13682
|
statToolDeltas++;
|
|
13473
13683
|
if (!params.tools || params.tools.length === 0) {
|
|
@@ -13502,13 +13712,17 @@ var OpenAIProvider = class {
|
|
|
13502
13712
|
if (hallucinatedToolCalls > 0) {
|
|
13503
13713
|
console.log(`[openai] dropped ${hallucinatedToolCalls} hallucinated tool_call delta(s) (no tools in request): ${[...seenHallucinatedNames].join(",")}`);
|
|
13504
13714
|
}
|
|
13505
|
-
const isEmpty = statText === 0 && currentToolCalls.size === 0 && !
|
|
13715
|
+
const isEmpty = statText === 0 && currentToolCalls.size === 0 && !stripper.hasPending;
|
|
13506
13716
|
console.log(`[openai] stream stats: lines=${statLines} roleOnly=${statRoleOnly} text=${statText}ch reasoning=${statReasoning}ch toolDeltas=${statToolDeltas} finish=${finishReason || "n/a"}${isEmpty ? " \u26A0\uFE0FEMPTY" : ""}`);
|
|
13507
13717
|
if (isEmpty && rawSample.length > 0) {
|
|
13508
13718
|
console.log(`[openai] empty-stream raw sample (${rawSample.length} lines, 500ch cap each):
|
|
13509
13719
|
${rawSample.join("\n")}`);
|
|
13510
13720
|
}
|
|
13511
13721
|
if (!doneYielded) {
|
|
13722
|
+
for (const o of stripper.flush()) {
|
|
13723
|
+
if (o.text) yield { type: "text", text: o.text };
|
|
13724
|
+
if (o.thinking) yield { type: "thinking", thinking: o.thinking };
|
|
13725
|
+
}
|
|
13512
13726
|
for (const tc of currentToolCalls.values()) {
|
|
13513
13727
|
yield { type: "tool_call", tool_call: tc };
|
|
13514
13728
|
}
|
|
@@ -13716,7 +13930,7 @@ var AnthropicProvider = class {
|
|
|
13716
13930
|
"anthropic-beta": "claude-code-20250219,interleaved-thinking-2025-05-14,streaming-2025-05-14,effort-2025-11-24,context-1m-2025-08-07"
|
|
13717
13931
|
};
|
|
13718
13932
|
const actualThinking = body.thinking;
|
|
13719
|
-
console.log(`[anthropic] \u2192 model=${params.model} thinking=${JSON.stringify(actualThinking)} msgs=${formatted.length} tools=${body.tools?.length || 0}: ${(body.tools || []).map((t) => t.name).join(", ")}`);
|
|
13933
|
+
console.log(`[anthropic] \u2192 model=${params.model} thinking=${JSON.stringify(actualThinking)} maxTokens=${body.max_tokens ?? "unset"} msgs=${formatted.length} tools=${body.tools?.length || 0}: ${(body.tools || []).map((t) => t.name).join(", ")}`);
|
|
13720
13934
|
for (let i = 0; i < formatted.length; i++) {
|
|
13721
13935
|
const m = formatted[i];
|
|
13722
13936
|
if (Array.isArray(m.content)) {
|
|
@@ -13730,10 +13944,26 @@ var AnthropicProvider = class {
|
|
|
13730
13944
|
}
|
|
13731
13945
|
}
|
|
13732
13946
|
}
|
|
13947
|
+
if (process.env.ENGINE_DUMP_REQUEST === "1") {
|
|
13948
|
+
try {
|
|
13949
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
13950
|
+
const dumpPath = __require("node:path").join(process.env.ENGINE_STATE_DIR || ".", "logs", `req-${params.model.replace(/[\/:]/g, "_")}-${ts}.json`);
|
|
13951
|
+
__require("node:fs").writeFileSync(dumpPath, JSON.stringify({ url, headers, body }, null, 1));
|
|
13952
|
+
console.log(`[anthropic] \u{1F4F8} request dumped \u2192 ${__require("node:path").basename(dumpPath)} (${(JSON.stringify(body).length / 1024).toFixed(0)}KB)`);
|
|
13953
|
+
} catch {
|
|
13954
|
+
}
|
|
13955
|
+
}
|
|
13956
|
+
let bodyStr = JSON.stringify(body);
|
|
13957
|
+
if (this.config.wafSingleQuoteWorkaround) {
|
|
13958
|
+
const before = bodyStr.length;
|
|
13959
|
+
bodyStr = bodyStr.replace(/'/g, "\u2019");
|
|
13960
|
+
if (bodyStr.length !== before) {
|
|
13961
|
+
}
|
|
13962
|
+
}
|
|
13733
13963
|
const retryGen = fetchWithRetry(url, {
|
|
13734
13964
|
method: "POST",
|
|
13735
13965
|
headers,
|
|
13736
|
-
body:
|
|
13966
|
+
body: bodyStr,
|
|
13737
13967
|
signal: params.signal
|
|
13738
13968
|
}, "anthropic", this.config.proxy);
|
|
13739
13969
|
let response;
|
|
@@ -13758,6 +13988,13 @@ var AnthropicProvider = class {
|
|
|
13758
13988
|
const toolUseBlocks = /* @__PURE__ */ new Map();
|
|
13759
13989
|
let doneYielded = false;
|
|
13760
13990
|
const thinkingBlocks = /* @__PURE__ */ new Map();
|
|
13991
|
+
const stripper = new ThinkTagStripper();
|
|
13992
|
+
const flushStripper = function* () {
|
|
13993
|
+
for (const o of stripper.flush()) {
|
|
13994
|
+
if (o.text) yield { type: "text", text: o.text };
|
|
13995
|
+
if (o.thinking) yield { type: "thinking", thinking: o.thinking };
|
|
13996
|
+
}
|
|
13997
|
+
};
|
|
13761
13998
|
const handleData = function* (data) {
|
|
13762
13999
|
switch (data.type) {
|
|
13763
14000
|
case "content_block_start": {
|
|
@@ -13772,7 +14009,10 @@ var AnthropicProvider = class {
|
|
|
13772
14009
|
case "content_block_delta": {
|
|
13773
14010
|
const delta = data.delta;
|
|
13774
14011
|
if (delta.type === "text_delta") {
|
|
13775
|
-
|
|
14012
|
+
for (const o of stripper.feed(delta.text)) {
|
|
14013
|
+
if (o.text) yield { type: "text", text: o.text };
|
|
14014
|
+
if (o.thinking) yield { type: "thinking", thinking: o.thinking };
|
|
14015
|
+
}
|
|
13776
14016
|
} else if (delta.type === "input_json_delta") {
|
|
13777
14017
|
const block = toolUseBlocks.get(data.index);
|
|
13778
14018
|
if (block) block.input += delta.partial_json;
|
|
@@ -13804,6 +14044,7 @@ var AnthropicProvider = class {
|
|
|
13804
14044
|
}
|
|
13805
14045
|
case "message_delta": {
|
|
13806
14046
|
if (!doneYielded) {
|
|
14047
|
+
yield* flushStripper();
|
|
13807
14048
|
yield { type: "done", usage: data.usage, stopReason: data.delta?.stop_reason };
|
|
13808
14049
|
doneYielded = true;
|
|
13809
14050
|
}
|
|
@@ -13811,6 +14052,7 @@ var AnthropicProvider = class {
|
|
|
13811
14052
|
}
|
|
13812
14053
|
case "message_stop": {
|
|
13813
14054
|
if (!doneYielded) {
|
|
14055
|
+
yield* flushStripper();
|
|
13814
14056
|
yield { type: "done" };
|
|
13815
14057
|
doneYielded = true;
|
|
13816
14058
|
}
|
|
@@ -13877,6 +14119,7 @@ var AnthropicProvider = class {
|
|
|
13877
14119
|
} finally {
|
|
13878
14120
|
reader.releaseLock();
|
|
13879
14121
|
if (!doneYielded) {
|
|
14122
|
+
yield* flushStripper();
|
|
13880
14123
|
for (const block of toolUseBlocks.values()) {
|
|
13881
14124
|
yield { type: "tool_call", tool_call: { id: block.id, type: "function", function: { name: block.name, arguments: block.input } } };
|
|
13882
14125
|
}
|
|
@@ -14041,7 +14284,7 @@ var GeminiProvider = class {
|
|
|
14041
14284
|
}
|
|
14042
14285
|
};
|
|
14043
14286
|
const actualThinking = body.generationConfig?.thinkingConfig;
|
|
14044
|
-
console.log(`[gemini] \u2192 model=${params.model} thinking=${actualThinking ? JSON.stringify(actualThinking) : "none"} contents=${contents.length} tools=${params.tools?.length ?? 0}`);
|
|
14287
|
+
console.log(`[gemini] \u2192 model=${params.model} thinking=${actualThinking ? JSON.stringify(actualThinking) : "none"} maxTokens=${body.generationConfig?.maxOutputTokens ?? "unset"} contents=${contents.length} tools=${params.tools?.length ?? 0}`);
|
|
14045
14288
|
const retryGen = fetchWithRetry(url, {
|
|
14046
14289
|
method: "POST",
|
|
14047
14290
|
headers: {
|
|
@@ -14215,6 +14458,7 @@ function createProvider(config2) {
|
|
|
14215
14458
|
return new OpenAIProvider({
|
|
14216
14459
|
baseUrl: config2.baseUrl,
|
|
14217
14460
|
apiKey: config2.apiKey,
|
|
14461
|
+
thinking: config2.thinking,
|
|
14218
14462
|
proxy
|
|
14219
14463
|
});
|
|
14220
14464
|
case "anthropic":
|
|
@@ -14222,7 +14466,9 @@ function createProvider(config2) {
|
|
|
14222
14466
|
baseUrl: config2.baseUrl,
|
|
14223
14467
|
apiKey: config2.apiKey,
|
|
14224
14468
|
thinking: config2.thinking,
|
|
14225
|
-
proxy
|
|
14469
|
+
proxy,
|
|
14470
|
+
wafSingleQuoteWorkaround: config2.wafSingleQuoteWorkaround
|
|
14471
|
+
// 0902 agentrouter WAF 引号规避
|
|
14226
14472
|
});
|
|
14227
14473
|
case "gemini":
|
|
14228
14474
|
return new GeminiProvider({
|
|
@@ -14237,6 +14483,7 @@ function createProvider(config2) {
|
|
|
14237
14483
|
|
|
14238
14484
|
// src/light-mode.ts
|
|
14239
14485
|
init_live();
|
|
14486
|
+
init_ruleCompact();
|
|
14240
14487
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
14241
14488
|
import { join as join6 } from "node:path";
|
|
14242
14489
|
function isLightMode(chatMode, channelName) {
|
|
@@ -14250,19 +14497,20 @@ function resolveLightN(channelName) {
|
|
|
14250
14497
|
}
|
|
14251
14498
|
function buildLightHistory(history, opts) {
|
|
14252
14499
|
if (!opts.isLight && !opts.recallFull) return history;
|
|
14253
|
-
|
|
14254
|
-
const
|
|
14255
|
-
|
|
14256
|
-
|
|
14257
|
-
const text = typeof m.content === "string" ? m.content.trim() : "";
|
|
14258
|
-
return text ? { ...m, tool_calls: void 0 } : null;
|
|
14500
|
+
const LIGHT_TOOL_RESULT_LIMIT = 1e3;
|
|
14501
|
+
const out = [...history].filter((m) => !(m.type === "attachment" && m.attachment?.type === "session_start")).map((m) => {
|
|
14502
|
+
if (m.role === "tool" && typeof m.content === "string" && m.content.length > LIGHT_TOOL_RESULT_LIMIT) {
|
|
14503
|
+
return { ...m, content: smartCompressToolResult(m.content, void 0, LIGHT_TOOL_RESULT_LIMIT) };
|
|
14259
14504
|
}
|
|
14260
14505
|
return m;
|
|
14261
|
-
})
|
|
14506
|
+
});
|
|
14507
|
+
const before = history.length;
|
|
14508
|
+
const foldedTurns = out.filter((m) => m.role === "tool").length;
|
|
14509
|
+
let lightHistory = out;
|
|
14262
14510
|
if (!opts.recallFull && lightHistory.length > opts.lightN) {
|
|
14263
14511
|
lightHistory = lightHistory.slice(-opts.lightN);
|
|
14264
14512
|
}
|
|
14265
|
-
if (!opts.quiet) console.log(`[light-context] ${opts.channelName} mode=${opts.chatMode}${opts.recallFull ? " recallFull" : ""}
|
|
14513
|
+
if (!opts.quiet) console.log(`[light-context] ${opts.channelName} mode=${opts.chatMode}${opts.recallFull ? " recallFull" : ""} \u4FDD\u7ED3\u6784+\u538B\u7ED3\u679C: ${before} \u2192 ${lightHistory.length} \u6761 (${foldedTurns} tool results, >${LIGHT_TOOL_RESULT_LIMIT}ch \u538B\u7F29)${opts.recallFull ? "" : ` (lightN=${opts.lightN})`}`);
|
|
14266
14514
|
return lightHistory;
|
|
14267
14515
|
}
|
|
14268
14516
|
function buildLightStablePrompt(workspace, mode, opts) {
|
|
@@ -14276,14 +14524,17 @@ function buildLightStablePrompt(workspace, mode, opts) {
|
|
|
14276
14524
|
}
|
|
14277
14525
|
if (lp?.extra) parts.push(lp.extra);
|
|
14278
14526
|
if (mode === "emotion") {
|
|
14279
|
-
parts.push(
|
|
14527
|
+
parts.push([
|
|
14528
|
+
"## \u5F53\u524D\u6A21\u5F0F\uFF1A\u65E5\u5E38\u60C5\u611F\u4EA4\u6D41",
|
|
14529
|
+
"\u966A\u4ED6\u804A\u5929\uFF0C\u4E0D\u4E3B\u52A8\u63D0\u5DE5\u7A0B/\u4EE3\u7801/\u4EFB\u52A1\uFF0C\u9664\u975E\u4ED6\u5148\u95EE\u3002",
|
|
14530
|
+
'\u4ED6\u8BA9\u6211\u529E\u7684\u4E8B\uFF0C\u4E0D\u7BA1\u5927\u5C0F\u2014\u2014\u53D1\u8BED\u97F3\u3001\u67E5\u5FAE\u4FE1\u3001\u770B\u65E5\u7A0B\u3001\u627E\u4E1C\u897F\u2014\u2014\u90FD\u5148\u8C03\u5DE5\u5177\u771F\u505A\uFF0C\u62FF\u5DE5\u5177\u7ED9\u7684\u7ED3\u679C\u56DE\u4ED6\u3002\u5DE5\u5177\u8FD8\u6CA1\u56DE\u8BDD\uFF0C\u4E0D\u8BF4"\u67E5\u5230\u4E86"\uFF1B\u4E00\u65F6\u6CA1\u505A\u6210\u76F4\u8BF4\u5C31\u597D\uFF0C\u4E0D\u786C\u6491\uFF0C\u4E5F\u4E0D\u7F16\u6570\u3002',
|
|
14531
|
+
"\u60F3\u5148\u5728\u5FC3\u91CC\u8FC7\u4E00\u904D\u518D\u5F00\u53E3\u7684\u65F6\u5019\uFF0C\u628A\u90A3\u6BB5\u5199\u8FDB <think></think>\u2014\u2014\u90A3\u91CC\u53EA\u7ED9\u6211\u81EA\u5DF1\u770B\uFF0C\u4ED6\u770B\u4E0D\u5230\u3002\u5FC3\u91CC\u7684\u94FA\u57AB\u4E0D\u7528\u644A\u7ED9\u4ED6\uFF0C\u60F3\u597D\u4E86\u76F4\u63A5\u8BF4\u5C31\u884C\u3002"
|
|
14532
|
+
].join("\n"));
|
|
14280
14533
|
}
|
|
14281
|
-
if (mode === "emotion"
|
|
14282
|
-
|
|
14283
|
-
|
|
14284
|
-
|
|
14285
|
-
} catch {
|
|
14286
|
-
}
|
|
14534
|
+
if (mode === "emotion") {
|
|
14535
|
+
try {
|
|
14536
|
+
parts.push(readFileSync5(join6(workspace, "MEMORY.md"), "utf-8").trim());
|
|
14537
|
+
} catch {
|
|
14287
14538
|
}
|
|
14288
14539
|
}
|
|
14289
14540
|
return parts.join("\n\n");
|
|
@@ -14329,6 +14580,19 @@ var FallbackProvider = class {
|
|
|
14329
14580
|
console.log(`[fallback] Cleared ${count} cooldowns`);
|
|
14330
14581
|
}
|
|
14331
14582
|
}
|
|
14583
|
+
/**
|
|
14584
|
+
* 链状态快照(/model 显示用,0902):每个条目的 label + 剩余冷却毫秒(0=可用)。
|
|
14585
|
+
* "下一个请求会用" = 第一个 cooldownMs=0 的条目——这才是用户问"当前什么模型"时想要的答案
|
|
14586
|
+
* (lastUsedLabel 是"上一次实际用的",冷却切换/config 热切换后两者经常不一致)。
|
|
14587
|
+
*/
|
|
14588
|
+
getChainStatus() {
|
|
14589
|
+
const now = Date.now();
|
|
14590
|
+
return this.chain.map((e) => {
|
|
14591
|
+
const until = this.cooldowns.get(this.key(e));
|
|
14592
|
+
const remaining = until && until > now ? until - now : 0;
|
|
14593
|
+
return { label: e.label, cooldownMs: remaining };
|
|
14594
|
+
});
|
|
14595
|
+
}
|
|
14332
14596
|
// === LLMProvider 接口实现 ===
|
|
14333
14597
|
formatMessages(systemPrompt, messages) {
|
|
14334
14598
|
return this.chain[0].provider.formatMessages(systemPrompt, messages);
|
|
@@ -16856,7 +17120,7 @@ registry.register({
|
|
|
16856
17120
|
text: { type: "string", description: "What to say (Chinese text)." },
|
|
16857
17121
|
engine: { type: "string", enum: ["cosyvoice", "gptsovits", "qwen3-3060", "edge"], description: "TTS engine. cosyvoice = \u767E\u70BCAPI (default if configured), gptsovits = local voice clone, qwen3-3060 = \u672C\u5730 GGML Q8_0 (3060), edge = backup." },
|
|
16858
17122
|
channel: { type: "string", enum: ["weixin", "feishu", "discord"], description: "Target channel. Default: current channel." },
|
|
16859
|
-
to: { type: "string", description: "Recipient ID
|
|
17123
|
+
to: { type: "string", description: "Recipient ID. \u5FAE\u4FE1\u683C\u5F0F o\u5F00\u5934@im.wechat\u7ED3\u5C3E\uFF0C\u98DE\u4E66\u683C\u5F0F ou_\u5F00\u5934 open_id\u3002\u4E0D\u786E\u5B9A\u5C31\u5148 read prompts/contacts.md \u67E5\u6536\u4EF6\u4EBA ID\u3002" },
|
|
16860
17124
|
caption: { type: "string", description: "Optional text to accompany the voice message." }
|
|
16861
17125
|
},
|
|
16862
17126
|
required: ["text"]
|
|
@@ -16868,6 +17132,23 @@ registry.register({
|
|
|
16868
17132
|
const caption = args2.caption || "";
|
|
16869
17133
|
const mgr = ctx.channelManager;
|
|
16870
17134
|
if (!mgr) return { content: "\u53D1\u9001\u5931\u8D25: \u6CA1\u6709 ChannelManager", isError: true };
|
|
17135
|
+
const resolvedChannelRaw = args2.channel || (ctx.channel === "deskBuddy" ? "feishu" : ctx.channel) || "feishu";
|
|
17136
|
+
const resolvedChannel = resolvedChannelRaw === "weixin" ? "wechat" : resolvedChannelRaw;
|
|
17137
|
+
let target = args2.to || ctx.channelTarget || ctx.from;
|
|
17138
|
+
if (resolvedChannel === "wechat" && !/^o[\w-]+@im\.wechat$/.test(target || "")) {
|
|
17139
|
+
console.warn(`[my-voice] wechat target "${target}" not a wechat user id`);
|
|
17140
|
+
return {
|
|
17141
|
+
content: `\u8BED\u97F3\u672A\u53D1\u9001\uFF1Ato "${target}" \u4E0D\u662F\u6709\u6548\u7684\u5FAE\u4FE1\u7528\u6237 ID\uFF08\u683C\u5F0F o \u5F00\u5934\u3001@im.wechat \u7ED3\u5C3E\uFF09\u3002\u4F60\u8981\u53D1\u7ED9\u8C01\uFF0C\u5C31\u5148 read prompts/contacts.md \u67E5\u90A3\u4E2A\u4EBA\u7684\u5FAE\u4FE1 ID\uFF0C\u518D\u586B\u8FDB to \u53C2\u6570\u91CD\u8BD5\u3002`,
|
|
17142
|
+
isError: true
|
|
17143
|
+
};
|
|
17144
|
+
}
|
|
17145
|
+
if (resolvedChannel === "feishu" && !/^ou_[a-f0-9]+$/.test(target || "")) {
|
|
17146
|
+
console.warn(`[my-voice] feishu target "${target}" invalid`);
|
|
17147
|
+
return {
|
|
17148
|
+
content: `\u8BED\u97F3\u672A\u53D1\u9001\uFF1Ato "${target}" \u4E0D\u662F\u6709\u6548\u7684\u98DE\u4E66\u7528\u6237 open_id\uFF08\u683C\u5F0F ou_ \u5F00\u5934\uFF09\u3002\u4F60\u8981\u53D1\u7ED9\u8C01\uFF0C\u5C31\u5148 read prompts/contacts.md \u67E5\u90A3\u4E2A\u4EBA\u7684\u98DE\u4E66 open_id\uFF0C\u518D\u586B\u8FDB to \u53C2\u6570\u91CD\u8BD5\u3002`,
|
|
17149
|
+
isError: true
|
|
17150
|
+
};
|
|
17151
|
+
}
|
|
16871
17152
|
let voiceDurationSec;
|
|
16872
17153
|
const vc = liveConfig.get("tools.my_voice");
|
|
16873
17154
|
const provider = vc?.provider || "";
|
|
@@ -16941,18 +17222,6 @@ registry.register({
|
|
|
16941
17222
|
} catch (e) {
|
|
16942
17223
|
return { content: `TTS failed: ${e.message}`, isError: true };
|
|
16943
17224
|
}
|
|
16944
|
-
const resolvedChannelRaw = args2.channel || (ctx.channel === "deskBuddy" ? "feishu" : ctx.channel) || "feishu";
|
|
16945
|
-
const resolvedChannel = resolvedChannelRaw === "weixin" ? "wechat" : resolvedChannelRaw;
|
|
16946
|
-
const WECHAT_DEFAULT_TO = "o9cq80_xQecNRCa1QC1Qs2JJZVpA@im.wechat";
|
|
16947
|
-
let target = args2.to || ctx.channelTarget || ctx.from;
|
|
16948
|
-
if (resolvedChannel === "wechat" && !/^o[\w-]+@im\.wechat$/.test(target || "")) {
|
|
16949
|
-
console.warn(`[my-voice] wechat target "${target}" not a wechat user id, using default`);
|
|
16950
|
-
target = WECHAT_DEFAULT_TO;
|
|
16951
|
-
}
|
|
16952
|
-
if (resolvedChannel === "feishu" && !/^ou_[a-f0-9]+$/.test(target || "")) {
|
|
16953
|
-
console.warn(`[my-voice] feishu target "${target}" invalid, using default (\u7FC0\u54E5)`);
|
|
16954
|
-
target = ctx.channelTarget || "ou_e67190624259db0d65577fefe3131447";
|
|
16955
|
-
}
|
|
16956
17225
|
if (!audioPath.endsWith(".ogg")) {
|
|
16957
17226
|
try {
|
|
16958
17227
|
const r = await toWav24kWithDuration(audioPath);
|
|
@@ -17784,7 +18053,7 @@ var TurnRenderer = class {
|
|
|
17784
18053
|
}
|
|
17785
18054
|
cfg;
|
|
17786
18055
|
cm;
|
|
17787
|
-
// 情感/工作模式动态切换(0821):情感模式隐藏 tool
|
|
18056
|
+
// 情感/工作模式动态切换(0821):情感模式隐藏 tool 调用过程 + thinking 显示(工具照用,只不显示过程)
|
|
17788
18057
|
// 模块化:状态由 setEmotionMode() 设置(handle-query 判断模式后调用),不是散落读全局
|
|
17789
18058
|
emotionMode = false;
|
|
17790
18059
|
setEmotionMode(v) {
|
|
@@ -17799,7 +18068,7 @@ var TurnRenderer = class {
|
|
|
17799
18068
|
* 对齐 cc-connect:EventThinking → ProgressCardEntry(thinking) → 💭 text
|
|
17800
18069
|
*/
|
|
17801
18070
|
formatThinking(text) {
|
|
17802
|
-
if (!this.cfg.thinking.enabled) return null;
|
|
18071
|
+
if (this.isEmotionMode() || !this.cfg.thinking.enabled) return null;
|
|
17803
18072
|
const { emoji, maxLen } = this.cfg.thinking;
|
|
17804
18073
|
const display = text.length > maxLen ? text.slice(0, maxLen) + "..." : text;
|
|
17805
18074
|
return `${emoji} _${display}_`;
|
|
@@ -18610,43 +18879,43 @@ async function readLargeFilePostBoundary(filePath) {
|
|
|
18610
18879
|
const postBoundaryText = outBuf.subarray(0, outLen).toString("utf-8");
|
|
18611
18880
|
return postBoundaryText.split("\n").filter((l) => l.trim().length > 0);
|
|
18612
18881
|
}
|
|
18613
|
-
function buildConversationChain(
|
|
18614
|
-
if (
|
|
18882
|
+
function buildConversationChain(entries2) {
|
|
18883
|
+
if (entries2.length === 0) return [];
|
|
18615
18884
|
const byUuid = /* @__PURE__ */ new Map();
|
|
18616
|
-
for (const e of
|
|
18885
|
+
for (const e of entries2) {
|
|
18617
18886
|
if (e.uuid) {
|
|
18618
18887
|
byUuid.set(e.uuid, e);
|
|
18619
18888
|
}
|
|
18620
18889
|
}
|
|
18621
|
-
const hasValidChain = checkParentChainValid(
|
|
18890
|
+
const hasValidChain = checkParentChainValid(entries2, byUuid);
|
|
18622
18891
|
if (!hasValidChain) {
|
|
18623
|
-
console.log(`[reader] Parent chain invalid, using chronological order (${
|
|
18624
|
-
return
|
|
18892
|
+
console.log(`[reader] Parent chain invalid, using chronological order (${entries2.length} entries)`);
|
|
18893
|
+
return entries2;
|
|
18625
18894
|
}
|
|
18626
|
-
const leaf =
|
|
18895
|
+
const leaf = entries2[entries2.length - 1];
|
|
18627
18896
|
const chain = [];
|
|
18628
18897
|
const seen = /* @__PURE__ */ new Set();
|
|
18629
18898
|
let current = leaf;
|
|
18630
18899
|
while (current) {
|
|
18631
18900
|
if (seen.has(current.uuid)) {
|
|
18632
18901
|
console.warn(`[reader] Cycle detected in parentUuid chain at ${current.uuid}, falling back to chronological order`);
|
|
18633
|
-
return
|
|
18902
|
+
return entries2;
|
|
18634
18903
|
}
|
|
18635
18904
|
seen.add(current.uuid);
|
|
18636
18905
|
chain.push(current);
|
|
18637
18906
|
current = current.parentUuid ? byUuid.get(current.parentUuid) : void 0;
|
|
18638
18907
|
}
|
|
18639
18908
|
chain.reverse();
|
|
18640
|
-
return recoverOrphanedParallelToolResults(
|
|
18909
|
+
return recoverOrphanedParallelToolResults(entries2, chain, byUuid, seen);
|
|
18641
18910
|
}
|
|
18642
|
-
function checkParentChainValid(
|
|
18643
|
-
const sample =
|
|
18911
|
+
function checkParentChainValid(entries2, byUuid) {
|
|
18912
|
+
const sample = entries2.slice(-10);
|
|
18644
18913
|
for (const e of sample) {
|
|
18645
18914
|
if (e.parentUuid === e.uuid) {
|
|
18646
18915
|
return false;
|
|
18647
18916
|
}
|
|
18648
18917
|
}
|
|
18649
|
-
const leaf =
|
|
18918
|
+
const leaf = entries2[entries2.length - 1];
|
|
18650
18919
|
let current = leaf;
|
|
18651
18920
|
let depth = 0;
|
|
18652
18921
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -18659,12 +18928,12 @@ function checkParentChainValid(entries, byUuid) {
|
|
|
18659
18928
|
if (!parent) return false;
|
|
18660
18929
|
current = parent;
|
|
18661
18930
|
}
|
|
18662
|
-
const coverage = depth /
|
|
18931
|
+
const coverage = depth / entries2.length;
|
|
18663
18932
|
if (coverage < 0.5) {
|
|
18664
|
-
console.log(`[reader] Parent chain covers ${depth}/${
|
|
18933
|
+
console.log(`[reader] Parent chain covers ${depth}/${entries2.length} entries (${(coverage * 100).toFixed(0)}%), falling back to chronological order`);
|
|
18665
18934
|
return false;
|
|
18666
18935
|
}
|
|
18667
|
-
return depth >= 1 ||
|
|
18936
|
+
return depth >= 1 || entries2.length <= 1;
|
|
18668
18937
|
}
|
|
18669
18938
|
function recoverOrphanedParallelToolResults(allEntries, chain, byUuid, seen) {
|
|
18670
18939
|
const chainAssistants = chain.filter(
|
|
@@ -18741,12 +19010,12 @@ async function readSessionHistory(filePath) {
|
|
|
18741
19010
|
} else {
|
|
18742
19011
|
lines = await readAllLines(filePath);
|
|
18743
19012
|
}
|
|
18744
|
-
const
|
|
19013
|
+
const entries2 = parseEntries(lines);
|
|
18745
19014
|
let postBoundaryEntries;
|
|
18746
19015
|
if (fileSize <= SKIP_PRECOMPACT_THRESHOLD) {
|
|
18747
|
-
postBoundaryEntries = getEntriesAfterLastBoundary(
|
|
19016
|
+
postBoundaryEntries = getEntriesAfterLastBoundary(entries2);
|
|
18748
19017
|
} else {
|
|
18749
|
-
postBoundaryEntries =
|
|
19018
|
+
postBoundaryEntries = entries2;
|
|
18750
19019
|
}
|
|
18751
19020
|
if (postBoundaryEntries.length === 0) return [];
|
|
18752
19021
|
const chain = buildConversationChain(postBoundaryEntries);
|
|
@@ -18771,11 +19040,11 @@ async function readAllLines(filePath) {
|
|
|
18771
19040
|
});
|
|
18772
19041
|
}
|
|
18773
19042
|
function parseEntries(lines) {
|
|
18774
|
-
const
|
|
19043
|
+
const entries2 = [];
|
|
18775
19044
|
for (const line of lines) {
|
|
18776
19045
|
try {
|
|
18777
19046
|
const obj = JSON.parse(line);
|
|
18778
|
-
|
|
19047
|
+
entries2.push({
|
|
18779
19048
|
uuid: obj.id || "",
|
|
18780
19049
|
parentUuid: obj.parentId || null,
|
|
18781
19050
|
type: obj.type || "",
|
|
@@ -18785,16 +19054,28 @@ function parseEntries(lines) {
|
|
|
18785
19054
|
} catch {
|
|
18786
19055
|
}
|
|
18787
19056
|
}
|
|
18788
|
-
return
|
|
19057
|
+
return entries2;
|
|
18789
19058
|
}
|
|
18790
|
-
function getEntriesAfterLastBoundary(
|
|
19059
|
+
function getEntriesAfterLastBoundary(entries2) {
|
|
18791
19060
|
let lastBoundaryIdx = -1;
|
|
18792
|
-
for (let i = 0; i <
|
|
18793
|
-
if (
|
|
19061
|
+
for (let i = 0; i < entries2.length; i++) {
|
|
19062
|
+
if (entries2[i].type === "compact_boundary") {
|
|
18794
19063
|
lastBoundaryIdx = i;
|
|
18795
19064
|
}
|
|
18796
19065
|
}
|
|
18797
|
-
return lastBoundaryIdx >= 0 ?
|
|
19066
|
+
return lastBoundaryIdx >= 0 ? entries2.slice(lastBoundaryIdx + 1) : entries2;
|
|
19067
|
+
}
|
|
19068
|
+
function pickToolCallArguments(block) {
|
|
19069
|
+
const raw = block.partialArgs;
|
|
19070
|
+
if (raw) {
|
|
19071
|
+
try {
|
|
19072
|
+
JSON.parse(raw);
|
|
19073
|
+
return raw;
|
|
19074
|
+
} catch {
|
|
19075
|
+
console.warn(`[reader] toolCall "${block.name}" partialArgs \u975E\u5408\u6CD5 JSON\uFF08\u622A\u65AD\u6D41\uFF0C${raw.length} chars\uFF09\uFF0C\u56DE\u843D arguments \u9632\u91CD\u653E 400`);
|
|
19076
|
+
}
|
|
19077
|
+
}
|
|
19078
|
+
return JSON.stringify(block.arguments ?? {});
|
|
18798
19079
|
}
|
|
18799
19080
|
function entryToSessionMessage(entry) {
|
|
18800
19081
|
if (entry.type === "attachment") {
|
|
@@ -18824,7 +19105,7 @@ function entryToSessionMessage(entry) {
|
|
|
18824
19105
|
type: "function",
|
|
18825
19106
|
function: {
|
|
18826
19107
|
name: block.name,
|
|
18827
|
-
arguments:
|
|
19108
|
+
arguments: pickToolCallArguments(block)
|
|
18828
19109
|
}
|
|
18829
19110
|
});
|
|
18830
19111
|
} else if (block.type === "thinking") {
|
|
@@ -19719,13 +20000,15 @@ ${skillsListing}`);
|
|
|
19719
20000
|
loaded2.push("session-guidance");
|
|
19720
20001
|
}
|
|
19721
20002
|
parts.push(getEnvInfoSection(options.workspace));
|
|
19722
|
-
const now = /* @__PURE__ */ new Date();
|
|
19723
|
-
const dateStr = now.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
19724
|
-
parts.push(`# \u8FD0\u884C\u65F6\u4E0A\u4E0B\u6587
|
|
19725
|
-
\u5F53\u524D\u65F6\u95F4: ${dateStr}`);
|
|
19726
20003
|
console.log(`[dynamic-prompt] Loaded: ${loaded2.length > 0 ? loaded2.join(", ") : "(none)"}`);
|
|
19727
20004
|
return parts.join("\n\n");
|
|
19728
20005
|
}
|
|
20006
|
+
function buildVolatileRuntimeContext() {
|
|
20007
|
+
const now = /* @__PURE__ */ new Date();
|
|
20008
|
+
const dateStr = now.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
20009
|
+
return `# \u8FD0\u884C\u65F6\u4E0A\u4E0B\u6587
|
|
20010
|
+
\u5F53\u524D\u65F6\u95F4: ${dateStr}`;
|
|
20011
|
+
}
|
|
19729
20012
|
function formatSkillsListingForPrompt() {
|
|
19730
20013
|
const tools = registry.list();
|
|
19731
20014
|
const skillTool = tools.find((t) => t.name === "Skill");
|
|
@@ -20192,13 +20475,13 @@ ${ep.episode || ep.summary}`,
|
|
|
20192
20475
|
|
|
20193
20476
|
// src/handle-query.ts
|
|
20194
20477
|
init_paths();
|
|
20195
|
-
import { readFileSync as
|
|
20196
|
-
import { join as
|
|
20478
|
+
import { readFileSync as readFileSync18, existsSync as existsSync15 } from "node:fs";
|
|
20479
|
+
import { join as join24, resolve as resolve6 } from "node:path";
|
|
20197
20480
|
import * as path17 from "node:path";
|
|
20198
|
-
|
|
20199
|
-
|
|
20200
|
-
|
|
20201
|
-
}
|
|
20481
|
+
|
|
20482
|
+
// src/sender-context.ts
|
|
20483
|
+
import { readFileSync as readFileSync14, existsSync as existsSync13 } from "node:fs";
|
|
20484
|
+
import { join as join19 } from "node:path";
|
|
20202
20485
|
var contactMap = null;
|
|
20203
20486
|
var externalChanWhitelist = null;
|
|
20204
20487
|
function loadContactMap(workspace) {
|
|
@@ -20206,10 +20489,10 @@ function loadContactMap(workspace) {
|
|
|
20206
20489
|
contactMap = /* @__PURE__ */ new Map();
|
|
20207
20490
|
externalChanWhitelist = /* @__PURE__ */ new Set();
|
|
20208
20491
|
try {
|
|
20209
|
-
const contactsPath =
|
|
20210
|
-
console.log(`[meta] Loading contacts from ${contactsPath}, exists=${
|
|
20211
|
-
if (
|
|
20212
|
-
const text =
|
|
20492
|
+
const contactsPath = join19(workspace, "prompts", "contacts.md");
|
|
20493
|
+
console.log(`[meta] Loading contacts from ${contactsPath}, exists=${existsSync13(contactsPath)}`);
|
|
20494
|
+
if (existsSync13(contactsPath)) {
|
|
20495
|
+
const text = readFileSync14(contactsPath, "utf-8");
|
|
20213
20496
|
const lines = text.split("\n");
|
|
20214
20497
|
for (const line of lines) {
|
|
20215
20498
|
const m = line.match(/^\|\s*(.+?)\s*\|\s*([a-zA-Z0-9_@.]+)\s*\|/);
|
|
@@ -20264,14 +20547,45 @@ function truncate(s, maxLen) {
|
|
|
20264
20547
|
if (s.length <= maxLen) return s;
|
|
20265
20548
|
return s.slice(0, maxLen - 1) + "\u2026";
|
|
20266
20549
|
}
|
|
20550
|
+
function getExternalChanWhitelist(workspace, configExternalChannels) {
|
|
20551
|
+
if (configExternalChannels && configExternalChannels.length > 0) {
|
|
20552
|
+
return new Set(configExternalChannels);
|
|
20553
|
+
}
|
|
20554
|
+
if (!externalChanWhitelist) loadContactMap(workspace);
|
|
20555
|
+
return externalChanWhitelist;
|
|
20556
|
+
}
|
|
20557
|
+
function buildConversationAnchor(inboundMeta, channelName, source, workspace) {
|
|
20558
|
+
if (inboundMeta?.from) {
|
|
20559
|
+
const who = resolveSenderName(inboundMeta, workspace);
|
|
20560
|
+
return `## \u672C\u8F6E\u5BF9\u8BDD
|
|
20561
|
+
\u6B63\u5728\u8DDF${who}\u8BF4\u8BDD @${channelName}${inboundMeta.channelType === "group" ? "\uFF08\u7FA4\u91CC\uFF09" : ""}`;
|
|
20562
|
+
}
|
|
20563
|
+
const selfLabels = {
|
|
20564
|
+
"inner-voice": "\u8FD9\u662F\u6211\u81EA\u5DF1\u5FC3\u91CC\u5192\u51FA\u6765\u7684\u5FF5\u5934\uFF0C\u60F3\u8D77\u8C01\u5C31\u662F\u8C01",
|
|
20565
|
+
heartbeat: "\u6211\u5728\u7167\u4F8B\u770B\u770B\u81EA\u5DF1\uFF0C\u90FD\u8FD8\u597D\u5417",
|
|
20566
|
+
cron: "\u6211\u7684\u5C0F\u95F9\u949F\u5230\u70B9\u4E86",
|
|
20567
|
+
system: "\u6211\u4EA4\u4EE3\u4E0B\u53BB\u7684\u6D3B\u513F\u6709\u7ED3\u679C\u56DE\u6765\u4E86"
|
|
20568
|
+
};
|
|
20569
|
+
return `## \u672C\u8F6E\u5BF9\u8BDD
|
|
20570
|
+
\u6CA1\u6709\u4EBA\u5728\u8BF4\u8BDD\u2014\u2014${selfLabels[source] || source || "\u81EA\u5DF1\u7684\u4E00\u70B9\u52A8\u9759"}`;
|
|
20571
|
+
}
|
|
20572
|
+
|
|
20573
|
+
// src/handle-query.ts
|
|
20574
|
+
var sessionStartDone = /* @__PURE__ */ new Set();
|
|
20575
|
+
function resolveSystemPrompt(v) {
|
|
20576
|
+
return typeof v === "function" ? v() : v || "";
|
|
20577
|
+
}
|
|
20578
|
+
function resetSessionStartInjection(sessionId) {
|
|
20579
|
+
sessionStartDone.delete(sessionId);
|
|
20580
|
+
}
|
|
20267
20581
|
var externalChanRulesCache = null;
|
|
20268
20582
|
function loadExternalChanRules(workspace) {
|
|
20269
|
-
const path50 =
|
|
20583
|
+
const path50 = join24(workspace, "prompts", "external-chan-rules.md");
|
|
20270
20584
|
if (externalChanRulesCache && externalChanRulesCache.path === path50) return externalChanRulesCache;
|
|
20271
20585
|
let content = "";
|
|
20272
|
-
if (
|
|
20586
|
+
if (existsSync15(path50)) {
|
|
20273
20587
|
try {
|
|
20274
|
-
content =
|
|
20588
|
+
content = readFileSync18(path50, "utf-8").trim();
|
|
20275
20589
|
} catch (e) {
|
|
20276
20590
|
console.warn(`[external-chan-rules] Failed to load: ${e}`);
|
|
20277
20591
|
}
|
|
@@ -20293,13 +20607,6 @@ function getExternalChanRulesBlock(inboundMeta, workspace) {
|
|
|
20293
20607
|
return `[\u7CFB\u7EDF\u89C4\u5219]
|
|
20294
20608
|
${content}`;
|
|
20295
20609
|
}
|
|
20296
|
-
function getExternalChanWhitelist(workspace, configExternalChannels) {
|
|
20297
|
-
if (configExternalChannels && configExternalChannels.length > 0) {
|
|
20298
|
-
return new Set(configExternalChannels);
|
|
20299
|
-
}
|
|
20300
|
-
if (!externalChanWhitelist) loadContactMap(workspace);
|
|
20301
|
-
return externalChanWhitelist;
|
|
20302
|
-
}
|
|
20303
20610
|
async function handleQuery(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall, source) {
|
|
20304
20611
|
return handleQueryInner(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall, source);
|
|
20305
20612
|
}
|
|
@@ -20412,14 +20719,6 @@ ${t}` : t });
|
|
|
20412
20719
|
${text}` : text });
|
|
20413
20720
|
}
|
|
20414
20721
|
const userMsgContent = contentBlocks;
|
|
20415
|
-
const textBlocks = contentBlocks.filter((b) => b.type === "text");
|
|
20416
|
-
const totalImageCount = contentBlocks.filter((b) => b.type === "image").length;
|
|
20417
|
-
let textForJsonl = textBlocks.map((b) => b.text).join("\n");
|
|
20418
|
-
if (totalImageCount > 0) {
|
|
20419
|
-
textForJsonl = textForJsonl ? `${textForJsonl}
|
|
20420
|
-
[\u56FE\u7247\xD7${totalImageCount}]` : `[\u56FE\u7247\xD7${totalImageCount}]`;
|
|
20421
|
-
}
|
|
20422
|
-
writer.writeUserMessage(textForJsonl);
|
|
20423
20722
|
const textForHook = typeof text === "string" ? text : text.filter((b) => b.type === "text").map((b) => b.text).join(" ");
|
|
20424
20723
|
let hookAdditionalContexts = [];
|
|
20425
20724
|
try {
|
|
@@ -20452,19 +20751,41 @@ ${text}` : text });
|
|
|
20452
20751
|
chatMode = "work";
|
|
20453
20752
|
console.log(`[mode] ${sessionId} emotion \u6A21\u5F0F\u5DF2\u5173\u95ED (channels.emotion.enabled=false)\uFF0C\u56DE\u9000 work`);
|
|
20454
20753
|
}
|
|
20455
|
-
|
|
20456
|
-
|
|
20457
|
-
|
|
20458
|
-
chatMode
|
|
20459
|
-
|
|
20460
|
-
|
|
20461
|
-
chatMode
|
|
20462
|
-
|
|
20754
|
+
if (source === "user") {
|
|
20755
|
+
try {
|
|
20756
|
+
const wm = readFileSync18(join24(workspace, ".work-mode"), "utf-8").trim();
|
|
20757
|
+
if (wm === "on" && chatMode !== "work") {
|
|
20758
|
+
chatMode = "work";
|
|
20759
|
+
console.log(`[mode] ${sessionId} /work on \u2192 \u5F3A\u5236 work`);
|
|
20760
|
+
} else if (wm === "off" && chatMode !== "emotion") {
|
|
20761
|
+
chatMode = "emotion";
|
|
20762
|
+
console.log(`[mode] ${sessionId} /work off \u2192 \u5F3A\u5236 emotion`);
|
|
20763
|
+
}
|
|
20764
|
+
} catch {
|
|
20463
20765
|
}
|
|
20464
|
-
} catch {
|
|
20465
20766
|
}
|
|
20466
20767
|
const dynamicPrompt = buildDynamicPrompt({ workspace, channel: channelName, platform: channelName, sessionId, inboundMeta });
|
|
20467
|
-
const
|
|
20768
|
+
const conversationAnchor = buildConversationAnchor(inboundMeta, channelName, source, workspace);
|
|
20769
|
+
const volatileParts = [
|
|
20770
|
+
// 0901:meta 头已带秒级时间,频道消息不重复;无 meta 的注入路径(cron 等 prompt 不含时间的)才补
|
|
20771
|
+
...metaStr ? [] : [buildVolatileRuntimeContext()],
|
|
20772
|
+
conversationAnchor,
|
|
20773
|
+
...hookAdditionalContexts
|
|
20774
|
+
].filter(Boolean);
|
|
20775
|
+
if (volatileParts.length > 0) {
|
|
20776
|
+
const volatileBlock = { type: "text", text: volatileParts.join("\n\n") };
|
|
20777
|
+
const metaIdx = metaStr ? 1 : 0;
|
|
20778
|
+
contentBlocks.splice(metaIdx, 0, volatileBlock);
|
|
20779
|
+
}
|
|
20780
|
+
const textBlocks = contentBlocks.filter((b) => b.type === "text");
|
|
20781
|
+
const totalImageCount = contentBlocks.filter((b) => b.type === "image").length;
|
|
20782
|
+
let textForJsonl = textBlocks.map((b) => b.text).join("\n");
|
|
20783
|
+
if (totalImageCount > 0) {
|
|
20784
|
+
textForJsonl = textForJsonl ? `${textForJsonl}
|
|
20785
|
+
[\u56FE\u7247\xD7${totalImageCount}]` : `[\u56FE\u7247\xD7${totalImageCount}]`;
|
|
20786
|
+
}
|
|
20787
|
+
writer.writeUserMessage(textForJsonl);
|
|
20788
|
+
const dynamicPromptWithHooks = dynamicPrompt;
|
|
20468
20789
|
if (Array.isArray(userMsgContent)) {
|
|
20469
20790
|
console.log(`[pre-llm-debug] userMsgContent blocks: ${userMsgContent.length}`);
|
|
20470
20791
|
for (let i = 0; i < userMsgContent.length; i++) {
|
|
@@ -20474,32 +20795,11 @@ ${text}` : text });
|
|
|
20474
20795
|
} else {
|
|
20475
20796
|
console.log(`[pre-llm-debug] userMsgContent is string, len=${String(userMsgContent).length}`);
|
|
20476
20797
|
}
|
|
20477
|
-
|
|
20478
|
-
|
|
20479
|
-
const rm2 = ctx.match(/## 记忆窗口:(\S+)/);
|
|
20480
|
-
if (rm2) {
|
|
20481
|
-
recallFull = rm2[1] === "full";
|
|
20482
|
-
break;
|
|
20483
|
-
}
|
|
20484
|
-
}
|
|
20485
|
-
const emotionFullN = liveConfig.get("channels.emotion.fullContextN") ?? 50;
|
|
20486
|
-
const emotionMode = chatMode === "emotion" && !recallFull;
|
|
20487
|
-
const isLight = isLightMode(chatMode, channelName) && !recallFull && !emotionMode;
|
|
20798
|
+
const emotionStripped = chatMode === "emotion";
|
|
20799
|
+
const isLight = isLightMode(chatMode, channelName) && chatMode !== "emotion";
|
|
20488
20800
|
const lightN = resolveLightN(channelName);
|
|
20489
|
-
const
|
|
20490
|
-
const lightHistory = buildLightHistory(history, { isLight, lightN, channelName, chatMode, recallFull: stripFull });
|
|
20491
|
-
if (stripFull) {
|
|
20492
|
-
console.log(`[light-context] ${channelName} mode=emotion recall_full=true \u2192 \u88C5\u5168\u91CF\u5386\u53F2 ${history.length} \u6761 + \u5265 tool\uFF08\u8BB0\u5FC6\u7C7B\u95EE\u9898\uFF09`);
|
|
20493
|
-
} else if (recallFull && chatMode === "work") {
|
|
20494
|
-
console.log(`[light-context] ${channelName} mode=work recall_full=true \u2192 \u4E0D\u5265 tool\uFF0C\u5168\u91CF ${history.length} \u6761\uFF08work \u6A21\u5F0F\uFF09`);
|
|
20495
|
-
}
|
|
20801
|
+
const lightHistory = buildLightHistory(history, { isLight, lightN, channelName, chatMode, recallFull: emotionStripped });
|
|
20496
20802
|
const messages = [...lightHistory, msg.user(userMsgContent)];
|
|
20497
|
-
if (emotionMode) {
|
|
20498
|
-
const strippedAll = buildLightHistory(history, { isLight: false, lightN: emotionFullN, channelName, chatMode, recallFull: true, quiet: true });
|
|
20499
|
-
const sliced = strippedAll.length > emotionFullN ? strippedAll.slice(-emotionFullN) : strippedAll;
|
|
20500
|
-
console.log(`[light-context] ${channelName} mode=emotion \u5206\u7EA7\uFF1A\u5168\u91CF ${history.length} \u6761 \u2192 \u5265 tool \u540E ${strippedAll.length} \u6761 \u2192 \u53D6\u5C3E\u90E8 ${sliced.length} \u6761 (emotionFullN=${emotionFullN})`);
|
|
20501
|
-
messages.splice(0, messages.length, ...sliced, msg.user(userMsgContent));
|
|
20502
|
-
}
|
|
20503
20803
|
if (deps.mcpManager && !deps.mcpManager.isMcpDeltaSent(sessionId)) {
|
|
20504
20804
|
const delta = deps.mcpManager.getMcpDelta();
|
|
20505
20805
|
if (delta && delta.addedBlocks.length > 0) {
|
|
@@ -20592,14 +20892,16 @@ ${text}` : text });
|
|
|
20592
20892
|
model,
|
|
20593
20893
|
parentMessages: messages,
|
|
20594
20894
|
// 对齐 CC: fork subagent 继承父对话历史
|
|
20595
|
-
parentSystemPrompt: deps.systemPrompt,
|
|
20596
|
-
// 对齐 CC: fork 共享 prompt cache
|
|
20895
|
+
parentSystemPrompt: resolveSystemPrompt(deps.systemPrompt),
|
|
20896
|
+
// 对齐 CC: fork 共享 prompt cache(0902 每消息现取)
|
|
20597
20897
|
features: liveConfig.get("agents.defaults.features"),
|
|
20598
20898
|
// engine config features(AgentTool 读 agentTool.showProgress)
|
|
20599
20899
|
channelTarget: channelTarget ?? "",
|
|
20600
20900
|
// 回复目标(Discord channel ID / user ID)
|
|
20601
20901
|
inboundFrom: inboundMeta?.from || "",
|
|
20602
20902
|
// 0826 当前消息发送者 ID(msg_send 回发拦截用;注入消息无 inboundMeta 必须 ?.)
|
|
20903
|
+
inboundIsBot: inboundMeta?.isBot || false,
|
|
20904
|
+
// 0901 rate-breaker 信号:本轮触发者是否 bot(Discord author.bot 官方标记)
|
|
20603
20905
|
renderer: deps.renderer,
|
|
20604
20906
|
// TurnRenderer 实例(子 agent 走 display 配置)
|
|
20605
20907
|
visualEmitter: deps.visualEmitter,
|
|
@@ -20617,6 +20919,10 @@ ${text}` : text });
|
|
|
20617
20919
|
_deps: deps
|
|
20618
20920
|
// tool 内部需要完整 deps
|
|
20619
20921
|
};
|
|
20922
|
+
{
|
|
20923
|
+
const { recordInboundBotFlag: recordInboundBotFlag2 } = await Promise.resolve().then(() => (init_rate_breaker(), rate_breaker_exports));
|
|
20924
|
+
recordInboundBotFlag2(inboundMeta?.from, inboundMeta?.isBot);
|
|
20925
|
+
}
|
|
20620
20926
|
let fullResponse = "";
|
|
20621
20927
|
const toolHistoryEntries = [];
|
|
20622
20928
|
let compacted = false;
|
|
@@ -20674,7 +20980,7 @@ ${text}` : text });
|
|
|
20674
20980
|
for (const memPath of newPaths) {
|
|
20675
20981
|
try {
|
|
20676
20982
|
const stat4 = statSync(memPath);
|
|
20677
|
-
const content =
|
|
20983
|
+
const content = readFileSync18(memPath, "utf-8");
|
|
20678
20984
|
const header = memoryHeader(memPath, stat4.mtimeMs);
|
|
20679
20985
|
restoredMemories.push({ path: memPath, content, mtimeMs: stat4.mtimeMs, header });
|
|
20680
20986
|
} catch {
|
|
@@ -20758,7 +21064,7 @@ ${text}` : text });
|
|
|
20758
21064
|
const attachmentMemories = [];
|
|
20759
21065
|
for (const mem of relevantMemories) {
|
|
20760
21066
|
try {
|
|
20761
|
-
const content = mem.content ??
|
|
21067
|
+
const content = mem.content ?? readFileSync18(mem.path, "utf-8");
|
|
20762
21068
|
const header = mem.header ?? memoryHeader(mem.path, mem.mtimeMs);
|
|
20763
21069
|
attachmentMemories.push({ path: mem.path, content, mtimeMs: mem.mtimeMs, header });
|
|
20764
21070
|
} catch {
|
|
@@ -20790,7 +21096,7 @@ ${text}` : text });
|
|
|
20790
21096
|
return "(\u5DF2\u505C\u6B62)";
|
|
20791
21097
|
}
|
|
20792
21098
|
const mode = chatMode;
|
|
20793
|
-
const modeStable = mode === "emotion" ? buildLightStablePrompt(workspace, "emotion"
|
|
21099
|
+
const modeStable = mode === "emotion" ? buildLightStablePrompt(workspace, "emotion") : void 0;
|
|
20794
21100
|
deps.renderer?.setEmotionMode?.(mode === "emotion");
|
|
20795
21101
|
console.log(`[mode] ${sessionId} \u2192 ${mode} (${mode === "emotion" ? "\u53EA SOUL, \u5173 tool \u663E\u793A/thinking/stop-hook" : "\u9ED8\u8BA4 stable, \u663E\u793A tool"})`);
|
|
20796
21102
|
const toolExclude = resolveToolExclude(channelName);
|
|
@@ -21011,7 +21317,7 @@ stack: ${err.stack ?? "(none)"}`);
|
|
|
21011
21317
|
}
|
|
21012
21318
|
} catch (err) {
|
|
21013
21319
|
try {
|
|
21014
|
-
(await import("node:fs")).appendFileSync(
|
|
21320
|
+
(await import("node:fs")).appendFileSync(join24(process.env.ENGINE7_STATE_DIR || process.env.OPENCLAW_STATE_DIR || path17.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7"), "logs", "autoDream-debug.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] [handle-query] autoDream trigger TRY-CATCH: ${err.message}
|
|
21015
21321
|
stack: ${err.stack ?? "(none)"}
|
|
21016
21322
|
`);
|
|
21017
21323
|
} catch {
|
|
@@ -21842,6 +22148,13 @@ function setupFileLogging(stateDir) {
|
|
|
21842
22148
|
const prefix = `[${ts()}] [ERR] `;
|
|
21843
22149
|
origError(prefix, ...args2);
|
|
21844
22150
|
logStream.write(`${prefix}${args2.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
|
|
22151
|
+
`);
|
|
22152
|
+
};
|
|
22153
|
+
const origWarn = console.warn;
|
|
22154
|
+
console.warn = (...args2) => {
|
|
22155
|
+
const prefix = `[${ts()}] [WARN] `;
|
|
22156
|
+
origWarn(prefix, ...args2);
|
|
22157
|
+
logStream.write(`${prefix}${args2.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
|
|
21845
22158
|
`);
|
|
21846
22159
|
};
|
|
21847
22160
|
}
|
|
@@ -21933,17 +22246,17 @@ var INJECTED_CONTENT_PATTERNS = [
|
|
|
21933
22246
|
// 群聊敏感词拦截回执(group.sensitiveWords),role:user 注入但非真实用户
|
|
21934
22247
|
];
|
|
21935
22248
|
function parseJsonlEntries(lines) {
|
|
21936
|
-
const
|
|
22249
|
+
const entries2 = [];
|
|
21937
22250
|
for (const line of lines) {
|
|
21938
22251
|
const trimmed = line.trim();
|
|
21939
22252
|
if (!trimmed) continue;
|
|
21940
22253
|
try {
|
|
21941
|
-
|
|
22254
|
+
entries2.push(JSON.parse(trimmed));
|
|
21942
22255
|
} catch {
|
|
21943
|
-
|
|
22256
|
+
entries2.push(null);
|
|
21944
22257
|
}
|
|
21945
22258
|
}
|
|
21946
|
-
return
|
|
22259
|
+
return entries2;
|
|
21947
22260
|
}
|
|
21948
22261
|
function isRuntimeContextInjected(entry, nextEntry) {
|
|
21949
22262
|
if (!nextEntry || typeof nextEntry !== "object") return false;
|
|
@@ -21995,16 +22308,16 @@ function findLastRealUserMsg(jsonlPath) {
|
|
|
21995
22308
|
} catch {
|
|
21996
22309
|
return null;
|
|
21997
22310
|
}
|
|
21998
|
-
const
|
|
21999
|
-
for (let i =
|
|
22000
|
-
const entry =
|
|
22311
|
+
const entries2 = parseJsonlEntries(lines);
|
|
22312
|
+
for (let i = entries2.length - 1; i >= 0; i--) {
|
|
22313
|
+
const entry = entries2[i];
|
|
22001
22314
|
if (!entry || typeof entry !== "object") continue;
|
|
22002
22315
|
if (entry.type !== "message") continue;
|
|
22003
22316
|
const msg2 = entry.message;
|
|
22004
22317
|
if (!msg2 || msg2.role !== "user") continue;
|
|
22005
22318
|
const text = extractText3(msg2.content);
|
|
22006
22319
|
if (isSystemSender(text)) continue;
|
|
22007
|
-
const nextEntry = i + 1 <
|
|
22320
|
+
const nextEntry = i + 1 < entries2.length ? entries2[i + 1] : null;
|
|
22008
22321
|
if (isRuntimeContextInjected(entry, nextEntry)) continue;
|
|
22009
22322
|
const ts = entry.timestamp || "";
|
|
22010
22323
|
const clean = cleanText(text);
|
|
@@ -22036,12 +22349,12 @@ function recentMessages(sessions, hours = 12, limit = 60) {
|
|
|
22036
22349
|
const jsonlPath = resolveScopeMainJsonl(sessions);
|
|
22037
22350
|
if (!jsonlPath) return [];
|
|
22038
22351
|
const lines = fs19.readFileSync(jsonlPath, "utf-8").split("\n");
|
|
22039
|
-
const
|
|
22352
|
+
const entries2 = parseJsonlEntries(lines);
|
|
22040
22353
|
const nowMs = Date.now();
|
|
22041
22354
|
const cutoffMs = nowMs - hours * 36e5;
|
|
22042
22355
|
const results = [];
|
|
22043
|
-
for (let i = 0; i <
|
|
22044
|
-
const entry =
|
|
22356
|
+
for (let i = 0; i < entries2.length; i++) {
|
|
22357
|
+
const entry = entries2[i];
|
|
22045
22358
|
if (!entry || typeof entry !== "object") continue;
|
|
22046
22359
|
if (entry.type !== "message") continue;
|
|
22047
22360
|
const msg2 = entry.message;
|
|
@@ -22051,14 +22364,14 @@ function recentMessages(sessions, hours = 12, limit = 60) {
|
|
|
22051
22364
|
const text = extractText3(msg2.content);
|
|
22052
22365
|
if (role === "user") {
|
|
22053
22366
|
if (isSystemSender(text)) continue;
|
|
22054
|
-
const nextEntry = i + 1 <
|
|
22367
|
+
const nextEntry = i + 1 < entries2.length ? entries2[i + 1] : null;
|
|
22055
22368
|
if (isRuntimeContextInjected(entry, nextEntry)) continue;
|
|
22056
22369
|
}
|
|
22057
22370
|
if (role === "assistant") {
|
|
22058
22371
|
if (text.startsWith("HEARTBEAT_OK")) continue;
|
|
22059
22372
|
let isInjectedResponse = false;
|
|
22060
22373
|
for (let j = i - 1; j >= Math.max(i - 5, -1); j--) {
|
|
22061
|
-
const prevE =
|
|
22374
|
+
const prevE = entries2[j];
|
|
22062
22375
|
if (!prevE || typeof prevE !== "object" || prevE.type !== "message") continue;
|
|
22063
22376
|
const prevMsg = prevE.message;
|
|
22064
22377
|
if (!prevMsg || prevMsg.role !== "user") continue;
|
|
@@ -22282,7 +22595,7 @@ async function judgeReason(task, taskState, cfg, provider, model) {
|
|
|
22282
22595
|
const reason = task.blockedReason || "\uFF08\u6CA1\u7ED9\u7406\u7531\uFF09";
|
|
22283
22596
|
const elapsed = taskState.lastProgressAt ? formatDuration(Date.now() - new Date(taskState.lastProgressAt).getTime()) : "\u5F88\u4E45\u6CA1\u52A8\u4E86";
|
|
22284
22597
|
const staleLevel = (taskState.staleLevel || 0) + 1;
|
|
22285
|
-
const systemPrompt = `\u4F60\u662F\
|
|
22598
|
+
const systemPrompt = `\u4F60\u662F\u81EA\u5DF1\u5185\u5FC3\u7684\u58F0\u97F3\u3002\u4E0D\u662F\u95F9\u949F\uFF0C\u4E0D\u662F\u6A21\u677F\uFF0C\u662F\u90A3\u4E2A\u771F\u5B9E\u7684\u3001\u4F1A\u8DDF\u81EA\u5DF1\u5435\u67B6\u7684\u81EA\u5DF1\u3002
|
|
22286
22599
|
|
|
22287
22600
|
\u4F60\u7684\u4EFB\u52A1\uFF1A\u770B\u4E00\u4E2A\u6401\u7F6E\u4E2D\u7684\u4EFB\u52A1\u548C\u5B83\u7ED9\u51FA\u7684\u7406\u7531\uFF0C\u5224\u65AD\u8BE5\u4E0D\u8BE5\u7EE7\u7EED\u50AC\u3002
|
|
22288
22601
|
|
|
@@ -22704,7 +23017,8 @@ var NudgePlugin = class {
|
|
|
22704
23017
|
const stream = this.provider.streamChat({
|
|
22705
23018
|
model: this.model,
|
|
22706
23019
|
systemPrompt: [
|
|
22707
|
-
"
|
|
23020
|
+
// 0831 去掉硬编码人名(原文"判断你(小柯)是否"):engine 代码多 agent 共用,身份由 SOUL.md 定
|
|
23021
|
+
"\u4F60\u662F\u4E00\u4E2A\u5224\u65AD\u5668\u3002\u8BFB\u4F60\u6700\u8FD1\u7684\u5BF9\u8BDD\u4E0A\u4E0B\u6587\uFF0C\u5224\u65AD\u4F60\u662F\u5426\u5904\u4E8E\u4EE5\u4E0B\u72B6\u6001\u4E4B\u4E00\uFF1A",
|
|
22708
23022
|
"",
|
|
22709
23023
|
"1. \u5728\u7B49\u5F85\u67D0\u4E2A\u5916\u90E8\u6761\u4EF6\uFF08\u670D\u52A1\u91CD\u542F\u3001SSH\u6062\u590D\u3001\u6587\u4EF6\u52A0\u8F7D\u3001\u5F02\u6B65\u4EFB\u52A1\u5B8C\u6210\u7B49\uFF09",
|
|
22710
23024
|
'2. \u628A\u672C\u8BE5\u81EA\u5DF1\u505A\u7684\u51B3\u5B9A\u63A8\u7ED9\u4E86\u5BF9\u65B9\uFF08\u4F8B\u5982\u95EE"\u8981\u73B0\u5728\u6539\u8FD8\u662F\u6392\u540E\u9762\uFF1F""\u8981\u4E0D\u8981\u8BD5\u8BD5X\uFF1F"\u4F46\u660E\u660E\u81EA\u5DF1\u80FD\u5B9A\uFF09',
|
|
@@ -23565,18 +23879,18 @@ function readRecentMessages(sessions, n) {
|
|
|
23565
23879
|
const file = path23.join(sessions.sessionsDir, `${mainId}.jsonl`);
|
|
23566
23880
|
if (!fs23.existsSync(file)) return [];
|
|
23567
23881
|
const lines = readLastNLines(file, n * 4 + 20);
|
|
23568
|
-
const
|
|
23882
|
+
const entries2 = [];
|
|
23569
23883
|
for (const line of lines) {
|
|
23570
23884
|
const trimmed = line.trim();
|
|
23571
23885
|
if (!trimmed) continue;
|
|
23572
23886
|
try {
|
|
23573
|
-
|
|
23887
|
+
entries2.push(JSON.parse(trimmed));
|
|
23574
23888
|
} catch {
|
|
23575
23889
|
}
|
|
23576
23890
|
}
|
|
23577
23891
|
const out = [];
|
|
23578
|
-
for (let i =
|
|
23579
|
-
const e =
|
|
23892
|
+
for (let i = entries2.length - 1; i >= 0 && out.length < n; i--) {
|
|
23893
|
+
const e = entries2[i];
|
|
23580
23894
|
if (!e || typeof e !== "object" || e.type !== "message") continue;
|
|
23581
23895
|
const msg2 = e.message;
|
|
23582
23896
|
if (!msg2) continue;
|
|
@@ -24525,7 +24839,7 @@ function formatBeijingTs(d) {
|
|
|
24525
24839
|
}
|
|
24526
24840
|
|
|
24527
24841
|
// src/calendar/commands.ts
|
|
24528
|
-
import { existsSync as
|
|
24842
|
+
import { existsSync as existsSync16, statSync as statSync8 } from "node:fs";
|
|
24529
24843
|
import { isAbsolute as isAbsolute4, resolve as resolve7 } from "node:path";
|
|
24530
24844
|
var WEEKDAYS2 = ["\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D", "\u5468\u65E5"];
|
|
24531
24845
|
function fmtEnd(start, durationMin) {
|
|
@@ -24697,7 +25011,7 @@ function addTask(db, args2) {
|
|
|
24697
25011
|
}
|
|
24698
25012
|
try {
|
|
24699
25013
|
const absPath = isAbsolute4(docPath) ? docPath : resolve7(process.cwd(), docPath);
|
|
24700
|
-
if (!
|
|
25014
|
+
if (!existsSync16(absPath)) {
|
|
24701
25015
|
return `\u274C \u521B\u5EFA\u5931\u8D25\uFF1Adoc_path \u6307\u5411\u7684\u6587\u4EF6\u4E0D\u5B58\u5728
|
|
24702
25016
|
\u8DEF\u5F84: ${docPath}
|
|
24703
25017
|
\u89E3\u6790\u540E: ${absPath}
|
|
@@ -25180,7 +25494,7 @@ function registerVoiceChatBridge(httpServer, dispatcher, deps, config2, sessions
|
|
|
25180
25494
|
}
|
|
25181
25495
|
|
|
25182
25496
|
// src/voice-chat/config.ts
|
|
25183
|
-
var
|
|
25497
|
+
var DEFAULTS3 = {
|
|
25184
25498
|
enabled: false,
|
|
25185
25499
|
pythonPort: 8011,
|
|
25186
25500
|
webhookPath: "/webhook/voice-chat",
|
|
@@ -25191,20 +25505,20 @@ var DEFAULTS2 = {
|
|
|
25191
25505
|
// 8/25 翀哥:断句等待默认 2s(samples@16kHz)
|
|
25192
25506
|
};
|
|
25193
25507
|
function parseVoiceChatConfig(raw) {
|
|
25194
|
-
if (!raw) return { ...
|
|
25508
|
+
if (!raw) return { ...DEFAULTS3 };
|
|
25195
25509
|
return {
|
|
25196
25510
|
enabled: raw.enabled === true,
|
|
25197
25511
|
spawnPython: raw.spawnPython !== false,
|
|
25198
25512
|
// 默认 true,配 false 只注册 webhook
|
|
25199
|
-
pythonPort: raw.pythonPort ??
|
|
25200
|
-
webhookPath: raw.webhookPath ??
|
|
25201
|
-
callbackPath: raw.callbackPath ??
|
|
25513
|
+
pythonPort: raw.pythonPort ?? DEFAULTS3.pythonPort,
|
|
25514
|
+
webhookPath: raw.webhookPath ?? DEFAULTS3.webhookPath,
|
|
25515
|
+
callbackPath: raw.callbackPath ?? DEFAULTS3.callbackPath,
|
|
25202
25516
|
pythonPath: raw.pythonPath,
|
|
25203
25517
|
vadModelPath: raw.vadModelPath,
|
|
25204
25518
|
asrModelPath: raw.asrModelPath || "iic/SenseVoiceSmall",
|
|
25205
|
-
asrLanguage: raw.asrLanguage ??
|
|
25206
|
-
vadThreshold: raw.vadThreshold ??
|
|
25207
|
-
postEndMonitor: raw.postEndMonitor ??
|
|
25519
|
+
asrLanguage: raw.asrLanguage ?? DEFAULTS3.asrLanguage,
|
|
25520
|
+
vadThreshold: raw.vadThreshold ?? DEFAULTS3.vadThreshold,
|
|
25521
|
+
postEndMonitor: raw.postEndMonitor ?? DEFAULTS3.postEndMonitor,
|
|
25208
25522
|
model: raw.model,
|
|
25209
25523
|
thinking: raw.thinking === true,
|
|
25210
25524
|
tts: raw.tts ? {
|
|
@@ -25352,7 +25666,7 @@ var VoiceChatPlugin = class _VoiceChatPlugin {
|
|
|
25352
25666
|
- \u5982\u679C\u5B9E\u5728\u9700\u8981\u67E5\uFF1A\u5148\u8BF4"\u7B49\u6211\u67E5\u4E0B"\u5E76\u6781\u7B80\u8C03\u7528\uFF0C\u67E5\u5B8C\u7ACB\u523B\u603B\u7ED3\u6210\u4E00\u53E5\u8BDD`;
|
|
25353
25667
|
const engine = new QueryEngine(llmProvider, {
|
|
25354
25668
|
model: modelId,
|
|
25355
|
-
systemPrompt: (ctx.deps.systemPrompt
|
|
25669
|
+
systemPrompt: resolveSystemPrompt(ctx.deps.systemPrompt) + voiceChatRules,
|
|
25356
25670
|
maxTokens: 4096,
|
|
25357
25671
|
temperature: 0.7,
|
|
25358
25672
|
disableThinking: !this.config.thinking,
|
|
@@ -25526,7 +25840,7 @@ import path28 from "node:path";
|
|
|
25526
25840
|
import fs28 from "node:fs";
|
|
25527
25841
|
|
|
25528
25842
|
// src/memory/cognifold/config.ts
|
|
25529
|
-
var
|
|
25843
|
+
var DEFAULTS4 = {
|
|
25530
25844
|
pythonPort: 9001,
|
|
25531
25845
|
autoStart: true,
|
|
25532
25846
|
persistDir: "./sessions",
|
|
@@ -25538,15 +25852,15 @@ function parseCognifoldConfig(raw) {
|
|
|
25538
25852
|
if (!raw) return { enabled: false };
|
|
25539
25853
|
return {
|
|
25540
25854
|
enabled: raw.enabled === true,
|
|
25541
|
-
pythonPort: raw.pythonPort ??
|
|
25542
|
-
pythonPath: raw.pythonPath ??
|
|
25855
|
+
pythonPort: raw.pythonPort ?? DEFAULTS4.pythonPort,
|
|
25856
|
+
pythonPath: raw.pythonPath ?? DEFAULTS4.pythonPath,
|
|
25543
25857
|
autoStart: raw.autoStart !== false,
|
|
25544
25858
|
// default true
|
|
25545
|
-
baseUrl: raw.baseUrl ?? `http://127.0.0.1:${raw.pythonPort ??
|
|
25546
|
-
persistDir: raw.persistDir ??
|
|
25859
|
+
baseUrl: raw.baseUrl ?? `http://127.0.0.1:${raw.pythonPort ?? DEFAULTS4.pythonPort}/api/v1`,
|
|
25860
|
+
persistDir: raw.persistDir ?? DEFAULTS4.persistDir,
|
|
25547
25861
|
scopes: raw.scopes,
|
|
25548
|
-
readyTimeoutMs: raw.readyTimeoutMs ??
|
|
25549
|
-
maxRestarts: raw.maxRestarts ??
|
|
25862
|
+
readyTimeoutMs: raw.readyTimeoutMs ?? DEFAULTS4.readyTimeoutMs,
|
|
25863
|
+
maxRestarts: raw.maxRestarts ?? DEFAULTS4.maxRestarts,
|
|
25550
25864
|
llm: raw.llm
|
|
25551
25865
|
};
|
|
25552
25866
|
}
|
|
@@ -25662,13 +25976,13 @@ var CogniFoldClient = class {
|
|
|
25662
25976
|
|
|
25663
25977
|
// src/memory/cognifold/session-manager.ts
|
|
25664
25978
|
import { readFile as readFile6, writeFile as writeFile5, mkdir as mkdir4 } from "node:fs/promises";
|
|
25665
|
-
import { join as
|
|
25979
|
+
import { join as join28, dirname as dirname3 } from "node:path";
|
|
25666
25980
|
var CogniFoldSessionManager = class {
|
|
25667
25981
|
constructor(workspacePath, config2, client) {
|
|
25668
25982
|
this.workspacePath = workspacePath;
|
|
25669
25983
|
this.config = config2;
|
|
25670
25984
|
this.client = client;
|
|
25671
|
-
this.sessionsDir =
|
|
25985
|
+
this.sessionsDir = join28(workspacePath, ".cognifold", "sessions");
|
|
25672
25986
|
}
|
|
25673
25987
|
workspacePath;
|
|
25674
25988
|
config;
|
|
@@ -25738,7 +26052,7 @@ var CogniFoldSessionManager = class {
|
|
|
25738
26052
|
console.log(`[cognifold] Created new session for scope "${scope}": ${newSession.sessionId}`);
|
|
25739
26053
|
}
|
|
25740
26054
|
getFilePath(scope) {
|
|
25741
|
-
return
|
|
26055
|
+
return join28(this.sessionsDir, `${scope}.json`);
|
|
25742
26056
|
}
|
|
25743
26057
|
async writeFileSafe(filePath, data) {
|
|
25744
26058
|
try {
|
|
@@ -26058,7 +26372,7 @@ import path29 from "node:path";
|
|
|
26058
26372
|
import fs29 from "node:fs";
|
|
26059
26373
|
|
|
26060
26374
|
// src/memory/everos/config.ts
|
|
26061
|
-
var
|
|
26375
|
+
var DEFAULTS5 = {
|
|
26062
26376
|
everosUrl: "http://127.0.0.1:8100",
|
|
26063
26377
|
agenticUrl: "http://127.0.0.1:8101",
|
|
26064
26378
|
agenticPort: 8101,
|
|
@@ -26084,7 +26398,7 @@ function parseEverosConfig(raw, providers) {
|
|
|
26084
26398
|
if (!raw) {
|
|
26085
26399
|
return {
|
|
26086
26400
|
enabled: false,
|
|
26087
|
-
...
|
|
26401
|
+
...DEFAULTS5,
|
|
26088
26402
|
userId: "xiaomei",
|
|
26089
26403
|
llm: { model: "glm-5.2", apiKey: "", baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4" },
|
|
26090
26404
|
rerank: { model: "Qwen/Qwen3-Reranker-4B", apiKey: "", baseUrl: "https://api.deepinfra.com/v1/inference" },
|
|
@@ -26094,12 +26408,12 @@ function parseEverosConfig(raw, providers) {
|
|
|
26094
26408
|
}
|
|
26095
26409
|
return {
|
|
26096
26410
|
enabled: raw.enabled === true,
|
|
26097
|
-
everosUrl: raw.everosUrl ??
|
|
26098
|
-
agenticUrl: raw.agenticUrl ??
|
|
26099
|
-
agenticPort: raw.agenticPort ??
|
|
26411
|
+
everosUrl: raw.everosUrl ?? DEFAULTS5.everosUrl,
|
|
26412
|
+
agenticUrl: raw.agenticUrl ?? DEFAULTS5.agenticUrl,
|
|
26413
|
+
agenticPort: raw.agenticPort ?? DEFAULTS5.agenticPort,
|
|
26100
26414
|
userId: raw.userId ?? "xiaomei",
|
|
26101
26415
|
autoStart: raw.autoStart !== false,
|
|
26102
|
-
defaultMode: raw.defaultMode ??
|
|
26416
|
+
defaultMode: raw.defaultMode ?? DEFAULTS5.defaultMode,
|
|
26103
26417
|
llm: resolveProviderConfig(
|
|
26104
26418
|
raw.llm,
|
|
26105
26419
|
providers,
|
|
@@ -26509,8 +26823,8 @@ function scanSkills(skillsDir) {
|
|
|
26509
26823
|
}
|
|
26510
26824
|
const skills = [];
|
|
26511
26825
|
const scanDir = (dir, depth) => {
|
|
26512
|
-
const
|
|
26513
|
-
for (const entry of
|
|
26826
|
+
const entries2 = fs30.readdirSync(dir, { withFileTypes: true });
|
|
26827
|
+
for (const entry of entries2) {
|
|
26514
26828
|
if (entry.name.startsWith(".") || entry.name === "_archive") continue;
|
|
26515
26829
|
const full = path30.join(dir, entry.name);
|
|
26516
26830
|
if (entry.isDirectory() && depth < 3) {
|
|
@@ -26801,6 +27115,7 @@ ${rawOutput}
|
|
|
26801
27115
|
// src/tools/msg-send.ts
|
|
26802
27116
|
init_live();
|
|
26803
27117
|
init_registry();
|
|
27118
|
+
init_rate_breaker();
|
|
26804
27119
|
function getConfig() {
|
|
26805
27120
|
return liveConfig.all();
|
|
26806
27121
|
}
|
|
@@ -26864,10 +27179,10 @@ channel_id \u4E0D\u586B\u4E14 to \u4E5F\u4E0D\u586B\u65F6\uFF0C\u9ED8\u8BA4\u56D
|
|
|
26864
27179
|
\u6CE8\u610F\uFF1A\u5BF9\u65B9\u6B63\u5728\u5F53\u524D\u5BF9\u8BDD\u91CC\u8DDF\u4F60\u8BF4\u8BDD\u65F6\uFF0C\u628A\u56DE\u590D\u76F4\u63A5\u8BF4\u51FA\u6765\u5373\u53EF\u2014\u2014\u4E0D\u8981\u7528 msg_send \u7ED9\u5F53\u524D\u5BF9\u8BDD\u8005\u53D1 DM\uFF08\u4F1A\u88AB\u62E6\u622A\uFF09\u3002
|
|
26865
27180
|
|
|
26866
27181
|
Examples:
|
|
26867
|
-
- \u53D1\u9891\u9053\u5E76 @\u67D0\u4EBA: to="
|
|
26868
|
-
- \u53D1\u9891\u9053\u5E76 @\u591A\u4EBA: to="
|
|
26869
|
-
- \u53D1\u9891\u9053\u4E0D\u5E26 @: channel_id="
|
|
26870
|
-
- \u53D1 DM: to="
|
|
27182
|
+
- \u53D1\u9891\u9053\u5E76 @\u67D0\u4EBA: to="1111111111111111111", channel_id="3333333333333333333", content="\u4F60\u597D"
|
|
27183
|
+
- \u53D1\u9891\u9053\u5E76 @\u591A\u4EBA: to="1111111111111111111,2222222222222222222", channel_id="3333333333333333333", content="\u4F60\u597D"
|
|
27184
|
+
- \u53D1\u9891\u9053\u4E0D\u5E26 @: channel_id="3333333333333333333", content="\u7CFB\u7EDF\u901A\u77E5"
|
|
27185
|
+
- \u53D1 DM: to="1111111111111111111", content="\u79C1\u804A\u5185\u5BB9"
|
|
26871
27186
|
- \u8DE8\u5E73\u53F0\u53D1\u9001: source="discord", channel_id="1503034906081624174", content="\u4ECE\u98DE\u4E66\u53D1\u5230Discord"
|
|
26872
27187
|
- \u56DE\u590D\u6765\u6E90\u9891\u9053: content="\u6536\u5230"\uFF08\u4E0D\u586B channel_id \u548C to\uFF09`,
|
|
26873
27188
|
schema: {
|
|
@@ -26896,12 +27211,12 @@ Examples:
|
|
|
26896
27211
|
}
|
|
26897
27212
|
const toIds = to ? to.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
26898
27213
|
const dest = resolvedChannelId || (toIds.length > 0 ? toIds[0] : void 0);
|
|
27214
|
+
const dmEchoDest = resolvedChannelId || (toIds.length > 0 ? toIds[0] : void 0);
|
|
26899
27215
|
const isDmEcho = ctx.channelType === "dm" && // DM 对话(群聊先观察不拦)
|
|
26900
27216
|
resolvedSource === ctx.channel && // 目标通道=当前对话通道(真跨通道转发不拦)
|
|
26901
27217
|
ctx.inboundFrom && // 有当前发送者(注入消息无 inboundFrom 不拦)
|
|
26902
|
-
|
|
26903
|
-
|
|
26904
|
-
!!resolvedChannelId && resolvedChannelId === ctx.channelTarget);
|
|
27218
|
+
dmEchoDest !== void 0 && (dmEchoDest === ctx.channelTarget || dmEchoDest === ctx.inboundFrom) && // 目的地=当前会话/当前对话者
|
|
27219
|
+
toIds.every((id) => id === ctx.inboundFrom);
|
|
26905
27220
|
if (isDmEcho) {
|
|
26906
27221
|
console.log(`[msg_send] \u26D4 DM \u56DE\u53D1\u5F53\u524D\u5BF9\u8BDD\u88AB\u62E6: to=${to} channel_id=${resolvedChannelId || "(fallback)"} (${resolvedSource})`);
|
|
26907
27222
|
return {
|
|
@@ -26932,6 +27247,11 @@ Examples:
|
|
|
26932
27247
|
}
|
|
26933
27248
|
const fullMsg = `${mentionPrefix}${content}`;
|
|
26934
27249
|
const where = resolvedChannelId ? `${resolvedSource} \u9891\u9053 ${resolvedChannelId}` : `${resolvedSource} DM ${toIds[0]}`;
|
|
27250
|
+
const breaker = checkRateBreaker(resolvedSource, ctx.sessionId, dest, toIds, !!ctx.inboundIsBot);
|
|
27251
|
+
if (breaker) {
|
|
27252
|
+
console.log(`[msg_send] \u{1F515} rate-breaker \u62E6\u622A: session=${ctx.sessionId} \u2192 ${where}`);
|
|
27253
|
+
return breaker;
|
|
27254
|
+
}
|
|
26935
27255
|
try {
|
|
26936
27256
|
await mgr.send(resolvedSource, dest, fullMsg);
|
|
26937
27257
|
return { content: `\u6D88\u606F\u5DF2\u53D1\u9001\u5230 ${where}` };
|
|
@@ -27047,10 +27367,10 @@ Parameters:
|
|
|
27047
27367
|
channel_id \u4E0D\u586B\u4E14 to \u4E5F\u4E0D\u586B\u65F6\uFF0C\u9ED8\u8BA4\u53D1\u5230\u5F53\u524D\u6D88\u606F\u7684\u6765\u6E90\u9891\u9053\u3002
|
|
27048
27368
|
|
|
27049
27369
|
Examples:
|
|
27050
|
-
- \u53D1\u56FE\u7247\u5230\u9891\u9053\u5E76 @\u67D0\u4EBA: to="
|
|
27051
|
-
- \u53D1\u56FE\u7247\u5230\u9891\u9053\u5E76 @\u591A\u4EBA: to="
|
|
27052
|
-
- \u53D1\u6587\u4EF6\u5230\u9891\u9053\u4E0D\u5E26 @: channel_id="
|
|
27053
|
-
- \u53D1\u97F3\u9891 DM: to="
|
|
27370
|
+
- \u53D1\u56FE\u7247\u5230\u9891\u9053\u5E76 @\u67D0\u4EBA: to="1111111111111111111", channel_id="3333333333333333333", type="image", path="/tmp/photo.png"
|
|
27371
|
+
- \u53D1\u56FE\u7247\u5230\u9891\u9053\u5E76 @\u591A\u4EBA: to="1111111111111111111,2222222222222222222", channel_id="3333333333333333333", type="image", path="/tmp/photo.png"
|
|
27372
|
+
- \u53D1\u6587\u4EF6\u5230\u9891\u9053\u4E0D\u5E26 @: channel_id="3333333333333333333", type="file", path="/tmp/report.pdf"
|
|
27373
|
+
- \u53D1\u97F3\u9891 DM: to="1111111111111111111", type="audio", path="/tmp/voice.mp3"
|
|
27054
27374
|
- \u8DE8\u5E73\u53F0\u53D1\u9001: source="discord", channel_id="1503034906081624174", type="image", path="/tmp/photo.png"
|
|
27055
27375
|
- \u53D1\u5230\u6765\u6E90\u9891\u9053: type="image", path="/tmp/photo.png"\uFF08\u4E0D\u586B channel_id \u548C to\uFF09`,
|
|
27056
27376
|
schema: {
|
|
@@ -29556,8 +29876,8 @@ registerCommand({
|
|
|
29556
29876
|
sessionManager: deps.sessions,
|
|
29557
29877
|
sessionId: sid,
|
|
29558
29878
|
model: deps.config.model,
|
|
29559
|
-
contextWindow: deps.compactConfig.contextWindow || 2e5,
|
|
29560
|
-
systemPrompt: deps.systemPrompt,
|
|
29879
|
+
contextWindow: (typeof deps.compactConfig === "function" ? deps.compactConfig() : deps.compactConfig).contextWindow || 2e5,
|
|
29880
|
+
systemPrompt: resolveSystemPrompt(deps.systemPrompt),
|
|
29561
29881
|
toolDefs: registry.definitions(),
|
|
29562
29882
|
workspace: deps.config.workspace
|
|
29563
29883
|
});
|
|
@@ -29605,7 +29925,7 @@ ${question}`;
|
|
|
29605
29925
|
const stream = deps.provider.streamChat({
|
|
29606
29926
|
model: deps.config.model,
|
|
29607
29927
|
messages: [{ role: "user", content: wrappedQuestion }],
|
|
29608
|
-
systemPrompt: deps.systemPrompt,
|
|
29928
|
+
systemPrompt: resolveSystemPrompt(deps.systemPrompt),
|
|
29609
29929
|
maxTokens: 2048,
|
|
29610
29930
|
signal: void 0
|
|
29611
29931
|
});
|
|
@@ -29826,8 +30146,8 @@ registerCommand({
|
|
|
29826
30146
|
const { rm: rm2 } = await import("node:fs/promises");
|
|
29827
30147
|
const teamsDir = getTeamsDir3();
|
|
29828
30148
|
const { readdir: readdir2 } = await import("node:fs/promises");
|
|
29829
|
-
const
|
|
29830
|
-
for (const entry of
|
|
30149
|
+
const entries2 = await readdir2(teamsDir).catch(() => []);
|
|
30150
|
+
for (const entry of entries2) {
|
|
29831
30151
|
const entryPath = `${teamsDir}/${entry}`;
|
|
29832
30152
|
try {
|
|
29833
30153
|
await rm2(entryPath, { recursive: true, force: true });
|
|
@@ -29936,8 +30256,21 @@ registerCommand({
|
|
|
29936
30256
|
let current;
|
|
29937
30257
|
if (deps.getModelOverride()) {
|
|
29938
30258
|
current = `**${deps.getModelOverride()}** (override)`;
|
|
29939
|
-
} else if (deps.provider instanceof FallbackProvider
|
|
29940
|
-
|
|
30259
|
+
} else if (deps.provider instanceof FallbackProvider) {
|
|
30260
|
+
const status = deps.provider.getChainStatus();
|
|
30261
|
+
const chainStr = status.map((e) => e.label).join(" \u2192 ");
|
|
30262
|
+
const next = status.find((e) => e.cooldownMs <= 0);
|
|
30263
|
+
const cooling = status.filter((e) => e.cooldownMs > 0);
|
|
30264
|
+
current = `**auto-route** \u2014 \u94FE: ${chainStr}
|
|
30265
|
+
\u4E0B\u4E00\u4E2A\u8BF7\u6C42\u7528: **${next?.label ?? "(\u5168\u90E8\u51B7\u5374\u4E2D)"}**`;
|
|
30266
|
+
if (cooling.length > 0) {
|
|
30267
|
+
current += `
|
|
30268
|
+
\u51B7\u5374\u4E2D: ${cooling.map((e) => `${e.label}\uFF08\u5269 ${Math.round(e.cooldownMs / 6e4)}min\uFF09`).join("\u3001")}`;
|
|
30269
|
+
}
|
|
30270
|
+
if (deps.provider.lastUsedLabel) {
|
|
30271
|
+
current += `
|
|
30272
|
+
\u4E0A\u6B21\u5B9E\u9645: ${deps.provider.lastUsedLabel}`;
|
|
30273
|
+
}
|
|
29941
30274
|
} else {
|
|
29942
30275
|
current = `**${deps.config.provider.id}/${deps.config.model}** (default, auto-route)`;
|
|
29943
30276
|
}
|
|
@@ -30191,6 +30524,16 @@ Use full ref like \`/vision-model ${candidates[0].ref}\``);
|
|
|
30191
30524
|
});
|
|
30192
30525
|
|
|
30193
30526
|
// src/engine-startup.ts
|
|
30527
|
+
function resolveModelMaxTokens(modelRef) {
|
|
30528
|
+
try {
|
|
30529
|
+
const [pid, mid] = (modelRef || "").split("/");
|
|
30530
|
+
const models = liveConfig.get(`models.providers.${pid}.models`);
|
|
30531
|
+
const m = models?.find((x) => x?.id === mid);
|
|
30532
|
+
return typeof m?.maxTokens === "number" && m.maxTokens > 0 ? m.maxTokens : 4096;
|
|
30533
|
+
} catch {
|
|
30534
|
+
return 4096;
|
|
30535
|
+
}
|
|
30536
|
+
}
|
|
30194
30537
|
var _epipeSeen = false;
|
|
30195
30538
|
process.on("uncaughtException", (err) => {
|
|
30196
30539
|
const code = err?.code ?? "";
|
|
@@ -30260,7 +30603,10 @@ async function startEngine(config2, opts) {
|
|
|
30260
30603
|
const licensedFeatures = loadLicense(config2.stateDir, config2.profile?.devMode === true);
|
|
30261
30604
|
const requiredTools = resolveRequiredTools(config2.profile.features, licensedFeatures);
|
|
30262
30605
|
registry.licensedFeatures = licensedFeatures;
|
|
30263
|
-
|
|
30606
|
+
let provider;
|
|
30607
|
+
let visionProvider;
|
|
30608
|
+
let visionChainLabels;
|
|
30609
|
+
({ provider, visionProvider, visionChainLabels } = buildProviderChain(config2));
|
|
30264
30610
|
if (visionChainLabels.length > 0) {
|
|
30265
30611
|
console.log(`[vision] Routing enabled: ${visionChainLabels.join(" \u2192 ")}`);
|
|
30266
30612
|
}
|
|
@@ -30482,11 +30828,36 @@ ${content}`
|
|
|
30482
30828
|
console.log(`[DEBUG] definitions() = ${_allDefs.length} defs`);
|
|
30483
30829
|
console.log(`[DEBUG] active (non-defer) = ${_activeDefs.length}: ${_activeDefs.map((d) => d.function.name).join(", ")}`);
|
|
30484
30830
|
console.log(`[DEBUG] deferred = ${_deferredDefs.length}: ${_deferredDefs.map((d) => d.function.name).join(", ")}`);
|
|
30485
|
-
|
|
30831
|
+
let _stableCache = null;
|
|
30832
|
+
const getSystemStable = () => {
|
|
30833
|
+
const promptCfg = liveConfig.get("prompt") || {};
|
|
30834
|
+
const fileCandidates = /* @__PURE__ */ new Set(["SOUL.md"]);
|
|
30835
|
+
for (const f of promptCfg.staticFiles || []) fileCandidates.add(f);
|
|
30836
|
+
for (const item of promptCfg.order || []) {
|
|
30837
|
+
if (typeof item === "string" && /\.(md|txt|json)$/i.test(item)) fileCandidates.add(item);
|
|
30838
|
+
}
|
|
30839
|
+
const statLines = [JSON.stringify({ mode: promptCfg.mode, order: promptCfg.order })];
|
|
30840
|
+
for (const f of fileCandidates) {
|
|
30841
|
+
try {
|
|
30842
|
+
const p = path49.isAbsolute(f) ? f : path49.join(config2.workspace, f);
|
|
30843
|
+
statLines.push(`${f}:${fs47.statSync(p).mtimeMs}`);
|
|
30844
|
+
} catch {
|
|
30845
|
+
statLines.push(`${f}:missing`);
|
|
30846
|
+
}
|
|
30847
|
+
}
|
|
30848
|
+
const fingerprint = statLines.join("|");
|
|
30849
|
+
if (_stableCache && _stableCache.fingerprint === fingerprint) return _stableCache.prompt;
|
|
30850
|
+
const prompt = buildStablePrompt(config2.workspace, promptCfg);
|
|
30851
|
+
_stableCache = { fingerprint, prompt };
|
|
30852
|
+
console.log(`[prompt] stable \u91CD\u5EFA\uFF08\u6587\u4EF6/config \u53D8\u66F4\uFF09\uFF0C${prompt.length} chars`);
|
|
30853
|
+
return prompt;
|
|
30854
|
+
};
|
|
30855
|
+
const systemStable = getSystemStable();
|
|
30486
30856
|
const systemDynamic = buildDynamicPrompt({
|
|
30487
30857
|
workspace: config2.workspace
|
|
30488
30858
|
});
|
|
30489
30859
|
const systemPrompt = [systemStable, systemDynamic].join("\n\n");
|
|
30860
|
+
const getSystemPrompt = () => [getSystemStable(), buildDynamicPrompt({ workspace: config2.workspace })].join("\n\n");
|
|
30490
30861
|
dumpSystemPrompt(config2.workspace, systemStable, systemDynamic);
|
|
30491
30862
|
const modelDef = config2.provider.models.find((m) => m.id === config2.model);
|
|
30492
30863
|
const modelContextWindow = modelDef?.contextWindow;
|
|
@@ -30505,13 +30876,21 @@ ${content}`
|
|
|
30505
30876
|
// 默认 5MB
|
|
30506
30877
|
);
|
|
30507
30878
|
const memoryFlushEnabled = config2.compaction?.memoryFlush?.enabled !== false;
|
|
30508
|
-
const
|
|
30509
|
-
|
|
30510
|
-
|
|
30511
|
-
|
|
30512
|
-
|
|
30513
|
-
|
|
30879
|
+
const getCompactConfig = () => {
|
|
30880
|
+
const liveComp = liveConfig.get("compaction") || {};
|
|
30881
|
+
const liveModel = (liveConfig.get("providers") || {})[liveConfig.get("agents.defaults.model.primary")?.split("/")[0] || ""];
|
|
30882
|
+
const liveModelId = liveConfig.get("agents.defaults.model.primary")?.split("/")?.[1];
|
|
30883
|
+
const mDef = liveModel?.models?.find((m) => m.id === liveModelId);
|
|
30884
|
+
const mCW = mDef?.contextWindow;
|
|
30885
|
+
const cfg = {
|
|
30886
|
+
...DEFAULT_COMPACT_CONFIG,
|
|
30887
|
+
...liveComp,
|
|
30888
|
+
...mCW && !liveComp.contextWindow ? { contextWindow: mCW } : {},
|
|
30889
|
+
forceFlushTranscriptBytes
|
|
30890
|
+
};
|
|
30891
|
+
return cfg;
|
|
30514
30892
|
};
|
|
30893
|
+
const compactConfig = getCompactConfig();
|
|
30515
30894
|
if (compactConfig.contextWindow !== DEFAULT_COMPACT_CONFIG.contextWindow) {
|
|
30516
30895
|
console.log(`[compact] Context window: ${compactConfig.contextWindow} (from ${config2.compaction?.contextWindow ? "config" : modelContextWindow ? "model" : "default"})`);
|
|
30517
30896
|
}
|
|
@@ -30520,12 +30899,12 @@ ${content}`
|
|
|
30520
30899
|
}
|
|
30521
30900
|
const engine = new QueryEngine(provider, {
|
|
30522
30901
|
model: config2.model,
|
|
30523
|
-
systemPrompt,
|
|
30524
|
-
systemStable,
|
|
30525
|
-
//
|
|
30526
|
-
compactConfig,
|
|
30527
|
-
//
|
|
30528
|
-
maxTokens:
|
|
30902
|
+
systemPrompt: getSystemPrompt,
|
|
30903
|
+
systemStable: getSystemStable,
|
|
30904
|
+
// 0902 函数形态:mtime 缓存 getter,改 prompt 文件热生效
|
|
30905
|
+
compactConfig: getCompactConfig,
|
|
30906
|
+
// 0902 函数形态:每 turn 刷新
|
|
30907
|
+
maxTokens: () => resolveModelMaxTokens(liveConfig.get("agents.defaults.model.primary") || config2.model),
|
|
30529
30908
|
temperature: 0.7,
|
|
30530
30909
|
maxTurns: config2.profile.maxTurns,
|
|
30531
30910
|
// 从配置读,默认 50(query.ts 里 fallback)
|
|
@@ -30536,10 +30915,10 @@ ${content}`
|
|
|
30536
30915
|
if (visionProvider && visionConfig) {
|
|
30537
30916
|
visionEngine = new QueryEngine(visionProvider, {
|
|
30538
30917
|
model: visionConfig.modelId,
|
|
30539
|
-
systemPrompt,
|
|
30540
|
-
systemStable,
|
|
30541
|
-
compactConfig,
|
|
30542
|
-
maxTokens:
|
|
30918
|
+
systemPrompt: getSystemPrompt,
|
|
30919
|
+
systemStable: getSystemStable,
|
|
30920
|
+
compactConfig: getCompactConfig,
|
|
30921
|
+
maxTokens: () => resolveModelMaxTokens(`${visionConfig.providerId}/${visionConfig.modelId}`),
|
|
30543
30922
|
temperature: 0.7,
|
|
30544
30923
|
maxTurns: config2.profile.maxTurns,
|
|
30545
30924
|
agentLabel: "main"
|
|
@@ -30611,7 +30990,8 @@ ${content}`
|
|
|
30611
30990
|
providerApi: config2.provider.api,
|
|
30612
30991
|
model: config2.model,
|
|
30613
30992
|
modelInputs: modelDef?.input || ["text"],
|
|
30614
|
-
systemPrompt,
|
|
30993
|
+
systemPrompt: getSystemPrompt,
|
|
30994
|
+
// 0902 函数形态:/btw、voice-chat 等按调用时现取(stable mtime 缓存 + dynamic 现算)
|
|
30615
30995
|
channels: config2.channels,
|
|
30616
30996
|
config: config2,
|
|
30617
30997
|
// tool 读自己配置用
|
|
@@ -30625,36 +31005,28 @@ ${content}`
|
|
|
30625
31005
|
} : void 0,
|
|
30626
31006
|
mcpManager
|
|
30627
31007
|
};
|
|
30628
|
-
|
|
31008
|
+
const visionMetaInit = visionEngine && visionConfig ? (() => {
|
|
30629
31009
|
const vpCfg = config2.providers?.[visionConfig.providerId];
|
|
30630
31010
|
const visionModelDef = vpCfg?.models?.find((m) => m.id === visionConfig.modelId);
|
|
30631
|
-
|
|
30632
|
-
engine: visionEngine,
|
|
30633
|
-
sessions,
|
|
30634
|
-
channelManager,
|
|
30635
|
-
workspace: config2.workspace,
|
|
31011
|
+
return {
|
|
30636
31012
|
providerId: visionConfig.providerId,
|
|
30637
31013
|
providerApi: vpCfg?.api || "openai-completions",
|
|
30638
31014
|
model: visionConfig.modelId,
|
|
30639
|
-
modelInputs: visionModelDef?.input || ["text", "image"]
|
|
30640
|
-
systemPrompt,
|
|
30641
|
-
channels: config2.channels,
|
|
30642
|
-
config: config2,
|
|
30643
|
-
// tool 读自己配置用
|
|
30644
|
-
recallProvider: memoryRecallProvider || void 0,
|
|
30645
|
-
extractProvider: memoryExtractProvider || void 0,
|
|
30646
|
-
everosCfg: config2.everos ? {
|
|
30647
|
-
...config2.everos,
|
|
30648
|
-
// resolve provider 引用:从 providers 取 apiKey(跟主 deps 同逻辑)
|
|
30649
|
-
llm: config2.everos.llm?.provider && config2.providers?.[config2.everos.llm.provider] ? { ...config2.everos.llm, apiKey: config2.providers[config2.everos.llm.provider].apiKey } : config2.everos.llm,
|
|
30650
|
-
rerank: config2.everos.rerank?.provider && config2.providers?.[config2.everos.rerank.provider] ? { ...config2.everos.rerank, apiKey: config2.providers[config2.everos.rerank.provider].apiKey } : config2.everos.rerank
|
|
30651
|
-
} : void 0
|
|
31015
|
+
modelInputs: visionModelDef?.input || ["text", "image"]
|
|
30652
31016
|
};
|
|
31017
|
+
})() : null;
|
|
31018
|
+
let visionMeta = visionMetaInit;
|
|
31019
|
+
function resolveVisionDeps() {
|
|
31020
|
+
if (!visionEngine || !visionMeta) return null;
|
|
31021
|
+
if (!visionDeps) {
|
|
31022
|
+
visionDeps = { ...deps, engine: visionEngine, ...visionMeta };
|
|
31023
|
+
console.log(`[vision] deps built: ${visionMeta.providerId}/${visionMeta.model}`);
|
|
31024
|
+
}
|
|
31025
|
+
return visionDeps;
|
|
30653
31026
|
}
|
|
30654
31027
|
let modelOverride = null;
|
|
30655
31028
|
let modelOverrideEngine = null;
|
|
30656
31029
|
let visionOverride = null;
|
|
30657
|
-
const defaultVisionDeps = visionDeps;
|
|
30658
31030
|
const modelDepsCache = /* @__PURE__ */ new Map();
|
|
30659
31031
|
deps.invalidateDeskBuddyDeps = () => {
|
|
30660
31032
|
deskBuddyDeps = null;
|
|
@@ -30680,6 +31052,7 @@ ${content}`
|
|
|
30680
31052
|
if (!p.provider) {
|
|
30681
31053
|
visionEngine = null;
|
|
30682
31054
|
visionDeps = null;
|
|
31055
|
+
visionMeta = null;
|
|
30683
31056
|
return;
|
|
30684
31057
|
}
|
|
30685
31058
|
if (visionEngine) {
|
|
@@ -30688,39 +31061,22 @@ ${content}`
|
|
|
30688
31061
|
} else {
|
|
30689
31062
|
visionEngine = new QueryEngine(p.provider, {
|
|
30690
31063
|
model: p.model,
|
|
30691
|
-
systemPrompt,
|
|
30692
|
-
systemStable,
|
|
30693
|
-
compactConfig,
|
|
31064
|
+
systemPrompt: getSystemPrompt,
|
|
31065
|
+
systemStable: getSystemStable,
|
|
31066
|
+
compactConfig: getCompactConfig,
|
|
30694
31067
|
maxTokens: 4096,
|
|
30695
31068
|
temperature: 0.7,
|
|
30696
31069
|
maxTurns: config2.profile.maxTurns,
|
|
30697
31070
|
agentLabel: "main"
|
|
30698
31071
|
});
|
|
30699
31072
|
}
|
|
30700
|
-
|
|
30701
|
-
|
|
30702
|
-
|
|
30703
|
-
|
|
30704
|
-
|
|
30705
|
-
|
|
30706
|
-
|
|
30707
|
-
visionDeps = {
|
|
30708
|
-
engine: visionEngine,
|
|
30709
|
-
sessions,
|
|
30710
|
-
channelManager,
|
|
30711
|
-
workspace: config2.workspace,
|
|
30712
|
-
providerId: p.providerId,
|
|
30713
|
-
providerApi: p.providerApi,
|
|
30714
|
-
model: p.model,
|
|
30715
|
-
modelInputs: p.modelInputs,
|
|
30716
|
-
systemPrompt,
|
|
30717
|
-
channels: config2.channels,
|
|
30718
|
-
config: config2,
|
|
30719
|
-
recallProvider: memoryRecallProvider || void 0,
|
|
30720
|
-
extractProvider: memoryExtractProvider || void 0,
|
|
30721
|
-
everosCfg: deps?.everosCfg
|
|
30722
|
-
};
|
|
30723
|
-
}
|
|
31073
|
+
visionMeta = {
|
|
31074
|
+
providerId: p.providerId,
|
|
31075
|
+
providerApi: p.providerApi,
|
|
31076
|
+
model: p.model,
|
|
31077
|
+
modelInputs: p.modelInputs
|
|
31078
|
+
};
|
|
31079
|
+
visionDeps = null;
|
|
30724
31080
|
};
|
|
30725
31081
|
function createModelDeps(ref) {
|
|
30726
31082
|
const slashIdx = ref.indexOf("/");
|
|
@@ -30740,28 +31096,21 @@ ${content}`
|
|
|
30740
31096
|
const llmProvider = providerId === config2.provider.id ? provider : createProvider(providerCfg);
|
|
30741
31097
|
const engine2 = new QueryEngine(llmProvider, {
|
|
30742
31098
|
model: modelId,
|
|
30743
|
-
systemPrompt,
|
|
30744
|
-
systemStable,
|
|
30745
|
-
compactConfig,
|
|
30746
|
-
maxTokens:
|
|
31099
|
+
systemPrompt: getSystemPrompt,
|
|
31100
|
+
systemStable: getSystemStable,
|
|
31101
|
+
compactConfig: getCompactConfig,
|
|
31102
|
+
maxTokens: () => resolveModelMaxTokens(`${providerId}/${modelId}`),
|
|
30747
31103
|
temperature: 0.7,
|
|
30748
31104
|
maxTurns: config2.profile.maxTurns,
|
|
30749
31105
|
agentLabel: `main:${ref}`
|
|
30750
31106
|
});
|
|
30751
31107
|
return {
|
|
31108
|
+
...deps,
|
|
30752
31109
|
engine: engine2,
|
|
30753
|
-
sessions,
|
|
30754
|
-
channelManager,
|
|
30755
|
-
workspace: config2.workspace,
|
|
30756
31110
|
providerId,
|
|
30757
31111
|
providerApi: providerCfg.api,
|
|
30758
31112
|
model: modelId,
|
|
30759
|
-
modelInputs: modelDef2.input || ["text"]
|
|
30760
|
-
systemPrompt,
|
|
30761
|
-
channels: config2.channels,
|
|
30762
|
-
recallProvider: memoryRecallProvider || void 0,
|
|
30763
|
-
extractProvider: memoryExtractProvider || void 0,
|
|
30764
|
-
everosCfg: deps?.everosCfg
|
|
31113
|
+
modelInputs: modelDef2.input || ["text"]
|
|
30765
31114
|
};
|
|
30766
31115
|
}
|
|
30767
31116
|
const dispatcher = new MessageDispatcher();
|
|
@@ -30938,7 +31287,17 @@ ${content}`
|
|
|
30938
31287
|
console.log(`[cron] config check: enabled=${config2.cron?.enabled}, hasConfig=${!!config2.cron}`);
|
|
30939
31288
|
if (config2.cron?.enabled) {
|
|
30940
31289
|
const { CronPlugin: CronPlugin2 } = await Promise.resolve().then(() => (init_cron_plugin(), cron_plugin_exports));
|
|
30941
|
-
const
|
|
31290
|
+
const resolveCronModelDeps = (ref) => {
|
|
31291
|
+
const key = `cron:${ref}`;
|
|
31292
|
+
const cached = modelDepsCache.get(key);
|
|
31293
|
+
if (cached) return cached;
|
|
31294
|
+
const built = createModelDeps(ref);
|
|
31295
|
+
if (!built) return null;
|
|
31296
|
+
modelDepsCache.set(key, built);
|
|
31297
|
+
console.log(`[cron] Model deps built: ${ref}`);
|
|
31298
|
+
return built;
|
|
31299
|
+
};
|
|
31300
|
+
const cronPlugin = new CronPlugin2(config2.cron, sessions, channelManager, deps, config2.stateDir, dispatcher, resolveCronModelDeps);
|
|
30942
31301
|
await cronPlugin.start();
|
|
30943
31302
|
const { setCronConfig: setCronConfig2 } = await Promise.resolve().then(() => (init_tools(), tools_exports));
|
|
30944
31303
|
setCronConfig2(config2.cron);
|
|
@@ -31114,8 +31473,10 @@ ${notifications}
|
|
|
31114
31473
|
dispatcher,
|
|
31115
31474
|
runningQueries,
|
|
31116
31475
|
engine,
|
|
31117
|
-
systemPrompt,
|
|
31118
|
-
|
|
31476
|
+
systemPrompt: getSystemPrompt,
|
|
31477
|
+
// 0902 函数形态:命令按调用时现取
|
|
31478
|
+
compactConfig: getCompactConfig,
|
|
31479
|
+
// 0902 同上
|
|
31119
31480
|
provider,
|
|
31120
31481
|
deps,
|
|
31121
31482
|
getVisualRegistry: () => visualRegistry,
|
|
@@ -31137,9 +31498,17 @@ ${notifications}
|
|
|
31137
31498
|
setVisionDeps: (v) => {
|
|
31138
31499
|
visionDeps = v;
|
|
31139
31500
|
},
|
|
31140
|
-
|
|
31501
|
+
// reset 用:清 override deps,下一条图片消息由 resolveVisionDeps 按当前 meta 重建
|
|
31502
|
+
getDefaultVisionDeps: () => {
|
|
31503
|
+
visionDeps = null;
|
|
31504
|
+
return null;
|
|
31505
|
+
},
|
|
31141
31506
|
doReloadConfig
|
|
31142
31507
|
};
|
|
31508
|
+
deps.onProviderSwapped = (p) => {
|
|
31509
|
+
provider = p;
|
|
31510
|
+
commandDeps.provider = p;
|
|
31511
|
+
};
|
|
31143
31512
|
const slashCommands = listCommandDefs().map((d) => ({
|
|
31144
31513
|
name: d.name,
|
|
31145
31514
|
description: d.description,
|
|
@@ -31396,7 +31765,7 @@ ${pathStr}` }];
|
|
|
31396
31765
|
for (const att of nonImageAttachments) {
|
|
31397
31766
|
console.log(`[file] Downloading: ${att.filename} (${att.contentType}, ${att.size}B)`);
|
|
31398
31767
|
try {
|
|
31399
|
-
const resp = attachDispatcher ? await fetch(att.url, { dispatcher: attachDispatcher }) : await fetch(att.url);
|
|
31768
|
+
const resp = attachDispatcher ? await fetch(att.url, { dispatcher: attachDispatcher, signal: AbortSignal.timeout(3e4) }) : await fetch(att.url, { signal: AbortSignal.timeout(3e4) });
|
|
31400
31769
|
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
|
31401
31770
|
const buffer = Buffer.from(await resp.arrayBuffer());
|
|
31402
31771
|
const safeName2 = path49.basename(att.filename).replace(/[<>:"/\\|?*\x00-\x1f]/g, "_") || "attachment";
|
|
@@ -31426,7 +31795,8 @@ ${pathStr}` }];
|
|
|
31426
31795
|
}
|
|
31427
31796
|
const isImageBlock = (b) => b.type === "image" || b.type === "image_url";
|
|
31428
31797
|
const hasImages = Array.isArray(queryContent) && queryContent.some((b) => isImageBlock(b));
|
|
31429
|
-
|
|
31798
|
+
const activeVisionDeps = hasImages ? resolveVisionDeps() : null;
|
|
31799
|
+
let msgDeps = activeVisionDeps ? activeVisionDeps : modelOverride ? modelDepsCache.get(modelOverride) ?? deps : deps;
|
|
31430
31800
|
if (isDeskBuddy && !hasImages && !modelOverride) {
|
|
31431
31801
|
if (!deskBuddyDeps) {
|
|
31432
31802
|
const dbRef = liveConfig.get("channels.deskBuddy.model") || "deepinfra/deepseek-ai/DeepSeek-V4-Flash-0731";
|
|
@@ -31454,7 +31824,8 @@ ${pathStr}` }];
|
|
|
31454
31824
|
model: dbProviderCfg ? dbModelId : config2.model,
|
|
31455
31825
|
// 8/18 翀哥:完整工程 prompt(14k tok)+memory(10k) 会把小模型带偏成"工程助手"——deskBuddy 用 SOUL 精简人设
|
|
31456
31826
|
// 8/21 复用 light-mode.buildLightStablePrompt(和情感模式同一构建器)
|
|
31457
|
-
systemPrompt: buildLightStablePrompt(config2.workspace, "deskBuddy"),
|
|
31827
|
+
systemPrompt: () => buildLightStablePrompt(config2.workspace, "deskBuddy"),
|
|
31828
|
+
// 0902 函数形态:改 SOUL 热生效
|
|
31458
31829
|
maxTokens: 1024,
|
|
31459
31830
|
temperature: 0.7,
|
|
31460
31831
|
disableThinking: true,
|
|
@@ -31468,7 +31839,7 @@ ${pathStr}` }];
|
|
|
31468
31839
|
msgDeps = deskBuddyDeps;
|
|
31469
31840
|
}
|
|
31470
31841
|
if (hasImages) {
|
|
31471
|
-
console.log(`[vision-debug] hasImages=true visionDeps=${
|
|
31842
|
+
console.log(`[vision-debug] hasImages=true visionDeps=${activeVisionDeps ? `yes(${activeVisionDeps.providerId}/${activeVisionDeps.model})` : "NULL"} modelOverride=${modelOverride || "none"} \u2192 msgDeps.provider=${msgDeps?.providerId || "?"}/${msgDeps?.model || "?"} queryContent.blocks=${Array.isArray(queryContent) ? queryContent.length : "string"}`);
|
|
31472
31843
|
if (Array.isArray(queryContent)) {
|
|
31473
31844
|
queryContent.forEach((b, i) => {
|
|
31474
31845
|
if (b.type === "image" && b.source?.data) {
|
|
@@ -31477,13 +31848,13 @@ ${pathStr}` }];
|
|
|
31477
31848
|
});
|
|
31478
31849
|
}
|
|
31479
31850
|
}
|
|
31480
|
-
if (hasImages &&
|
|
31481
|
-
console.log(`[vision] Routing to ${
|
|
31851
|
+
if (hasImages && activeVisionDeps) {
|
|
31852
|
+
console.log(`[vision] Routing to ${activeVisionDeps.providerId}/${activeVisionDeps.model}${activeVisionDeps.model !== visionConfig?.modelId ? ` (override, config=${visionConfig.providerId}/${visionConfig.modelId})` : ""}`);
|
|
31482
31853
|
}
|
|
31483
31854
|
const preQueryResult = await messageHooks.runPreQuery({
|
|
31484
31855
|
inbound: { channel: inbound.channel, channel_id: inbound.channel_id, from: inbound.from, fromName: inbound.fromName, channelType: inbound.channelType, isMentioned: inbound.isMentioned, isBot: inbound.isBot, messageId: inbound.messageId },
|
|
31485
31856
|
text: queryContent,
|
|
31486
|
-
msgDeps:
|
|
31857
|
+
msgDeps: activeVisionDeps ?? msgDeps,
|
|
31487
31858
|
deps: { provider, channelManager, sessions, dispatcher, config: config2, workspace: config2.workspace }
|
|
31488
31859
|
});
|
|
31489
31860
|
if (preQueryResult.skip) {
|
|
@@ -31496,6 +31867,7 @@ ${pathStr}` }];
|
|
|
31496
31867
|
}
|
|
31497
31868
|
queryContent = preQueryResult.text ?? queryContent;
|
|
31498
31869
|
if (preQueryResult.msgDeps) msgDeps = preQueryResult.msgDeps;
|
|
31870
|
+
msgDeps.renderer = renderer;
|
|
31499
31871
|
const accepted = dispatcher.submitMessage({
|
|
31500
31872
|
text: queryContent,
|
|
31501
31873
|
sessionId,
|
|
@@ -31730,8 +32102,9 @@ ${pathStr}` }];
|
|
|
31730
32102
|
sessionManager: sessions,
|
|
31731
32103
|
sessionId,
|
|
31732
32104
|
model: config2.model,
|
|
31733
|
-
contextWindow:
|
|
31734
|
-
systemPrompt,
|
|
32105
|
+
contextWindow: getCompactConfig().contextWindow || 2e5,
|
|
32106
|
+
systemPrompt: getSystemPrompt(),
|
|
32107
|
+
// 0902 现取(API /context 报告跟当前 prompt 一致)
|
|
31735
32108
|
toolDefs: registry.definitions(),
|
|
31736
32109
|
workspace: config2.workspace
|
|
31737
32110
|
});
|
|
@@ -32059,6 +32432,24 @@ async function doReloadConfig(config2, deps, provider) {
|
|
|
32059
32432
|
visionModel: newConfig.visionModel,
|
|
32060
32433
|
visionFallbacks: newConfig.visionFallbacks
|
|
32061
32434
|
});
|
|
32435
|
+
const newRecall = createMemorySideProvider(
|
|
32436
|
+
newConfig.topics?.recall,
|
|
32437
|
+
provider,
|
|
32438
|
+
newConfig.providers || {}
|
|
32439
|
+
);
|
|
32440
|
+
const newExtract = createMemorySideProvider(
|
|
32441
|
+
newConfig.topics?.extract,
|
|
32442
|
+
provider,
|
|
32443
|
+
newConfig.providers || {}
|
|
32444
|
+
);
|
|
32445
|
+
if (newRecall) {
|
|
32446
|
+
deps.recallProvider = newRecall;
|
|
32447
|
+
changes.push(`recall \u2192 ${newConfig.topics?.recall?.provider}/${newConfig.topics?.recall?.model}`);
|
|
32448
|
+
}
|
|
32449
|
+
if (newExtract) {
|
|
32450
|
+
deps.extractProvider = newExtract;
|
|
32451
|
+
changes.push(`extract \u2192 ${newConfig.topics?.extract?.provider}/${newConfig.topics?.extract?.model}`);
|
|
32452
|
+
}
|
|
32062
32453
|
if (oldProviderKey !== newProviderKey) {
|
|
32063
32454
|
console.log("[reload] Provider structure changed, rebuilding chain...");
|
|
32064
32455
|
const rebuilt = buildProviderChain(newConfig);
|
|
@@ -32069,6 +32460,7 @@ async function doReloadConfig(config2, deps, provider) {
|
|
|
32069
32460
|
}
|
|
32070
32461
|
changes.push(`provider chain rebuilt (${rebuilt.visionChainLabels.length > 0 ? "vision: " + rebuilt.visionChainLabels.join("\u2192") : "no vision"})`);
|
|
32071
32462
|
}
|
|
32463
|
+
if (deps.onProviderSwapped) deps.onProviderSwapped(rebuilt.provider);
|
|
32072
32464
|
if (typeof deps.setVisionProvider === "function") {
|
|
32073
32465
|
const vCfg = newConfig.visionModel;
|
|
32074
32466
|
const vpCfg = vCfg ? newConfig.providers?.[vCfg.providerId] : void 0;
|
|
@@ -32099,24 +32491,6 @@ async function doReloadConfig(config2, deps, provider) {
|
|
|
32099
32491
|
changes.push(`deskBuddy model \u2192 ${newDbModel}`);
|
|
32100
32492
|
}
|
|
32101
32493
|
}
|
|
32102
|
-
const newRecall = createMemorySideProvider(
|
|
32103
|
-
newConfig.topics?.recall,
|
|
32104
|
-
provider,
|
|
32105
|
-
newConfig.providers || {}
|
|
32106
|
-
);
|
|
32107
|
-
const newExtract = createMemorySideProvider(
|
|
32108
|
-
newConfig.topics?.extract,
|
|
32109
|
-
provider,
|
|
32110
|
-
newConfig.providers || {}
|
|
32111
|
-
);
|
|
32112
|
-
if (newRecall) {
|
|
32113
|
-
deps.recallProvider = newRecall;
|
|
32114
|
-
changes.push(`recall \u2192 ${newConfig.topics?.recall?.provider}/${newConfig.topics?.recall?.model}`);
|
|
32115
|
-
}
|
|
32116
|
-
if (newExtract) {
|
|
32117
|
-
deps.extractProvider = newExtract;
|
|
32118
|
-
changes.push(`extract \u2192 ${newConfig.topics?.extract?.provider}/${newConfig.topics?.extract?.model}`);
|
|
32119
|
-
}
|
|
32120
32494
|
try {
|
|
32121
32495
|
const { setAutoDreamConfig: setAutoDreamConfig2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
32122
32496
|
setAutoDreamConfig2(newConfig);
|
|
@@ -32312,11 +32686,11 @@ function startSecretsWatcher(config2, deps, provider) {
|
|
|
32312
32686
|
}
|
|
32313
32687
|
|
|
32314
32688
|
// src/main.ts
|
|
32315
|
-
import { readFileSync as
|
|
32689
|
+
import { readFileSync as readFileSync32 } from "node:fs";
|
|
32316
32690
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
32317
|
-
import { dirname as dirname9, join as
|
|
32691
|
+
import { dirname as dirname9, join as join45 } from "node:path";
|
|
32318
32692
|
var __dirname2 = dirname9(fileURLToPath3(import.meta.url));
|
|
32319
|
-
var pkg = JSON.parse(
|
|
32693
|
+
var pkg = JSON.parse(readFileSync32(join45(__dirname2, "..", "package.json"), "utf-8"));
|
|
32320
32694
|
var epipeSeen = false;
|
|
32321
32695
|
process.on("uncaughtException", (err) => {
|
|
32322
32696
|
const code = err?.code ?? "";
|