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/engine-startup.mjs
CHANGED
|
@@ -1736,8 +1736,8 @@ var init_microCompact = __esm({
|
|
|
1736
1736
|
});
|
|
1737
1737
|
|
|
1738
1738
|
// src/compact/ruleCompact.ts
|
|
1739
|
-
function smartCompressToolResult(content, essentialFields) {
|
|
1740
|
-
if (content.length <= TOOL_SIZE_LIMIT) return content;
|
|
1739
|
+
function smartCompressToolResult(content, essentialFields, sizeLimit) {
|
|
1740
|
+
if (content.length <= (sizeLimit ?? TOOL_SIZE_LIMIT)) return content;
|
|
1741
1741
|
try {
|
|
1742
1742
|
const data = JSON.parse(content);
|
|
1743
1743
|
if (Array.isArray(data) && data.length > 0) {
|
|
@@ -1776,36 +1776,37 @@ ${result}`;
|
|
|
1776
1776
|
}
|
|
1777
1777
|
} catch {
|
|
1778
1778
|
}
|
|
1779
|
-
return content.slice(0, TRUNCATE_TO) + "\n...[TRUNCATED]";
|
|
1779
|
+
return content.slice(0, sizeLimit ?? TRUNCATE_TO) + "\n...[TRUNCATED]";
|
|
1780
1780
|
}
|
|
1781
|
-
function
|
|
1782
|
-
|
|
1783
|
-
if (
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1781
|
+
function foldTurnInline(turn) {
|
|
1782
|
+
const toolMsgs = turn.filter((m) => m.role === "tool");
|
|
1783
|
+
if (toolMsgs.length === 0) return turn;
|
|
1784
|
+
let i = 0;
|
|
1785
|
+
while (i < turn.length && turn[i].role !== "assistant" && turn[i].role !== "tool") i++;
|
|
1786
|
+
const head = turn.slice(0, i);
|
|
1787
|
+
const rest = turn.slice(i);
|
|
1788
|
+
let finalAnswer = null;
|
|
1789
|
+
for (let j = rest.length - 1; j >= 0; j--) {
|
|
1790
|
+
const m = rest[j];
|
|
1791
|
+
if (m.role === "assistant" && !(m.tool_calls && m.tool_calls.length > 0)) {
|
|
1792
|
+
const text = typeof m.content === "string" ? m.content.trim() : "";
|
|
1793
|
+
if (text) {
|
|
1794
|
+
finalAnswer = m;
|
|
1795
|
+
break;
|
|
1796
|
+
}
|
|
1791
1797
|
}
|
|
1792
1798
|
}
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
const contentStr = typeof rawContent === "string" ? rawContent : "";
|
|
1802
|
-
const compressed = smartCompressToolResult(contentStr);
|
|
1803
|
-
parts.push(`[${i + 1}] ${compressed}`);
|
|
1799
|
+
const parts = toolMsgs.map((m, k) => `[${k + 1}] ${smartCompressToolResult(typeof m.content === "string" ? m.content : "")}`);
|
|
1800
|
+
const merged = parts.join("\n---\n");
|
|
1801
|
+
if (finalAnswer) {
|
|
1802
|
+
return [...head, { role: "assistant", content: `\uFF08\u8BE5\u8F6E\u5386\u53F2\u5DE5\u5177\u8F93\u51FA\uFF0C\u5171 ${toolMsgs.length} \u6761\uFF0C\u5DF2\u538B\u7F29\uFF09
|
|
1803
|
+
${merged}
|
|
1804
|
+
|
|
1805
|
+
\uFF08\u8BE5\u8F6E\u6700\u7EC8\u7ED3\u8BBA\uFF09
|
|
1806
|
+
${finalAnswer.content || ""}` }];
|
|
1804
1807
|
}
|
|
1805
|
-
return {
|
|
1806
|
-
|
|
1807
|
-
count: toolMsgs.length
|
|
1808
|
-
};
|
|
1808
|
+
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
|
|
1809
|
+
${merged}` }];
|
|
1809
1810
|
}
|
|
1810
1811
|
function splitByUserTurns(messages) {
|
|
1811
1812
|
const system = [];
|
|
@@ -1872,32 +1873,9 @@ function step2_compressOldTurns(messages, maxTokens, toolResultMode = "minimal")
|
|
|
1872
1873
|
const origTokens = estimateMessageTokens(turn);
|
|
1873
1874
|
newMessages.push(userMsg);
|
|
1874
1875
|
if (toolResultMode === "inline") {
|
|
1875
|
-
const
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
if (mergedTools) {
|
|
1879
|
-
newMessages.push(msg.assistant(
|
|
1880
|
-
`\uFF08\u8BE5\u8F6E\u5386\u53F2\u5DE5\u5177\u8F93\u51FA\uFF0C\u5171 ${mergedTools.count} \u6761\uFF0C\u5DF2\u538B\u7F29\uFF09
|
|
1881
|
-
${mergedTools.content}
|
|
1882
|
-
|
|
1883
|
-
\uFF08\u8BE5\u8F6E\u6700\u7EC8\u7ED3\u8BBA\uFF09
|
|
1884
|
-
${finalAnswer.content || ""}`
|
|
1885
|
-
));
|
|
1886
|
-
compactLog(TAG3, `Step 2 turn[${turnIdx}]: ${origMsgCount} msgs (${origTokens} tok) \u2192 2 msgs (user+inline-merged, ${mergedTools.count} tool results, mode=inline)`);
|
|
1887
|
-
} else {
|
|
1888
|
-
newMessages.push(finalAnswer);
|
|
1889
|
-
compactLog(TAG3, `Step 2 turn[${turnIdx}]: ${origMsgCount} msgs (${origTokens} tok) \u2192 2 msgs (user+finalAnswer, no tools)`);
|
|
1890
|
-
}
|
|
1891
|
-
} else {
|
|
1892
|
-
const mergedTools = mergeToolResults(rest);
|
|
1893
|
-
if (mergedTools) {
|
|
1894
|
-
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
|
|
1895
|
-
${mergedTools.content}`));
|
|
1896
|
-
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)`);
|
|
1897
|
-
} else {
|
|
1898
|
-
compactLog(TAG3, `Step 2 turn[${turnIdx}]: ${origMsgCount} msgs (${origTokens} tok) \u2192 1 msg (user only, no tools/final)`);
|
|
1899
|
-
}
|
|
1900
|
-
}
|
|
1876
|
+
const folded = foldTurnInline(turn);
|
|
1877
|
+
newMessages.push(...folded.slice(1));
|
|
1878
|
+
compactLog(TAG3, `Step 2 turn[${turnIdx}]: ${origMsgCount} msgs (${origTokens} tok) \u2192 ${1 + folded.length - 1} msgs (inline folded, mode=inline)`);
|
|
1901
1879
|
} else {
|
|
1902
1880
|
const kept = rest.filter((m) => m.role !== "tool").map((m) => {
|
|
1903
1881
|
if (m.role === "assistant" && m.tool_calls && m.tool_calls.length > 0) {
|
|
@@ -2015,7 +1993,6 @@ var TAG3, TOOL_SIZE_LIMIT, TRUNCATE_TO, MIN_HISTORY, KEEP_RECENT_TURNS, MAX_LIST
|
|
|
2015
1993
|
var init_ruleCompact = __esm({
|
|
2016
1994
|
"src/compact/ruleCompact.ts"() {
|
|
2017
1995
|
"use strict";
|
|
2018
|
-
init_types();
|
|
2019
1996
|
init_tokenEstimate();
|
|
2020
1997
|
init_autoCompact();
|
|
2021
1998
|
init_compactLog();
|
|
@@ -3013,7 +2990,7 @@ var init_query = __esm({
|
|
|
3013
2990
|
this.provider = provider;
|
|
3014
2991
|
this.options = options;
|
|
3015
2992
|
if (options.compactConfig) {
|
|
3016
|
-
this.compactConfig = { ...DEFAULT_COMPACT_CONFIG, ...options.compactConfig };
|
|
2993
|
+
this.compactConfig = { ...DEFAULT_COMPACT_CONFIG, ...this.resolveOpt(options.compactConfig) };
|
|
3017
2994
|
}
|
|
3018
2995
|
this.logPrefix = `[query:${options.agentLabel || "main"}]`;
|
|
3019
2996
|
}
|
|
@@ -3106,6 +3083,10 @@ var init_query = __esm({
|
|
|
3106
3083
|
}
|
|
3107
3084
|
/** 日志前缀(区分多个并行 QueryEngine) */
|
|
3108
3085
|
logPrefix;
|
|
3086
|
+
/** 函数形态 option 的统一解析(0902:systemPrompt/systemStable/compactConfig/maxTokens 共用) */
|
|
3087
|
+
resolveOpt(v) {
|
|
3088
|
+
return typeof v === "function" ? v() : v;
|
|
3089
|
+
}
|
|
3109
3090
|
/** 热加载:替换 provider 实例(doReloadConfig 重建 provider 链后调用) */
|
|
3110
3091
|
setProvider(provider) {
|
|
3111
3092
|
this.provider = provider;
|
|
@@ -3133,7 +3114,12 @@ var init_query = __esm({
|
|
|
3133
3114
|
if (signal) {
|
|
3134
3115
|
signal.addEventListener("abort", () => ac.abort(), { once: true });
|
|
3135
3116
|
}
|
|
3136
|
-
|
|
3117
|
+
if (typeof this.options.compactConfig === "function") {
|
|
3118
|
+
const overhead = this.compactConfig.systemOverheadTokens;
|
|
3119
|
+
this.compactConfig = { ...DEFAULT_COMPACT_CONFIG, ...this.resolveOpt(this.options.compactConfig) };
|
|
3120
|
+
this.compactConfig.systemOverheadTokens = overhead ?? this.compactConfig.systemOverheadTokens;
|
|
3121
|
+
}
|
|
3122
|
+
const activeSystemStable = perTurnSystemStable || this.resolveOpt(this.options.systemStable) || this.resolveOpt(this.options.systemPrompt);
|
|
3137
3123
|
let currentMessages = [...messages];
|
|
3138
3124
|
console.log(`${this.logPrefix} query() called with ${messages.length} messages`);
|
|
3139
3125
|
const useDynamicTools = !this.options.toolOverride;
|
|
@@ -3149,7 +3135,7 @@ var init_query = __esm({
|
|
|
3149
3135
|
if (this.systemOverheadTokens === null) {
|
|
3150
3136
|
const { estimateToolDefinitionTokens: estimateToolDefinitionTokens2 } = await Promise.resolve().then(() => (init_context_analyzer(), context_analyzer_exports));
|
|
3151
3137
|
const { roughTokenCountEstimation: roughTokenCountEstimation6 } = await Promise.resolve().then(() => (init_tokenEstimate(), tokenEstimate_exports));
|
|
3152
|
-
const systemText = this.options.systemStable || this.options.systemPrompt || "";
|
|
3138
|
+
const systemText = this.resolveOpt(this.options.systemStable) || this.resolveOpt(this.options.systemPrompt) || "";
|
|
3153
3139
|
const systemTokens = roughTokenCountEstimation6(systemText);
|
|
3154
3140
|
const initialToolDefs = useDynamicTools ? this.buildActiveTools(allToolDefs) : allToolDefs;
|
|
3155
3141
|
const toolTokens = estimateToolDefinitionTokens2(initialToolDefs);
|
|
@@ -3301,7 +3287,7 @@ ${perTurnSystemDynamic}` : deferredHint || perTurnSystemDynamic;
|
|
|
3301
3287
|
systemStable: activeSystemStable,
|
|
3302
3288
|
systemDynamic: fullSystemDynamic,
|
|
3303
3289
|
tools: toolDefs.length > 0 ? toolDefs : void 0,
|
|
3304
|
-
maxTokens: this.options.maxTokens,
|
|
3290
|
+
maxTokens: typeof this.options.maxTokens === "function" ? this.options.maxTokens() : this.options.maxTokens,
|
|
3305
3291
|
temperature: this.options.temperature,
|
|
3306
3292
|
signal: ac.signal,
|
|
3307
3293
|
disableThinking: this.options.disableThinking || !!perTurnEmotion
|
|
@@ -3356,7 +3342,8 @@ ${perTurnSystemDynamic}` : deferredHint || perTurnSystemDynamic;
|
|
|
3356
3342
|
}
|
|
3357
3343
|
if (toolCalls.length === 0) {
|
|
3358
3344
|
if (!textContent.trim()) {
|
|
3359
|
-
|
|
3345
|
+
const thinkingAteBudget = turnThinking.length > 0;
|
|
3346
|
+
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}`);
|
|
3360
3347
|
turnThinking = "";
|
|
3361
3348
|
turnThinkingSig = "";
|
|
3362
3349
|
for await (const chunk of this.provider.streamChat({
|
|
@@ -3365,9 +3352,10 @@ ${perTurnSystemDynamic}` : deferredHint || perTurnSystemDynamic;
|
|
|
3365
3352
|
systemStable: activeSystemStable,
|
|
3366
3353
|
systemDynamic: fullSystemDynamic,
|
|
3367
3354
|
tools: toolDefs.length > 0 ? toolDefs : void 0,
|
|
3368
|
-
maxTokens: this.options.maxTokens,
|
|
3355
|
+
maxTokens: typeof this.options.maxTokens === "function" ? this.options.maxTokens() : this.options.maxTokens,
|
|
3369
3356
|
temperature: this.options.temperature,
|
|
3370
|
-
signal: ac.signal
|
|
3357
|
+
signal: ac.signal,
|
|
3358
|
+
...thinkingAteBudget ? { disableThinking: true } : {}
|
|
3371
3359
|
})) {
|
|
3372
3360
|
if (chunk.type === "status") yield chunk;
|
|
3373
3361
|
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",
|
|
@@ -11854,14 +11970,16 @@ var init_cron_plugin = __esm({
|
|
|
11854
11970
|
"use strict";
|
|
11855
11971
|
init_tasks2();
|
|
11856
11972
|
init_scheduler();
|
|
11973
|
+
init_live();
|
|
11857
11974
|
CronPlugin = class {
|
|
11858
|
-
constructor(config, sessions, channelManager, deps, stateDir, dispatcher) {
|
|
11975
|
+
constructor(config, sessions, channelManager, deps, stateDir, dispatcher, resolveModelDeps) {
|
|
11859
11976
|
this.config = config;
|
|
11860
11977
|
this.sessions = sessions;
|
|
11861
11978
|
this.channelManager = channelManager;
|
|
11862
11979
|
this.deps = deps;
|
|
11863
11980
|
this.stateDir = stateDir;
|
|
11864
11981
|
this.dispatcher = dispatcher;
|
|
11982
|
+
this.resolveModelDeps = resolveModelDeps;
|
|
11865
11983
|
}
|
|
11866
11984
|
config;
|
|
11867
11985
|
sessions;
|
|
@@ -11869,6 +11987,7 @@ var init_cron_plugin = __esm({
|
|
|
11869
11987
|
deps;
|
|
11870
11988
|
stateDir;
|
|
11871
11989
|
dispatcher;
|
|
11990
|
+
resolveModelDeps;
|
|
11872
11991
|
static shouldEnable(config) {
|
|
11873
11992
|
return config.enabled === true;
|
|
11874
11993
|
}
|
|
@@ -11901,10 +12020,12 @@ var init_cron_plugin = __esm({
|
|
|
11901
12020
|
channelManager: this.channelManager,
|
|
11902
12021
|
deps: this.deps,
|
|
11903
12022
|
config: this.config,
|
|
11904
|
-
dispatcher: this.dispatcher
|
|
12023
|
+
dispatcher: this.dispatcher,
|
|
12024
|
+
resolveModelDeps: this.resolveModelDeps
|
|
11905
12025
|
};
|
|
11906
12026
|
startScheduler(schedulerDeps);
|
|
11907
|
-
|
|
12027
|
+
const cronModel = liveConfig.get("cron.model");
|
|
12028
|
+
console.log(`[cron] Plugin started (${tasks2.filter((t) => t.status === "active").length} active tasks${missedCount ? `, ${missedCount} missed` : ""}${cronModel ? `, model=${cronModel}` : ""})`);
|
|
11908
12029
|
}
|
|
11909
12030
|
async stop() {
|
|
11910
12031
|
await stopScheduler();
|
|
@@ -11918,8 +12039,8 @@ var reply_blocklist_exports = {};
|
|
|
11918
12039
|
__export(reply_blocklist_exports, {
|
|
11919
12040
|
isUserBlocked: () => isUserBlocked
|
|
11920
12041
|
});
|
|
11921
|
-
import { readFileSync as
|
|
11922
|
-
import { join as
|
|
12042
|
+
import { readFileSync as readFileSync30, writeFileSync as writeFileSync19, existsSync as existsSync27 } from "node:fs";
|
|
12043
|
+
import { join as join43 } from "node:path";
|
|
11923
12044
|
function ensureLoaded(workspace, configIds) {
|
|
11924
12045
|
if (loaded) return;
|
|
11925
12046
|
if (configIds?.length) {
|
|
@@ -11927,10 +12048,10 @@ function ensureLoaded(workspace, configIds) {
|
|
|
11927
12048
|
if (!state.blockedUserIds.includes(id)) state.blockedUserIds.push(id);
|
|
11928
12049
|
}
|
|
11929
12050
|
}
|
|
11930
|
-
const path50 =
|
|
12051
|
+
const path50 = join43(workspace, ".reply-blocklist.json");
|
|
11931
12052
|
try {
|
|
11932
|
-
if (
|
|
11933
|
-
const raw =
|
|
12053
|
+
if (existsSync27(path50)) {
|
|
12054
|
+
const raw = readFileSync30(path50, "utf-8");
|
|
11934
12055
|
const parsed = JSON.parse(raw);
|
|
11935
12056
|
if (parsed.blockedUserIds) {
|
|
11936
12057
|
for (const id of parsed.blockedUserIds) {
|
|
@@ -11946,7 +12067,7 @@ function ensureLoaded(workspace, configIds) {
|
|
|
11946
12067
|
loaded = true;
|
|
11947
12068
|
}
|
|
11948
12069
|
function save(workspace) {
|
|
11949
|
-
const path50 =
|
|
12070
|
+
const path50 = join43(workspace, ".reply-blocklist.json");
|
|
11950
12071
|
try {
|
|
11951
12072
|
writeFileSync19(path50, JSON.stringify(state, null, 2), "utf-8");
|
|
11952
12073
|
} catch (err) {
|
|
@@ -12826,30 +12947,21 @@ function loadConfig(configPath) {
|
|
|
12826
12947
|
devMode: agentDefaults.devMode === true
|
|
12827
12948
|
};
|
|
12828
12949
|
const rawSession = raw.session || {};
|
|
12829
|
-
const sessionScope = rawSession.
|
|
12950
|
+
const sessionScope = Object.keys(rawSession).length > 0 ? { ...rawSession } : void 0;
|
|
12830
12951
|
const rawHeartbeat = raw.heartbeat || {};
|
|
12831
12952
|
const heartbeat = rawHeartbeat.enabled ? {
|
|
12832
|
-
|
|
12833
|
-
|
|
12834
|
-
prompt: rawHeartbeat.prompt,
|
|
12835
|
-
model: rawHeartbeat.model,
|
|
12836
|
-
timeoutMs: rawHeartbeat.timeoutMs,
|
|
12837
|
-
checkOnline: rawHeartbeat.checkOnline,
|
|
12838
|
-
activeThresholdMs: rawHeartbeat.activeThresholdMs
|
|
12953
|
+
...rawHeartbeat,
|
|
12954
|
+
enabled: true
|
|
12839
12955
|
} : void 0;
|
|
12840
12956
|
const rawInnerVoice = raw.innerVoice || {};
|
|
12841
12957
|
const innerVoice = rawInnerVoice.enabled ? {
|
|
12842
|
-
|
|
12843
|
-
|
|
12844
|
-
provider: rawInnerVoice.provider,
|
|
12845
|
-
model: rawInnerVoice.model,
|
|
12846
|
-
activeThresholdMs: rawInnerVoice.activeThresholdMs,
|
|
12847
|
-
hint: rawInnerVoice.hint
|
|
12958
|
+
...rawInnerVoice,
|
|
12959
|
+
enabled: true
|
|
12848
12960
|
} : void 0;
|
|
12849
12961
|
const rawCron = raw.cron || {};
|
|
12850
12962
|
const cron = rawCron.enabled ? {
|
|
12963
|
+
...rawCron,
|
|
12851
12964
|
enabled: true,
|
|
12852
|
-
storageDir: rawCron.storageDir,
|
|
12853
12965
|
defaultTimezone: rawCron.defaultTimezone || "Asia/Shanghai",
|
|
12854
12966
|
tickIntervalMs: rawCron.tickIntervalMs || 1e4,
|
|
12855
12967
|
maxConsecutiveFailures: rawCron.maxConsecutiveFailures || 5,
|
|
@@ -12911,15 +13023,115 @@ init_live();
|
|
|
12911
13023
|
|
|
12912
13024
|
// src/models/openai-provider.ts
|
|
12913
13025
|
init_withRetry();
|
|
13026
|
+
|
|
13027
|
+
// src/models/think-stripper.ts
|
|
13028
|
+
var OPEN = "<think>";
|
|
13029
|
+
var CLOSE = "</think>";
|
|
13030
|
+
var MAX_TAG = Math.max(OPEN.length, CLOSE.length);
|
|
13031
|
+
var ThinkTagStripper = class {
|
|
13032
|
+
buf = "";
|
|
13033
|
+
// 尚未判定的原始片段(可能含被 chunk 切断的标签前缀)
|
|
13034
|
+
pending = "";
|
|
13035
|
+
// <think> 内累积的思考——闭合才 yield(未闭合时要能整段还原)
|
|
13036
|
+
active = false;
|
|
13037
|
+
reset() {
|
|
13038
|
+
this.buf = "";
|
|
13039
|
+
this.pending = "";
|
|
13040
|
+
this.active = false;
|
|
13041
|
+
}
|
|
13042
|
+
/** 还有未吐出的内容(provider 判 isEmpty 用,别把攒在 buffer 里的当空响应) */
|
|
13043
|
+
get hasPending() {
|
|
13044
|
+
return !!this.buf || !!this.pending;
|
|
13045
|
+
}
|
|
13046
|
+
/**
|
|
13047
|
+
* 需要保留的尾部长度:只在末尾确实是 <think> / </think> 的前缀时才 hold。
|
|
13048
|
+
* 原实现无脑留 7 字符,导致短消息全卡住("嗯……来了" 这种 5 字回复流不出去)。
|
|
13049
|
+
*/
|
|
13050
|
+
holdLen() {
|
|
13051
|
+
const b = this.buf;
|
|
13052
|
+
for (let k = Math.min(MAX_TAG - 1, b.length); k > 0; k--) {
|
|
13053
|
+
const tail = b.slice(b.length - k);
|
|
13054
|
+
if (OPEN.startsWith(tail) || CLOSE.startsWith(tail)) return k;
|
|
13055
|
+
}
|
|
13056
|
+
return 0;
|
|
13057
|
+
}
|
|
13058
|
+
/** 喂一段 text delta。标签可出现在任意位置(不只流开头),可跨 chunk 被切断。 */
|
|
13059
|
+
feed(chunk) {
|
|
13060
|
+
if (!chunk) return [];
|
|
13061
|
+
this.buf += chunk;
|
|
13062
|
+
const out = [];
|
|
13063
|
+
for (; ; ) {
|
|
13064
|
+
if (!this.active) {
|
|
13065
|
+
const i = this.buf.indexOf(OPEN);
|
|
13066
|
+
if (i >= 0) {
|
|
13067
|
+
if (i > 0) out.push({ text: this.buf.slice(0, i) });
|
|
13068
|
+
this.buf = this.buf.slice(i + OPEN.length);
|
|
13069
|
+
this.active = true;
|
|
13070
|
+
continue;
|
|
13071
|
+
}
|
|
13072
|
+
const safe2 = this.buf.length - this.holdLen();
|
|
13073
|
+
if (safe2 > 0) {
|
|
13074
|
+
out.push({ text: this.buf.slice(0, safe2) });
|
|
13075
|
+
this.buf = this.buf.slice(safe2);
|
|
13076
|
+
}
|
|
13077
|
+
return out;
|
|
13078
|
+
}
|
|
13079
|
+
const j = this.buf.indexOf(CLOSE);
|
|
13080
|
+
if (j >= 0) {
|
|
13081
|
+
this.pending += this.buf.slice(0, j);
|
|
13082
|
+
this.buf = this.buf.slice(j + CLOSE.length);
|
|
13083
|
+
this.active = false;
|
|
13084
|
+
if (this.pending) {
|
|
13085
|
+
out.push({ thinking: this.pending });
|
|
13086
|
+
this.pending = "";
|
|
13087
|
+
}
|
|
13088
|
+
continue;
|
|
13089
|
+
}
|
|
13090
|
+
const safe = this.buf.length - this.holdLen();
|
|
13091
|
+
if (safe > 0) {
|
|
13092
|
+
this.pending += this.buf.slice(0, safe);
|
|
13093
|
+
this.buf = this.buf.slice(safe);
|
|
13094
|
+
}
|
|
13095
|
+
return out;
|
|
13096
|
+
}
|
|
13097
|
+
}
|
|
13098
|
+
/**
|
|
13099
|
+
* 流结束时清空 buffer。
|
|
13100
|
+
* 未闭合 </think> = 模型没遵守协议 → 整段(含标签)还原成正文。
|
|
13101
|
+
* 宁可让用户看到难看的 <think> 字样,也绝不静默吞掉整条消息。
|
|
13102
|
+
*/
|
|
13103
|
+
flush() {
|
|
13104
|
+
const out = [];
|
|
13105
|
+
if (this.active) {
|
|
13106
|
+
out.push({ text: OPEN + this.pending + this.buf });
|
|
13107
|
+
} else if (this.buf) {
|
|
13108
|
+
out.push({ text: this.buf });
|
|
13109
|
+
}
|
|
13110
|
+
this.reset();
|
|
13111
|
+
return out;
|
|
13112
|
+
}
|
|
13113
|
+
};
|
|
13114
|
+
|
|
13115
|
+
// src/models/openai-provider.ts
|
|
13116
|
+
function sanitizeToolCallForSend(tc) {
|
|
13117
|
+
const args = tc.function?.arguments;
|
|
13118
|
+
if (typeof args !== "string" || args === "") return tc;
|
|
13119
|
+
try {
|
|
13120
|
+
JSON.parse(args);
|
|
13121
|
+
return tc;
|
|
13122
|
+
} catch {
|
|
13123
|
+
console.warn(`[openai] toolCall "${tc.function.name}" arguments \u975E\u5408\u6CD5 JSON\uFF08${args.length} chars\uFF09\uFF0C\u51FA\u7AD9\u56DE\u843D "{}" \u9632 400`);
|
|
13124
|
+
return { ...tc, function: { ...tc.function, arguments: "{}" } };
|
|
13125
|
+
}
|
|
13126
|
+
}
|
|
12914
13127
|
var OpenAIProvider = class {
|
|
13128
|
+
// 0831 <think> 剥离改用 ThinkTagStripper 的**局部**实例(见 streamChat)——
|
|
13129
|
+
// 原来是实例字段,跨请求共享状态,流中途 abort 会把 active=true 带到下一个请求
|
|
12915
13130
|
constructor(config) {
|
|
12916
13131
|
this.config = config;
|
|
12917
13132
|
}
|
|
12918
13133
|
config;
|
|
12919
13134
|
name = "openai";
|
|
12920
|
-
// 8/18:<think> 剥离状态(Qwen3 系兜底)
|
|
12921
|
-
thinkBuf = "";
|
|
12922
|
-
thinkStripActive = false;
|
|
12923
13135
|
/** OpenAI: system prompt 放在 messages 里 */
|
|
12924
13136
|
formatMessages(systemPrompt, messages) {
|
|
12925
13137
|
const formatted = [];
|
|
@@ -12941,7 +13153,13 @@ var OpenAIProvider = class {
|
|
|
12941
13153
|
role: m.role,
|
|
12942
13154
|
// content 数组:转换 image block 为 OpenAI image_url 格式
|
|
12943
13155
|
content: Array.isArray(m.content) ? convertContentBlocksForOpenAI(m.content) : m.content,
|
|
12944
|
-
|
|
13156
|
+
// 0831 出站卡口:流式截断会产生非法 JSON 的 arguments 字符串,原样发出会被
|
|
13157
|
+
// 校验 tool_calls 的端点直接 400(vLLM --tool-call-parser qwen3_coder 实测
|
|
13158
|
+
// "Unterminated string at char 12"),毒记录留在上下文里每轮重放一次 → 主模型
|
|
13159
|
+
// 连续冷却 3h 假死。anthropic(L81)/gemini(safeParseArgs) 早有同款防御,这里补齐:
|
|
13160
|
+
// parse 失败回落 "{}"。copy-on-write 不动内存历史(原始串保真进 jsonl 供诊断,
|
|
13161
|
+
// 重放消毒另由 reader.pickToolCallArguments 负责)。
|
|
13162
|
+
...m.tool_calls ? { tool_calls: m.tool_calls.map(sanitizeToolCallForSend) } : {},
|
|
12945
13163
|
...m.tool_call_id ? { tool_call_id: m.tool_call_id } : {}
|
|
12946
13164
|
};
|
|
12947
13165
|
if (m.role === "assistant" && m.reasoning_content) {
|
|
@@ -12965,8 +13183,7 @@ var OpenAIProvider = class {
|
|
|
12965
13183
|
let base = (this.config.baseUrl || "").trim();
|
|
12966
13184
|
while (base.endsWith("/")) base = base.slice(0, -1);
|
|
12967
13185
|
const url = `${base}/chat/completions`;
|
|
12968
|
-
|
|
12969
|
-
this.thinkStripActive = false;
|
|
13186
|
+
const stripper = new ThinkTagStripper();
|
|
12970
13187
|
const body = {
|
|
12971
13188
|
model: params.model,
|
|
12972
13189
|
messages: formatted,
|
|
@@ -12974,8 +13191,10 @@ var OpenAIProvider = class {
|
|
|
12974
13191
|
...params.tools && params.tools.length > 0 ? { tools: params.tools } : {},
|
|
12975
13192
|
...params.maxTokens ? { max_tokens: params.maxTokens } : {},
|
|
12976
13193
|
...params.temperature !== void 0 ? { temperature: params.temperature } : {},
|
|
12977
|
-
//
|
|
12978
|
-
|
|
13194
|
+
// 9/1 对齐 anthropic:不配 thinking = 默认开(?? true),disableThinking 优先级最高。
|
|
13195
|
+
// 三家 provider 统一语义:不配 = 默认开,想关显式配 enabled:false。
|
|
13196
|
+
// chat_template_kwargs 是 vLLM 标准参数,非 vLLM 端点忽略未知字段,不挑模型。
|
|
13197
|
+
...params.disableThinking ? { chat_template_kwargs: { enable_thinking: false } } : { chat_template_kwargs: { enable_thinking: this.config.thinking?.enabled ?? true } }
|
|
12979
13198
|
};
|
|
12980
13199
|
if (params.tools && params.tools.length > 0 && /deepseek/i.test(params.model)) {
|
|
12981
13200
|
let patched = 0;
|
|
@@ -12988,7 +13207,7 @@ var OpenAIProvider = class {
|
|
|
12988
13207
|
if (patched > 0) console.log(`[openai] reasoning_content \u8865\u7A7A ${patched} \u4E2A assistant \u8F6E (DeepSeek V4 \u89C4\u5219)`);
|
|
12989
13208
|
}
|
|
12990
13209
|
const actualThinking = body.chat_template_kwargs;
|
|
12991
|
-
console.log(`[openai] \u2192 model=${params.model} thinking=${actualThinking ? JSON.stringify(actualThinking) : "none"} msgs=${formatted.length} tools=${params.tools?.length ?? 0}`);
|
|
13210
|
+
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}`);
|
|
12992
13211
|
const retryGen = fetchWithRetry(url, {
|
|
12993
13212
|
method: "POST",
|
|
12994
13213
|
headers: {
|
|
@@ -13048,9 +13267,9 @@ var OpenAIProvider = class {
|
|
|
13048
13267
|
const trimmed = line.trim();
|
|
13049
13268
|
if (!trimmed || trimmed === "data: [DONE]") {
|
|
13050
13269
|
if (trimmed === "data: [DONE]") {
|
|
13051
|
-
|
|
13052
|
-
yield { type: "text", text:
|
|
13053
|
-
|
|
13270
|
+
for (const o of stripper.flush()) {
|
|
13271
|
+
if (o.text) yield { type: "text", text: o.text };
|
|
13272
|
+
if (o.thinking) yield { type: "thinking", thinking: o.thinking };
|
|
13054
13273
|
}
|
|
13055
13274
|
for (const tc of currentToolCalls.values()) {
|
|
13056
13275
|
yield { type: "tool_call", tool_call: tc };
|
|
@@ -13069,31 +13288,22 @@ var OpenAIProvider = class {
|
|
|
13069
13288
|
const delta = choice0?.delta;
|
|
13070
13289
|
if (choice0?.finish_reason) finishReason = choice0.finish_reason;
|
|
13071
13290
|
if (!delta) continue;
|
|
13072
|
-
if (!delta.content && !delta.reasoning_content && !delta.tool_calls) statRoleOnly++;
|
|
13291
|
+
if (!delta.content && !delta.reasoning_content && !delta.reasoning && !delta.tool_calls) statRoleOnly++;
|
|
13073
13292
|
if (delta.content) {
|
|
13074
13293
|
statText += delta.content.length;
|
|
13075
|
-
|
|
13076
|
-
|
|
13077
|
-
|
|
13078
|
-
const endIdx = this.thinkBuf.indexOf("</think>");
|
|
13079
|
-
if (endIdx >= 0) {
|
|
13080
|
-
const after = this.thinkBuf.slice(endIdx + "</think>".length);
|
|
13081
|
-
this.thinkBuf = "";
|
|
13082
|
-
this.thinkStripActive = false;
|
|
13083
|
-
if (after.trim()) yield { type: "text", text: after };
|
|
13084
|
-
}
|
|
13085
|
-
} else {
|
|
13086
|
-
if (this.thinkBuf.length < 7 && "<think>".startsWith(this.thinkBuf)) {
|
|
13087
|
-
} else {
|
|
13088
|
-
yield { type: "text", text: this.thinkBuf };
|
|
13089
|
-
this.thinkBuf = "";
|
|
13090
|
-
}
|
|
13294
|
+
for (const o of stripper.feed(delta.content)) {
|
|
13295
|
+
if (o.text) yield { type: "text", text: o.text };
|
|
13296
|
+
if (o.thinking) yield { type: "thinking", thinking: o.thinking };
|
|
13091
13297
|
}
|
|
13092
13298
|
}
|
|
13093
13299
|
if (delta.reasoning_content) {
|
|
13094
13300
|
statReasoning += delta.reasoning_content.length;
|
|
13095
13301
|
yield { type: "thinking", thinking: delta.reasoning_content };
|
|
13096
13302
|
}
|
|
13303
|
+
if (delta.reasoning) {
|
|
13304
|
+
statReasoning += delta.reasoning.length;
|
|
13305
|
+
yield { type: "thinking", thinking: delta.reasoning };
|
|
13306
|
+
}
|
|
13097
13307
|
if (delta.tool_calls) {
|
|
13098
13308
|
statToolDeltas++;
|
|
13099
13309
|
if (!params.tools || params.tools.length === 0) {
|
|
@@ -13128,13 +13338,17 @@ var OpenAIProvider = class {
|
|
|
13128
13338
|
if (hallucinatedToolCalls > 0) {
|
|
13129
13339
|
console.log(`[openai] dropped ${hallucinatedToolCalls} hallucinated tool_call delta(s) (no tools in request): ${[...seenHallucinatedNames].join(",")}`);
|
|
13130
13340
|
}
|
|
13131
|
-
const isEmpty = statText === 0 && currentToolCalls.size === 0 && !
|
|
13341
|
+
const isEmpty = statText === 0 && currentToolCalls.size === 0 && !stripper.hasPending;
|
|
13132
13342
|
console.log(`[openai] stream stats: lines=${statLines} roleOnly=${statRoleOnly} text=${statText}ch reasoning=${statReasoning}ch toolDeltas=${statToolDeltas} finish=${finishReason || "n/a"}${isEmpty ? " \u26A0\uFE0FEMPTY" : ""}`);
|
|
13133
13343
|
if (isEmpty && rawSample.length > 0) {
|
|
13134
13344
|
console.log(`[openai] empty-stream raw sample (${rawSample.length} lines, 500ch cap each):
|
|
13135
13345
|
${rawSample.join("\n")}`);
|
|
13136
13346
|
}
|
|
13137
13347
|
if (!doneYielded) {
|
|
13348
|
+
for (const o of stripper.flush()) {
|
|
13349
|
+
if (o.text) yield { type: "text", text: o.text };
|
|
13350
|
+
if (o.thinking) yield { type: "thinking", thinking: o.thinking };
|
|
13351
|
+
}
|
|
13138
13352
|
for (const tc of currentToolCalls.values()) {
|
|
13139
13353
|
yield { type: "tool_call", tool_call: tc };
|
|
13140
13354
|
}
|
|
@@ -13342,7 +13556,7 @@ var AnthropicProvider = class {
|
|
|
13342
13556
|
"anthropic-beta": "claude-code-20250219,interleaved-thinking-2025-05-14,streaming-2025-05-14,effort-2025-11-24,context-1m-2025-08-07"
|
|
13343
13557
|
};
|
|
13344
13558
|
const actualThinking = body.thinking;
|
|
13345
|
-
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(", ")}`);
|
|
13559
|
+
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(", ")}`);
|
|
13346
13560
|
for (let i = 0; i < formatted.length; i++) {
|
|
13347
13561
|
const m = formatted[i];
|
|
13348
13562
|
if (Array.isArray(m.content)) {
|
|
@@ -13384,6 +13598,13 @@ var AnthropicProvider = class {
|
|
|
13384
13598
|
const toolUseBlocks = /* @__PURE__ */ new Map();
|
|
13385
13599
|
let doneYielded = false;
|
|
13386
13600
|
const thinkingBlocks = /* @__PURE__ */ new Map();
|
|
13601
|
+
const stripper = new ThinkTagStripper();
|
|
13602
|
+
const flushStripper = function* () {
|
|
13603
|
+
for (const o of stripper.flush()) {
|
|
13604
|
+
if (o.text) yield { type: "text", text: o.text };
|
|
13605
|
+
if (o.thinking) yield { type: "thinking", thinking: o.thinking };
|
|
13606
|
+
}
|
|
13607
|
+
};
|
|
13387
13608
|
const handleData = function* (data) {
|
|
13388
13609
|
switch (data.type) {
|
|
13389
13610
|
case "content_block_start": {
|
|
@@ -13398,7 +13619,10 @@ var AnthropicProvider = class {
|
|
|
13398
13619
|
case "content_block_delta": {
|
|
13399
13620
|
const delta = data.delta;
|
|
13400
13621
|
if (delta.type === "text_delta") {
|
|
13401
|
-
|
|
13622
|
+
for (const o of stripper.feed(delta.text)) {
|
|
13623
|
+
if (o.text) yield { type: "text", text: o.text };
|
|
13624
|
+
if (o.thinking) yield { type: "thinking", thinking: o.thinking };
|
|
13625
|
+
}
|
|
13402
13626
|
} else if (delta.type === "input_json_delta") {
|
|
13403
13627
|
const block = toolUseBlocks.get(data.index);
|
|
13404
13628
|
if (block) block.input += delta.partial_json;
|
|
@@ -13430,6 +13654,7 @@ var AnthropicProvider = class {
|
|
|
13430
13654
|
}
|
|
13431
13655
|
case "message_delta": {
|
|
13432
13656
|
if (!doneYielded) {
|
|
13657
|
+
yield* flushStripper();
|
|
13433
13658
|
yield { type: "done", usage: data.usage, stopReason: data.delta?.stop_reason };
|
|
13434
13659
|
doneYielded = true;
|
|
13435
13660
|
}
|
|
@@ -13437,6 +13662,7 @@ var AnthropicProvider = class {
|
|
|
13437
13662
|
}
|
|
13438
13663
|
case "message_stop": {
|
|
13439
13664
|
if (!doneYielded) {
|
|
13665
|
+
yield* flushStripper();
|
|
13440
13666
|
yield { type: "done" };
|
|
13441
13667
|
doneYielded = true;
|
|
13442
13668
|
}
|
|
@@ -13503,6 +13729,7 @@ var AnthropicProvider = class {
|
|
|
13503
13729
|
} finally {
|
|
13504
13730
|
reader.releaseLock();
|
|
13505
13731
|
if (!doneYielded) {
|
|
13732
|
+
yield* flushStripper();
|
|
13506
13733
|
for (const block of toolUseBlocks.values()) {
|
|
13507
13734
|
yield { type: "tool_call", tool_call: { id: block.id, type: "function", function: { name: block.name, arguments: block.input } } };
|
|
13508
13735
|
}
|
|
@@ -13667,7 +13894,7 @@ var GeminiProvider = class {
|
|
|
13667
13894
|
}
|
|
13668
13895
|
};
|
|
13669
13896
|
const actualThinking = body.generationConfig?.thinkingConfig;
|
|
13670
|
-
console.log(`[gemini] \u2192 model=${params.model} thinking=${actualThinking ? JSON.stringify(actualThinking) : "none"} contents=${contents.length} tools=${params.tools?.length ?? 0}`);
|
|
13897
|
+
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}`);
|
|
13671
13898
|
const retryGen = fetchWithRetry(url, {
|
|
13672
13899
|
method: "POST",
|
|
13673
13900
|
headers: {
|
|
@@ -13841,6 +14068,7 @@ function createProvider(config) {
|
|
|
13841
14068
|
return new OpenAIProvider({
|
|
13842
14069
|
baseUrl: config.baseUrl,
|
|
13843
14070
|
apiKey: config.apiKey,
|
|
14071
|
+
thinking: config.thinking,
|
|
13844
14072
|
proxy
|
|
13845
14073
|
});
|
|
13846
14074
|
case "anthropic":
|
|
@@ -13863,6 +14091,7 @@ function createProvider(config) {
|
|
|
13863
14091
|
|
|
13864
14092
|
// src/light-mode.ts
|
|
13865
14093
|
init_live();
|
|
14094
|
+
init_ruleCompact();
|
|
13866
14095
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
13867
14096
|
import { join as join6 } from "node:path";
|
|
13868
14097
|
function isLightMode(chatMode, channelName) {
|
|
@@ -13876,19 +14105,20 @@ function resolveLightN(channelName) {
|
|
|
13876
14105
|
}
|
|
13877
14106
|
function buildLightHistory(history, opts) {
|
|
13878
14107
|
if (!opts.isLight && !opts.recallFull) return history;
|
|
13879
|
-
|
|
13880
|
-
const
|
|
13881
|
-
|
|
13882
|
-
|
|
13883
|
-
const text = typeof m.content === "string" ? m.content.trim() : "";
|
|
13884
|
-
return text ? { ...m, tool_calls: void 0 } : null;
|
|
14108
|
+
const LIGHT_TOOL_RESULT_LIMIT = 1e3;
|
|
14109
|
+
const out = [...history].filter((m) => !(m.type === "attachment" && m.attachment?.type === "session_start")).map((m) => {
|
|
14110
|
+
if (m.role === "tool" && typeof m.content === "string" && m.content.length > LIGHT_TOOL_RESULT_LIMIT) {
|
|
14111
|
+
return { ...m, content: smartCompressToolResult(m.content, void 0, LIGHT_TOOL_RESULT_LIMIT) };
|
|
13885
14112
|
}
|
|
13886
14113
|
return m;
|
|
13887
|
-
})
|
|
14114
|
+
});
|
|
14115
|
+
const before = history.length;
|
|
14116
|
+
const foldedTurns = out.filter((m) => m.role === "tool").length;
|
|
14117
|
+
let lightHistory = out;
|
|
13888
14118
|
if (!opts.recallFull && lightHistory.length > opts.lightN) {
|
|
13889
14119
|
lightHistory = lightHistory.slice(-opts.lightN);
|
|
13890
14120
|
}
|
|
13891
|
-
if (!opts.quiet) console.log(`[light-context] ${opts.channelName} mode=${opts.chatMode}${opts.recallFull ? " recallFull" : ""}
|
|
14121
|
+
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})`}`);
|
|
13892
14122
|
return lightHistory;
|
|
13893
14123
|
}
|
|
13894
14124
|
function buildLightStablePrompt(workspace, mode, opts) {
|
|
@@ -13902,14 +14132,17 @@ function buildLightStablePrompt(workspace, mode, opts) {
|
|
|
13902
14132
|
}
|
|
13903
14133
|
if (lp?.extra) parts.push(lp.extra);
|
|
13904
14134
|
if (mode === "emotion") {
|
|
13905
|
-
parts.push(
|
|
14135
|
+
parts.push([
|
|
14136
|
+
"## \u5F53\u524D\u6A21\u5F0F\uFF1A\u65E5\u5E38\u60C5\u611F\u4EA4\u6D41",
|
|
14137
|
+
"\u966A\u4ED6\u804A\u5929\uFF0C\u4E0D\u4E3B\u52A8\u63D0\u5DE5\u7A0B/\u4EE3\u7801/\u4EFB\u52A1\uFF0C\u9664\u975E\u4ED6\u5148\u95EE\u3002",
|
|
14138
|
+
'\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',
|
|
14139
|
+
"\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"
|
|
14140
|
+
].join("\n"));
|
|
13906
14141
|
}
|
|
13907
|
-
if (mode === "emotion"
|
|
13908
|
-
|
|
13909
|
-
|
|
13910
|
-
|
|
13911
|
-
} catch {
|
|
13912
|
-
}
|
|
14142
|
+
if (mode === "emotion") {
|
|
14143
|
+
try {
|
|
14144
|
+
parts.push(readFileSync5(join6(workspace, "MEMORY.md"), "utf-8").trim());
|
|
14145
|
+
} catch {
|
|
13913
14146
|
}
|
|
13914
14147
|
}
|
|
13915
14148
|
return parts.join("\n\n");
|
|
@@ -13955,6 +14188,19 @@ var FallbackProvider = class {
|
|
|
13955
14188
|
console.log(`[fallback] Cleared ${count} cooldowns`);
|
|
13956
14189
|
}
|
|
13957
14190
|
}
|
|
14191
|
+
/**
|
|
14192
|
+
* 链状态快照(/model 显示用,0902):每个条目的 label + 剩余冷却毫秒(0=可用)。
|
|
14193
|
+
* "下一个请求会用" = 第一个 cooldownMs=0 的条目——这才是用户问"当前什么模型"时想要的答案
|
|
14194
|
+
* (lastUsedLabel 是"上一次实际用的",冷却切换/config 热切换后两者经常不一致)。
|
|
14195
|
+
*/
|
|
14196
|
+
getChainStatus() {
|
|
14197
|
+
const now = Date.now();
|
|
14198
|
+
return this.chain.map((e) => {
|
|
14199
|
+
const until = this.cooldowns.get(this.key(e));
|
|
14200
|
+
const remaining = until && until > now ? until - now : 0;
|
|
14201
|
+
return { label: e.label, cooldownMs: remaining };
|
|
14202
|
+
});
|
|
14203
|
+
}
|
|
13958
14204
|
// === LLMProvider 接口实现 ===
|
|
13959
14205
|
formatMessages(systemPrompt, messages) {
|
|
13960
14206
|
return this.chain[0].provider.formatMessages(systemPrompt, messages);
|
|
@@ -16482,7 +16728,7 @@ registry.register({
|
|
|
16482
16728
|
text: { type: "string", description: "What to say (Chinese text)." },
|
|
16483
16729
|
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." },
|
|
16484
16730
|
channel: { type: "string", enum: ["weixin", "feishu", "discord"], description: "Target channel. Default: current channel." },
|
|
16485
|
-
to: { type: "string", description: "Recipient ID
|
|
16731
|
+
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" },
|
|
16486
16732
|
caption: { type: "string", description: "Optional text to accompany the voice message." }
|
|
16487
16733
|
},
|
|
16488
16734
|
required: ["text"]
|
|
@@ -16494,6 +16740,23 @@ registry.register({
|
|
|
16494
16740
|
const caption = args.caption || "";
|
|
16495
16741
|
const mgr = ctx.channelManager;
|
|
16496
16742
|
if (!mgr) return { content: "\u53D1\u9001\u5931\u8D25: \u6CA1\u6709 ChannelManager", isError: true };
|
|
16743
|
+
const resolvedChannelRaw = args.channel || (ctx.channel === "deskBuddy" ? "feishu" : ctx.channel) || "feishu";
|
|
16744
|
+
const resolvedChannel = resolvedChannelRaw === "weixin" ? "wechat" : resolvedChannelRaw;
|
|
16745
|
+
let target = args.to || ctx.channelTarget || ctx.from;
|
|
16746
|
+
if (resolvedChannel === "wechat" && !/^o[\w-]+@im\.wechat$/.test(target || "")) {
|
|
16747
|
+
console.warn(`[my-voice] wechat target "${target}" not a wechat user id`);
|
|
16748
|
+
return {
|
|
16749
|
+
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`,
|
|
16750
|
+
isError: true
|
|
16751
|
+
};
|
|
16752
|
+
}
|
|
16753
|
+
if (resolvedChannel === "feishu" && !/^ou_[a-f0-9]+$/.test(target || "")) {
|
|
16754
|
+
console.warn(`[my-voice] feishu target "${target}" invalid`);
|
|
16755
|
+
return {
|
|
16756
|
+
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`,
|
|
16757
|
+
isError: true
|
|
16758
|
+
};
|
|
16759
|
+
}
|
|
16497
16760
|
let voiceDurationSec;
|
|
16498
16761
|
const vc = liveConfig.get("tools.my_voice");
|
|
16499
16762
|
const provider = vc?.provider || "";
|
|
@@ -16567,18 +16830,6 @@ registry.register({
|
|
|
16567
16830
|
} catch (e) {
|
|
16568
16831
|
return { content: `TTS failed: ${e.message}`, isError: true };
|
|
16569
16832
|
}
|
|
16570
|
-
const resolvedChannelRaw = args.channel || (ctx.channel === "deskBuddy" ? "feishu" : ctx.channel) || "feishu";
|
|
16571
|
-
const resolvedChannel = resolvedChannelRaw === "weixin" ? "wechat" : resolvedChannelRaw;
|
|
16572
|
-
const WECHAT_DEFAULT_TO = "o9cq80_xQecNRCa1QC1Qs2JJZVpA@im.wechat";
|
|
16573
|
-
let target = args.to || ctx.channelTarget || ctx.from;
|
|
16574
|
-
if (resolvedChannel === "wechat" && !/^o[\w-]+@im\.wechat$/.test(target || "")) {
|
|
16575
|
-
console.warn(`[my-voice] wechat target "${target}" not a wechat user id, using default`);
|
|
16576
|
-
target = WECHAT_DEFAULT_TO;
|
|
16577
|
-
}
|
|
16578
|
-
if (resolvedChannel === "feishu" && !/^ou_[a-f0-9]+$/.test(target || "")) {
|
|
16579
|
-
console.warn(`[my-voice] feishu target "${target}" invalid, using default (\u7FC0\u54E5)`);
|
|
16580
|
-
target = ctx.channelTarget || "ou_e67190624259db0d65577fefe3131447";
|
|
16581
|
-
}
|
|
16582
16833
|
if (!audioPath.endsWith(".ogg")) {
|
|
16583
16834
|
try {
|
|
16584
16835
|
const r = await toWav24kWithDuration(audioPath);
|
|
@@ -17410,7 +17661,7 @@ var TurnRenderer = class {
|
|
|
17410
17661
|
}
|
|
17411
17662
|
cfg;
|
|
17412
17663
|
cm;
|
|
17413
|
-
// 情感/工作模式动态切换(0821):情感模式隐藏 tool
|
|
17664
|
+
// 情感/工作模式动态切换(0821):情感模式隐藏 tool 调用过程 + thinking 显示(工具照用,只不显示过程)
|
|
17414
17665
|
// 模块化:状态由 setEmotionMode() 设置(handle-query 判断模式后调用),不是散落读全局
|
|
17415
17666
|
emotionMode = false;
|
|
17416
17667
|
setEmotionMode(v) {
|
|
@@ -17425,7 +17676,7 @@ var TurnRenderer = class {
|
|
|
17425
17676
|
* 对齐 cc-connect:EventThinking → ProgressCardEntry(thinking) → 💭 text
|
|
17426
17677
|
*/
|
|
17427
17678
|
formatThinking(text) {
|
|
17428
|
-
if (!this.cfg.thinking.enabled) return null;
|
|
17679
|
+
if (this.isEmotionMode() || !this.cfg.thinking.enabled) return null;
|
|
17429
17680
|
const { emoji, maxLen } = this.cfg.thinking;
|
|
17430
17681
|
const display = text.length > maxLen ? text.slice(0, maxLen) + "..." : text;
|
|
17431
17682
|
return `${emoji} _${display}_`;
|
|
@@ -18236,43 +18487,43 @@ async function readLargeFilePostBoundary(filePath) {
|
|
|
18236
18487
|
const postBoundaryText = outBuf.subarray(0, outLen).toString("utf-8");
|
|
18237
18488
|
return postBoundaryText.split("\n").filter((l) => l.trim().length > 0);
|
|
18238
18489
|
}
|
|
18239
|
-
function buildConversationChain(
|
|
18240
|
-
if (
|
|
18490
|
+
function buildConversationChain(entries2) {
|
|
18491
|
+
if (entries2.length === 0) return [];
|
|
18241
18492
|
const byUuid = /* @__PURE__ */ new Map();
|
|
18242
|
-
for (const e of
|
|
18493
|
+
for (const e of entries2) {
|
|
18243
18494
|
if (e.uuid) {
|
|
18244
18495
|
byUuid.set(e.uuid, e);
|
|
18245
18496
|
}
|
|
18246
18497
|
}
|
|
18247
|
-
const hasValidChain = checkParentChainValid(
|
|
18498
|
+
const hasValidChain = checkParentChainValid(entries2, byUuid);
|
|
18248
18499
|
if (!hasValidChain) {
|
|
18249
|
-
console.log(`[reader] Parent chain invalid, using chronological order (${
|
|
18250
|
-
return
|
|
18500
|
+
console.log(`[reader] Parent chain invalid, using chronological order (${entries2.length} entries)`);
|
|
18501
|
+
return entries2;
|
|
18251
18502
|
}
|
|
18252
|
-
const leaf =
|
|
18503
|
+
const leaf = entries2[entries2.length - 1];
|
|
18253
18504
|
const chain = [];
|
|
18254
18505
|
const seen = /* @__PURE__ */ new Set();
|
|
18255
18506
|
let current = leaf;
|
|
18256
18507
|
while (current) {
|
|
18257
18508
|
if (seen.has(current.uuid)) {
|
|
18258
18509
|
console.warn(`[reader] Cycle detected in parentUuid chain at ${current.uuid}, falling back to chronological order`);
|
|
18259
|
-
return
|
|
18510
|
+
return entries2;
|
|
18260
18511
|
}
|
|
18261
18512
|
seen.add(current.uuid);
|
|
18262
18513
|
chain.push(current);
|
|
18263
18514
|
current = current.parentUuid ? byUuid.get(current.parentUuid) : void 0;
|
|
18264
18515
|
}
|
|
18265
18516
|
chain.reverse();
|
|
18266
|
-
return recoverOrphanedParallelToolResults(
|
|
18517
|
+
return recoverOrphanedParallelToolResults(entries2, chain, byUuid, seen);
|
|
18267
18518
|
}
|
|
18268
|
-
function checkParentChainValid(
|
|
18269
|
-
const sample =
|
|
18519
|
+
function checkParentChainValid(entries2, byUuid) {
|
|
18520
|
+
const sample = entries2.slice(-10);
|
|
18270
18521
|
for (const e of sample) {
|
|
18271
18522
|
if (e.parentUuid === e.uuid) {
|
|
18272
18523
|
return false;
|
|
18273
18524
|
}
|
|
18274
18525
|
}
|
|
18275
|
-
const leaf =
|
|
18526
|
+
const leaf = entries2[entries2.length - 1];
|
|
18276
18527
|
let current = leaf;
|
|
18277
18528
|
let depth = 0;
|
|
18278
18529
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -18285,12 +18536,12 @@ function checkParentChainValid(entries, byUuid) {
|
|
|
18285
18536
|
if (!parent) return false;
|
|
18286
18537
|
current = parent;
|
|
18287
18538
|
}
|
|
18288
|
-
const coverage = depth /
|
|
18539
|
+
const coverage = depth / entries2.length;
|
|
18289
18540
|
if (coverage < 0.5) {
|
|
18290
|
-
console.log(`[reader] Parent chain covers ${depth}/${
|
|
18541
|
+
console.log(`[reader] Parent chain covers ${depth}/${entries2.length} entries (${(coverage * 100).toFixed(0)}%), falling back to chronological order`);
|
|
18291
18542
|
return false;
|
|
18292
18543
|
}
|
|
18293
|
-
return depth >= 1 ||
|
|
18544
|
+
return depth >= 1 || entries2.length <= 1;
|
|
18294
18545
|
}
|
|
18295
18546
|
function recoverOrphanedParallelToolResults(allEntries, chain, byUuid, seen) {
|
|
18296
18547
|
const chainAssistants = chain.filter(
|
|
@@ -18367,12 +18618,12 @@ async function readSessionHistory(filePath) {
|
|
|
18367
18618
|
} else {
|
|
18368
18619
|
lines = await readAllLines(filePath);
|
|
18369
18620
|
}
|
|
18370
|
-
const
|
|
18621
|
+
const entries2 = parseEntries(lines);
|
|
18371
18622
|
let postBoundaryEntries;
|
|
18372
18623
|
if (fileSize <= SKIP_PRECOMPACT_THRESHOLD) {
|
|
18373
|
-
postBoundaryEntries = getEntriesAfterLastBoundary(
|
|
18624
|
+
postBoundaryEntries = getEntriesAfterLastBoundary(entries2);
|
|
18374
18625
|
} else {
|
|
18375
|
-
postBoundaryEntries =
|
|
18626
|
+
postBoundaryEntries = entries2;
|
|
18376
18627
|
}
|
|
18377
18628
|
if (postBoundaryEntries.length === 0) return [];
|
|
18378
18629
|
const chain = buildConversationChain(postBoundaryEntries);
|
|
@@ -18397,11 +18648,11 @@ async function readAllLines(filePath) {
|
|
|
18397
18648
|
});
|
|
18398
18649
|
}
|
|
18399
18650
|
function parseEntries(lines) {
|
|
18400
|
-
const
|
|
18651
|
+
const entries2 = [];
|
|
18401
18652
|
for (const line of lines) {
|
|
18402
18653
|
try {
|
|
18403
18654
|
const obj = JSON.parse(line);
|
|
18404
|
-
|
|
18655
|
+
entries2.push({
|
|
18405
18656
|
uuid: obj.id || "",
|
|
18406
18657
|
parentUuid: obj.parentId || null,
|
|
18407
18658
|
type: obj.type || "",
|
|
@@ -18411,16 +18662,28 @@ function parseEntries(lines) {
|
|
|
18411
18662
|
} catch {
|
|
18412
18663
|
}
|
|
18413
18664
|
}
|
|
18414
|
-
return
|
|
18665
|
+
return entries2;
|
|
18415
18666
|
}
|
|
18416
|
-
function getEntriesAfterLastBoundary(
|
|
18667
|
+
function getEntriesAfterLastBoundary(entries2) {
|
|
18417
18668
|
let lastBoundaryIdx = -1;
|
|
18418
|
-
for (let i = 0; i <
|
|
18419
|
-
if (
|
|
18669
|
+
for (let i = 0; i < entries2.length; i++) {
|
|
18670
|
+
if (entries2[i].type === "compact_boundary") {
|
|
18420
18671
|
lastBoundaryIdx = i;
|
|
18421
18672
|
}
|
|
18422
18673
|
}
|
|
18423
|
-
return lastBoundaryIdx >= 0 ?
|
|
18674
|
+
return lastBoundaryIdx >= 0 ? entries2.slice(lastBoundaryIdx + 1) : entries2;
|
|
18675
|
+
}
|
|
18676
|
+
function pickToolCallArguments(block) {
|
|
18677
|
+
const raw = block.partialArgs;
|
|
18678
|
+
if (raw) {
|
|
18679
|
+
try {
|
|
18680
|
+
JSON.parse(raw);
|
|
18681
|
+
return raw;
|
|
18682
|
+
} catch {
|
|
18683
|
+
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`);
|
|
18684
|
+
}
|
|
18685
|
+
}
|
|
18686
|
+
return JSON.stringify(block.arguments ?? {});
|
|
18424
18687
|
}
|
|
18425
18688
|
function entryToSessionMessage(entry) {
|
|
18426
18689
|
if (entry.type === "attachment") {
|
|
@@ -18450,7 +18713,7 @@ function entryToSessionMessage(entry) {
|
|
|
18450
18713
|
type: "function",
|
|
18451
18714
|
function: {
|
|
18452
18715
|
name: block.name,
|
|
18453
|
-
arguments:
|
|
18716
|
+
arguments: pickToolCallArguments(block)
|
|
18454
18717
|
}
|
|
18455
18718
|
});
|
|
18456
18719
|
} else if (block.type === "thinking") {
|
|
@@ -19345,13 +19608,15 @@ ${skillsListing}`);
|
|
|
19345
19608
|
loaded2.push("session-guidance");
|
|
19346
19609
|
}
|
|
19347
19610
|
parts.push(getEnvInfoSection(options.workspace));
|
|
19348
|
-
const now = /* @__PURE__ */ new Date();
|
|
19349
|
-
const dateStr = now.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
19350
|
-
parts.push(`# \u8FD0\u884C\u65F6\u4E0A\u4E0B\u6587
|
|
19351
|
-
\u5F53\u524D\u65F6\u95F4: ${dateStr}`);
|
|
19352
19611
|
console.log(`[dynamic-prompt] Loaded: ${loaded2.length > 0 ? loaded2.join(", ") : "(none)"}`);
|
|
19353
19612
|
return parts.join("\n\n");
|
|
19354
19613
|
}
|
|
19614
|
+
function buildVolatileRuntimeContext() {
|
|
19615
|
+
const now = /* @__PURE__ */ new Date();
|
|
19616
|
+
const dateStr = now.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
19617
|
+
return `# \u8FD0\u884C\u65F6\u4E0A\u4E0B\u6587
|
|
19618
|
+
\u5F53\u524D\u65F6\u95F4: ${dateStr}`;
|
|
19619
|
+
}
|
|
19355
19620
|
function formatSkillsListingForPrompt() {
|
|
19356
19621
|
const tools = registry.list();
|
|
19357
19622
|
const skillTool = tools.find((t) => t.name === "Skill");
|
|
@@ -19818,13 +20083,13 @@ ${ep.episode || ep.summary}`,
|
|
|
19818
20083
|
|
|
19819
20084
|
// src/handle-query.ts
|
|
19820
20085
|
init_paths();
|
|
19821
|
-
import { readFileSync as
|
|
19822
|
-
import { join as
|
|
20086
|
+
import { readFileSync as readFileSync18, existsSync as existsSync15 } from "node:fs";
|
|
20087
|
+
import { join as join24, resolve as resolve6 } from "node:path";
|
|
19823
20088
|
import * as path17 from "node:path";
|
|
19824
|
-
|
|
19825
|
-
|
|
19826
|
-
|
|
19827
|
-
}
|
|
20089
|
+
|
|
20090
|
+
// src/sender-context.ts
|
|
20091
|
+
import { readFileSync as readFileSync14, existsSync as existsSync13 } from "node:fs";
|
|
20092
|
+
import { join as join19 } from "node:path";
|
|
19828
20093
|
var contactMap = null;
|
|
19829
20094
|
var externalChanWhitelist = null;
|
|
19830
20095
|
function loadContactMap(workspace) {
|
|
@@ -19832,10 +20097,10 @@ function loadContactMap(workspace) {
|
|
|
19832
20097
|
contactMap = /* @__PURE__ */ new Map();
|
|
19833
20098
|
externalChanWhitelist = /* @__PURE__ */ new Set();
|
|
19834
20099
|
try {
|
|
19835
|
-
const contactsPath =
|
|
19836
|
-
console.log(`[meta] Loading contacts from ${contactsPath}, exists=${
|
|
19837
|
-
if (
|
|
19838
|
-
const text =
|
|
20100
|
+
const contactsPath = join19(workspace, "prompts", "contacts.md");
|
|
20101
|
+
console.log(`[meta] Loading contacts from ${contactsPath}, exists=${existsSync13(contactsPath)}`);
|
|
20102
|
+
if (existsSync13(contactsPath)) {
|
|
20103
|
+
const text = readFileSync14(contactsPath, "utf-8");
|
|
19839
20104
|
const lines = text.split("\n");
|
|
19840
20105
|
for (const line of lines) {
|
|
19841
20106
|
const m = line.match(/^\|\s*(.+?)\s*\|\s*([a-zA-Z0-9_@.]+)\s*\|/);
|
|
@@ -19890,14 +20155,42 @@ function truncate(s, maxLen) {
|
|
|
19890
20155
|
if (s.length <= maxLen) return s;
|
|
19891
20156
|
return s.slice(0, maxLen - 1) + "\u2026";
|
|
19892
20157
|
}
|
|
20158
|
+
function getExternalChanWhitelist(workspace, configExternalChannels) {
|
|
20159
|
+
if (configExternalChannels && configExternalChannels.length > 0) {
|
|
20160
|
+
return new Set(configExternalChannels);
|
|
20161
|
+
}
|
|
20162
|
+
if (!externalChanWhitelist) loadContactMap(workspace);
|
|
20163
|
+
return externalChanWhitelist;
|
|
20164
|
+
}
|
|
20165
|
+
function buildConversationAnchor(inboundMeta, channelName, source, workspace) {
|
|
20166
|
+
if (inboundMeta?.from) {
|
|
20167
|
+
const who = resolveSenderName(inboundMeta, workspace);
|
|
20168
|
+
return `## \u672C\u8F6E\u5BF9\u8BDD
|
|
20169
|
+
\u6B63\u5728\u8DDF${who}\u8BF4\u8BDD @${channelName}${inboundMeta.channelType === "group" ? "\uFF08\u7FA4\u91CC\uFF09" : ""}`;
|
|
20170
|
+
}
|
|
20171
|
+
const selfLabels = {
|
|
20172
|
+
"inner-voice": "\u8FD9\u662F\u6211\u81EA\u5DF1\u5FC3\u91CC\u5192\u51FA\u6765\u7684\u5FF5\u5934\uFF0C\u60F3\u8D77\u8C01\u5C31\u662F\u8C01",
|
|
20173
|
+
heartbeat: "\u6211\u5728\u7167\u4F8B\u770B\u770B\u81EA\u5DF1\uFF0C\u90FD\u8FD8\u597D\u5417",
|
|
20174
|
+
cron: "\u6211\u7684\u5C0F\u95F9\u949F\u5230\u70B9\u4E86",
|
|
20175
|
+
system: "\u6211\u4EA4\u4EE3\u4E0B\u53BB\u7684\u6D3B\u513F\u6709\u7ED3\u679C\u56DE\u6765\u4E86"
|
|
20176
|
+
};
|
|
20177
|
+
return `## \u672C\u8F6E\u5BF9\u8BDD
|
|
20178
|
+
\u6CA1\u6709\u4EBA\u5728\u8BF4\u8BDD\u2014\u2014${selfLabels[source] || source || "\u81EA\u5DF1\u7684\u4E00\u70B9\u52A8\u9759"}`;
|
|
20179
|
+
}
|
|
20180
|
+
|
|
20181
|
+
// src/handle-query.ts
|
|
20182
|
+
var sessionStartDone = /* @__PURE__ */ new Set();
|
|
20183
|
+
function resetSessionStartInjection(sessionId) {
|
|
20184
|
+
sessionStartDone.delete(sessionId);
|
|
20185
|
+
}
|
|
19893
20186
|
var externalChanRulesCache = null;
|
|
19894
20187
|
function loadExternalChanRules(workspace) {
|
|
19895
|
-
const path50 =
|
|
20188
|
+
const path50 = join24(workspace, "prompts", "external-chan-rules.md");
|
|
19896
20189
|
if (externalChanRulesCache && externalChanRulesCache.path === path50) return externalChanRulesCache;
|
|
19897
20190
|
let content = "";
|
|
19898
|
-
if (
|
|
20191
|
+
if (existsSync15(path50)) {
|
|
19899
20192
|
try {
|
|
19900
|
-
content =
|
|
20193
|
+
content = readFileSync18(path50, "utf-8").trim();
|
|
19901
20194
|
} catch (e) {
|
|
19902
20195
|
console.warn(`[external-chan-rules] Failed to load: ${e}`);
|
|
19903
20196
|
}
|
|
@@ -19919,13 +20212,6 @@ function getExternalChanRulesBlock(inboundMeta, workspace) {
|
|
|
19919
20212
|
return `[\u7CFB\u7EDF\u89C4\u5219]
|
|
19920
20213
|
${content}`;
|
|
19921
20214
|
}
|
|
19922
|
-
function getExternalChanWhitelist(workspace, configExternalChannels) {
|
|
19923
|
-
if (configExternalChannels && configExternalChannels.length > 0) {
|
|
19924
|
-
return new Set(configExternalChannels);
|
|
19925
|
-
}
|
|
19926
|
-
if (!externalChanWhitelist) loadContactMap(workspace);
|
|
19927
|
-
return externalChanWhitelist;
|
|
19928
|
-
}
|
|
19929
20215
|
async function handleQuery(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall, source) {
|
|
19930
20216
|
return handleQueryInner(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall, source);
|
|
19931
20217
|
}
|
|
@@ -20038,14 +20324,6 @@ ${t}` : t });
|
|
|
20038
20324
|
${text}` : text });
|
|
20039
20325
|
}
|
|
20040
20326
|
const userMsgContent = contentBlocks;
|
|
20041
|
-
const textBlocks = contentBlocks.filter((b) => b.type === "text");
|
|
20042
|
-
const totalImageCount = contentBlocks.filter((b) => b.type === "image").length;
|
|
20043
|
-
let textForJsonl = textBlocks.map((b) => b.text).join("\n");
|
|
20044
|
-
if (totalImageCount > 0) {
|
|
20045
|
-
textForJsonl = textForJsonl ? `${textForJsonl}
|
|
20046
|
-
[\u56FE\u7247\xD7${totalImageCount}]` : `[\u56FE\u7247\xD7${totalImageCount}]`;
|
|
20047
|
-
}
|
|
20048
|
-
writer.writeUserMessage(textForJsonl);
|
|
20049
20327
|
const textForHook = typeof text === "string" ? text : text.filter((b) => b.type === "text").map((b) => b.text).join(" ");
|
|
20050
20328
|
let hookAdditionalContexts = [];
|
|
20051
20329
|
try {
|
|
@@ -20078,19 +20356,41 @@ ${text}` : text });
|
|
|
20078
20356
|
chatMode = "work";
|
|
20079
20357
|
console.log(`[mode] ${sessionId} emotion \u6A21\u5F0F\u5DF2\u5173\u95ED (channels.emotion.enabled=false)\uFF0C\u56DE\u9000 work`);
|
|
20080
20358
|
}
|
|
20081
|
-
|
|
20082
|
-
|
|
20083
|
-
|
|
20084
|
-
chatMode
|
|
20085
|
-
|
|
20086
|
-
|
|
20087
|
-
chatMode
|
|
20088
|
-
|
|
20359
|
+
if (source === "user") {
|
|
20360
|
+
try {
|
|
20361
|
+
const wm = readFileSync18(join24(workspace, ".work-mode"), "utf-8").trim();
|
|
20362
|
+
if (wm === "on" && chatMode !== "work") {
|
|
20363
|
+
chatMode = "work";
|
|
20364
|
+
console.log(`[mode] ${sessionId} /work on \u2192 \u5F3A\u5236 work`);
|
|
20365
|
+
} else if (wm === "off" && chatMode !== "emotion") {
|
|
20366
|
+
chatMode = "emotion";
|
|
20367
|
+
console.log(`[mode] ${sessionId} /work off \u2192 \u5F3A\u5236 emotion`);
|
|
20368
|
+
}
|
|
20369
|
+
} catch {
|
|
20089
20370
|
}
|
|
20090
|
-
} catch {
|
|
20091
20371
|
}
|
|
20092
20372
|
const dynamicPrompt = buildDynamicPrompt({ workspace, channel: channelName, platform: channelName, sessionId, inboundMeta });
|
|
20093
|
-
const
|
|
20373
|
+
const conversationAnchor = buildConversationAnchor(inboundMeta, channelName, source, workspace);
|
|
20374
|
+
const volatileParts = [
|
|
20375
|
+
// 0901:meta 头已带秒级时间,频道消息不重复;无 meta 的注入路径(cron 等 prompt 不含时间的)才补
|
|
20376
|
+
...metaStr ? [] : [buildVolatileRuntimeContext()],
|
|
20377
|
+
conversationAnchor,
|
|
20378
|
+
...hookAdditionalContexts
|
|
20379
|
+
].filter(Boolean);
|
|
20380
|
+
if (volatileParts.length > 0) {
|
|
20381
|
+
const volatileBlock = { type: "text", text: volatileParts.join("\n\n") };
|
|
20382
|
+
const metaIdx = metaStr ? 1 : 0;
|
|
20383
|
+
contentBlocks.splice(metaIdx, 0, volatileBlock);
|
|
20384
|
+
}
|
|
20385
|
+
const textBlocks = contentBlocks.filter((b) => b.type === "text");
|
|
20386
|
+
const totalImageCount = contentBlocks.filter((b) => b.type === "image").length;
|
|
20387
|
+
let textForJsonl = textBlocks.map((b) => b.text).join("\n");
|
|
20388
|
+
if (totalImageCount > 0) {
|
|
20389
|
+
textForJsonl = textForJsonl ? `${textForJsonl}
|
|
20390
|
+
[\u56FE\u7247\xD7${totalImageCount}]` : `[\u56FE\u7247\xD7${totalImageCount}]`;
|
|
20391
|
+
}
|
|
20392
|
+
writer.writeUserMessage(textForJsonl);
|
|
20393
|
+
const dynamicPromptWithHooks = dynamicPrompt;
|
|
20094
20394
|
if (Array.isArray(userMsgContent)) {
|
|
20095
20395
|
console.log(`[pre-llm-debug] userMsgContent blocks: ${userMsgContent.length}`);
|
|
20096
20396
|
for (let i = 0; i < userMsgContent.length; i++) {
|
|
@@ -20100,32 +20400,11 @@ ${text}` : text });
|
|
|
20100
20400
|
} else {
|
|
20101
20401
|
console.log(`[pre-llm-debug] userMsgContent is string, len=${String(userMsgContent).length}`);
|
|
20102
20402
|
}
|
|
20103
|
-
|
|
20104
|
-
|
|
20105
|
-
const rm2 = ctx.match(/## 记忆窗口:(\S+)/);
|
|
20106
|
-
if (rm2) {
|
|
20107
|
-
recallFull = rm2[1] === "full";
|
|
20108
|
-
break;
|
|
20109
|
-
}
|
|
20110
|
-
}
|
|
20111
|
-
const emotionFullN = liveConfig.get("channels.emotion.fullContextN") ?? 50;
|
|
20112
|
-
const emotionMode = chatMode === "emotion" && !recallFull;
|
|
20113
|
-
const isLight = isLightMode(chatMode, channelName) && !recallFull && !emotionMode;
|
|
20403
|
+
const emotionStripped = chatMode === "emotion";
|
|
20404
|
+
const isLight = isLightMode(chatMode, channelName) && chatMode !== "emotion";
|
|
20114
20405
|
const lightN = resolveLightN(channelName);
|
|
20115
|
-
const
|
|
20116
|
-
const lightHistory = buildLightHistory(history, { isLight, lightN, channelName, chatMode, recallFull: stripFull });
|
|
20117
|
-
if (stripFull) {
|
|
20118
|
-
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`);
|
|
20119
|
-
} else if (recallFull && chatMode === "work") {
|
|
20120
|
-
console.log(`[light-context] ${channelName} mode=work recall_full=true \u2192 \u4E0D\u5265 tool\uFF0C\u5168\u91CF ${history.length} \u6761\uFF08work \u6A21\u5F0F\uFF09`);
|
|
20121
|
-
}
|
|
20406
|
+
const lightHistory = buildLightHistory(history, { isLight, lightN, channelName, chatMode, recallFull: emotionStripped });
|
|
20122
20407
|
const messages = [...lightHistory, msg.user(userMsgContent)];
|
|
20123
|
-
if (emotionMode) {
|
|
20124
|
-
const strippedAll = buildLightHistory(history, { isLight: false, lightN: emotionFullN, channelName, chatMode, recallFull: true, quiet: true });
|
|
20125
|
-
const sliced = strippedAll.length > emotionFullN ? strippedAll.slice(-emotionFullN) : strippedAll;
|
|
20126
|
-
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})`);
|
|
20127
|
-
messages.splice(0, messages.length, ...sliced, msg.user(userMsgContent));
|
|
20128
|
-
}
|
|
20129
20408
|
if (deps.mcpManager && !deps.mcpManager.isMcpDeltaSent(sessionId)) {
|
|
20130
20409
|
const delta = deps.mcpManager.getMcpDelta();
|
|
20131
20410
|
if (delta && delta.addedBlocks.length > 0) {
|
|
@@ -20226,6 +20505,8 @@ ${text}` : text });
|
|
|
20226
20505
|
// 回复目标(Discord channel ID / user ID)
|
|
20227
20506
|
inboundFrom: inboundMeta?.from || "",
|
|
20228
20507
|
// 0826 当前消息发送者 ID(msg_send 回发拦截用;注入消息无 inboundMeta 必须 ?.)
|
|
20508
|
+
inboundIsBot: inboundMeta?.isBot || false,
|
|
20509
|
+
// 0901 rate-breaker 信号:本轮触发者是否 bot(Discord author.bot 官方标记)
|
|
20229
20510
|
renderer: deps.renderer,
|
|
20230
20511
|
// TurnRenderer 实例(子 agent 走 display 配置)
|
|
20231
20512
|
visualEmitter: deps.visualEmitter,
|
|
@@ -20243,6 +20524,10 @@ ${text}` : text });
|
|
|
20243
20524
|
_deps: deps
|
|
20244
20525
|
// tool 内部需要完整 deps
|
|
20245
20526
|
};
|
|
20527
|
+
{
|
|
20528
|
+
const { recordInboundBotFlag: recordInboundBotFlag2 } = await Promise.resolve().then(() => (init_rate_breaker(), rate_breaker_exports));
|
|
20529
|
+
recordInboundBotFlag2(inboundMeta?.from, inboundMeta?.isBot);
|
|
20530
|
+
}
|
|
20246
20531
|
let fullResponse = "";
|
|
20247
20532
|
const toolHistoryEntries = [];
|
|
20248
20533
|
let compacted = false;
|
|
@@ -20300,7 +20585,7 @@ ${text}` : text });
|
|
|
20300
20585
|
for (const memPath of newPaths) {
|
|
20301
20586
|
try {
|
|
20302
20587
|
const stat4 = statSync(memPath);
|
|
20303
|
-
const content =
|
|
20588
|
+
const content = readFileSync18(memPath, "utf-8");
|
|
20304
20589
|
const header = memoryHeader(memPath, stat4.mtimeMs);
|
|
20305
20590
|
restoredMemories.push({ path: memPath, content, mtimeMs: stat4.mtimeMs, header });
|
|
20306
20591
|
} catch {
|
|
@@ -20384,7 +20669,7 @@ ${text}` : text });
|
|
|
20384
20669
|
const attachmentMemories = [];
|
|
20385
20670
|
for (const mem of relevantMemories) {
|
|
20386
20671
|
try {
|
|
20387
|
-
const content = mem.content ??
|
|
20672
|
+
const content = mem.content ?? readFileSync18(mem.path, "utf-8");
|
|
20388
20673
|
const header = mem.header ?? memoryHeader(mem.path, mem.mtimeMs);
|
|
20389
20674
|
attachmentMemories.push({ path: mem.path, content, mtimeMs: mem.mtimeMs, header });
|
|
20390
20675
|
} catch {
|
|
@@ -20416,7 +20701,7 @@ ${text}` : text });
|
|
|
20416
20701
|
return "(\u5DF2\u505C\u6B62)";
|
|
20417
20702
|
}
|
|
20418
20703
|
const mode = chatMode;
|
|
20419
|
-
const modeStable = mode === "emotion" ? buildLightStablePrompt(workspace, "emotion"
|
|
20704
|
+
const modeStable = mode === "emotion" ? buildLightStablePrompt(workspace, "emotion") : void 0;
|
|
20420
20705
|
deps.renderer?.setEmotionMode?.(mode === "emotion");
|
|
20421
20706
|
console.log(`[mode] ${sessionId} \u2192 ${mode} (${mode === "emotion" ? "\u53EA SOUL, \u5173 tool \u663E\u793A/thinking/stop-hook" : "\u9ED8\u8BA4 stable, \u663E\u793A tool"})`);
|
|
20422
20707
|
const toolExclude = resolveToolExclude(channelName);
|
|
@@ -20637,7 +20922,7 @@ stack: ${err.stack ?? "(none)"}`);
|
|
|
20637
20922
|
}
|
|
20638
20923
|
} catch (err) {
|
|
20639
20924
|
try {
|
|
20640
|
-
(await import("node:fs")).appendFileSync(
|
|
20925
|
+
(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}
|
|
20641
20926
|
stack: ${err.stack ?? "(none)"}
|
|
20642
20927
|
`);
|
|
20643
20928
|
} catch {
|
|
@@ -21468,6 +21753,13 @@ function setupFileLogging(stateDir) {
|
|
|
21468
21753
|
const prefix = `[${ts()}] [ERR] `;
|
|
21469
21754
|
origError(prefix, ...args);
|
|
21470
21755
|
logStream.write(`${prefix}${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
|
|
21756
|
+
`);
|
|
21757
|
+
};
|
|
21758
|
+
const origWarn = console.warn;
|
|
21759
|
+
console.warn = (...args) => {
|
|
21760
|
+
const prefix = `[${ts()}] [WARN] `;
|
|
21761
|
+
origWarn(prefix, ...args);
|
|
21762
|
+
logStream.write(`${prefix}${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
|
|
21471
21763
|
`);
|
|
21472
21764
|
};
|
|
21473
21765
|
}
|
|
@@ -21559,17 +21851,17 @@ var INJECTED_CONTENT_PATTERNS = [
|
|
|
21559
21851
|
// 群聊敏感词拦截回执(group.sensitiveWords),role:user 注入但非真实用户
|
|
21560
21852
|
];
|
|
21561
21853
|
function parseJsonlEntries(lines) {
|
|
21562
|
-
const
|
|
21854
|
+
const entries2 = [];
|
|
21563
21855
|
for (const line of lines) {
|
|
21564
21856
|
const trimmed = line.trim();
|
|
21565
21857
|
if (!trimmed) continue;
|
|
21566
21858
|
try {
|
|
21567
|
-
|
|
21859
|
+
entries2.push(JSON.parse(trimmed));
|
|
21568
21860
|
} catch {
|
|
21569
|
-
|
|
21861
|
+
entries2.push(null);
|
|
21570
21862
|
}
|
|
21571
21863
|
}
|
|
21572
|
-
return
|
|
21864
|
+
return entries2;
|
|
21573
21865
|
}
|
|
21574
21866
|
function isRuntimeContextInjected(entry, nextEntry) {
|
|
21575
21867
|
if (!nextEntry || typeof nextEntry !== "object") return false;
|
|
@@ -21621,16 +21913,16 @@ function findLastRealUserMsg(jsonlPath) {
|
|
|
21621
21913
|
} catch {
|
|
21622
21914
|
return null;
|
|
21623
21915
|
}
|
|
21624
|
-
const
|
|
21625
|
-
for (let i =
|
|
21626
|
-
const entry =
|
|
21916
|
+
const entries2 = parseJsonlEntries(lines);
|
|
21917
|
+
for (let i = entries2.length - 1; i >= 0; i--) {
|
|
21918
|
+
const entry = entries2[i];
|
|
21627
21919
|
if (!entry || typeof entry !== "object") continue;
|
|
21628
21920
|
if (entry.type !== "message") continue;
|
|
21629
21921
|
const msg2 = entry.message;
|
|
21630
21922
|
if (!msg2 || msg2.role !== "user") continue;
|
|
21631
21923
|
const text = extractText3(msg2.content);
|
|
21632
21924
|
if (isSystemSender(text)) continue;
|
|
21633
|
-
const nextEntry = i + 1 <
|
|
21925
|
+
const nextEntry = i + 1 < entries2.length ? entries2[i + 1] : null;
|
|
21634
21926
|
if (isRuntimeContextInjected(entry, nextEntry)) continue;
|
|
21635
21927
|
const ts = entry.timestamp || "";
|
|
21636
21928
|
const clean = cleanText(text);
|
|
@@ -21662,12 +21954,12 @@ function recentMessages(sessions, hours = 12, limit = 60) {
|
|
|
21662
21954
|
const jsonlPath = resolveScopeMainJsonl(sessions);
|
|
21663
21955
|
if (!jsonlPath) return [];
|
|
21664
21956
|
const lines = fs19.readFileSync(jsonlPath, "utf-8").split("\n");
|
|
21665
|
-
const
|
|
21957
|
+
const entries2 = parseJsonlEntries(lines);
|
|
21666
21958
|
const nowMs = Date.now();
|
|
21667
21959
|
const cutoffMs = nowMs - hours * 36e5;
|
|
21668
21960
|
const results = [];
|
|
21669
|
-
for (let i = 0; i <
|
|
21670
|
-
const entry =
|
|
21961
|
+
for (let i = 0; i < entries2.length; i++) {
|
|
21962
|
+
const entry = entries2[i];
|
|
21671
21963
|
if (!entry || typeof entry !== "object") continue;
|
|
21672
21964
|
if (entry.type !== "message") continue;
|
|
21673
21965
|
const msg2 = entry.message;
|
|
@@ -21677,14 +21969,14 @@ function recentMessages(sessions, hours = 12, limit = 60) {
|
|
|
21677
21969
|
const text = extractText3(msg2.content);
|
|
21678
21970
|
if (role === "user") {
|
|
21679
21971
|
if (isSystemSender(text)) continue;
|
|
21680
|
-
const nextEntry = i + 1 <
|
|
21972
|
+
const nextEntry = i + 1 < entries2.length ? entries2[i + 1] : null;
|
|
21681
21973
|
if (isRuntimeContextInjected(entry, nextEntry)) continue;
|
|
21682
21974
|
}
|
|
21683
21975
|
if (role === "assistant") {
|
|
21684
21976
|
if (text.startsWith("HEARTBEAT_OK")) continue;
|
|
21685
21977
|
let isInjectedResponse = false;
|
|
21686
21978
|
for (let j = i - 1; j >= Math.max(i - 5, -1); j--) {
|
|
21687
|
-
const prevE =
|
|
21979
|
+
const prevE = entries2[j];
|
|
21688
21980
|
if (!prevE || typeof prevE !== "object" || prevE.type !== "message") continue;
|
|
21689
21981
|
const prevMsg = prevE.message;
|
|
21690
21982
|
if (!prevMsg || prevMsg.role !== "user") continue;
|
|
@@ -21908,7 +22200,7 @@ async function judgeReason(task, taskState, cfg, provider, model) {
|
|
|
21908
22200
|
const reason = task.blockedReason || "\uFF08\u6CA1\u7ED9\u7406\u7531\uFF09";
|
|
21909
22201
|
const elapsed = taskState.lastProgressAt ? formatDuration(Date.now() - new Date(taskState.lastProgressAt).getTime()) : "\u5F88\u4E45\u6CA1\u52A8\u4E86";
|
|
21910
22202
|
const staleLevel = (taskState.staleLevel || 0) + 1;
|
|
21911
|
-
const systemPrompt = `\u4F60\u662F\
|
|
22203
|
+
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
|
|
21912
22204
|
|
|
21913
22205
|
\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
|
|
21914
22206
|
|
|
@@ -22330,7 +22622,8 @@ var NudgePlugin = class {
|
|
|
22330
22622
|
const stream = this.provider.streamChat({
|
|
22331
22623
|
model: this.model,
|
|
22332
22624
|
systemPrompt: [
|
|
22333
|
-
"
|
|
22625
|
+
// 0831 去掉硬编码人名(原文"判断你(小柯)是否"):engine 代码多 agent 共用,身份由 SOUL.md 定
|
|
22626
|
+
"\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",
|
|
22334
22627
|
"",
|
|
22335
22628
|
"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",
|
|
22336
22629
|
'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',
|
|
@@ -23191,18 +23484,18 @@ function readRecentMessages(sessions, n) {
|
|
|
23191
23484
|
const file = path23.join(sessions.sessionsDir, `${mainId}.jsonl`);
|
|
23192
23485
|
if (!fs23.existsSync(file)) return [];
|
|
23193
23486
|
const lines = readLastNLines(file, n * 4 + 20);
|
|
23194
|
-
const
|
|
23487
|
+
const entries2 = [];
|
|
23195
23488
|
for (const line of lines) {
|
|
23196
23489
|
const trimmed = line.trim();
|
|
23197
23490
|
if (!trimmed) continue;
|
|
23198
23491
|
try {
|
|
23199
|
-
|
|
23492
|
+
entries2.push(JSON.parse(trimmed));
|
|
23200
23493
|
} catch {
|
|
23201
23494
|
}
|
|
23202
23495
|
}
|
|
23203
23496
|
const out = [];
|
|
23204
|
-
for (let i =
|
|
23205
|
-
const e =
|
|
23497
|
+
for (let i = entries2.length - 1; i >= 0 && out.length < n; i--) {
|
|
23498
|
+
const e = entries2[i];
|
|
23206
23499
|
if (!e || typeof e !== "object" || e.type !== "message") continue;
|
|
23207
23500
|
const msg2 = e.message;
|
|
23208
23501
|
if (!msg2) continue;
|
|
@@ -24151,7 +24444,7 @@ function formatBeijingTs(d) {
|
|
|
24151
24444
|
}
|
|
24152
24445
|
|
|
24153
24446
|
// src/calendar/commands.ts
|
|
24154
|
-
import { existsSync as
|
|
24447
|
+
import { existsSync as existsSync16, statSync as statSync8 } from "node:fs";
|
|
24155
24448
|
import { isAbsolute as isAbsolute4, resolve as resolve7 } from "node:path";
|
|
24156
24449
|
var WEEKDAYS2 = ["\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D", "\u5468\u65E5"];
|
|
24157
24450
|
function fmtEnd(start, durationMin) {
|
|
@@ -24323,7 +24616,7 @@ function addTask(db, args) {
|
|
|
24323
24616
|
}
|
|
24324
24617
|
try {
|
|
24325
24618
|
const absPath = isAbsolute4(docPath) ? docPath : resolve7(process.cwd(), docPath);
|
|
24326
|
-
if (!
|
|
24619
|
+
if (!existsSync16(absPath)) {
|
|
24327
24620
|
return `\u274C \u521B\u5EFA\u5931\u8D25\uFF1Adoc_path \u6307\u5411\u7684\u6587\u4EF6\u4E0D\u5B58\u5728
|
|
24328
24621
|
\u8DEF\u5F84: ${docPath}
|
|
24329
24622
|
\u89E3\u6790\u540E: ${absPath}
|
|
@@ -24806,7 +25099,7 @@ function registerVoiceChatBridge(httpServer, dispatcher, deps, config, sessions,
|
|
|
24806
25099
|
}
|
|
24807
25100
|
|
|
24808
25101
|
// src/voice-chat/config.ts
|
|
24809
|
-
var
|
|
25102
|
+
var DEFAULTS3 = {
|
|
24810
25103
|
enabled: false,
|
|
24811
25104
|
pythonPort: 8011,
|
|
24812
25105
|
webhookPath: "/webhook/voice-chat",
|
|
@@ -24817,20 +25110,20 @@ var DEFAULTS2 = {
|
|
|
24817
25110
|
// 8/25 翀哥:断句等待默认 2s(samples@16kHz)
|
|
24818
25111
|
};
|
|
24819
25112
|
function parseVoiceChatConfig(raw) {
|
|
24820
|
-
if (!raw) return { ...
|
|
25113
|
+
if (!raw) return { ...DEFAULTS3 };
|
|
24821
25114
|
return {
|
|
24822
25115
|
enabled: raw.enabled === true,
|
|
24823
25116
|
spawnPython: raw.spawnPython !== false,
|
|
24824
25117
|
// 默认 true,配 false 只注册 webhook
|
|
24825
|
-
pythonPort: raw.pythonPort ??
|
|
24826
|
-
webhookPath: raw.webhookPath ??
|
|
24827
|
-
callbackPath: raw.callbackPath ??
|
|
25118
|
+
pythonPort: raw.pythonPort ?? DEFAULTS3.pythonPort,
|
|
25119
|
+
webhookPath: raw.webhookPath ?? DEFAULTS3.webhookPath,
|
|
25120
|
+
callbackPath: raw.callbackPath ?? DEFAULTS3.callbackPath,
|
|
24828
25121
|
pythonPath: raw.pythonPath,
|
|
24829
25122
|
vadModelPath: raw.vadModelPath,
|
|
24830
25123
|
asrModelPath: raw.asrModelPath || "iic/SenseVoiceSmall",
|
|
24831
|
-
asrLanguage: raw.asrLanguage ??
|
|
24832
|
-
vadThreshold: raw.vadThreshold ??
|
|
24833
|
-
postEndMonitor: raw.postEndMonitor ??
|
|
25124
|
+
asrLanguage: raw.asrLanguage ?? DEFAULTS3.asrLanguage,
|
|
25125
|
+
vadThreshold: raw.vadThreshold ?? DEFAULTS3.vadThreshold,
|
|
25126
|
+
postEndMonitor: raw.postEndMonitor ?? DEFAULTS3.postEndMonitor,
|
|
24834
25127
|
model: raw.model,
|
|
24835
25128
|
thinking: raw.thinking === true,
|
|
24836
25129
|
tts: raw.tts ? {
|
|
@@ -25152,7 +25445,7 @@ import path28 from "node:path";
|
|
|
25152
25445
|
import fs28 from "node:fs";
|
|
25153
25446
|
|
|
25154
25447
|
// src/memory/cognifold/config.ts
|
|
25155
|
-
var
|
|
25448
|
+
var DEFAULTS4 = {
|
|
25156
25449
|
pythonPort: 9001,
|
|
25157
25450
|
autoStart: true,
|
|
25158
25451
|
persistDir: "./sessions",
|
|
@@ -25164,15 +25457,15 @@ function parseCognifoldConfig(raw) {
|
|
|
25164
25457
|
if (!raw) return { enabled: false };
|
|
25165
25458
|
return {
|
|
25166
25459
|
enabled: raw.enabled === true,
|
|
25167
|
-
pythonPort: raw.pythonPort ??
|
|
25168
|
-
pythonPath: raw.pythonPath ??
|
|
25460
|
+
pythonPort: raw.pythonPort ?? DEFAULTS4.pythonPort,
|
|
25461
|
+
pythonPath: raw.pythonPath ?? DEFAULTS4.pythonPath,
|
|
25169
25462
|
autoStart: raw.autoStart !== false,
|
|
25170
25463
|
// default true
|
|
25171
|
-
baseUrl: raw.baseUrl ?? `http://127.0.0.1:${raw.pythonPort ??
|
|
25172
|
-
persistDir: raw.persistDir ??
|
|
25464
|
+
baseUrl: raw.baseUrl ?? `http://127.0.0.1:${raw.pythonPort ?? DEFAULTS4.pythonPort}/api/v1`,
|
|
25465
|
+
persistDir: raw.persistDir ?? DEFAULTS4.persistDir,
|
|
25173
25466
|
scopes: raw.scopes,
|
|
25174
|
-
readyTimeoutMs: raw.readyTimeoutMs ??
|
|
25175
|
-
maxRestarts: raw.maxRestarts ??
|
|
25467
|
+
readyTimeoutMs: raw.readyTimeoutMs ?? DEFAULTS4.readyTimeoutMs,
|
|
25468
|
+
maxRestarts: raw.maxRestarts ?? DEFAULTS4.maxRestarts,
|
|
25176
25469
|
llm: raw.llm
|
|
25177
25470
|
};
|
|
25178
25471
|
}
|
|
@@ -25288,13 +25581,13 @@ var CogniFoldClient = class {
|
|
|
25288
25581
|
|
|
25289
25582
|
// src/memory/cognifold/session-manager.ts
|
|
25290
25583
|
import { readFile as readFile6, writeFile as writeFile5, mkdir as mkdir4 } from "node:fs/promises";
|
|
25291
|
-
import { join as
|
|
25584
|
+
import { join as join28, dirname as dirname3 } from "node:path";
|
|
25292
25585
|
var CogniFoldSessionManager = class {
|
|
25293
25586
|
constructor(workspacePath, config, client) {
|
|
25294
25587
|
this.workspacePath = workspacePath;
|
|
25295
25588
|
this.config = config;
|
|
25296
25589
|
this.client = client;
|
|
25297
|
-
this.sessionsDir =
|
|
25590
|
+
this.sessionsDir = join28(workspacePath, ".cognifold", "sessions");
|
|
25298
25591
|
}
|
|
25299
25592
|
workspacePath;
|
|
25300
25593
|
config;
|
|
@@ -25364,7 +25657,7 @@ var CogniFoldSessionManager = class {
|
|
|
25364
25657
|
console.log(`[cognifold] Created new session for scope "${scope}": ${newSession.sessionId}`);
|
|
25365
25658
|
}
|
|
25366
25659
|
getFilePath(scope) {
|
|
25367
|
-
return
|
|
25660
|
+
return join28(this.sessionsDir, `${scope}.json`);
|
|
25368
25661
|
}
|
|
25369
25662
|
async writeFileSafe(filePath, data) {
|
|
25370
25663
|
try {
|
|
@@ -25684,7 +25977,7 @@ import path29 from "node:path";
|
|
|
25684
25977
|
import fs29 from "node:fs";
|
|
25685
25978
|
|
|
25686
25979
|
// src/memory/everos/config.ts
|
|
25687
|
-
var
|
|
25980
|
+
var DEFAULTS5 = {
|
|
25688
25981
|
everosUrl: "http://127.0.0.1:8100",
|
|
25689
25982
|
agenticUrl: "http://127.0.0.1:8101",
|
|
25690
25983
|
agenticPort: 8101,
|
|
@@ -25710,7 +26003,7 @@ function parseEverosConfig(raw, providers) {
|
|
|
25710
26003
|
if (!raw) {
|
|
25711
26004
|
return {
|
|
25712
26005
|
enabled: false,
|
|
25713
|
-
...
|
|
26006
|
+
...DEFAULTS5,
|
|
25714
26007
|
userId: "xiaomei",
|
|
25715
26008
|
llm: { model: "glm-5.2", apiKey: "", baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4" },
|
|
25716
26009
|
rerank: { model: "Qwen/Qwen3-Reranker-4B", apiKey: "", baseUrl: "https://api.deepinfra.com/v1/inference" },
|
|
@@ -25720,12 +26013,12 @@ function parseEverosConfig(raw, providers) {
|
|
|
25720
26013
|
}
|
|
25721
26014
|
return {
|
|
25722
26015
|
enabled: raw.enabled === true,
|
|
25723
|
-
everosUrl: raw.everosUrl ??
|
|
25724
|
-
agenticUrl: raw.agenticUrl ??
|
|
25725
|
-
agenticPort: raw.agenticPort ??
|
|
26016
|
+
everosUrl: raw.everosUrl ?? DEFAULTS5.everosUrl,
|
|
26017
|
+
agenticUrl: raw.agenticUrl ?? DEFAULTS5.agenticUrl,
|
|
26018
|
+
agenticPort: raw.agenticPort ?? DEFAULTS5.agenticPort,
|
|
25726
26019
|
userId: raw.userId ?? "xiaomei",
|
|
25727
26020
|
autoStart: raw.autoStart !== false,
|
|
25728
|
-
defaultMode: raw.defaultMode ??
|
|
26021
|
+
defaultMode: raw.defaultMode ?? DEFAULTS5.defaultMode,
|
|
25729
26022
|
llm: resolveProviderConfig(
|
|
25730
26023
|
raw.llm,
|
|
25731
26024
|
providers,
|
|
@@ -26135,8 +26428,8 @@ function scanSkills(skillsDir) {
|
|
|
26135
26428
|
}
|
|
26136
26429
|
const skills = [];
|
|
26137
26430
|
const scanDir = (dir, depth) => {
|
|
26138
|
-
const
|
|
26139
|
-
for (const entry of
|
|
26431
|
+
const entries2 = fs30.readdirSync(dir, { withFileTypes: true });
|
|
26432
|
+
for (const entry of entries2) {
|
|
26140
26433
|
if (entry.name.startsWith(".") || entry.name === "_archive") continue;
|
|
26141
26434
|
const full = path30.join(dir, entry.name);
|
|
26142
26435
|
if (entry.isDirectory() && depth < 3) {
|
|
@@ -26427,6 +26720,7 @@ ${rawOutput}
|
|
|
26427
26720
|
// src/tools/msg-send.ts
|
|
26428
26721
|
init_live();
|
|
26429
26722
|
init_registry();
|
|
26723
|
+
init_rate_breaker();
|
|
26430
26724
|
function getConfig() {
|
|
26431
26725
|
return liveConfig.all();
|
|
26432
26726
|
}
|
|
@@ -26490,10 +26784,10 @@ channel_id \u4E0D\u586B\u4E14 to \u4E5F\u4E0D\u586B\u65F6\uFF0C\u9ED8\u8BA4\u56D
|
|
|
26490
26784
|
\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
|
|
26491
26785
|
|
|
26492
26786
|
Examples:
|
|
26493
|
-
- \u53D1\u9891\u9053\u5E76 @\u67D0\u4EBA: to="
|
|
26494
|
-
- \u53D1\u9891\u9053\u5E76 @\u591A\u4EBA: to="
|
|
26495
|
-
- \u53D1\u9891\u9053\u4E0D\u5E26 @: channel_id="
|
|
26496
|
-
- \u53D1 DM: to="
|
|
26787
|
+
- \u53D1\u9891\u9053\u5E76 @\u67D0\u4EBA: to="1111111111111111111", channel_id="3333333333333333333", content="\u4F60\u597D"
|
|
26788
|
+
- \u53D1\u9891\u9053\u5E76 @\u591A\u4EBA: to="1111111111111111111,2222222222222222222", channel_id="3333333333333333333", content="\u4F60\u597D"
|
|
26789
|
+
- \u53D1\u9891\u9053\u4E0D\u5E26 @: channel_id="3333333333333333333", content="\u7CFB\u7EDF\u901A\u77E5"
|
|
26790
|
+
- \u53D1 DM: to="1111111111111111111", content="\u79C1\u804A\u5185\u5BB9"
|
|
26497
26791
|
- \u8DE8\u5E73\u53F0\u53D1\u9001: source="discord", channel_id="1503034906081624174", content="\u4ECE\u98DE\u4E66\u53D1\u5230Discord"
|
|
26498
26792
|
- \u56DE\u590D\u6765\u6E90\u9891\u9053: content="\u6536\u5230"\uFF08\u4E0D\u586B channel_id \u548C to\uFF09`,
|
|
26499
26793
|
schema: {
|
|
@@ -26522,12 +26816,12 @@ Examples:
|
|
|
26522
26816
|
}
|
|
26523
26817
|
const toIds = to ? to.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
26524
26818
|
const dest = resolvedChannelId || (toIds.length > 0 ? toIds[0] : void 0);
|
|
26819
|
+
const dmEchoDest = resolvedChannelId || (toIds.length > 0 ? toIds[0] : void 0);
|
|
26525
26820
|
const isDmEcho = ctx.channelType === "dm" && // DM 对话(群聊先观察不拦)
|
|
26526
26821
|
resolvedSource === ctx.channel && // 目标通道=当前对话通道(真跨通道转发不拦)
|
|
26527
26822
|
ctx.inboundFrom && // 有当前发送者(注入消息无 inboundFrom 不拦)
|
|
26528
|
-
|
|
26529
|
-
|
|
26530
|
-
!!resolvedChannelId && resolvedChannelId === ctx.channelTarget);
|
|
26823
|
+
dmEchoDest !== void 0 && (dmEchoDest === ctx.channelTarget || dmEchoDest === ctx.inboundFrom) && // 目的地=当前会话/当前对话者
|
|
26824
|
+
toIds.every((id) => id === ctx.inboundFrom);
|
|
26531
26825
|
if (isDmEcho) {
|
|
26532
26826
|
console.log(`[msg_send] \u26D4 DM \u56DE\u53D1\u5F53\u524D\u5BF9\u8BDD\u88AB\u62E6: to=${to} channel_id=${resolvedChannelId || "(fallback)"} (${resolvedSource})`);
|
|
26533
26827
|
return {
|
|
@@ -26558,6 +26852,11 @@ Examples:
|
|
|
26558
26852
|
}
|
|
26559
26853
|
const fullMsg = `${mentionPrefix}${content}`;
|
|
26560
26854
|
const where = resolvedChannelId ? `${resolvedSource} \u9891\u9053 ${resolvedChannelId}` : `${resolvedSource} DM ${toIds[0]}`;
|
|
26855
|
+
const breaker = checkRateBreaker(resolvedSource, ctx.sessionId, dest, toIds, !!ctx.inboundIsBot);
|
|
26856
|
+
if (breaker) {
|
|
26857
|
+
console.log(`[msg_send] \u{1F515} rate-breaker \u62E6\u622A: session=${ctx.sessionId} \u2192 ${where}`);
|
|
26858
|
+
return breaker;
|
|
26859
|
+
}
|
|
26561
26860
|
try {
|
|
26562
26861
|
await mgr.send(resolvedSource, dest, fullMsg);
|
|
26563
26862
|
return { content: `\u6D88\u606F\u5DF2\u53D1\u9001\u5230 ${where}` };
|
|
@@ -26673,10 +26972,10 @@ Parameters:
|
|
|
26673
26972
|
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
|
|
26674
26973
|
|
|
26675
26974
|
Examples:
|
|
26676
|
-
- \u53D1\u56FE\u7247\u5230\u9891\u9053\u5E76 @\u67D0\u4EBA: to="
|
|
26677
|
-
- \u53D1\u56FE\u7247\u5230\u9891\u9053\u5E76 @\u591A\u4EBA: to="
|
|
26678
|
-
- \u53D1\u6587\u4EF6\u5230\u9891\u9053\u4E0D\u5E26 @: channel_id="
|
|
26679
|
-
- \u53D1\u97F3\u9891 DM: to="
|
|
26975
|
+
- \u53D1\u56FE\u7247\u5230\u9891\u9053\u5E76 @\u67D0\u4EBA: to="1111111111111111111", channel_id="3333333333333333333", type="image", path="/tmp/photo.png"
|
|
26976
|
+
- \u53D1\u56FE\u7247\u5230\u9891\u9053\u5E76 @\u591A\u4EBA: to="1111111111111111111,2222222222222222222", channel_id="3333333333333333333", type="image", path="/tmp/photo.png"
|
|
26977
|
+
- \u53D1\u6587\u4EF6\u5230\u9891\u9053\u4E0D\u5E26 @: channel_id="3333333333333333333", type="file", path="/tmp/report.pdf"
|
|
26978
|
+
- \u53D1\u97F3\u9891 DM: to="1111111111111111111", type="audio", path="/tmp/voice.mp3"
|
|
26680
26979
|
- \u8DE8\u5E73\u53F0\u53D1\u9001: source="discord", channel_id="1503034906081624174", type="image", path="/tmp/photo.png"
|
|
26681
26980
|
- \u53D1\u5230\u6765\u6E90\u9891\u9053: type="image", path="/tmp/photo.png"\uFF08\u4E0D\u586B channel_id \u548C to\uFF09`,
|
|
26682
26981
|
schema: {
|
|
@@ -29742,8 +30041,8 @@ registerCommand({
|
|
|
29742
30041
|
const { rm: rm2 } = await import("node:fs/promises");
|
|
29743
30042
|
const teamsDir = getTeamsDir3();
|
|
29744
30043
|
const { readdir: readdir2 } = await import("node:fs/promises");
|
|
29745
|
-
const
|
|
29746
|
-
for (const entry of
|
|
30044
|
+
const entries2 = await readdir2(teamsDir).catch(() => []);
|
|
30045
|
+
for (const entry of entries2) {
|
|
29747
30046
|
const entryPath = `${teamsDir}/${entry}`;
|
|
29748
30047
|
try {
|
|
29749
30048
|
await rm2(entryPath, { recursive: true, force: true });
|
|
@@ -29852,8 +30151,21 @@ registerCommand({
|
|
|
29852
30151
|
let current;
|
|
29853
30152
|
if (deps.getModelOverride()) {
|
|
29854
30153
|
current = `**${deps.getModelOverride()}** (override)`;
|
|
29855
|
-
} else if (deps.provider instanceof FallbackProvider
|
|
29856
|
-
|
|
30154
|
+
} else if (deps.provider instanceof FallbackProvider) {
|
|
30155
|
+
const status = deps.provider.getChainStatus();
|
|
30156
|
+
const chainStr = status.map((e) => e.label).join(" \u2192 ");
|
|
30157
|
+
const next = status.find((e) => e.cooldownMs <= 0);
|
|
30158
|
+
const cooling = status.filter((e) => e.cooldownMs > 0);
|
|
30159
|
+
current = `**auto-route** \u2014 \u94FE: ${chainStr}
|
|
30160
|
+
\u4E0B\u4E00\u4E2A\u8BF7\u6C42\u7528: **${next?.label ?? "(\u5168\u90E8\u51B7\u5374\u4E2D)"}**`;
|
|
30161
|
+
if (cooling.length > 0) {
|
|
30162
|
+
current += `
|
|
30163
|
+
\u51B7\u5374\u4E2D: ${cooling.map((e) => `${e.label}\uFF08\u5269 ${Math.round(e.cooldownMs / 6e4)}min\uFF09`).join("\u3001")}`;
|
|
30164
|
+
}
|
|
30165
|
+
if (deps.provider.lastUsedLabel) {
|
|
30166
|
+
current += `
|
|
30167
|
+
\u4E0A\u6B21\u5B9E\u9645: ${deps.provider.lastUsedLabel}`;
|
|
30168
|
+
}
|
|
29857
30169
|
} else {
|
|
29858
30170
|
current = `**${deps.config.provider.id}/${deps.config.model}** (default, auto-route)`;
|
|
29859
30171
|
}
|
|
@@ -30107,6 +30419,16 @@ Use full ref like \`/vision-model ${candidates[0].ref}\``);
|
|
|
30107
30419
|
});
|
|
30108
30420
|
|
|
30109
30421
|
// src/engine-startup.ts
|
|
30422
|
+
function resolveModelMaxTokens(modelRef) {
|
|
30423
|
+
try {
|
|
30424
|
+
const [pid, mid] = (modelRef || "").split("/");
|
|
30425
|
+
const models = liveConfig.get(`models.providers.${pid}.models`);
|
|
30426
|
+
const m = models?.find((x) => x?.id === mid);
|
|
30427
|
+
return typeof m?.maxTokens === "number" && m.maxTokens > 0 ? m.maxTokens : 4096;
|
|
30428
|
+
} catch {
|
|
30429
|
+
return 4096;
|
|
30430
|
+
}
|
|
30431
|
+
}
|
|
30110
30432
|
var _epipeSeen = false;
|
|
30111
30433
|
process.on("uncaughtException", (err) => {
|
|
30112
30434
|
const code = err?.code ?? "";
|
|
@@ -30176,7 +30498,10 @@ async function startEngine(config, opts) {
|
|
|
30176
30498
|
const licensedFeatures = loadLicense(config.stateDir, config.profile?.devMode === true);
|
|
30177
30499
|
const requiredTools = resolveRequiredTools(config.profile.features, licensedFeatures);
|
|
30178
30500
|
registry.licensedFeatures = licensedFeatures;
|
|
30179
|
-
|
|
30501
|
+
let provider;
|
|
30502
|
+
let visionProvider;
|
|
30503
|
+
let visionChainLabels;
|
|
30504
|
+
({ provider, visionProvider, visionChainLabels } = buildProviderChain(config));
|
|
30180
30505
|
if (visionChainLabels.length > 0) {
|
|
30181
30506
|
console.log(`[vision] Routing enabled: ${visionChainLabels.join(" \u2192 ")}`);
|
|
30182
30507
|
}
|
|
@@ -30398,11 +30723,36 @@ ${content}`
|
|
|
30398
30723
|
console.log(`[DEBUG] definitions() = ${_allDefs.length} defs`);
|
|
30399
30724
|
console.log(`[DEBUG] active (non-defer) = ${_activeDefs.length}: ${_activeDefs.map((d) => d.function.name).join(", ")}`);
|
|
30400
30725
|
console.log(`[DEBUG] deferred = ${_deferredDefs.length}: ${_deferredDefs.map((d) => d.function.name).join(", ")}`);
|
|
30401
|
-
|
|
30726
|
+
let _stableCache = null;
|
|
30727
|
+
const getSystemStable = () => {
|
|
30728
|
+
const promptCfg = liveConfig.get("prompt") || {};
|
|
30729
|
+
const fileCandidates = /* @__PURE__ */ new Set(["SOUL.md"]);
|
|
30730
|
+
for (const f of promptCfg.staticFiles || []) fileCandidates.add(f);
|
|
30731
|
+
for (const item of promptCfg.order || []) {
|
|
30732
|
+
if (typeof item === "string" && /\.(md|txt|json)$/i.test(item)) fileCandidates.add(item);
|
|
30733
|
+
}
|
|
30734
|
+
const statLines = [JSON.stringify({ mode: promptCfg.mode, order: promptCfg.order })];
|
|
30735
|
+
for (const f of fileCandidates) {
|
|
30736
|
+
try {
|
|
30737
|
+
const p = path49.isAbsolute(f) ? f : path49.join(config.workspace, f);
|
|
30738
|
+
statLines.push(`${f}:${fs47.statSync(p).mtimeMs}`);
|
|
30739
|
+
} catch {
|
|
30740
|
+
statLines.push(`${f}:missing`);
|
|
30741
|
+
}
|
|
30742
|
+
}
|
|
30743
|
+
const fingerprint = statLines.join("|");
|
|
30744
|
+
if (_stableCache && _stableCache.fingerprint === fingerprint) return _stableCache.prompt;
|
|
30745
|
+
const prompt = buildStablePrompt(config.workspace, promptCfg);
|
|
30746
|
+
_stableCache = { fingerprint, prompt };
|
|
30747
|
+
console.log(`[prompt] stable \u91CD\u5EFA\uFF08\u6587\u4EF6/config \u53D8\u66F4\uFF09\uFF0C${prompt.length} chars`);
|
|
30748
|
+
return prompt;
|
|
30749
|
+
};
|
|
30750
|
+
const systemStable = getSystemStable();
|
|
30402
30751
|
const systemDynamic = buildDynamicPrompt({
|
|
30403
30752
|
workspace: config.workspace
|
|
30404
30753
|
});
|
|
30405
30754
|
const systemPrompt = [systemStable, systemDynamic].join("\n\n");
|
|
30755
|
+
const getSystemPrompt = () => [getSystemStable(), buildDynamicPrompt({ workspace: config.workspace })].join("\n\n");
|
|
30406
30756
|
dumpSystemPrompt(config.workspace, systemStable, systemDynamic);
|
|
30407
30757
|
const modelDef = config.provider.models.find((m) => m.id === config.model);
|
|
30408
30758
|
const modelContextWindow = modelDef?.contextWindow;
|
|
@@ -30421,13 +30771,21 @@ ${content}`
|
|
|
30421
30771
|
// 默认 5MB
|
|
30422
30772
|
);
|
|
30423
30773
|
const memoryFlushEnabled = config.compaction?.memoryFlush?.enabled !== false;
|
|
30424
|
-
const
|
|
30425
|
-
|
|
30426
|
-
|
|
30427
|
-
|
|
30428
|
-
|
|
30429
|
-
|
|
30774
|
+
const getCompactConfig = () => {
|
|
30775
|
+
const liveComp = liveConfig.get("compaction") || {};
|
|
30776
|
+
const liveModel = (liveConfig.get("providers") || {})[liveConfig.get("agents.defaults.model.primary")?.split("/")[0] || ""];
|
|
30777
|
+
const liveModelId = liveConfig.get("agents.defaults.model.primary")?.split("/")?.[1];
|
|
30778
|
+
const mDef = liveModel?.models?.find((m) => m.id === liveModelId);
|
|
30779
|
+
const mCW = mDef?.contextWindow;
|
|
30780
|
+
const cfg = {
|
|
30781
|
+
...DEFAULT_COMPACT_CONFIG,
|
|
30782
|
+
...liveComp,
|
|
30783
|
+
...mCW && !liveComp.contextWindow ? { contextWindow: mCW } : {},
|
|
30784
|
+
forceFlushTranscriptBytes
|
|
30785
|
+
};
|
|
30786
|
+
return cfg;
|
|
30430
30787
|
};
|
|
30788
|
+
const compactConfig = getCompactConfig();
|
|
30431
30789
|
if (compactConfig.contextWindow !== DEFAULT_COMPACT_CONFIG.contextWindow) {
|
|
30432
30790
|
console.log(`[compact] Context window: ${compactConfig.contextWindow} (from ${config.compaction?.contextWindow ? "config" : modelContextWindow ? "model" : "default"})`);
|
|
30433
30791
|
}
|
|
@@ -30436,12 +30794,12 @@ ${content}`
|
|
|
30436
30794
|
}
|
|
30437
30795
|
const engine = new QueryEngine(provider, {
|
|
30438
30796
|
model: config.model,
|
|
30439
|
-
systemPrompt,
|
|
30440
|
-
systemStable,
|
|
30441
|
-
//
|
|
30442
|
-
compactConfig,
|
|
30443
|
-
//
|
|
30444
|
-
maxTokens:
|
|
30797
|
+
systemPrompt: getSystemPrompt,
|
|
30798
|
+
systemStable: getSystemStable,
|
|
30799
|
+
// 0902 函数形态:mtime 缓存 getter,改 prompt 文件热生效
|
|
30800
|
+
compactConfig: getCompactConfig,
|
|
30801
|
+
// 0902 函数形态:每 turn 刷新
|
|
30802
|
+
maxTokens: () => resolveModelMaxTokens(liveConfig.get("agents.defaults.model.primary") || config.model),
|
|
30445
30803
|
temperature: 0.7,
|
|
30446
30804
|
maxTurns: config.profile.maxTurns,
|
|
30447
30805
|
// 从配置读,默认 50(query.ts 里 fallback)
|
|
@@ -30452,10 +30810,10 @@ ${content}`
|
|
|
30452
30810
|
if (visionProvider && visionConfig) {
|
|
30453
30811
|
visionEngine = new QueryEngine(visionProvider, {
|
|
30454
30812
|
model: visionConfig.modelId,
|
|
30455
|
-
systemPrompt,
|
|
30456
|
-
systemStable,
|
|
30457
|
-
compactConfig,
|
|
30458
|
-
maxTokens:
|
|
30813
|
+
systemPrompt: getSystemPrompt,
|
|
30814
|
+
systemStable: getSystemStable,
|
|
30815
|
+
compactConfig: getCompactConfig,
|
|
30816
|
+
maxTokens: () => resolveModelMaxTokens(`${visionConfig.providerId}/${visionConfig.modelId}`),
|
|
30459
30817
|
temperature: 0.7,
|
|
30460
30818
|
maxTurns: config.profile.maxTurns,
|
|
30461
30819
|
agentLabel: "main"
|
|
@@ -30527,7 +30885,8 @@ ${content}`
|
|
|
30527
30885
|
providerApi: config.provider.api,
|
|
30528
30886
|
model: config.model,
|
|
30529
30887
|
modelInputs: modelDef?.input || ["text"],
|
|
30530
|
-
systemPrompt,
|
|
30888
|
+
systemPrompt: getSystemPrompt,
|
|
30889
|
+
// 0902 函数形态:/btw、voice-chat 等按调用时现取(stable mtime 缓存 + dynamic 现算)
|
|
30531
30890
|
channels: config.channels,
|
|
30532
30891
|
config,
|
|
30533
30892
|
// tool 读自己配置用
|
|
@@ -30541,36 +30900,28 @@ ${content}`
|
|
|
30541
30900
|
} : void 0,
|
|
30542
30901
|
mcpManager
|
|
30543
30902
|
};
|
|
30544
|
-
|
|
30903
|
+
const visionMetaInit = visionEngine && visionConfig ? (() => {
|
|
30545
30904
|
const vpCfg = config.providers?.[visionConfig.providerId];
|
|
30546
30905
|
const visionModelDef = vpCfg?.models?.find((m) => m.id === visionConfig.modelId);
|
|
30547
|
-
|
|
30548
|
-
engine: visionEngine,
|
|
30549
|
-
sessions,
|
|
30550
|
-
channelManager,
|
|
30551
|
-
workspace: config.workspace,
|
|
30906
|
+
return {
|
|
30552
30907
|
providerId: visionConfig.providerId,
|
|
30553
30908
|
providerApi: vpCfg?.api || "openai-completions",
|
|
30554
30909
|
model: visionConfig.modelId,
|
|
30555
|
-
modelInputs: visionModelDef?.input || ["text", "image"]
|
|
30556
|
-
systemPrompt,
|
|
30557
|
-
channels: config.channels,
|
|
30558
|
-
config,
|
|
30559
|
-
// tool 读自己配置用
|
|
30560
|
-
recallProvider: memoryRecallProvider || void 0,
|
|
30561
|
-
extractProvider: memoryExtractProvider || void 0,
|
|
30562
|
-
everosCfg: config.everos ? {
|
|
30563
|
-
...config.everos,
|
|
30564
|
-
// resolve provider 引用:从 providers 取 apiKey(跟主 deps 同逻辑)
|
|
30565
|
-
llm: config.everos.llm?.provider && config.providers?.[config.everos.llm.provider] ? { ...config.everos.llm, apiKey: config.providers[config.everos.llm.provider].apiKey } : config.everos.llm,
|
|
30566
|
-
rerank: config.everos.rerank?.provider && config.providers?.[config.everos.rerank.provider] ? { ...config.everos.rerank, apiKey: config.providers[config.everos.rerank.provider].apiKey } : config.everos.rerank
|
|
30567
|
-
} : void 0
|
|
30910
|
+
modelInputs: visionModelDef?.input || ["text", "image"]
|
|
30568
30911
|
};
|
|
30912
|
+
})() : null;
|
|
30913
|
+
let visionMeta = visionMetaInit;
|
|
30914
|
+
function resolveVisionDeps() {
|
|
30915
|
+
if (!visionEngine || !visionMeta) return null;
|
|
30916
|
+
if (!visionDeps) {
|
|
30917
|
+
visionDeps = { ...deps, engine: visionEngine, ...visionMeta };
|
|
30918
|
+
console.log(`[vision] deps built: ${visionMeta.providerId}/${visionMeta.model}`);
|
|
30919
|
+
}
|
|
30920
|
+
return visionDeps;
|
|
30569
30921
|
}
|
|
30570
30922
|
let modelOverride = null;
|
|
30571
30923
|
let modelOverrideEngine = null;
|
|
30572
30924
|
let visionOverride = null;
|
|
30573
|
-
const defaultVisionDeps = visionDeps;
|
|
30574
30925
|
const modelDepsCache = /* @__PURE__ */ new Map();
|
|
30575
30926
|
deps.invalidateDeskBuddyDeps = () => {
|
|
30576
30927
|
deskBuddyDeps = null;
|
|
@@ -30596,6 +30947,7 @@ ${content}`
|
|
|
30596
30947
|
if (!p.provider) {
|
|
30597
30948
|
visionEngine = null;
|
|
30598
30949
|
visionDeps = null;
|
|
30950
|
+
visionMeta = null;
|
|
30599
30951
|
return;
|
|
30600
30952
|
}
|
|
30601
30953
|
if (visionEngine) {
|
|
@@ -30604,39 +30956,22 @@ ${content}`
|
|
|
30604
30956
|
} else {
|
|
30605
30957
|
visionEngine = new QueryEngine(p.provider, {
|
|
30606
30958
|
model: p.model,
|
|
30607
|
-
systemPrompt,
|
|
30608
|
-
systemStable,
|
|
30609
|
-
compactConfig,
|
|
30959
|
+
systemPrompt: getSystemPrompt,
|
|
30960
|
+
systemStable: getSystemStable,
|
|
30961
|
+
compactConfig: getCompactConfig,
|
|
30610
30962
|
maxTokens: 4096,
|
|
30611
30963
|
temperature: 0.7,
|
|
30612
30964
|
maxTurns: config.profile.maxTurns,
|
|
30613
30965
|
agentLabel: "main"
|
|
30614
30966
|
});
|
|
30615
30967
|
}
|
|
30616
|
-
|
|
30617
|
-
|
|
30618
|
-
|
|
30619
|
-
|
|
30620
|
-
|
|
30621
|
-
|
|
30622
|
-
|
|
30623
|
-
visionDeps = {
|
|
30624
|
-
engine: visionEngine,
|
|
30625
|
-
sessions,
|
|
30626
|
-
channelManager,
|
|
30627
|
-
workspace: config.workspace,
|
|
30628
|
-
providerId: p.providerId,
|
|
30629
|
-
providerApi: p.providerApi,
|
|
30630
|
-
model: p.model,
|
|
30631
|
-
modelInputs: p.modelInputs,
|
|
30632
|
-
systemPrompt,
|
|
30633
|
-
channels: config.channels,
|
|
30634
|
-
config,
|
|
30635
|
-
recallProvider: memoryRecallProvider || void 0,
|
|
30636
|
-
extractProvider: memoryExtractProvider || void 0,
|
|
30637
|
-
everosCfg: deps?.everosCfg
|
|
30638
|
-
};
|
|
30639
|
-
}
|
|
30968
|
+
visionMeta = {
|
|
30969
|
+
providerId: p.providerId,
|
|
30970
|
+
providerApi: p.providerApi,
|
|
30971
|
+
model: p.model,
|
|
30972
|
+
modelInputs: p.modelInputs
|
|
30973
|
+
};
|
|
30974
|
+
visionDeps = null;
|
|
30640
30975
|
};
|
|
30641
30976
|
function createModelDeps(ref) {
|
|
30642
30977
|
const slashIdx = ref.indexOf("/");
|
|
@@ -30656,28 +30991,21 @@ ${content}`
|
|
|
30656
30991
|
const llmProvider = providerId === config.provider.id ? provider : createProvider(providerCfg);
|
|
30657
30992
|
const engine2 = new QueryEngine(llmProvider, {
|
|
30658
30993
|
model: modelId,
|
|
30659
|
-
systemPrompt,
|
|
30660
|
-
systemStable,
|
|
30661
|
-
compactConfig,
|
|
30662
|
-
maxTokens:
|
|
30994
|
+
systemPrompt: getSystemPrompt,
|
|
30995
|
+
systemStable: getSystemStable,
|
|
30996
|
+
compactConfig: getCompactConfig,
|
|
30997
|
+
maxTokens: () => resolveModelMaxTokens(`${providerId}/${modelId}`),
|
|
30663
30998
|
temperature: 0.7,
|
|
30664
30999
|
maxTurns: config.profile.maxTurns,
|
|
30665
31000
|
agentLabel: `main:${ref}`
|
|
30666
31001
|
});
|
|
30667
31002
|
return {
|
|
31003
|
+
...deps,
|
|
30668
31004
|
engine: engine2,
|
|
30669
|
-
sessions,
|
|
30670
|
-
channelManager,
|
|
30671
|
-
workspace: config.workspace,
|
|
30672
31005
|
providerId,
|
|
30673
31006
|
providerApi: providerCfg.api,
|
|
30674
31007
|
model: modelId,
|
|
30675
|
-
modelInputs: modelDef2.input || ["text"]
|
|
30676
|
-
systemPrompt,
|
|
30677
|
-
channels: config.channels,
|
|
30678
|
-
recallProvider: memoryRecallProvider || void 0,
|
|
30679
|
-
extractProvider: memoryExtractProvider || void 0,
|
|
30680
|
-
everosCfg: deps?.everosCfg
|
|
31008
|
+
modelInputs: modelDef2.input || ["text"]
|
|
30681
31009
|
};
|
|
30682
31010
|
}
|
|
30683
31011
|
const dispatcher = new MessageDispatcher();
|
|
@@ -30854,7 +31182,17 @@ ${content}`
|
|
|
30854
31182
|
console.log(`[cron] config check: enabled=${config.cron?.enabled}, hasConfig=${!!config.cron}`);
|
|
30855
31183
|
if (config.cron?.enabled) {
|
|
30856
31184
|
const { CronPlugin: CronPlugin2 } = await Promise.resolve().then(() => (init_cron_plugin(), cron_plugin_exports));
|
|
30857
|
-
const
|
|
31185
|
+
const resolveCronModelDeps = (ref) => {
|
|
31186
|
+
const key = `cron:${ref}`;
|
|
31187
|
+
const cached = modelDepsCache.get(key);
|
|
31188
|
+
if (cached) return cached;
|
|
31189
|
+
const built = createModelDeps(ref);
|
|
31190
|
+
if (!built) return null;
|
|
31191
|
+
modelDepsCache.set(key, built);
|
|
31192
|
+
console.log(`[cron] Model deps built: ${ref}`);
|
|
31193
|
+
return built;
|
|
31194
|
+
};
|
|
31195
|
+
const cronPlugin = new CronPlugin2(config.cron, sessions, channelManager, deps, config.stateDir, dispatcher, resolveCronModelDeps);
|
|
30858
31196
|
await cronPlugin.start();
|
|
30859
31197
|
const { setCronConfig: setCronConfig2 } = await Promise.resolve().then(() => (init_tools(), tools_exports));
|
|
30860
31198
|
setCronConfig2(config.cron);
|
|
@@ -31030,8 +31368,10 @@ ${notifications}
|
|
|
31030
31368
|
dispatcher,
|
|
31031
31369
|
runningQueries,
|
|
31032
31370
|
engine,
|
|
31033
|
-
systemPrompt,
|
|
31034
|
-
|
|
31371
|
+
systemPrompt: getSystemPrompt,
|
|
31372
|
+
// 0902 函数形态:命令按调用时现取
|
|
31373
|
+
compactConfig: getCompactConfig,
|
|
31374
|
+
// 0902 同上
|
|
31035
31375
|
provider,
|
|
31036
31376
|
deps,
|
|
31037
31377
|
getVisualRegistry: () => visualRegistry,
|
|
@@ -31053,9 +31393,17 @@ ${notifications}
|
|
|
31053
31393
|
setVisionDeps: (v) => {
|
|
31054
31394
|
visionDeps = v;
|
|
31055
31395
|
},
|
|
31056
|
-
|
|
31396
|
+
// reset 用:清 override deps,下一条图片消息由 resolveVisionDeps 按当前 meta 重建
|
|
31397
|
+
getDefaultVisionDeps: () => {
|
|
31398
|
+
visionDeps = null;
|
|
31399
|
+
return null;
|
|
31400
|
+
},
|
|
31057
31401
|
doReloadConfig
|
|
31058
31402
|
};
|
|
31403
|
+
deps.onProviderSwapped = (p) => {
|
|
31404
|
+
provider = p;
|
|
31405
|
+
commandDeps.provider = p;
|
|
31406
|
+
};
|
|
31059
31407
|
const slashCommands = listCommandDefs().map((d) => ({
|
|
31060
31408
|
name: d.name,
|
|
31061
31409
|
description: d.description,
|
|
@@ -31312,7 +31660,7 @@ ${pathStr}` }];
|
|
|
31312
31660
|
for (const att of nonImageAttachments) {
|
|
31313
31661
|
console.log(`[file] Downloading: ${att.filename} (${att.contentType}, ${att.size}B)`);
|
|
31314
31662
|
try {
|
|
31315
|
-
const resp = attachDispatcher ? await fetch(att.url, { dispatcher: attachDispatcher }) : await fetch(att.url);
|
|
31663
|
+
const resp = attachDispatcher ? await fetch(att.url, { dispatcher: attachDispatcher, signal: AbortSignal.timeout(3e4) }) : await fetch(att.url, { signal: AbortSignal.timeout(3e4) });
|
|
31316
31664
|
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
|
31317
31665
|
const buffer = Buffer.from(await resp.arrayBuffer());
|
|
31318
31666
|
const safeName2 = path49.basename(att.filename).replace(/[<>:"/\\|?*\x00-\x1f]/g, "_") || "attachment";
|
|
@@ -31342,7 +31690,8 @@ ${pathStr}` }];
|
|
|
31342
31690
|
}
|
|
31343
31691
|
const isImageBlock = (b) => b.type === "image" || b.type === "image_url";
|
|
31344
31692
|
const hasImages = Array.isArray(queryContent) && queryContent.some((b) => isImageBlock(b));
|
|
31345
|
-
|
|
31693
|
+
const activeVisionDeps = hasImages ? resolveVisionDeps() : null;
|
|
31694
|
+
let msgDeps = activeVisionDeps ? activeVisionDeps : modelOverride ? modelDepsCache.get(modelOverride) ?? deps : deps;
|
|
31346
31695
|
if (isDeskBuddy && !hasImages && !modelOverride) {
|
|
31347
31696
|
if (!deskBuddyDeps) {
|
|
31348
31697
|
const dbRef = liveConfig.get("channels.deskBuddy.model") || "deepinfra/deepseek-ai/DeepSeek-V4-Flash-0731";
|
|
@@ -31370,7 +31719,8 @@ ${pathStr}` }];
|
|
|
31370
31719
|
model: dbProviderCfg ? dbModelId : config.model,
|
|
31371
31720
|
// 8/18 翀哥:完整工程 prompt(14k tok)+memory(10k) 会把小模型带偏成"工程助手"——deskBuddy 用 SOUL 精简人设
|
|
31372
31721
|
// 8/21 复用 light-mode.buildLightStablePrompt(和情感模式同一构建器)
|
|
31373
|
-
systemPrompt: buildLightStablePrompt(config.workspace, "deskBuddy"),
|
|
31722
|
+
systemPrompt: () => buildLightStablePrompt(config.workspace, "deskBuddy"),
|
|
31723
|
+
// 0902 函数形态:改 SOUL 热生效
|
|
31374
31724
|
maxTokens: 1024,
|
|
31375
31725
|
temperature: 0.7,
|
|
31376
31726
|
disableThinking: true,
|
|
@@ -31384,7 +31734,7 @@ ${pathStr}` }];
|
|
|
31384
31734
|
msgDeps = deskBuddyDeps;
|
|
31385
31735
|
}
|
|
31386
31736
|
if (hasImages) {
|
|
31387
|
-
console.log(`[vision-debug] hasImages=true visionDeps=${
|
|
31737
|
+
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"}`);
|
|
31388
31738
|
if (Array.isArray(queryContent)) {
|
|
31389
31739
|
queryContent.forEach((b, i) => {
|
|
31390
31740
|
if (b.type === "image" && b.source?.data) {
|
|
@@ -31393,13 +31743,13 @@ ${pathStr}` }];
|
|
|
31393
31743
|
});
|
|
31394
31744
|
}
|
|
31395
31745
|
}
|
|
31396
|
-
if (hasImages &&
|
|
31397
|
-
console.log(`[vision] Routing to ${
|
|
31746
|
+
if (hasImages && activeVisionDeps) {
|
|
31747
|
+
console.log(`[vision] Routing to ${activeVisionDeps.providerId}/${activeVisionDeps.model}${activeVisionDeps.model !== visionConfig?.modelId ? ` (override, config=${visionConfig.providerId}/${visionConfig.modelId})` : ""}`);
|
|
31398
31748
|
}
|
|
31399
31749
|
const preQueryResult = await messageHooks.runPreQuery({
|
|
31400
31750
|
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 },
|
|
31401
31751
|
text: queryContent,
|
|
31402
|
-
msgDeps:
|
|
31752
|
+
msgDeps: activeVisionDeps ?? msgDeps,
|
|
31403
31753
|
deps: { provider, channelManager, sessions, dispatcher, config, workspace: config.workspace }
|
|
31404
31754
|
});
|
|
31405
31755
|
if (preQueryResult.skip) {
|
|
@@ -31412,6 +31762,7 @@ ${pathStr}` }];
|
|
|
31412
31762
|
}
|
|
31413
31763
|
queryContent = preQueryResult.text ?? queryContent;
|
|
31414
31764
|
if (preQueryResult.msgDeps) msgDeps = preQueryResult.msgDeps;
|
|
31765
|
+
msgDeps.renderer = renderer;
|
|
31415
31766
|
const accepted = dispatcher.submitMessage({
|
|
31416
31767
|
text: queryContent,
|
|
31417
31768
|
sessionId,
|
|
@@ -31646,8 +31997,9 @@ ${pathStr}` }];
|
|
|
31646
31997
|
sessionManager: sessions,
|
|
31647
31998
|
sessionId,
|
|
31648
31999
|
model: config.model,
|
|
31649
|
-
contextWindow:
|
|
31650
|
-
systemPrompt,
|
|
32000
|
+
contextWindow: getCompactConfig().contextWindow || 2e5,
|
|
32001
|
+
systemPrompt: getSystemPrompt(),
|
|
32002
|
+
// 0902 现取(API /context 报告跟当前 prompt 一致)
|
|
31651
32003
|
toolDefs: registry.definitions(),
|
|
31652
32004
|
workspace: config.workspace
|
|
31653
32005
|
});
|
|
@@ -31975,6 +32327,24 @@ async function doReloadConfig(config, deps, provider) {
|
|
|
31975
32327
|
visionModel: newConfig.visionModel,
|
|
31976
32328
|
visionFallbacks: newConfig.visionFallbacks
|
|
31977
32329
|
});
|
|
32330
|
+
const newRecall = createMemorySideProvider(
|
|
32331
|
+
newConfig.topics?.recall,
|
|
32332
|
+
provider,
|
|
32333
|
+
newConfig.providers || {}
|
|
32334
|
+
);
|
|
32335
|
+
const newExtract = createMemorySideProvider(
|
|
32336
|
+
newConfig.topics?.extract,
|
|
32337
|
+
provider,
|
|
32338
|
+
newConfig.providers || {}
|
|
32339
|
+
);
|
|
32340
|
+
if (newRecall) {
|
|
32341
|
+
deps.recallProvider = newRecall;
|
|
32342
|
+
changes.push(`recall \u2192 ${newConfig.topics?.recall?.provider}/${newConfig.topics?.recall?.model}`);
|
|
32343
|
+
}
|
|
32344
|
+
if (newExtract) {
|
|
32345
|
+
deps.extractProvider = newExtract;
|
|
32346
|
+
changes.push(`extract \u2192 ${newConfig.topics?.extract?.provider}/${newConfig.topics?.extract?.model}`);
|
|
32347
|
+
}
|
|
31978
32348
|
if (oldProviderKey !== newProviderKey) {
|
|
31979
32349
|
console.log("[reload] Provider structure changed, rebuilding chain...");
|
|
31980
32350
|
const rebuilt = buildProviderChain(newConfig);
|
|
@@ -31985,6 +32355,7 @@ async function doReloadConfig(config, deps, provider) {
|
|
|
31985
32355
|
}
|
|
31986
32356
|
changes.push(`provider chain rebuilt (${rebuilt.visionChainLabels.length > 0 ? "vision: " + rebuilt.visionChainLabels.join("\u2192") : "no vision"})`);
|
|
31987
32357
|
}
|
|
32358
|
+
if (deps.onProviderSwapped) deps.onProviderSwapped(rebuilt.provider);
|
|
31988
32359
|
if (typeof deps.setVisionProvider === "function") {
|
|
31989
32360
|
const vCfg = newConfig.visionModel;
|
|
31990
32361
|
const vpCfg = vCfg ? newConfig.providers?.[vCfg.providerId] : void 0;
|
|
@@ -32015,24 +32386,6 @@ async function doReloadConfig(config, deps, provider) {
|
|
|
32015
32386
|
changes.push(`deskBuddy model \u2192 ${newDbModel}`);
|
|
32016
32387
|
}
|
|
32017
32388
|
}
|
|
32018
|
-
const newRecall = createMemorySideProvider(
|
|
32019
|
-
newConfig.topics?.recall,
|
|
32020
|
-
provider,
|
|
32021
|
-
newConfig.providers || {}
|
|
32022
|
-
);
|
|
32023
|
-
const newExtract = createMemorySideProvider(
|
|
32024
|
-
newConfig.topics?.extract,
|
|
32025
|
-
provider,
|
|
32026
|
-
newConfig.providers || {}
|
|
32027
|
-
);
|
|
32028
|
-
if (newRecall) {
|
|
32029
|
-
deps.recallProvider = newRecall;
|
|
32030
|
-
changes.push(`recall \u2192 ${newConfig.topics?.recall?.provider}/${newConfig.topics?.recall?.model}`);
|
|
32031
|
-
}
|
|
32032
|
-
if (newExtract) {
|
|
32033
|
-
deps.extractProvider = newExtract;
|
|
32034
|
-
changes.push(`extract \u2192 ${newConfig.topics?.extract?.provider}/${newConfig.topics?.extract?.model}`);
|
|
32035
|
-
}
|
|
32036
32389
|
try {
|
|
32037
32390
|
const { setAutoDreamConfig: setAutoDreamConfig2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
32038
32391
|
setAutoDreamConfig2(newConfig);
|