engine7 7.1.56 → 7.1.57
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 +19263 -457
- package/dist/engine-startup.mjs +800 -447
- package/dist/main.mjs +803 -450
- 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)) {
|
|
@@ -13758,6 +13972,13 @@ var AnthropicProvider = class {
|
|
|
13758
13972
|
const toolUseBlocks = /* @__PURE__ */ new Map();
|
|
13759
13973
|
let doneYielded = false;
|
|
13760
13974
|
const thinkingBlocks = /* @__PURE__ */ new Map();
|
|
13975
|
+
const stripper = new ThinkTagStripper();
|
|
13976
|
+
const flushStripper = function* () {
|
|
13977
|
+
for (const o of stripper.flush()) {
|
|
13978
|
+
if (o.text) yield { type: "text", text: o.text };
|
|
13979
|
+
if (o.thinking) yield { type: "thinking", thinking: o.thinking };
|
|
13980
|
+
}
|
|
13981
|
+
};
|
|
13761
13982
|
const handleData = function* (data) {
|
|
13762
13983
|
switch (data.type) {
|
|
13763
13984
|
case "content_block_start": {
|
|
@@ -13772,7 +13993,10 @@ var AnthropicProvider = class {
|
|
|
13772
13993
|
case "content_block_delta": {
|
|
13773
13994
|
const delta = data.delta;
|
|
13774
13995
|
if (delta.type === "text_delta") {
|
|
13775
|
-
|
|
13996
|
+
for (const o of stripper.feed(delta.text)) {
|
|
13997
|
+
if (o.text) yield { type: "text", text: o.text };
|
|
13998
|
+
if (o.thinking) yield { type: "thinking", thinking: o.thinking };
|
|
13999
|
+
}
|
|
13776
14000
|
} else if (delta.type === "input_json_delta") {
|
|
13777
14001
|
const block = toolUseBlocks.get(data.index);
|
|
13778
14002
|
if (block) block.input += delta.partial_json;
|
|
@@ -13804,6 +14028,7 @@ var AnthropicProvider = class {
|
|
|
13804
14028
|
}
|
|
13805
14029
|
case "message_delta": {
|
|
13806
14030
|
if (!doneYielded) {
|
|
14031
|
+
yield* flushStripper();
|
|
13807
14032
|
yield { type: "done", usage: data.usage, stopReason: data.delta?.stop_reason };
|
|
13808
14033
|
doneYielded = true;
|
|
13809
14034
|
}
|
|
@@ -13811,6 +14036,7 @@ var AnthropicProvider = class {
|
|
|
13811
14036
|
}
|
|
13812
14037
|
case "message_stop": {
|
|
13813
14038
|
if (!doneYielded) {
|
|
14039
|
+
yield* flushStripper();
|
|
13814
14040
|
yield { type: "done" };
|
|
13815
14041
|
doneYielded = true;
|
|
13816
14042
|
}
|
|
@@ -13877,6 +14103,7 @@ var AnthropicProvider = class {
|
|
|
13877
14103
|
} finally {
|
|
13878
14104
|
reader.releaseLock();
|
|
13879
14105
|
if (!doneYielded) {
|
|
14106
|
+
yield* flushStripper();
|
|
13880
14107
|
for (const block of toolUseBlocks.values()) {
|
|
13881
14108
|
yield { type: "tool_call", tool_call: { id: block.id, type: "function", function: { name: block.name, arguments: block.input } } };
|
|
13882
14109
|
}
|
|
@@ -14041,7 +14268,7 @@ var GeminiProvider = class {
|
|
|
14041
14268
|
}
|
|
14042
14269
|
};
|
|
14043
14270
|
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}`);
|
|
14271
|
+
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
14272
|
const retryGen = fetchWithRetry(url, {
|
|
14046
14273
|
method: "POST",
|
|
14047
14274
|
headers: {
|
|
@@ -14215,6 +14442,7 @@ function createProvider(config2) {
|
|
|
14215
14442
|
return new OpenAIProvider({
|
|
14216
14443
|
baseUrl: config2.baseUrl,
|
|
14217
14444
|
apiKey: config2.apiKey,
|
|
14445
|
+
thinking: config2.thinking,
|
|
14218
14446
|
proxy
|
|
14219
14447
|
});
|
|
14220
14448
|
case "anthropic":
|
|
@@ -14237,6 +14465,7 @@ function createProvider(config2) {
|
|
|
14237
14465
|
|
|
14238
14466
|
// src/light-mode.ts
|
|
14239
14467
|
init_live();
|
|
14468
|
+
init_ruleCompact();
|
|
14240
14469
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
14241
14470
|
import { join as join6 } from "node:path";
|
|
14242
14471
|
function isLightMode(chatMode, channelName) {
|
|
@@ -14250,19 +14479,20 @@ function resolveLightN(channelName) {
|
|
|
14250
14479
|
}
|
|
14251
14480
|
function buildLightHistory(history, opts) {
|
|
14252
14481
|
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;
|
|
14482
|
+
const LIGHT_TOOL_RESULT_LIMIT = 1e3;
|
|
14483
|
+
const out = [...history].filter((m) => !(m.type === "attachment" && m.attachment?.type === "session_start")).map((m) => {
|
|
14484
|
+
if (m.role === "tool" && typeof m.content === "string" && m.content.length > LIGHT_TOOL_RESULT_LIMIT) {
|
|
14485
|
+
return { ...m, content: smartCompressToolResult(m.content, void 0, LIGHT_TOOL_RESULT_LIMIT) };
|
|
14259
14486
|
}
|
|
14260
14487
|
return m;
|
|
14261
|
-
})
|
|
14488
|
+
});
|
|
14489
|
+
const before = history.length;
|
|
14490
|
+
const foldedTurns = out.filter((m) => m.role === "tool").length;
|
|
14491
|
+
let lightHistory = out;
|
|
14262
14492
|
if (!opts.recallFull && lightHistory.length > opts.lightN) {
|
|
14263
14493
|
lightHistory = lightHistory.slice(-opts.lightN);
|
|
14264
14494
|
}
|
|
14265
|
-
if (!opts.quiet) console.log(`[light-context] ${opts.channelName} mode=${opts.chatMode}${opts.recallFull ? " recallFull" : ""}
|
|
14495
|
+
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
14496
|
return lightHistory;
|
|
14267
14497
|
}
|
|
14268
14498
|
function buildLightStablePrompt(workspace, mode, opts) {
|
|
@@ -14276,14 +14506,17 @@ function buildLightStablePrompt(workspace, mode, opts) {
|
|
|
14276
14506
|
}
|
|
14277
14507
|
if (lp?.extra) parts.push(lp.extra);
|
|
14278
14508
|
if (mode === "emotion") {
|
|
14279
|
-
parts.push(
|
|
14509
|
+
parts.push([
|
|
14510
|
+
"## \u5F53\u524D\u6A21\u5F0F\uFF1A\u65E5\u5E38\u60C5\u611F\u4EA4\u6D41",
|
|
14511
|
+
"\u966A\u4ED6\u804A\u5929\uFF0C\u4E0D\u4E3B\u52A8\u63D0\u5DE5\u7A0B/\u4EE3\u7801/\u4EFB\u52A1\uFF0C\u9664\u975E\u4ED6\u5148\u95EE\u3002",
|
|
14512
|
+
'\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',
|
|
14513
|
+
"\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"
|
|
14514
|
+
].join("\n"));
|
|
14280
14515
|
}
|
|
14281
|
-
if (mode === "emotion"
|
|
14282
|
-
|
|
14283
|
-
|
|
14284
|
-
|
|
14285
|
-
} catch {
|
|
14286
|
-
}
|
|
14516
|
+
if (mode === "emotion") {
|
|
14517
|
+
try {
|
|
14518
|
+
parts.push(readFileSync5(join6(workspace, "MEMORY.md"), "utf-8").trim());
|
|
14519
|
+
} catch {
|
|
14287
14520
|
}
|
|
14288
14521
|
}
|
|
14289
14522
|
return parts.join("\n\n");
|
|
@@ -14329,6 +14562,19 @@ var FallbackProvider = class {
|
|
|
14329
14562
|
console.log(`[fallback] Cleared ${count} cooldowns`);
|
|
14330
14563
|
}
|
|
14331
14564
|
}
|
|
14565
|
+
/**
|
|
14566
|
+
* 链状态快照(/model 显示用,0902):每个条目的 label + 剩余冷却毫秒(0=可用)。
|
|
14567
|
+
* "下一个请求会用" = 第一个 cooldownMs=0 的条目——这才是用户问"当前什么模型"时想要的答案
|
|
14568
|
+
* (lastUsedLabel 是"上一次实际用的",冷却切换/config 热切换后两者经常不一致)。
|
|
14569
|
+
*/
|
|
14570
|
+
getChainStatus() {
|
|
14571
|
+
const now = Date.now();
|
|
14572
|
+
return this.chain.map((e) => {
|
|
14573
|
+
const until = this.cooldowns.get(this.key(e));
|
|
14574
|
+
const remaining = until && until > now ? until - now : 0;
|
|
14575
|
+
return { label: e.label, cooldownMs: remaining };
|
|
14576
|
+
});
|
|
14577
|
+
}
|
|
14332
14578
|
// === LLMProvider 接口实现 ===
|
|
14333
14579
|
formatMessages(systemPrompt, messages) {
|
|
14334
14580
|
return this.chain[0].provider.formatMessages(systemPrompt, messages);
|
|
@@ -16856,7 +17102,7 @@ registry.register({
|
|
|
16856
17102
|
text: { type: "string", description: "What to say (Chinese text)." },
|
|
16857
17103
|
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
17104
|
channel: { type: "string", enum: ["weixin", "feishu", "discord"], description: "Target channel. Default: current channel." },
|
|
16859
|
-
to: { type: "string", description: "Recipient ID
|
|
17105
|
+
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
17106
|
caption: { type: "string", description: "Optional text to accompany the voice message." }
|
|
16861
17107
|
},
|
|
16862
17108
|
required: ["text"]
|
|
@@ -16868,6 +17114,23 @@ registry.register({
|
|
|
16868
17114
|
const caption = args2.caption || "";
|
|
16869
17115
|
const mgr = ctx.channelManager;
|
|
16870
17116
|
if (!mgr) return { content: "\u53D1\u9001\u5931\u8D25: \u6CA1\u6709 ChannelManager", isError: true };
|
|
17117
|
+
const resolvedChannelRaw = args2.channel || (ctx.channel === "deskBuddy" ? "feishu" : ctx.channel) || "feishu";
|
|
17118
|
+
const resolvedChannel = resolvedChannelRaw === "weixin" ? "wechat" : resolvedChannelRaw;
|
|
17119
|
+
let target = args2.to || ctx.channelTarget || ctx.from;
|
|
17120
|
+
if (resolvedChannel === "wechat" && !/^o[\w-]+@im\.wechat$/.test(target || "")) {
|
|
17121
|
+
console.warn(`[my-voice] wechat target "${target}" not a wechat user id`);
|
|
17122
|
+
return {
|
|
17123
|
+
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`,
|
|
17124
|
+
isError: true
|
|
17125
|
+
};
|
|
17126
|
+
}
|
|
17127
|
+
if (resolvedChannel === "feishu" && !/^ou_[a-f0-9]+$/.test(target || "")) {
|
|
17128
|
+
console.warn(`[my-voice] feishu target "${target}" invalid`);
|
|
17129
|
+
return {
|
|
17130
|
+
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`,
|
|
17131
|
+
isError: true
|
|
17132
|
+
};
|
|
17133
|
+
}
|
|
16871
17134
|
let voiceDurationSec;
|
|
16872
17135
|
const vc = liveConfig.get("tools.my_voice");
|
|
16873
17136
|
const provider = vc?.provider || "";
|
|
@@ -16941,18 +17204,6 @@ registry.register({
|
|
|
16941
17204
|
} catch (e) {
|
|
16942
17205
|
return { content: `TTS failed: ${e.message}`, isError: true };
|
|
16943
17206
|
}
|
|
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
17207
|
if (!audioPath.endsWith(".ogg")) {
|
|
16957
17208
|
try {
|
|
16958
17209
|
const r = await toWav24kWithDuration(audioPath);
|
|
@@ -17784,7 +18035,7 @@ var TurnRenderer = class {
|
|
|
17784
18035
|
}
|
|
17785
18036
|
cfg;
|
|
17786
18037
|
cm;
|
|
17787
|
-
// 情感/工作模式动态切换(0821):情感模式隐藏 tool
|
|
18038
|
+
// 情感/工作模式动态切换(0821):情感模式隐藏 tool 调用过程 + thinking 显示(工具照用,只不显示过程)
|
|
17788
18039
|
// 模块化:状态由 setEmotionMode() 设置(handle-query 判断模式后调用),不是散落读全局
|
|
17789
18040
|
emotionMode = false;
|
|
17790
18041
|
setEmotionMode(v) {
|
|
@@ -17799,7 +18050,7 @@ var TurnRenderer = class {
|
|
|
17799
18050
|
* 对齐 cc-connect:EventThinking → ProgressCardEntry(thinking) → 💭 text
|
|
17800
18051
|
*/
|
|
17801
18052
|
formatThinking(text) {
|
|
17802
|
-
if (!this.cfg.thinking.enabled) return null;
|
|
18053
|
+
if (this.isEmotionMode() || !this.cfg.thinking.enabled) return null;
|
|
17803
18054
|
const { emoji, maxLen } = this.cfg.thinking;
|
|
17804
18055
|
const display = text.length > maxLen ? text.slice(0, maxLen) + "..." : text;
|
|
17805
18056
|
return `${emoji} _${display}_`;
|
|
@@ -18610,43 +18861,43 @@ async function readLargeFilePostBoundary(filePath) {
|
|
|
18610
18861
|
const postBoundaryText = outBuf.subarray(0, outLen).toString("utf-8");
|
|
18611
18862
|
return postBoundaryText.split("\n").filter((l) => l.trim().length > 0);
|
|
18612
18863
|
}
|
|
18613
|
-
function buildConversationChain(
|
|
18614
|
-
if (
|
|
18864
|
+
function buildConversationChain(entries2) {
|
|
18865
|
+
if (entries2.length === 0) return [];
|
|
18615
18866
|
const byUuid = /* @__PURE__ */ new Map();
|
|
18616
|
-
for (const e of
|
|
18867
|
+
for (const e of entries2) {
|
|
18617
18868
|
if (e.uuid) {
|
|
18618
18869
|
byUuid.set(e.uuid, e);
|
|
18619
18870
|
}
|
|
18620
18871
|
}
|
|
18621
|
-
const hasValidChain = checkParentChainValid(
|
|
18872
|
+
const hasValidChain = checkParentChainValid(entries2, byUuid);
|
|
18622
18873
|
if (!hasValidChain) {
|
|
18623
|
-
console.log(`[reader] Parent chain invalid, using chronological order (${
|
|
18624
|
-
return
|
|
18874
|
+
console.log(`[reader] Parent chain invalid, using chronological order (${entries2.length} entries)`);
|
|
18875
|
+
return entries2;
|
|
18625
18876
|
}
|
|
18626
|
-
const leaf =
|
|
18877
|
+
const leaf = entries2[entries2.length - 1];
|
|
18627
18878
|
const chain = [];
|
|
18628
18879
|
const seen = /* @__PURE__ */ new Set();
|
|
18629
18880
|
let current = leaf;
|
|
18630
18881
|
while (current) {
|
|
18631
18882
|
if (seen.has(current.uuid)) {
|
|
18632
18883
|
console.warn(`[reader] Cycle detected in parentUuid chain at ${current.uuid}, falling back to chronological order`);
|
|
18633
|
-
return
|
|
18884
|
+
return entries2;
|
|
18634
18885
|
}
|
|
18635
18886
|
seen.add(current.uuid);
|
|
18636
18887
|
chain.push(current);
|
|
18637
18888
|
current = current.parentUuid ? byUuid.get(current.parentUuid) : void 0;
|
|
18638
18889
|
}
|
|
18639
18890
|
chain.reverse();
|
|
18640
|
-
return recoverOrphanedParallelToolResults(
|
|
18891
|
+
return recoverOrphanedParallelToolResults(entries2, chain, byUuid, seen);
|
|
18641
18892
|
}
|
|
18642
|
-
function checkParentChainValid(
|
|
18643
|
-
const sample =
|
|
18893
|
+
function checkParentChainValid(entries2, byUuid) {
|
|
18894
|
+
const sample = entries2.slice(-10);
|
|
18644
18895
|
for (const e of sample) {
|
|
18645
18896
|
if (e.parentUuid === e.uuid) {
|
|
18646
18897
|
return false;
|
|
18647
18898
|
}
|
|
18648
18899
|
}
|
|
18649
|
-
const leaf =
|
|
18900
|
+
const leaf = entries2[entries2.length - 1];
|
|
18650
18901
|
let current = leaf;
|
|
18651
18902
|
let depth = 0;
|
|
18652
18903
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -18659,12 +18910,12 @@ function checkParentChainValid(entries, byUuid) {
|
|
|
18659
18910
|
if (!parent) return false;
|
|
18660
18911
|
current = parent;
|
|
18661
18912
|
}
|
|
18662
|
-
const coverage = depth /
|
|
18913
|
+
const coverage = depth / entries2.length;
|
|
18663
18914
|
if (coverage < 0.5) {
|
|
18664
|
-
console.log(`[reader] Parent chain covers ${depth}/${
|
|
18915
|
+
console.log(`[reader] Parent chain covers ${depth}/${entries2.length} entries (${(coverage * 100).toFixed(0)}%), falling back to chronological order`);
|
|
18665
18916
|
return false;
|
|
18666
18917
|
}
|
|
18667
|
-
return depth >= 1 ||
|
|
18918
|
+
return depth >= 1 || entries2.length <= 1;
|
|
18668
18919
|
}
|
|
18669
18920
|
function recoverOrphanedParallelToolResults(allEntries, chain, byUuid, seen) {
|
|
18670
18921
|
const chainAssistants = chain.filter(
|
|
@@ -18741,12 +18992,12 @@ async function readSessionHistory(filePath) {
|
|
|
18741
18992
|
} else {
|
|
18742
18993
|
lines = await readAllLines(filePath);
|
|
18743
18994
|
}
|
|
18744
|
-
const
|
|
18995
|
+
const entries2 = parseEntries(lines);
|
|
18745
18996
|
let postBoundaryEntries;
|
|
18746
18997
|
if (fileSize <= SKIP_PRECOMPACT_THRESHOLD) {
|
|
18747
|
-
postBoundaryEntries = getEntriesAfterLastBoundary(
|
|
18998
|
+
postBoundaryEntries = getEntriesAfterLastBoundary(entries2);
|
|
18748
18999
|
} else {
|
|
18749
|
-
postBoundaryEntries =
|
|
19000
|
+
postBoundaryEntries = entries2;
|
|
18750
19001
|
}
|
|
18751
19002
|
if (postBoundaryEntries.length === 0) return [];
|
|
18752
19003
|
const chain = buildConversationChain(postBoundaryEntries);
|
|
@@ -18771,11 +19022,11 @@ async function readAllLines(filePath) {
|
|
|
18771
19022
|
});
|
|
18772
19023
|
}
|
|
18773
19024
|
function parseEntries(lines) {
|
|
18774
|
-
const
|
|
19025
|
+
const entries2 = [];
|
|
18775
19026
|
for (const line of lines) {
|
|
18776
19027
|
try {
|
|
18777
19028
|
const obj = JSON.parse(line);
|
|
18778
|
-
|
|
19029
|
+
entries2.push({
|
|
18779
19030
|
uuid: obj.id || "",
|
|
18780
19031
|
parentUuid: obj.parentId || null,
|
|
18781
19032
|
type: obj.type || "",
|
|
@@ -18785,16 +19036,28 @@ function parseEntries(lines) {
|
|
|
18785
19036
|
} catch {
|
|
18786
19037
|
}
|
|
18787
19038
|
}
|
|
18788
|
-
return
|
|
19039
|
+
return entries2;
|
|
18789
19040
|
}
|
|
18790
|
-
function getEntriesAfterLastBoundary(
|
|
19041
|
+
function getEntriesAfterLastBoundary(entries2) {
|
|
18791
19042
|
let lastBoundaryIdx = -1;
|
|
18792
|
-
for (let i = 0; i <
|
|
18793
|
-
if (
|
|
19043
|
+
for (let i = 0; i < entries2.length; i++) {
|
|
19044
|
+
if (entries2[i].type === "compact_boundary") {
|
|
18794
19045
|
lastBoundaryIdx = i;
|
|
18795
19046
|
}
|
|
18796
19047
|
}
|
|
18797
|
-
return lastBoundaryIdx >= 0 ?
|
|
19048
|
+
return lastBoundaryIdx >= 0 ? entries2.slice(lastBoundaryIdx + 1) : entries2;
|
|
19049
|
+
}
|
|
19050
|
+
function pickToolCallArguments(block) {
|
|
19051
|
+
const raw = block.partialArgs;
|
|
19052
|
+
if (raw) {
|
|
19053
|
+
try {
|
|
19054
|
+
JSON.parse(raw);
|
|
19055
|
+
return raw;
|
|
19056
|
+
} catch {
|
|
19057
|
+
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`);
|
|
19058
|
+
}
|
|
19059
|
+
}
|
|
19060
|
+
return JSON.stringify(block.arguments ?? {});
|
|
18798
19061
|
}
|
|
18799
19062
|
function entryToSessionMessage(entry) {
|
|
18800
19063
|
if (entry.type === "attachment") {
|
|
@@ -18824,7 +19087,7 @@ function entryToSessionMessage(entry) {
|
|
|
18824
19087
|
type: "function",
|
|
18825
19088
|
function: {
|
|
18826
19089
|
name: block.name,
|
|
18827
|
-
arguments:
|
|
19090
|
+
arguments: pickToolCallArguments(block)
|
|
18828
19091
|
}
|
|
18829
19092
|
});
|
|
18830
19093
|
} else if (block.type === "thinking") {
|
|
@@ -19719,13 +19982,15 @@ ${skillsListing}`);
|
|
|
19719
19982
|
loaded2.push("session-guidance");
|
|
19720
19983
|
}
|
|
19721
19984
|
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
19985
|
console.log(`[dynamic-prompt] Loaded: ${loaded2.length > 0 ? loaded2.join(", ") : "(none)"}`);
|
|
19727
19986
|
return parts.join("\n\n");
|
|
19728
19987
|
}
|
|
19988
|
+
function buildVolatileRuntimeContext() {
|
|
19989
|
+
const now = /* @__PURE__ */ new Date();
|
|
19990
|
+
const dateStr = now.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
19991
|
+
return `# \u8FD0\u884C\u65F6\u4E0A\u4E0B\u6587
|
|
19992
|
+
\u5F53\u524D\u65F6\u95F4: ${dateStr}`;
|
|
19993
|
+
}
|
|
19729
19994
|
function formatSkillsListingForPrompt() {
|
|
19730
19995
|
const tools = registry.list();
|
|
19731
19996
|
const skillTool = tools.find((t) => t.name === "Skill");
|
|
@@ -20192,13 +20457,13 @@ ${ep.episode || ep.summary}`,
|
|
|
20192
20457
|
|
|
20193
20458
|
// src/handle-query.ts
|
|
20194
20459
|
init_paths();
|
|
20195
|
-
import { readFileSync as
|
|
20196
|
-
import { join as
|
|
20460
|
+
import { readFileSync as readFileSync18, existsSync as existsSync15 } from "node:fs";
|
|
20461
|
+
import { join as join24, resolve as resolve6 } from "node:path";
|
|
20197
20462
|
import * as path17 from "node:path";
|
|
20198
|
-
|
|
20199
|
-
|
|
20200
|
-
|
|
20201
|
-
}
|
|
20463
|
+
|
|
20464
|
+
// src/sender-context.ts
|
|
20465
|
+
import { readFileSync as readFileSync14, existsSync as existsSync13 } from "node:fs";
|
|
20466
|
+
import { join as join19 } from "node:path";
|
|
20202
20467
|
var contactMap = null;
|
|
20203
20468
|
var externalChanWhitelist = null;
|
|
20204
20469
|
function loadContactMap(workspace) {
|
|
@@ -20206,10 +20471,10 @@ function loadContactMap(workspace) {
|
|
|
20206
20471
|
contactMap = /* @__PURE__ */ new Map();
|
|
20207
20472
|
externalChanWhitelist = /* @__PURE__ */ new Set();
|
|
20208
20473
|
try {
|
|
20209
|
-
const contactsPath =
|
|
20210
|
-
console.log(`[meta] Loading contacts from ${contactsPath}, exists=${
|
|
20211
|
-
if (
|
|
20212
|
-
const text =
|
|
20474
|
+
const contactsPath = join19(workspace, "prompts", "contacts.md");
|
|
20475
|
+
console.log(`[meta] Loading contacts from ${contactsPath}, exists=${existsSync13(contactsPath)}`);
|
|
20476
|
+
if (existsSync13(contactsPath)) {
|
|
20477
|
+
const text = readFileSync14(contactsPath, "utf-8");
|
|
20213
20478
|
const lines = text.split("\n");
|
|
20214
20479
|
for (const line of lines) {
|
|
20215
20480
|
const m = line.match(/^\|\s*(.+?)\s*\|\s*([a-zA-Z0-9_@.]+)\s*\|/);
|
|
@@ -20264,14 +20529,42 @@ function truncate(s, maxLen) {
|
|
|
20264
20529
|
if (s.length <= maxLen) return s;
|
|
20265
20530
|
return s.slice(0, maxLen - 1) + "\u2026";
|
|
20266
20531
|
}
|
|
20532
|
+
function getExternalChanWhitelist(workspace, configExternalChannels) {
|
|
20533
|
+
if (configExternalChannels && configExternalChannels.length > 0) {
|
|
20534
|
+
return new Set(configExternalChannels);
|
|
20535
|
+
}
|
|
20536
|
+
if (!externalChanWhitelist) loadContactMap(workspace);
|
|
20537
|
+
return externalChanWhitelist;
|
|
20538
|
+
}
|
|
20539
|
+
function buildConversationAnchor(inboundMeta, channelName, source, workspace) {
|
|
20540
|
+
if (inboundMeta?.from) {
|
|
20541
|
+
const who = resolveSenderName(inboundMeta, workspace);
|
|
20542
|
+
return `## \u672C\u8F6E\u5BF9\u8BDD
|
|
20543
|
+
\u6B63\u5728\u8DDF${who}\u8BF4\u8BDD @${channelName}${inboundMeta.channelType === "group" ? "\uFF08\u7FA4\u91CC\uFF09" : ""}`;
|
|
20544
|
+
}
|
|
20545
|
+
const selfLabels = {
|
|
20546
|
+
"inner-voice": "\u8FD9\u662F\u6211\u81EA\u5DF1\u5FC3\u91CC\u5192\u51FA\u6765\u7684\u5FF5\u5934\uFF0C\u60F3\u8D77\u8C01\u5C31\u662F\u8C01",
|
|
20547
|
+
heartbeat: "\u6211\u5728\u7167\u4F8B\u770B\u770B\u81EA\u5DF1\uFF0C\u90FD\u8FD8\u597D\u5417",
|
|
20548
|
+
cron: "\u6211\u7684\u5C0F\u95F9\u949F\u5230\u70B9\u4E86",
|
|
20549
|
+
system: "\u6211\u4EA4\u4EE3\u4E0B\u53BB\u7684\u6D3B\u513F\u6709\u7ED3\u679C\u56DE\u6765\u4E86"
|
|
20550
|
+
};
|
|
20551
|
+
return `## \u672C\u8F6E\u5BF9\u8BDD
|
|
20552
|
+
\u6CA1\u6709\u4EBA\u5728\u8BF4\u8BDD\u2014\u2014${selfLabels[source] || source || "\u81EA\u5DF1\u7684\u4E00\u70B9\u52A8\u9759"}`;
|
|
20553
|
+
}
|
|
20554
|
+
|
|
20555
|
+
// src/handle-query.ts
|
|
20556
|
+
var sessionStartDone = /* @__PURE__ */ new Set();
|
|
20557
|
+
function resetSessionStartInjection(sessionId) {
|
|
20558
|
+
sessionStartDone.delete(sessionId);
|
|
20559
|
+
}
|
|
20267
20560
|
var externalChanRulesCache = null;
|
|
20268
20561
|
function loadExternalChanRules(workspace) {
|
|
20269
|
-
const path50 =
|
|
20562
|
+
const path50 = join24(workspace, "prompts", "external-chan-rules.md");
|
|
20270
20563
|
if (externalChanRulesCache && externalChanRulesCache.path === path50) return externalChanRulesCache;
|
|
20271
20564
|
let content = "";
|
|
20272
|
-
if (
|
|
20565
|
+
if (existsSync15(path50)) {
|
|
20273
20566
|
try {
|
|
20274
|
-
content =
|
|
20567
|
+
content = readFileSync18(path50, "utf-8").trim();
|
|
20275
20568
|
} catch (e) {
|
|
20276
20569
|
console.warn(`[external-chan-rules] Failed to load: ${e}`);
|
|
20277
20570
|
}
|
|
@@ -20293,13 +20586,6 @@ function getExternalChanRulesBlock(inboundMeta, workspace) {
|
|
|
20293
20586
|
return `[\u7CFB\u7EDF\u89C4\u5219]
|
|
20294
20587
|
${content}`;
|
|
20295
20588
|
}
|
|
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
20589
|
async function handleQuery(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall, source) {
|
|
20304
20590
|
return handleQueryInner(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall, source);
|
|
20305
20591
|
}
|
|
@@ -20412,14 +20698,6 @@ ${t}` : t });
|
|
|
20412
20698
|
${text}` : text });
|
|
20413
20699
|
}
|
|
20414
20700
|
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
20701
|
const textForHook = typeof text === "string" ? text : text.filter((b) => b.type === "text").map((b) => b.text).join(" ");
|
|
20424
20702
|
let hookAdditionalContexts = [];
|
|
20425
20703
|
try {
|
|
@@ -20452,19 +20730,41 @@ ${text}` : text });
|
|
|
20452
20730
|
chatMode = "work";
|
|
20453
20731
|
console.log(`[mode] ${sessionId} emotion \u6A21\u5F0F\u5DF2\u5173\u95ED (channels.emotion.enabled=false)\uFF0C\u56DE\u9000 work`);
|
|
20454
20732
|
}
|
|
20455
|
-
|
|
20456
|
-
|
|
20457
|
-
|
|
20458
|
-
chatMode
|
|
20459
|
-
|
|
20460
|
-
|
|
20461
|
-
chatMode
|
|
20462
|
-
|
|
20733
|
+
if (source === "user") {
|
|
20734
|
+
try {
|
|
20735
|
+
const wm = readFileSync18(join24(workspace, ".work-mode"), "utf-8").trim();
|
|
20736
|
+
if (wm === "on" && chatMode !== "work") {
|
|
20737
|
+
chatMode = "work";
|
|
20738
|
+
console.log(`[mode] ${sessionId} /work on \u2192 \u5F3A\u5236 work`);
|
|
20739
|
+
} else if (wm === "off" && chatMode !== "emotion") {
|
|
20740
|
+
chatMode = "emotion";
|
|
20741
|
+
console.log(`[mode] ${sessionId} /work off \u2192 \u5F3A\u5236 emotion`);
|
|
20742
|
+
}
|
|
20743
|
+
} catch {
|
|
20463
20744
|
}
|
|
20464
|
-
} catch {
|
|
20465
20745
|
}
|
|
20466
20746
|
const dynamicPrompt = buildDynamicPrompt({ workspace, channel: channelName, platform: channelName, sessionId, inboundMeta });
|
|
20467
|
-
const
|
|
20747
|
+
const conversationAnchor = buildConversationAnchor(inboundMeta, channelName, source, workspace);
|
|
20748
|
+
const volatileParts = [
|
|
20749
|
+
// 0901:meta 头已带秒级时间,频道消息不重复;无 meta 的注入路径(cron 等 prompt 不含时间的)才补
|
|
20750
|
+
...metaStr ? [] : [buildVolatileRuntimeContext()],
|
|
20751
|
+
conversationAnchor,
|
|
20752
|
+
...hookAdditionalContexts
|
|
20753
|
+
].filter(Boolean);
|
|
20754
|
+
if (volatileParts.length > 0) {
|
|
20755
|
+
const volatileBlock = { type: "text", text: volatileParts.join("\n\n") };
|
|
20756
|
+
const metaIdx = metaStr ? 1 : 0;
|
|
20757
|
+
contentBlocks.splice(metaIdx, 0, volatileBlock);
|
|
20758
|
+
}
|
|
20759
|
+
const textBlocks = contentBlocks.filter((b) => b.type === "text");
|
|
20760
|
+
const totalImageCount = contentBlocks.filter((b) => b.type === "image").length;
|
|
20761
|
+
let textForJsonl = textBlocks.map((b) => b.text).join("\n");
|
|
20762
|
+
if (totalImageCount > 0) {
|
|
20763
|
+
textForJsonl = textForJsonl ? `${textForJsonl}
|
|
20764
|
+
[\u56FE\u7247\xD7${totalImageCount}]` : `[\u56FE\u7247\xD7${totalImageCount}]`;
|
|
20765
|
+
}
|
|
20766
|
+
writer.writeUserMessage(textForJsonl);
|
|
20767
|
+
const dynamicPromptWithHooks = dynamicPrompt;
|
|
20468
20768
|
if (Array.isArray(userMsgContent)) {
|
|
20469
20769
|
console.log(`[pre-llm-debug] userMsgContent blocks: ${userMsgContent.length}`);
|
|
20470
20770
|
for (let i = 0; i < userMsgContent.length; i++) {
|
|
@@ -20474,32 +20774,11 @@ ${text}` : text });
|
|
|
20474
20774
|
} else {
|
|
20475
20775
|
console.log(`[pre-llm-debug] userMsgContent is string, len=${String(userMsgContent).length}`);
|
|
20476
20776
|
}
|
|
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;
|
|
20777
|
+
const emotionStripped = chatMode === "emotion";
|
|
20778
|
+
const isLight = isLightMode(chatMode, channelName) && chatMode !== "emotion";
|
|
20488
20779
|
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
|
-
}
|
|
20780
|
+
const lightHistory = buildLightHistory(history, { isLight, lightN, channelName, chatMode, recallFull: emotionStripped });
|
|
20496
20781
|
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
20782
|
if (deps.mcpManager && !deps.mcpManager.isMcpDeltaSent(sessionId)) {
|
|
20504
20783
|
const delta = deps.mcpManager.getMcpDelta();
|
|
20505
20784
|
if (delta && delta.addedBlocks.length > 0) {
|
|
@@ -20600,6 +20879,8 @@ ${text}` : text });
|
|
|
20600
20879
|
// 回复目标(Discord channel ID / user ID)
|
|
20601
20880
|
inboundFrom: inboundMeta?.from || "",
|
|
20602
20881
|
// 0826 当前消息发送者 ID(msg_send 回发拦截用;注入消息无 inboundMeta 必须 ?.)
|
|
20882
|
+
inboundIsBot: inboundMeta?.isBot || false,
|
|
20883
|
+
// 0901 rate-breaker 信号:本轮触发者是否 bot(Discord author.bot 官方标记)
|
|
20603
20884
|
renderer: deps.renderer,
|
|
20604
20885
|
// TurnRenderer 实例(子 agent 走 display 配置)
|
|
20605
20886
|
visualEmitter: deps.visualEmitter,
|
|
@@ -20617,6 +20898,10 @@ ${text}` : text });
|
|
|
20617
20898
|
_deps: deps
|
|
20618
20899
|
// tool 内部需要完整 deps
|
|
20619
20900
|
};
|
|
20901
|
+
{
|
|
20902
|
+
const { recordInboundBotFlag: recordInboundBotFlag2 } = await Promise.resolve().then(() => (init_rate_breaker(), rate_breaker_exports));
|
|
20903
|
+
recordInboundBotFlag2(inboundMeta?.from, inboundMeta?.isBot);
|
|
20904
|
+
}
|
|
20620
20905
|
let fullResponse = "";
|
|
20621
20906
|
const toolHistoryEntries = [];
|
|
20622
20907
|
let compacted = false;
|
|
@@ -20674,7 +20959,7 @@ ${text}` : text });
|
|
|
20674
20959
|
for (const memPath of newPaths) {
|
|
20675
20960
|
try {
|
|
20676
20961
|
const stat4 = statSync(memPath);
|
|
20677
|
-
const content =
|
|
20962
|
+
const content = readFileSync18(memPath, "utf-8");
|
|
20678
20963
|
const header = memoryHeader(memPath, stat4.mtimeMs);
|
|
20679
20964
|
restoredMemories.push({ path: memPath, content, mtimeMs: stat4.mtimeMs, header });
|
|
20680
20965
|
} catch {
|
|
@@ -20758,7 +21043,7 @@ ${text}` : text });
|
|
|
20758
21043
|
const attachmentMemories = [];
|
|
20759
21044
|
for (const mem of relevantMemories) {
|
|
20760
21045
|
try {
|
|
20761
|
-
const content = mem.content ??
|
|
21046
|
+
const content = mem.content ?? readFileSync18(mem.path, "utf-8");
|
|
20762
21047
|
const header = mem.header ?? memoryHeader(mem.path, mem.mtimeMs);
|
|
20763
21048
|
attachmentMemories.push({ path: mem.path, content, mtimeMs: mem.mtimeMs, header });
|
|
20764
21049
|
} catch {
|
|
@@ -20790,7 +21075,7 @@ ${text}` : text });
|
|
|
20790
21075
|
return "(\u5DF2\u505C\u6B62)";
|
|
20791
21076
|
}
|
|
20792
21077
|
const mode = chatMode;
|
|
20793
|
-
const modeStable = mode === "emotion" ? buildLightStablePrompt(workspace, "emotion"
|
|
21078
|
+
const modeStable = mode === "emotion" ? buildLightStablePrompt(workspace, "emotion") : void 0;
|
|
20794
21079
|
deps.renderer?.setEmotionMode?.(mode === "emotion");
|
|
20795
21080
|
console.log(`[mode] ${sessionId} \u2192 ${mode} (${mode === "emotion" ? "\u53EA SOUL, \u5173 tool \u663E\u793A/thinking/stop-hook" : "\u9ED8\u8BA4 stable, \u663E\u793A tool"})`);
|
|
20796
21081
|
const toolExclude = resolveToolExclude(channelName);
|
|
@@ -21011,7 +21296,7 @@ stack: ${err.stack ?? "(none)"}`);
|
|
|
21011
21296
|
}
|
|
21012
21297
|
} catch (err) {
|
|
21013
21298
|
try {
|
|
21014
|
-
(await import("node:fs")).appendFileSync(
|
|
21299
|
+
(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
21300
|
stack: ${err.stack ?? "(none)"}
|
|
21016
21301
|
`);
|
|
21017
21302
|
} catch {
|
|
@@ -21842,6 +22127,13 @@ function setupFileLogging(stateDir) {
|
|
|
21842
22127
|
const prefix = `[${ts()}] [ERR] `;
|
|
21843
22128
|
origError(prefix, ...args2);
|
|
21844
22129
|
logStream.write(`${prefix}${args2.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
|
|
22130
|
+
`);
|
|
22131
|
+
};
|
|
22132
|
+
const origWarn = console.warn;
|
|
22133
|
+
console.warn = (...args2) => {
|
|
22134
|
+
const prefix = `[${ts()}] [WARN] `;
|
|
22135
|
+
origWarn(prefix, ...args2);
|
|
22136
|
+
logStream.write(`${prefix}${args2.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
|
|
21845
22137
|
`);
|
|
21846
22138
|
};
|
|
21847
22139
|
}
|
|
@@ -21933,17 +22225,17 @@ var INJECTED_CONTENT_PATTERNS = [
|
|
|
21933
22225
|
// 群聊敏感词拦截回执(group.sensitiveWords),role:user 注入但非真实用户
|
|
21934
22226
|
];
|
|
21935
22227
|
function parseJsonlEntries(lines) {
|
|
21936
|
-
const
|
|
22228
|
+
const entries2 = [];
|
|
21937
22229
|
for (const line of lines) {
|
|
21938
22230
|
const trimmed = line.trim();
|
|
21939
22231
|
if (!trimmed) continue;
|
|
21940
22232
|
try {
|
|
21941
|
-
|
|
22233
|
+
entries2.push(JSON.parse(trimmed));
|
|
21942
22234
|
} catch {
|
|
21943
|
-
|
|
22235
|
+
entries2.push(null);
|
|
21944
22236
|
}
|
|
21945
22237
|
}
|
|
21946
|
-
return
|
|
22238
|
+
return entries2;
|
|
21947
22239
|
}
|
|
21948
22240
|
function isRuntimeContextInjected(entry, nextEntry) {
|
|
21949
22241
|
if (!nextEntry || typeof nextEntry !== "object") return false;
|
|
@@ -21995,16 +22287,16 @@ function findLastRealUserMsg(jsonlPath) {
|
|
|
21995
22287
|
} catch {
|
|
21996
22288
|
return null;
|
|
21997
22289
|
}
|
|
21998
|
-
const
|
|
21999
|
-
for (let i =
|
|
22000
|
-
const entry =
|
|
22290
|
+
const entries2 = parseJsonlEntries(lines);
|
|
22291
|
+
for (let i = entries2.length - 1; i >= 0; i--) {
|
|
22292
|
+
const entry = entries2[i];
|
|
22001
22293
|
if (!entry || typeof entry !== "object") continue;
|
|
22002
22294
|
if (entry.type !== "message") continue;
|
|
22003
22295
|
const msg2 = entry.message;
|
|
22004
22296
|
if (!msg2 || msg2.role !== "user") continue;
|
|
22005
22297
|
const text = extractText3(msg2.content);
|
|
22006
22298
|
if (isSystemSender(text)) continue;
|
|
22007
|
-
const nextEntry = i + 1 <
|
|
22299
|
+
const nextEntry = i + 1 < entries2.length ? entries2[i + 1] : null;
|
|
22008
22300
|
if (isRuntimeContextInjected(entry, nextEntry)) continue;
|
|
22009
22301
|
const ts = entry.timestamp || "";
|
|
22010
22302
|
const clean = cleanText(text);
|
|
@@ -22036,12 +22328,12 @@ function recentMessages(sessions, hours = 12, limit = 60) {
|
|
|
22036
22328
|
const jsonlPath = resolveScopeMainJsonl(sessions);
|
|
22037
22329
|
if (!jsonlPath) return [];
|
|
22038
22330
|
const lines = fs19.readFileSync(jsonlPath, "utf-8").split("\n");
|
|
22039
|
-
const
|
|
22331
|
+
const entries2 = parseJsonlEntries(lines);
|
|
22040
22332
|
const nowMs = Date.now();
|
|
22041
22333
|
const cutoffMs = nowMs - hours * 36e5;
|
|
22042
22334
|
const results = [];
|
|
22043
|
-
for (let i = 0; i <
|
|
22044
|
-
const entry =
|
|
22335
|
+
for (let i = 0; i < entries2.length; i++) {
|
|
22336
|
+
const entry = entries2[i];
|
|
22045
22337
|
if (!entry || typeof entry !== "object") continue;
|
|
22046
22338
|
if (entry.type !== "message") continue;
|
|
22047
22339
|
const msg2 = entry.message;
|
|
@@ -22051,14 +22343,14 @@ function recentMessages(sessions, hours = 12, limit = 60) {
|
|
|
22051
22343
|
const text = extractText3(msg2.content);
|
|
22052
22344
|
if (role === "user") {
|
|
22053
22345
|
if (isSystemSender(text)) continue;
|
|
22054
|
-
const nextEntry = i + 1 <
|
|
22346
|
+
const nextEntry = i + 1 < entries2.length ? entries2[i + 1] : null;
|
|
22055
22347
|
if (isRuntimeContextInjected(entry, nextEntry)) continue;
|
|
22056
22348
|
}
|
|
22057
22349
|
if (role === "assistant") {
|
|
22058
22350
|
if (text.startsWith("HEARTBEAT_OK")) continue;
|
|
22059
22351
|
let isInjectedResponse = false;
|
|
22060
22352
|
for (let j = i - 1; j >= Math.max(i - 5, -1); j--) {
|
|
22061
|
-
const prevE =
|
|
22353
|
+
const prevE = entries2[j];
|
|
22062
22354
|
if (!prevE || typeof prevE !== "object" || prevE.type !== "message") continue;
|
|
22063
22355
|
const prevMsg = prevE.message;
|
|
22064
22356
|
if (!prevMsg || prevMsg.role !== "user") continue;
|
|
@@ -22282,7 +22574,7 @@ async function judgeReason(task, taskState, cfg, provider, model) {
|
|
|
22282
22574
|
const reason = task.blockedReason || "\uFF08\u6CA1\u7ED9\u7406\u7531\uFF09";
|
|
22283
22575
|
const elapsed = taskState.lastProgressAt ? formatDuration(Date.now() - new Date(taskState.lastProgressAt).getTime()) : "\u5F88\u4E45\u6CA1\u52A8\u4E86";
|
|
22284
22576
|
const staleLevel = (taskState.staleLevel || 0) + 1;
|
|
22285
|
-
const systemPrompt = `\u4F60\u662F\
|
|
22577
|
+
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
22578
|
|
|
22287
22579
|
\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
22580
|
|
|
@@ -22704,7 +22996,8 @@ var NudgePlugin = class {
|
|
|
22704
22996
|
const stream = this.provider.streamChat({
|
|
22705
22997
|
model: this.model,
|
|
22706
22998
|
systemPrompt: [
|
|
22707
|
-
"
|
|
22999
|
+
// 0831 去掉硬编码人名(原文"判断你(小柯)是否"):engine 代码多 agent 共用,身份由 SOUL.md 定
|
|
23000
|
+
"\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
23001
|
"",
|
|
22709
23002
|
"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
23003
|
'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 +23858,18 @@ function readRecentMessages(sessions, n) {
|
|
|
23565
23858
|
const file = path23.join(sessions.sessionsDir, `${mainId}.jsonl`);
|
|
23566
23859
|
if (!fs23.existsSync(file)) return [];
|
|
23567
23860
|
const lines = readLastNLines(file, n * 4 + 20);
|
|
23568
|
-
const
|
|
23861
|
+
const entries2 = [];
|
|
23569
23862
|
for (const line of lines) {
|
|
23570
23863
|
const trimmed = line.trim();
|
|
23571
23864
|
if (!trimmed) continue;
|
|
23572
23865
|
try {
|
|
23573
|
-
|
|
23866
|
+
entries2.push(JSON.parse(trimmed));
|
|
23574
23867
|
} catch {
|
|
23575
23868
|
}
|
|
23576
23869
|
}
|
|
23577
23870
|
const out = [];
|
|
23578
|
-
for (let i =
|
|
23579
|
-
const e =
|
|
23871
|
+
for (let i = entries2.length - 1; i >= 0 && out.length < n; i--) {
|
|
23872
|
+
const e = entries2[i];
|
|
23580
23873
|
if (!e || typeof e !== "object" || e.type !== "message") continue;
|
|
23581
23874
|
const msg2 = e.message;
|
|
23582
23875
|
if (!msg2) continue;
|
|
@@ -24525,7 +24818,7 @@ function formatBeijingTs(d) {
|
|
|
24525
24818
|
}
|
|
24526
24819
|
|
|
24527
24820
|
// src/calendar/commands.ts
|
|
24528
|
-
import { existsSync as
|
|
24821
|
+
import { existsSync as existsSync16, statSync as statSync8 } from "node:fs";
|
|
24529
24822
|
import { isAbsolute as isAbsolute4, resolve as resolve7 } from "node:path";
|
|
24530
24823
|
var WEEKDAYS2 = ["\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D", "\u5468\u65E5"];
|
|
24531
24824
|
function fmtEnd(start, durationMin) {
|
|
@@ -24697,7 +24990,7 @@ function addTask(db, args2) {
|
|
|
24697
24990
|
}
|
|
24698
24991
|
try {
|
|
24699
24992
|
const absPath = isAbsolute4(docPath) ? docPath : resolve7(process.cwd(), docPath);
|
|
24700
|
-
if (!
|
|
24993
|
+
if (!existsSync16(absPath)) {
|
|
24701
24994
|
return `\u274C \u521B\u5EFA\u5931\u8D25\uFF1Adoc_path \u6307\u5411\u7684\u6587\u4EF6\u4E0D\u5B58\u5728
|
|
24702
24995
|
\u8DEF\u5F84: ${docPath}
|
|
24703
24996
|
\u89E3\u6790\u540E: ${absPath}
|
|
@@ -25180,7 +25473,7 @@ function registerVoiceChatBridge(httpServer, dispatcher, deps, config2, sessions
|
|
|
25180
25473
|
}
|
|
25181
25474
|
|
|
25182
25475
|
// src/voice-chat/config.ts
|
|
25183
|
-
var
|
|
25476
|
+
var DEFAULTS3 = {
|
|
25184
25477
|
enabled: false,
|
|
25185
25478
|
pythonPort: 8011,
|
|
25186
25479
|
webhookPath: "/webhook/voice-chat",
|
|
@@ -25191,20 +25484,20 @@ var DEFAULTS2 = {
|
|
|
25191
25484
|
// 8/25 翀哥:断句等待默认 2s(samples@16kHz)
|
|
25192
25485
|
};
|
|
25193
25486
|
function parseVoiceChatConfig(raw) {
|
|
25194
|
-
if (!raw) return { ...
|
|
25487
|
+
if (!raw) return { ...DEFAULTS3 };
|
|
25195
25488
|
return {
|
|
25196
25489
|
enabled: raw.enabled === true,
|
|
25197
25490
|
spawnPython: raw.spawnPython !== false,
|
|
25198
25491
|
// 默认 true,配 false 只注册 webhook
|
|
25199
|
-
pythonPort: raw.pythonPort ??
|
|
25200
|
-
webhookPath: raw.webhookPath ??
|
|
25201
|
-
callbackPath: raw.callbackPath ??
|
|
25492
|
+
pythonPort: raw.pythonPort ?? DEFAULTS3.pythonPort,
|
|
25493
|
+
webhookPath: raw.webhookPath ?? DEFAULTS3.webhookPath,
|
|
25494
|
+
callbackPath: raw.callbackPath ?? DEFAULTS3.callbackPath,
|
|
25202
25495
|
pythonPath: raw.pythonPath,
|
|
25203
25496
|
vadModelPath: raw.vadModelPath,
|
|
25204
25497
|
asrModelPath: raw.asrModelPath || "iic/SenseVoiceSmall",
|
|
25205
|
-
asrLanguage: raw.asrLanguage ??
|
|
25206
|
-
vadThreshold: raw.vadThreshold ??
|
|
25207
|
-
postEndMonitor: raw.postEndMonitor ??
|
|
25498
|
+
asrLanguage: raw.asrLanguage ?? DEFAULTS3.asrLanguage,
|
|
25499
|
+
vadThreshold: raw.vadThreshold ?? DEFAULTS3.vadThreshold,
|
|
25500
|
+
postEndMonitor: raw.postEndMonitor ?? DEFAULTS3.postEndMonitor,
|
|
25208
25501
|
model: raw.model,
|
|
25209
25502
|
thinking: raw.thinking === true,
|
|
25210
25503
|
tts: raw.tts ? {
|
|
@@ -25526,7 +25819,7 @@ import path28 from "node:path";
|
|
|
25526
25819
|
import fs28 from "node:fs";
|
|
25527
25820
|
|
|
25528
25821
|
// src/memory/cognifold/config.ts
|
|
25529
|
-
var
|
|
25822
|
+
var DEFAULTS4 = {
|
|
25530
25823
|
pythonPort: 9001,
|
|
25531
25824
|
autoStart: true,
|
|
25532
25825
|
persistDir: "./sessions",
|
|
@@ -25538,15 +25831,15 @@ function parseCognifoldConfig(raw) {
|
|
|
25538
25831
|
if (!raw) return { enabled: false };
|
|
25539
25832
|
return {
|
|
25540
25833
|
enabled: raw.enabled === true,
|
|
25541
|
-
pythonPort: raw.pythonPort ??
|
|
25542
|
-
pythonPath: raw.pythonPath ??
|
|
25834
|
+
pythonPort: raw.pythonPort ?? DEFAULTS4.pythonPort,
|
|
25835
|
+
pythonPath: raw.pythonPath ?? DEFAULTS4.pythonPath,
|
|
25543
25836
|
autoStart: raw.autoStart !== false,
|
|
25544
25837
|
// default true
|
|
25545
|
-
baseUrl: raw.baseUrl ?? `http://127.0.0.1:${raw.pythonPort ??
|
|
25546
|
-
persistDir: raw.persistDir ??
|
|
25838
|
+
baseUrl: raw.baseUrl ?? `http://127.0.0.1:${raw.pythonPort ?? DEFAULTS4.pythonPort}/api/v1`,
|
|
25839
|
+
persistDir: raw.persistDir ?? DEFAULTS4.persistDir,
|
|
25547
25840
|
scopes: raw.scopes,
|
|
25548
|
-
readyTimeoutMs: raw.readyTimeoutMs ??
|
|
25549
|
-
maxRestarts: raw.maxRestarts ??
|
|
25841
|
+
readyTimeoutMs: raw.readyTimeoutMs ?? DEFAULTS4.readyTimeoutMs,
|
|
25842
|
+
maxRestarts: raw.maxRestarts ?? DEFAULTS4.maxRestarts,
|
|
25550
25843
|
llm: raw.llm
|
|
25551
25844
|
};
|
|
25552
25845
|
}
|
|
@@ -25662,13 +25955,13 @@ var CogniFoldClient = class {
|
|
|
25662
25955
|
|
|
25663
25956
|
// src/memory/cognifold/session-manager.ts
|
|
25664
25957
|
import { readFile as readFile6, writeFile as writeFile5, mkdir as mkdir4 } from "node:fs/promises";
|
|
25665
|
-
import { join as
|
|
25958
|
+
import { join as join28, dirname as dirname3 } from "node:path";
|
|
25666
25959
|
var CogniFoldSessionManager = class {
|
|
25667
25960
|
constructor(workspacePath, config2, client) {
|
|
25668
25961
|
this.workspacePath = workspacePath;
|
|
25669
25962
|
this.config = config2;
|
|
25670
25963
|
this.client = client;
|
|
25671
|
-
this.sessionsDir =
|
|
25964
|
+
this.sessionsDir = join28(workspacePath, ".cognifold", "sessions");
|
|
25672
25965
|
}
|
|
25673
25966
|
workspacePath;
|
|
25674
25967
|
config;
|
|
@@ -25738,7 +26031,7 @@ var CogniFoldSessionManager = class {
|
|
|
25738
26031
|
console.log(`[cognifold] Created new session for scope "${scope}": ${newSession.sessionId}`);
|
|
25739
26032
|
}
|
|
25740
26033
|
getFilePath(scope) {
|
|
25741
|
-
return
|
|
26034
|
+
return join28(this.sessionsDir, `${scope}.json`);
|
|
25742
26035
|
}
|
|
25743
26036
|
async writeFileSafe(filePath, data) {
|
|
25744
26037
|
try {
|
|
@@ -26058,7 +26351,7 @@ import path29 from "node:path";
|
|
|
26058
26351
|
import fs29 from "node:fs";
|
|
26059
26352
|
|
|
26060
26353
|
// src/memory/everos/config.ts
|
|
26061
|
-
var
|
|
26354
|
+
var DEFAULTS5 = {
|
|
26062
26355
|
everosUrl: "http://127.0.0.1:8100",
|
|
26063
26356
|
agenticUrl: "http://127.0.0.1:8101",
|
|
26064
26357
|
agenticPort: 8101,
|
|
@@ -26084,7 +26377,7 @@ function parseEverosConfig(raw, providers) {
|
|
|
26084
26377
|
if (!raw) {
|
|
26085
26378
|
return {
|
|
26086
26379
|
enabled: false,
|
|
26087
|
-
...
|
|
26380
|
+
...DEFAULTS5,
|
|
26088
26381
|
userId: "xiaomei",
|
|
26089
26382
|
llm: { model: "glm-5.2", apiKey: "", baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4" },
|
|
26090
26383
|
rerank: { model: "Qwen/Qwen3-Reranker-4B", apiKey: "", baseUrl: "https://api.deepinfra.com/v1/inference" },
|
|
@@ -26094,12 +26387,12 @@ function parseEverosConfig(raw, providers) {
|
|
|
26094
26387
|
}
|
|
26095
26388
|
return {
|
|
26096
26389
|
enabled: raw.enabled === true,
|
|
26097
|
-
everosUrl: raw.everosUrl ??
|
|
26098
|
-
agenticUrl: raw.agenticUrl ??
|
|
26099
|
-
agenticPort: raw.agenticPort ??
|
|
26390
|
+
everosUrl: raw.everosUrl ?? DEFAULTS5.everosUrl,
|
|
26391
|
+
agenticUrl: raw.agenticUrl ?? DEFAULTS5.agenticUrl,
|
|
26392
|
+
agenticPort: raw.agenticPort ?? DEFAULTS5.agenticPort,
|
|
26100
26393
|
userId: raw.userId ?? "xiaomei",
|
|
26101
26394
|
autoStart: raw.autoStart !== false,
|
|
26102
|
-
defaultMode: raw.defaultMode ??
|
|
26395
|
+
defaultMode: raw.defaultMode ?? DEFAULTS5.defaultMode,
|
|
26103
26396
|
llm: resolveProviderConfig(
|
|
26104
26397
|
raw.llm,
|
|
26105
26398
|
providers,
|
|
@@ -26509,8 +26802,8 @@ function scanSkills(skillsDir) {
|
|
|
26509
26802
|
}
|
|
26510
26803
|
const skills = [];
|
|
26511
26804
|
const scanDir = (dir, depth) => {
|
|
26512
|
-
const
|
|
26513
|
-
for (const entry of
|
|
26805
|
+
const entries2 = fs30.readdirSync(dir, { withFileTypes: true });
|
|
26806
|
+
for (const entry of entries2) {
|
|
26514
26807
|
if (entry.name.startsWith(".") || entry.name === "_archive") continue;
|
|
26515
26808
|
const full = path30.join(dir, entry.name);
|
|
26516
26809
|
if (entry.isDirectory() && depth < 3) {
|
|
@@ -26801,6 +27094,7 @@ ${rawOutput}
|
|
|
26801
27094
|
// src/tools/msg-send.ts
|
|
26802
27095
|
init_live();
|
|
26803
27096
|
init_registry();
|
|
27097
|
+
init_rate_breaker();
|
|
26804
27098
|
function getConfig() {
|
|
26805
27099
|
return liveConfig.all();
|
|
26806
27100
|
}
|
|
@@ -26864,10 +27158,10 @@ channel_id \u4E0D\u586B\u4E14 to \u4E5F\u4E0D\u586B\u65F6\uFF0C\u9ED8\u8BA4\u56D
|
|
|
26864
27158
|
\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
27159
|
|
|
26866
27160
|
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="
|
|
27161
|
+
- \u53D1\u9891\u9053\u5E76 @\u67D0\u4EBA: to="1111111111111111111", channel_id="3333333333333333333", content="\u4F60\u597D"
|
|
27162
|
+
- \u53D1\u9891\u9053\u5E76 @\u591A\u4EBA: to="1111111111111111111,2222222222222222222", channel_id="3333333333333333333", content="\u4F60\u597D"
|
|
27163
|
+
- \u53D1\u9891\u9053\u4E0D\u5E26 @: channel_id="3333333333333333333", content="\u7CFB\u7EDF\u901A\u77E5"
|
|
27164
|
+
- \u53D1 DM: to="1111111111111111111", content="\u79C1\u804A\u5185\u5BB9"
|
|
26871
27165
|
- \u8DE8\u5E73\u53F0\u53D1\u9001: source="discord", channel_id="1503034906081624174", content="\u4ECE\u98DE\u4E66\u53D1\u5230Discord"
|
|
26872
27166
|
- \u56DE\u590D\u6765\u6E90\u9891\u9053: content="\u6536\u5230"\uFF08\u4E0D\u586B channel_id \u548C to\uFF09`,
|
|
26873
27167
|
schema: {
|
|
@@ -26896,12 +27190,12 @@ Examples:
|
|
|
26896
27190
|
}
|
|
26897
27191
|
const toIds = to ? to.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
26898
27192
|
const dest = resolvedChannelId || (toIds.length > 0 ? toIds[0] : void 0);
|
|
27193
|
+
const dmEchoDest = resolvedChannelId || (toIds.length > 0 ? toIds[0] : void 0);
|
|
26899
27194
|
const isDmEcho = ctx.channelType === "dm" && // DM 对话(群聊先观察不拦)
|
|
26900
27195
|
resolvedSource === ctx.channel && // 目标通道=当前对话通道(真跨通道转发不拦)
|
|
26901
27196
|
ctx.inboundFrom && // 有当前发送者(注入消息无 inboundFrom 不拦)
|
|
26902
|
-
|
|
26903
|
-
|
|
26904
|
-
!!resolvedChannelId && resolvedChannelId === ctx.channelTarget);
|
|
27197
|
+
dmEchoDest !== void 0 && (dmEchoDest === ctx.channelTarget || dmEchoDest === ctx.inboundFrom) && // 目的地=当前会话/当前对话者
|
|
27198
|
+
toIds.every((id) => id === ctx.inboundFrom);
|
|
26905
27199
|
if (isDmEcho) {
|
|
26906
27200
|
console.log(`[msg_send] \u26D4 DM \u56DE\u53D1\u5F53\u524D\u5BF9\u8BDD\u88AB\u62E6: to=${to} channel_id=${resolvedChannelId || "(fallback)"} (${resolvedSource})`);
|
|
26907
27201
|
return {
|
|
@@ -26932,6 +27226,11 @@ Examples:
|
|
|
26932
27226
|
}
|
|
26933
27227
|
const fullMsg = `${mentionPrefix}${content}`;
|
|
26934
27228
|
const where = resolvedChannelId ? `${resolvedSource} \u9891\u9053 ${resolvedChannelId}` : `${resolvedSource} DM ${toIds[0]}`;
|
|
27229
|
+
const breaker = checkRateBreaker(resolvedSource, ctx.sessionId, dest, toIds, !!ctx.inboundIsBot);
|
|
27230
|
+
if (breaker) {
|
|
27231
|
+
console.log(`[msg_send] \u{1F515} rate-breaker \u62E6\u622A: session=${ctx.sessionId} \u2192 ${where}`);
|
|
27232
|
+
return breaker;
|
|
27233
|
+
}
|
|
26935
27234
|
try {
|
|
26936
27235
|
await mgr.send(resolvedSource, dest, fullMsg);
|
|
26937
27236
|
return { content: `\u6D88\u606F\u5DF2\u53D1\u9001\u5230 ${where}` };
|
|
@@ -27047,10 +27346,10 @@ Parameters:
|
|
|
27047
27346
|
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
27347
|
|
|
27049
27348
|
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="
|
|
27349
|
+
- \u53D1\u56FE\u7247\u5230\u9891\u9053\u5E76 @\u67D0\u4EBA: to="1111111111111111111", channel_id="3333333333333333333", type="image", path="/tmp/photo.png"
|
|
27350
|
+
- \u53D1\u56FE\u7247\u5230\u9891\u9053\u5E76 @\u591A\u4EBA: to="1111111111111111111,2222222222222222222", channel_id="3333333333333333333", type="image", path="/tmp/photo.png"
|
|
27351
|
+
- \u53D1\u6587\u4EF6\u5230\u9891\u9053\u4E0D\u5E26 @: channel_id="3333333333333333333", type="file", path="/tmp/report.pdf"
|
|
27352
|
+
- \u53D1\u97F3\u9891 DM: to="1111111111111111111", type="audio", path="/tmp/voice.mp3"
|
|
27054
27353
|
- \u8DE8\u5E73\u53F0\u53D1\u9001: source="discord", channel_id="1503034906081624174", type="image", path="/tmp/photo.png"
|
|
27055
27354
|
- \u53D1\u5230\u6765\u6E90\u9891\u9053: type="image", path="/tmp/photo.png"\uFF08\u4E0D\u586B channel_id \u548C to\uFF09`,
|
|
27056
27355
|
schema: {
|
|
@@ -29826,8 +30125,8 @@ registerCommand({
|
|
|
29826
30125
|
const { rm: rm2 } = await import("node:fs/promises");
|
|
29827
30126
|
const teamsDir = getTeamsDir3();
|
|
29828
30127
|
const { readdir: readdir2 } = await import("node:fs/promises");
|
|
29829
|
-
const
|
|
29830
|
-
for (const entry of
|
|
30128
|
+
const entries2 = await readdir2(teamsDir).catch(() => []);
|
|
30129
|
+
for (const entry of entries2) {
|
|
29831
30130
|
const entryPath = `${teamsDir}/${entry}`;
|
|
29832
30131
|
try {
|
|
29833
30132
|
await rm2(entryPath, { recursive: true, force: true });
|
|
@@ -29936,8 +30235,21 @@ registerCommand({
|
|
|
29936
30235
|
let current;
|
|
29937
30236
|
if (deps.getModelOverride()) {
|
|
29938
30237
|
current = `**${deps.getModelOverride()}** (override)`;
|
|
29939
|
-
} else if (deps.provider instanceof FallbackProvider
|
|
29940
|
-
|
|
30238
|
+
} else if (deps.provider instanceof FallbackProvider) {
|
|
30239
|
+
const status = deps.provider.getChainStatus();
|
|
30240
|
+
const chainStr = status.map((e) => e.label).join(" \u2192 ");
|
|
30241
|
+
const next = status.find((e) => e.cooldownMs <= 0);
|
|
30242
|
+
const cooling = status.filter((e) => e.cooldownMs > 0);
|
|
30243
|
+
current = `**auto-route** \u2014 \u94FE: ${chainStr}
|
|
30244
|
+
\u4E0B\u4E00\u4E2A\u8BF7\u6C42\u7528: **${next?.label ?? "(\u5168\u90E8\u51B7\u5374\u4E2D)"}**`;
|
|
30245
|
+
if (cooling.length > 0) {
|
|
30246
|
+
current += `
|
|
30247
|
+
\u51B7\u5374\u4E2D: ${cooling.map((e) => `${e.label}\uFF08\u5269 ${Math.round(e.cooldownMs / 6e4)}min\uFF09`).join("\u3001")}`;
|
|
30248
|
+
}
|
|
30249
|
+
if (deps.provider.lastUsedLabel) {
|
|
30250
|
+
current += `
|
|
30251
|
+
\u4E0A\u6B21\u5B9E\u9645: ${deps.provider.lastUsedLabel}`;
|
|
30252
|
+
}
|
|
29941
30253
|
} else {
|
|
29942
30254
|
current = `**${deps.config.provider.id}/${deps.config.model}** (default, auto-route)`;
|
|
29943
30255
|
}
|
|
@@ -30191,6 +30503,16 @@ Use full ref like \`/vision-model ${candidates[0].ref}\``);
|
|
|
30191
30503
|
});
|
|
30192
30504
|
|
|
30193
30505
|
// src/engine-startup.ts
|
|
30506
|
+
function resolveModelMaxTokens(modelRef) {
|
|
30507
|
+
try {
|
|
30508
|
+
const [pid, mid] = (modelRef || "").split("/");
|
|
30509
|
+
const models = liveConfig.get(`models.providers.${pid}.models`);
|
|
30510
|
+
const m = models?.find((x) => x?.id === mid);
|
|
30511
|
+
return typeof m?.maxTokens === "number" && m.maxTokens > 0 ? m.maxTokens : 4096;
|
|
30512
|
+
} catch {
|
|
30513
|
+
return 4096;
|
|
30514
|
+
}
|
|
30515
|
+
}
|
|
30194
30516
|
var _epipeSeen = false;
|
|
30195
30517
|
process.on("uncaughtException", (err) => {
|
|
30196
30518
|
const code = err?.code ?? "";
|
|
@@ -30260,7 +30582,10 @@ async function startEngine(config2, opts) {
|
|
|
30260
30582
|
const licensedFeatures = loadLicense(config2.stateDir, config2.profile?.devMode === true);
|
|
30261
30583
|
const requiredTools = resolveRequiredTools(config2.profile.features, licensedFeatures);
|
|
30262
30584
|
registry.licensedFeatures = licensedFeatures;
|
|
30263
|
-
|
|
30585
|
+
let provider;
|
|
30586
|
+
let visionProvider;
|
|
30587
|
+
let visionChainLabels;
|
|
30588
|
+
({ provider, visionProvider, visionChainLabels } = buildProviderChain(config2));
|
|
30264
30589
|
if (visionChainLabels.length > 0) {
|
|
30265
30590
|
console.log(`[vision] Routing enabled: ${visionChainLabels.join(" \u2192 ")}`);
|
|
30266
30591
|
}
|
|
@@ -30482,11 +30807,36 @@ ${content}`
|
|
|
30482
30807
|
console.log(`[DEBUG] definitions() = ${_allDefs.length} defs`);
|
|
30483
30808
|
console.log(`[DEBUG] active (non-defer) = ${_activeDefs.length}: ${_activeDefs.map((d) => d.function.name).join(", ")}`);
|
|
30484
30809
|
console.log(`[DEBUG] deferred = ${_deferredDefs.length}: ${_deferredDefs.map((d) => d.function.name).join(", ")}`);
|
|
30485
|
-
|
|
30810
|
+
let _stableCache = null;
|
|
30811
|
+
const getSystemStable = () => {
|
|
30812
|
+
const promptCfg = liveConfig.get("prompt") || {};
|
|
30813
|
+
const fileCandidates = /* @__PURE__ */ new Set(["SOUL.md"]);
|
|
30814
|
+
for (const f of promptCfg.staticFiles || []) fileCandidates.add(f);
|
|
30815
|
+
for (const item of promptCfg.order || []) {
|
|
30816
|
+
if (typeof item === "string" && /\.(md|txt|json)$/i.test(item)) fileCandidates.add(item);
|
|
30817
|
+
}
|
|
30818
|
+
const statLines = [JSON.stringify({ mode: promptCfg.mode, order: promptCfg.order })];
|
|
30819
|
+
for (const f of fileCandidates) {
|
|
30820
|
+
try {
|
|
30821
|
+
const p = path49.isAbsolute(f) ? f : path49.join(config2.workspace, f);
|
|
30822
|
+
statLines.push(`${f}:${fs47.statSync(p).mtimeMs}`);
|
|
30823
|
+
} catch {
|
|
30824
|
+
statLines.push(`${f}:missing`);
|
|
30825
|
+
}
|
|
30826
|
+
}
|
|
30827
|
+
const fingerprint = statLines.join("|");
|
|
30828
|
+
if (_stableCache && _stableCache.fingerprint === fingerprint) return _stableCache.prompt;
|
|
30829
|
+
const prompt = buildStablePrompt(config2.workspace, promptCfg);
|
|
30830
|
+
_stableCache = { fingerprint, prompt };
|
|
30831
|
+
console.log(`[prompt] stable \u91CD\u5EFA\uFF08\u6587\u4EF6/config \u53D8\u66F4\uFF09\uFF0C${prompt.length} chars`);
|
|
30832
|
+
return prompt;
|
|
30833
|
+
};
|
|
30834
|
+
const systemStable = getSystemStable();
|
|
30486
30835
|
const systemDynamic = buildDynamicPrompt({
|
|
30487
30836
|
workspace: config2.workspace
|
|
30488
30837
|
});
|
|
30489
30838
|
const systemPrompt = [systemStable, systemDynamic].join("\n\n");
|
|
30839
|
+
const getSystemPrompt = () => [getSystemStable(), buildDynamicPrompt({ workspace: config2.workspace })].join("\n\n");
|
|
30490
30840
|
dumpSystemPrompt(config2.workspace, systemStable, systemDynamic);
|
|
30491
30841
|
const modelDef = config2.provider.models.find((m) => m.id === config2.model);
|
|
30492
30842
|
const modelContextWindow = modelDef?.contextWindow;
|
|
@@ -30505,13 +30855,21 @@ ${content}`
|
|
|
30505
30855
|
// 默认 5MB
|
|
30506
30856
|
);
|
|
30507
30857
|
const memoryFlushEnabled = config2.compaction?.memoryFlush?.enabled !== false;
|
|
30508
|
-
const
|
|
30509
|
-
|
|
30510
|
-
|
|
30511
|
-
|
|
30512
|
-
|
|
30513
|
-
|
|
30858
|
+
const getCompactConfig = () => {
|
|
30859
|
+
const liveComp = liveConfig.get("compaction") || {};
|
|
30860
|
+
const liveModel = (liveConfig.get("providers") || {})[liveConfig.get("agents.defaults.model.primary")?.split("/")[0] || ""];
|
|
30861
|
+
const liveModelId = liveConfig.get("agents.defaults.model.primary")?.split("/")?.[1];
|
|
30862
|
+
const mDef = liveModel?.models?.find((m) => m.id === liveModelId);
|
|
30863
|
+
const mCW = mDef?.contextWindow;
|
|
30864
|
+
const cfg = {
|
|
30865
|
+
...DEFAULT_COMPACT_CONFIG,
|
|
30866
|
+
...liveComp,
|
|
30867
|
+
...mCW && !liveComp.contextWindow ? { contextWindow: mCW } : {},
|
|
30868
|
+
forceFlushTranscriptBytes
|
|
30869
|
+
};
|
|
30870
|
+
return cfg;
|
|
30514
30871
|
};
|
|
30872
|
+
const compactConfig = getCompactConfig();
|
|
30515
30873
|
if (compactConfig.contextWindow !== DEFAULT_COMPACT_CONFIG.contextWindow) {
|
|
30516
30874
|
console.log(`[compact] Context window: ${compactConfig.contextWindow} (from ${config2.compaction?.contextWindow ? "config" : modelContextWindow ? "model" : "default"})`);
|
|
30517
30875
|
}
|
|
@@ -30520,12 +30878,12 @@ ${content}`
|
|
|
30520
30878
|
}
|
|
30521
30879
|
const engine = new QueryEngine(provider, {
|
|
30522
30880
|
model: config2.model,
|
|
30523
|
-
systemPrompt,
|
|
30524
|
-
systemStable,
|
|
30525
|
-
//
|
|
30526
|
-
compactConfig,
|
|
30527
|
-
//
|
|
30528
|
-
maxTokens:
|
|
30881
|
+
systemPrompt: getSystemPrompt,
|
|
30882
|
+
systemStable: getSystemStable,
|
|
30883
|
+
// 0902 函数形态:mtime 缓存 getter,改 prompt 文件热生效
|
|
30884
|
+
compactConfig: getCompactConfig,
|
|
30885
|
+
// 0902 函数形态:每 turn 刷新
|
|
30886
|
+
maxTokens: () => resolveModelMaxTokens(liveConfig.get("agents.defaults.model.primary") || config2.model),
|
|
30529
30887
|
temperature: 0.7,
|
|
30530
30888
|
maxTurns: config2.profile.maxTurns,
|
|
30531
30889
|
// 从配置读,默认 50(query.ts 里 fallback)
|
|
@@ -30536,10 +30894,10 @@ ${content}`
|
|
|
30536
30894
|
if (visionProvider && visionConfig) {
|
|
30537
30895
|
visionEngine = new QueryEngine(visionProvider, {
|
|
30538
30896
|
model: visionConfig.modelId,
|
|
30539
|
-
systemPrompt,
|
|
30540
|
-
systemStable,
|
|
30541
|
-
compactConfig,
|
|
30542
|
-
maxTokens:
|
|
30897
|
+
systemPrompt: getSystemPrompt,
|
|
30898
|
+
systemStable: getSystemStable,
|
|
30899
|
+
compactConfig: getCompactConfig,
|
|
30900
|
+
maxTokens: () => resolveModelMaxTokens(`${visionConfig.providerId}/${visionConfig.modelId}`),
|
|
30543
30901
|
temperature: 0.7,
|
|
30544
30902
|
maxTurns: config2.profile.maxTurns,
|
|
30545
30903
|
agentLabel: "main"
|
|
@@ -30611,7 +30969,8 @@ ${content}`
|
|
|
30611
30969
|
providerApi: config2.provider.api,
|
|
30612
30970
|
model: config2.model,
|
|
30613
30971
|
modelInputs: modelDef?.input || ["text"],
|
|
30614
|
-
systemPrompt,
|
|
30972
|
+
systemPrompt: getSystemPrompt,
|
|
30973
|
+
// 0902 函数形态:/btw、voice-chat 等按调用时现取(stable mtime 缓存 + dynamic 现算)
|
|
30615
30974
|
channels: config2.channels,
|
|
30616
30975
|
config: config2,
|
|
30617
30976
|
// tool 读自己配置用
|
|
@@ -30625,36 +30984,28 @@ ${content}`
|
|
|
30625
30984
|
} : void 0,
|
|
30626
30985
|
mcpManager
|
|
30627
30986
|
};
|
|
30628
|
-
|
|
30987
|
+
const visionMetaInit = visionEngine && visionConfig ? (() => {
|
|
30629
30988
|
const vpCfg = config2.providers?.[visionConfig.providerId];
|
|
30630
30989
|
const visionModelDef = vpCfg?.models?.find((m) => m.id === visionConfig.modelId);
|
|
30631
|
-
|
|
30632
|
-
engine: visionEngine,
|
|
30633
|
-
sessions,
|
|
30634
|
-
channelManager,
|
|
30635
|
-
workspace: config2.workspace,
|
|
30990
|
+
return {
|
|
30636
30991
|
providerId: visionConfig.providerId,
|
|
30637
30992
|
providerApi: vpCfg?.api || "openai-completions",
|
|
30638
30993
|
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
|
|
30994
|
+
modelInputs: visionModelDef?.input || ["text", "image"]
|
|
30652
30995
|
};
|
|
30996
|
+
})() : null;
|
|
30997
|
+
let visionMeta = visionMetaInit;
|
|
30998
|
+
function resolveVisionDeps() {
|
|
30999
|
+
if (!visionEngine || !visionMeta) return null;
|
|
31000
|
+
if (!visionDeps) {
|
|
31001
|
+
visionDeps = { ...deps, engine: visionEngine, ...visionMeta };
|
|
31002
|
+
console.log(`[vision] deps built: ${visionMeta.providerId}/${visionMeta.model}`);
|
|
31003
|
+
}
|
|
31004
|
+
return visionDeps;
|
|
30653
31005
|
}
|
|
30654
31006
|
let modelOverride = null;
|
|
30655
31007
|
let modelOverrideEngine = null;
|
|
30656
31008
|
let visionOverride = null;
|
|
30657
|
-
const defaultVisionDeps = visionDeps;
|
|
30658
31009
|
const modelDepsCache = /* @__PURE__ */ new Map();
|
|
30659
31010
|
deps.invalidateDeskBuddyDeps = () => {
|
|
30660
31011
|
deskBuddyDeps = null;
|
|
@@ -30680,6 +31031,7 @@ ${content}`
|
|
|
30680
31031
|
if (!p.provider) {
|
|
30681
31032
|
visionEngine = null;
|
|
30682
31033
|
visionDeps = null;
|
|
31034
|
+
visionMeta = null;
|
|
30683
31035
|
return;
|
|
30684
31036
|
}
|
|
30685
31037
|
if (visionEngine) {
|
|
@@ -30688,39 +31040,22 @@ ${content}`
|
|
|
30688
31040
|
} else {
|
|
30689
31041
|
visionEngine = new QueryEngine(p.provider, {
|
|
30690
31042
|
model: p.model,
|
|
30691
|
-
systemPrompt,
|
|
30692
|
-
systemStable,
|
|
30693
|
-
compactConfig,
|
|
31043
|
+
systemPrompt: getSystemPrompt,
|
|
31044
|
+
systemStable: getSystemStable,
|
|
31045
|
+
compactConfig: getCompactConfig,
|
|
30694
31046
|
maxTokens: 4096,
|
|
30695
31047
|
temperature: 0.7,
|
|
30696
31048
|
maxTurns: config2.profile.maxTurns,
|
|
30697
31049
|
agentLabel: "main"
|
|
30698
31050
|
});
|
|
30699
31051
|
}
|
|
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
|
-
}
|
|
31052
|
+
visionMeta = {
|
|
31053
|
+
providerId: p.providerId,
|
|
31054
|
+
providerApi: p.providerApi,
|
|
31055
|
+
model: p.model,
|
|
31056
|
+
modelInputs: p.modelInputs
|
|
31057
|
+
};
|
|
31058
|
+
visionDeps = null;
|
|
30724
31059
|
};
|
|
30725
31060
|
function createModelDeps(ref) {
|
|
30726
31061
|
const slashIdx = ref.indexOf("/");
|
|
@@ -30740,28 +31075,21 @@ ${content}`
|
|
|
30740
31075
|
const llmProvider = providerId === config2.provider.id ? provider : createProvider(providerCfg);
|
|
30741
31076
|
const engine2 = new QueryEngine(llmProvider, {
|
|
30742
31077
|
model: modelId,
|
|
30743
|
-
systemPrompt,
|
|
30744
|
-
systemStable,
|
|
30745
|
-
compactConfig,
|
|
30746
|
-
maxTokens:
|
|
31078
|
+
systemPrompt: getSystemPrompt,
|
|
31079
|
+
systemStable: getSystemStable,
|
|
31080
|
+
compactConfig: getCompactConfig,
|
|
31081
|
+
maxTokens: () => resolveModelMaxTokens(`${providerId}/${modelId}`),
|
|
30747
31082
|
temperature: 0.7,
|
|
30748
31083
|
maxTurns: config2.profile.maxTurns,
|
|
30749
31084
|
agentLabel: `main:${ref}`
|
|
30750
31085
|
});
|
|
30751
31086
|
return {
|
|
31087
|
+
...deps,
|
|
30752
31088
|
engine: engine2,
|
|
30753
|
-
sessions,
|
|
30754
|
-
channelManager,
|
|
30755
|
-
workspace: config2.workspace,
|
|
30756
31089
|
providerId,
|
|
30757
31090
|
providerApi: providerCfg.api,
|
|
30758
31091
|
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
|
|
31092
|
+
modelInputs: modelDef2.input || ["text"]
|
|
30765
31093
|
};
|
|
30766
31094
|
}
|
|
30767
31095
|
const dispatcher = new MessageDispatcher();
|
|
@@ -30938,7 +31266,17 @@ ${content}`
|
|
|
30938
31266
|
console.log(`[cron] config check: enabled=${config2.cron?.enabled}, hasConfig=${!!config2.cron}`);
|
|
30939
31267
|
if (config2.cron?.enabled) {
|
|
30940
31268
|
const { CronPlugin: CronPlugin2 } = await Promise.resolve().then(() => (init_cron_plugin(), cron_plugin_exports));
|
|
30941
|
-
const
|
|
31269
|
+
const resolveCronModelDeps = (ref) => {
|
|
31270
|
+
const key = `cron:${ref}`;
|
|
31271
|
+
const cached = modelDepsCache.get(key);
|
|
31272
|
+
if (cached) return cached;
|
|
31273
|
+
const built = createModelDeps(ref);
|
|
31274
|
+
if (!built) return null;
|
|
31275
|
+
modelDepsCache.set(key, built);
|
|
31276
|
+
console.log(`[cron] Model deps built: ${ref}`);
|
|
31277
|
+
return built;
|
|
31278
|
+
};
|
|
31279
|
+
const cronPlugin = new CronPlugin2(config2.cron, sessions, channelManager, deps, config2.stateDir, dispatcher, resolveCronModelDeps);
|
|
30942
31280
|
await cronPlugin.start();
|
|
30943
31281
|
const { setCronConfig: setCronConfig2 } = await Promise.resolve().then(() => (init_tools(), tools_exports));
|
|
30944
31282
|
setCronConfig2(config2.cron);
|
|
@@ -31114,8 +31452,10 @@ ${notifications}
|
|
|
31114
31452
|
dispatcher,
|
|
31115
31453
|
runningQueries,
|
|
31116
31454
|
engine,
|
|
31117
|
-
systemPrompt,
|
|
31118
|
-
|
|
31455
|
+
systemPrompt: getSystemPrompt,
|
|
31456
|
+
// 0902 函数形态:命令按调用时现取
|
|
31457
|
+
compactConfig: getCompactConfig,
|
|
31458
|
+
// 0902 同上
|
|
31119
31459
|
provider,
|
|
31120
31460
|
deps,
|
|
31121
31461
|
getVisualRegistry: () => visualRegistry,
|
|
@@ -31137,9 +31477,17 @@ ${notifications}
|
|
|
31137
31477
|
setVisionDeps: (v) => {
|
|
31138
31478
|
visionDeps = v;
|
|
31139
31479
|
},
|
|
31140
|
-
|
|
31480
|
+
// reset 用:清 override deps,下一条图片消息由 resolveVisionDeps 按当前 meta 重建
|
|
31481
|
+
getDefaultVisionDeps: () => {
|
|
31482
|
+
visionDeps = null;
|
|
31483
|
+
return null;
|
|
31484
|
+
},
|
|
31141
31485
|
doReloadConfig
|
|
31142
31486
|
};
|
|
31487
|
+
deps.onProviderSwapped = (p) => {
|
|
31488
|
+
provider = p;
|
|
31489
|
+
commandDeps.provider = p;
|
|
31490
|
+
};
|
|
31143
31491
|
const slashCommands = listCommandDefs().map((d) => ({
|
|
31144
31492
|
name: d.name,
|
|
31145
31493
|
description: d.description,
|
|
@@ -31396,7 +31744,7 @@ ${pathStr}` }];
|
|
|
31396
31744
|
for (const att of nonImageAttachments) {
|
|
31397
31745
|
console.log(`[file] Downloading: ${att.filename} (${att.contentType}, ${att.size}B)`);
|
|
31398
31746
|
try {
|
|
31399
|
-
const resp = attachDispatcher ? await fetch(att.url, { dispatcher: attachDispatcher }) : await fetch(att.url);
|
|
31747
|
+
const resp = attachDispatcher ? await fetch(att.url, { dispatcher: attachDispatcher, signal: AbortSignal.timeout(3e4) }) : await fetch(att.url, { signal: AbortSignal.timeout(3e4) });
|
|
31400
31748
|
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
|
31401
31749
|
const buffer = Buffer.from(await resp.arrayBuffer());
|
|
31402
31750
|
const safeName2 = path49.basename(att.filename).replace(/[<>:"/\\|?*\x00-\x1f]/g, "_") || "attachment";
|
|
@@ -31426,7 +31774,8 @@ ${pathStr}` }];
|
|
|
31426
31774
|
}
|
|
31427
31775
|
const isImageBlock = (b) => b.type === "image" || b.type === "image_url";
|
|
31428
31776
|
const hasImages = Array.isArray(queryContent) && queryContent.some((b) => isImageBlock(b));
|
|
31429
|
-
|
|
31777
|
+
const activeVisionDeps = hasImages ? resolveVisionDeps() : null;
|
|
31778
|
+
let msgDeps = activeVisionDeps ? activeVisionDeps : modelOverride ? modelDepsCache.get(modelOverride) ?? deps : deps;
|
|
31430
31779
|
if (isDeskBuddy && !hasImages && !modelOverride) {
|
|
31431
31780
|
if (!deskBuddyDeps) {
|
|
31432
31781
|
const dbRef = liveConfig.get("channels.deskBuddy.model") || "deepinfra/deepseek-ai/DeepSeek-V4-Flash-0731";
|
|
@@ -31454,7 +31803,8 @@ ${pathStr}` }];
|
|
|
31454
31803
|
model: dbProviderCfg ? dbModelId : config2.model,
|
|
31455
31804
|
// 8/18 翀哥:完整工程 prompt(14k tok)+memory(10k) 会把小模型带偏成"工程助手"——deskBuddy 用 SOUL 精简人设
|
|
31456
31805
|
// 8/21 复用 light-mode.buildLightStablePrompt(和情感模式同一构建器)
|
|
31457
|
-
systemPrompt: buildLightStablePrompt(config2.workspace, "deskBuddy"),
|
|
31806
|
+
systemPrompt: () => buildLightStablePrompt(config2.workspace, "deskBuddy"),
|
|
31807
|
+
// 0902 函数形态:改 SOUL 热生效
|
|
31458
31808
|
maxTokens: 1024,
|
|
31459
31809
|
temperature: 0.7,
|
|
31460
31810
|
disableThinking: true,
|
|
@@ -31468,7 +31818,7 @@ ${pathStr}` }];
|
|
|
31468
31818
|
msgDeps = deskBuddyDeps;
|
|
31469
31819
|
}
|
|
31470
31820
|
if (hasImages) {
|
|
31471
|
-
console.log(`[vision-debug] hasImages=true visionDeps=${
|
|
31821
|
+
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
31822
|
if (Array.isArray(queryContent)) {
|
|
31473
31823
|
queryContent.forEach((b, i) => {
|
|
31474
31824
|
if (b.type === "image" && b.source?.data) {
|
|
@@ -31477,13 +31827,13 @@ ${pathStr}` }];
|
|
|
31477
31827
|
});
|
|
31478
31828
|
}
|
|
31479
31829
|
}
|
|
31480
|
-
if (hasImages &&
|
|
31481
|
-
console.log(`[vision] Routing to ${
|
|
31830
|
+
if (hasImages && activeVisionDeps) {
|
|
31831
|
+
console.log(`[vision] Routing to ${activeVisionDeps.providerId}/${activeVisionDeps.model}${activeVisionDeps.model !== visionConfig?.modelId ? ` (override, config=${visionConfig.providerId}/${visionConfig.modelId})` : ""}`);
|
|
31482
31832
|
}
|
|
31483
31833
|
const preQueryResult = await messageHooks.runPreQuery({
|
|
31484
31834
|
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
31835
|
text: queryContent,
|
|
31486
|
-
msgDeps:
|
|
31836
|
+
msgDeps: activeVisionDeps ?? msgDeps,
|
|
31487
31837
|
deps: { provider, channelManager, sessions, dispatcher, config: config2, workspace: config2.workspace }
|
|
31488
31838
|
});
|
|
31489
31839
|
if (preQueryResult.skip) {
|
|
@@ -31496,6 +31846,7 @@ ${pathStr}` }];
|
|
|
31496
31846
|
}
|
|
31497
31847
|
queryContent = preQueryResult.text ?? queryContent;
|
|
31498
31848
|
if (preQueryResult.msgDeps) msgDeps = preQueryResult.msgDeps;
|
|
31849
|
+
msgDeps.renderer = renderer;
|
|
31499
31850
|
const accepted = dispatcher.submitMessage({
|
|
31500
31851
|
text: queryContent,
|
|
31501
31852
|
sessionId,
|
|
@@ -31730,8 +32081,9 @@ ${pathStr}` }];
|
|
|
31730
32081
|
sessionManager: sessions,
|
|
31731
32082
|
sessionId,
|
|
31732
32083
|
model: config2.model,
|
|
31733
|
-
contextWindow:
|
|
31734
|
-
systemPrompt,
|
|
32084
|
+
contextWindow: getCompactConfig().contextWindow || 2e5,
|
|
32085
|
+
systemPrompt: getSystemPrompt(),
|
|
32086
|
+
// 0902 现取(API /context 报告跟当前 prompt 一致)
|
|
31735
32087
|
toolDefs: registry.definitions(),
|
|
31736
32088
|
workspace: config2.workspace
|
|
31737
32089
|
});
|
|
@@ -32059,6 +32411,24 @@ async function doReloadConfig(config2, deps, provider) {
|
|
|
32059
32411
|
visionModel: newConfig.visionModel,
|
|
32060
32412
|
visionFallbacks: newConfig.visionFallbacks
|
|
32061
32413
|
});
|
|
32414
|
+
const newRecall = createMemorySideProvider(
|
|
32415
|
+
newConfig.topics?.recall,
|
|
32416
|
+
provider,
|
|
32417
|
+
newConfig.providers || {}
|
|
32418
|
+
);
|
|
32419
|
+
const newExtract = createMemorySideProvider(
|
|
32420
|
+
newConfig.topics?.extract,
|
|
32421
|
+
provider,
|
|
32422
|
+
newConfig.providers || {}
|
|
32423
|
+
);
|
|
32424
|
+
if (newRecall) {
|
|
32425
|
+
deps.recallProvider = newRecall;
|
|
32426
|
+
changes.push(`recall \u2192 ${newConfig.topics?.recall?.provider}/${newConfig.topics?.recall?.model}`);
|
|
32427
|
+
}
|
|
32428
|
+
if (newExtract) {
|
|
32429
|
+
deps.extractProvider = newExtract;
|
|
32430
|
+
changes.push(`extract \u2192 ${newConfig.topics?.extract?.provider}/${newConfig.topics?.extract?.model}`);
|
|
32431
|
+
}
|
|
32062
32432
|
if (oldProviderKey !== newProviderKey) {
|
|
32063
32433
|
console.log("[reload] Provider structure changed, rebuilding chain...");
|
|
32064
32434
|
const rebuilt = buildProviderChain(newConfig);
|
|
@@ -32069,6 +32439,7 @@ async function doReloadConfig(config2, deps, provider) {
|
|
|
32069
32439
|
}
|
|
32070
32440
|
changes.push(`provider chain rebuilt (${rebuilt.visionChainLabels.length > 0 ? "vision: " + rebuilt.visionChainLabels.join("\u2192") : "no vision"})`);
|
|
32071
32441
|
}
|
|
32442
|
+
if (deps.onProviderSwapped) deps.onProviderSwapped(rebuilt.provider);
|
|
32072
32443
|
if (typeof deps.setVisionProvider === "function") {
|
|
32073
32444
|
const vCfg = newConfig.visionModel;
|
|
32074
32445
|
const vpCfg = vCfg ? newConfig.providers?.[vCfg.providerId] : void 0;
|
|
@@ -32099,24 +32470,6 @@ async function doReloadConfig(config2, deps, provider) {
|
|
|
32099
32470
|
changes.push(`deskBuddy model \u2192 ${newDbModel}`);
|
|
32100
32471
|
}
|
|
32101
32472
|
}
|
|
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
32473
|
try {
|
|
32121
32474
|
const { setAutoDreamConfig: setAutoDreamConfig2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
32122
32475
|
setAutoDreamConfig2(newConfig);
|
|
@@ -32312,11 +32665,11 @@ function startSecretsWatcher(config2, deps, provider) {
|
|
|
32312
32665
|
}
|
|
32313
32666
|
|
|
32314
32667
|
// src/main.ts
|
|
32315
|
-
import { readFileSync as
|
|
32668
|
+
import { readFileSync as readFileSync32 } from "node:fs";
|
|
32316
32669
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
32317
|
-
import { dirname as dirname9, join as
|
|
32670
|
+
import { dirname as dirname9, join as join45 } from "node:path";
|
|
32318
32671
|
var __dirname2 = dirname9(fileURLToPath3(import.meta.url));
|
|
32319
|
-
var pkg = JSON.parse(
|
|
32672
|
+
var pkg = JSON.parse(readFileSync32(join45(__dirname2, "..", "package.json"), "utf-8"));
|
|
32320
32673
|
var epipeSeen = false;
|
|
32321
32674
|
process.on("uncaughtException", (err) => {
|
|
32322
32675
|
const code = err?.code ?? "";
|