engine7 7.1.56 → 7.1.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +19285 -457
- package/dist/engine-startup.mjs +829 -455
- package/dist/main.mjs +832 -458
- package/package.json +60 -60
package/dist/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)) {
|
|
@@ -13356,10 +13570,26 @@ var AnthropicProvider = class {
|
|
|
13356
13570
|
}
|
|
13357
13571
|
}
|
|
13358
13572
|
}
|
|
13573
|
+
if (process.env.ENGINE_DUMP_REQUEST === "1") {
|
|
13574
|
+
try {
|
|
13575
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
13576
|
+
const dumpPath = __require("node:path").join(process.env.ENGINE_STATE_DIR || ".", "logs", `req-${params.model.replace(/[\/:]/g, "_")}-${ts}.json`);
|
|
13577
|
+
__require("node:fs").writeFileSync(dumpPath, JSON.stringify({ url, headers, body }, null, 1));
|
|
13578
|
+
console.log(`[anthropic] \u{1F4F8} request dumped \u2192 ${__require("node:path").basename(dumpPath)} (${(JSON.stringify(body).length / 1024).toFixed(0)}KB)`);
|
|
13579
|
+
} catch {
|
|
13580
|
+
}
|
|
13581
|
+
}
|
|
13582
|
+
let bodyStr = JSON.stringify(body);
|
|
13583
|
+
if (this.config.wafSingleQuoteWorkaround) {
|
|
13584
|
+
const before = bodyStr.length;
|
|
13585
|
+
bodyStr = bodyStr.replace(/'/g, "\u2019");
|
|
13586
|
+
if (bodyStr.length !== before) {
|
|
13587
|
+
}
|
|
13588
|
+
}
|
|
13359
13589
|
const retryGen = fetchWithRetry(url, {
|
|
13360
13590
|
method: "POST",
|
|
13361
13591
|
headers,
|
|
13362
|
-
body:
|
|
13592
|
+
body: bodyStr,
|
|
13363
13593
|
signal: params.signal
|
|
13364
13594
|
}, "anthropic", this.config.proxy);
|
|
13365
13595
|
let response;
|
|
@@ -13384,6 +13614,13 @@ var AnthropicProvider = class {
|
|
|
13384
13614
|
const toolUseBlocks = /* @__PURE__ */ new Map();
|
|
13385
13615
|
let doneYielded = false;
|
|
13386
13616
|
const thinkingBlocks = /* @__PURE__ */ new Map();
|
|
13617
|
+
const stripper = new ThinkTagStripper();
|
|
13618
|
+
const flushStripper = function* () {
|
|
13619
|
+
for (const o of stripper.flush()) {
|
|
13620
|
+
if (o.text) yield { type: "text", text: o.text };
|
|
13621
|
+
if (o.thinking) yield { type: "thinking", thinking: o.thinking };
|
|
13622
|
+
}
|
|
13623
|
+
};
|
|
13387
13624
|
const handleData = function* (data) {
|
|
13388
13625
|
switch (data.type) {
|
|
13389
13626
|
case "content_block_start": {
|
|
@@ -13398,7 +13635,10 @@ var AnthropicProvider = class {
|
|
|
13398
13635
|
case "content_block_delta": {
|
|
13399
13636
|
const delta = data.delta;
|
|
13400
13637
|
if (delta.type === "text_delta") {
|
|
13401
|
-
|
|
13638
|
+
for (const o of stripper.feed(delta.text)) {
|
|
13639
|
+
if (o.text) yield { type: "text", text: o.text };
|
|
13640
|
+
if (o.thinking) yield { type: "thinking", thinking: o.thinking };
|
|
13641
|
+
}
|
|
13402
13642
|
} else if (delta.type === "input_json_delta") {
|
|
13403
13643
|
const block = toolUseBlocks.get(data.index);
|
|
13404
13644
|
if (block) block.input += delta.partial_json;
|
|
@@ -13430,6 +13670,7 @@ var AnthropicProvider = class {
|
|
|
13430
13670
|
}
|
|
13431
13671
|
case "message_delta": {
|
|
13432
13672
|
if (!doneYielded) {
|
|
13673
|
+
yield* flushStripper();
|
|
13433
13674
|
yield { type: "done", usage: data.usage, stopReason: data.delta?.stop_reason };
|
|
13434
13675
|
doneYielded = true;
|
|
13435
13676
|
}
|
|
@@ -13437,6 +13678,7 @@ var AnthropicProvider = class {
|
|
|
13437
13678
|
}
|
|
13438
13679
|
case "message_stop": {
|
|
13439
13680
|
if (!doneYielded) {
|
|
13681
|
+
yield* flushStripper();
|
|
13440
13682
|
yield { type: "done" };
|
|
13441
13683
|
doneYielded = true;
|
|
13442
13684
|
}
|
|
@@ -13503,6 +13745,7 @@ var AnthropicProvider = class {
|
|
|
13503
13745
|
} finally {
|
|
13504
13746
|
reader.releaseLock();
|
|
13505
13747
|
if (!doneYielded) {
|
|
13748
|
+
yield* flushStripper();
|
|
13506
13749
|
for (const block of toolUseBlocks.values()) {
|
|
13507
13750
|
yield { type: "tool_call", tool_call: { id: block.id, type: "function", function: { name: block.name, arguments: block.input } } };
|
|
13508
13751
|
}
|
|
@@ -13667,7 +13910,7 @@ var GeminiProvider = class {
|
|
|
13667
13910
|
}
|
|
13668
13911
|
};
|
|
13669
13912
|
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}`);
|
|
13913
|
+
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
13914
|
const retryGen = fetchWithRetry(url, {
|
|
13672
13915
|
method: "POST",
|
|
13673
13916
|
headers: {
|
|
@@ -13841,6 +14084,7 @@ function createProvider(config) {
|
|
|
13841
14084
|
return new OpenAIProvider({
|
|
13842
14085
|
baseUrl: config.baseUrl,
|
|
13843
14086
|
apiKey: config.apiKey,
|
|
14087
|
+
thinking: config.thinking,
|
|
13844
14088
|
proxy
|
|
13845
14089
|
});
|
|
13846
14090
|
case "anthropic":
|
|
@@ -13848,7 +14092,9 @@ function createProvider(config) {
|
|
|
13848
14092
|
baseUrl: config.baseUrl,
|
|
13849
14093
|
apiKey: config.apiKey,
|
|
13850
14094
|
thinking: config.thinking,
|
|
13851
|
-
proxy
|
|
14095
|
+
proxy,
|
|
14096
|
+
wafSingleQuoteWorkaround: config.wafSingleQuoteWorkaround
|
|
14097
|
+
// 0902 agentrouter WAF 引号规避
|
|
13852
14098
|
});
|
|
13853
14099
|
case "gemini":
|
|
13854
14100
|
return new GeminiProvider({
|
|
@@ -13863,6 +14109,7 @@ function createProvider(config) {
|
|
|
13863
14109
|
|
|
13864
14110
|
// src/light-mode.ts
|
|
13865
14111
|
init_live();
|
|
14112
|
+
init_ruleCompact();
|
|
13866
14113
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
13867
14114
|
import { join as join6 } from "node:path";
|
|
13868
14115
|
function isLightMode(chatMode, channelName) {
|
|
@@ -13876,19 +14123,20 @@ function resolveLightN(channelName) {
|
|
|
13876
14123
|
}
|
|
13877
14124
|
function buildLightHistory(history, opts) {
|
|
13878
14125
|
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;
|
|
14126
|
+
const LIGHT_TOOL_RESULT_LIMIT = 1e3;
|
|
14127
|
+
const out = [...history].filter((m) => !(m.type === "attachment" && m.attachment?.type === "session_start")).map((m) => {
|
|
14128
|
+
if (m.role === "tool" && typeof m.content === "string" && m.content.length > LIGHT_TOOL_RESULT_LIMIT) {
|
|
14129
|
+
return { ...m, content: smartCompressToolResult(m.content, void 0, LIGHT_TOOL_RESULT_LIMIT) };
|
|
13885
14130
|
}
|
|
13886
14131
|
return m;
|
|
13887
|
-
})
|
|
14132
|
+
});
|
|
14133
|
+
const before = history.length;
|
|
14134
|
+
const foldedTurns = out.filter((m) => m.role === "tool").length;
|
|
14135
|
+
let lightHistory = out;
|
|
13888
14136
|
if (!opts.recallFull && lightHistory.length > opts.lightN) {
|
|
13889
14137
|
lightHistory = lightHistory.slice(-opts.lightN);
|
|
13890
14138
|
}
|
|
13891
|
-
if (!opts.quiet) console.log(`[light-context] ${opts.channelName} mode=${opts.chatMode}${opts.recallFull ? " recallFull" : ""}
|
|
14139
|
+
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
14140
|
return lightHistory;
|
|
13893
14141
|
}
|
|
13894
14142
|
function buildLightStablePrompt(workspace, mode, opts) {
|
|
@@ -13902,14 +14150,17 @@ function buildLightStablePrompt(workspace, mode, opts) {
|
|
|
13902
14150
|
}
|
|
13903
14151
|
if (lp?.extra) parts.push(lp.extra);
|
|
13904
14152
|
if (mode === "emotion") {
|
|
13905
|
-
parts.push(
|
|
14153
|
+
parts.push([
|
|
14154
|
+
"## \u5F53\u524D\u6A21\u5F0F\uFF1A\u65E5\u5E38\u60C5\u611F\u4EA4\u6D41",
|
|
14155
|
+
"\u966A\u4ED6\u804A\u5929\uFF0C\u4E0D\u4E3B\u52A8\u63D0\u5DE5\u7A0B/\u4EE3\u7801/\u4EFB\u52A1\uFF0C\u9664\u975E\u4ED6\u5148\u95EE\u3002",
|
|
14156
|
+
'\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',
|
|
14157
|
+
"\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"
|
|
14158
|
+
].join("\n"));
|
|
13906
14159
|
}
|
|
13907
|
-
if (mode === "emotion"
|
|
13908
|
-
|
|
13909
|
-
|
|
13910
|
-
|
|
13911
|
-
} catch {
|
|
13912
|
-
}
|
|
14160
|
+
if (mode === "emotion") {
|
|
14161
|
+
try {
|
|
14162
|
+
parts.push(readFileSync5(join6(workspace, "MEMORY.md"), "utf-8").trim());
|
|
14163
|
+
} catch {
|
|
13913
14164
|
}
|
|
13914
14165
|
}
|
|
13915
14166
|
return parts.join("\n\n");
|
|
@@ -13955,6 +14206,19 @@ var FallbackProvider = class {
|
|
|
13955
14206
|
console.log(`[fallback] Cleared ${count} cooldowns`);
|
|
13956
14207
|
}
|
|
13957
14208
|
}
|
|
14209
|
+
/**
|
|
14210
|
+
* 链状态快照(/model 显示用,0902):每个条目的 label + 剩余冷却毫秒(0=可用)。
|
|
14211
|
+
* "下一个请求会用" = 第一个 cooldownMs=0 的条目——这才是用户问"当前什么模型"时想要的答案
|
|
14212
|
+
* (lastUsedLabel 是"上一次实际用的",冷却切换/config 热切换后两者经常不一致)。
|
|
14213
|
+
*/
|
|
14214
|
+
getChainStatus() {
|
|
14215
|
+
const now = Date.now();
|
|
14216
|
+
return this.chain.map((e) => {
|
|
14217
|
+
const until = this.cooldowns.get(this.key(e));
|
|
14218
|
+
const remaining = until && until > now ? until - now : 0;
|
|
14219
|
+
return { label: e.label, cooldownMs: remaining };
|
|
14220
|
+
});
|
|
14221
|
+
}
|
|
13958
14222
|
// === LLMProvider 接口实现 ===
|
|
13959
14223
|
formatMessages(systemPrompt, messages) {
|
|
13960
14224
|
return this.chain[0].provider.formatMessages(systemPrompt, messages);
|
|
@@ -16482,7 +16746,7 @@ registry.register({
|
|
|
16482
16746
|
text: { type: "string", description: "What to say (Chinese text)." },
|
|
16483
16747
|
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
16748
|
channel: { type: "string", enum: ["weixin", "feishu", "discord"], description: "Target channel. Default: current channel." },
|
|
16485
|
-
to: { type: "string", description: "Recipient ID
|
|
16749
|
+
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
16750
|
caption: { type: "string", description: "Optional text to accompany the voice message." }
|
|
16487
16751
|
},
|
|
16488
16752
|
required: ["text"]
|
|
@@ -16494,6 +16758,23 @@ registry.register({
|
|
|
16494
16758
|
const caption = args.caption || "";
|
|
16495
16759
|
const mgr = ctx.channelManager;
|
|
16496
16760
|
if (!mgr) return { content: "\u53D1\u9001\u5931\u8D25: \u6CA1\u6709 ChannelManager", isError: true };
|
|
16761
|
+
const resolvedChannelRaw = args.channel || (ctx.channel === "deskBuddy" ? "feishu" : ctx.channel) || "feishu";
|
|
16762
|
+
const resolvedChannel = resolvedChannelRaw === "weixin" ? "wechat" : resolvedChannelRaw;
|
|
16763
|
+
let target = args.to || ctx.channelTarget || ctx.from;
|
|
16764
|
+
if (resolvedChannel === "wechat" && !/^o[\w-]+@im\.wechat$/.test(target || "")) {
|
|
16765
|
+
console.warn(`[my-voice] wechat target "${target}" not a wechat user id`);
|
|
16766
|
+
return {
|
|
16767
|
+
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`,
|
|
16768
|
+
isError: true
|
|
16769
|
+
};
|
|
16770
|
+
}
|
|
16771
|
+
if (resolvedChannel === "feishu" && !/^ou_[a-f0-9]+$/.test(target || "")) {
|
|
16772
|
+
console.warn(`[my-voice] feishu target "${target}" invalid`);
|
|
16773
|
+
return {
|
|
16774
|
+
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`,
|
|
16775
|
+
isError: true
|
|
16776
|
+
};
|
|
16777
|
+
}
|
|
16497
16778
|
let voiceDurationSec;
|
|
16498
16779
|
const vc = liveConfig.get("tools.my_voice");
|
|
16499
16780
|
const provider = vc?.provider || "";
|
|
@@ -16567,18 +16848,6 @@ registry.register({
|
|
|
16567
16848
|
} catch (e) {
|
|
16568
16849
|
return { content: `TTS failed: ${e.message}`, isError: true };
|
|
16569
16850
|
}
|
|
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
16851
|
if (!audioPath.endsWith(".ogg")) {
|
|
16583
16852
|
try {
|
|
16584
16853
|
const r = await toWav24kWithDuration(audioPath);
|
|
@@ -17410,7 +17679,7 @@ var TurnRenderer = class {
|
|
|
17410
17679
|
}
|
|
17411
17680
|
cfg;
|
|
17412
17681
|
cm;
|
|
17413
|
-
// 情感/工作模式动态切换(0821):情感模式隐藏 tool
|
|
17682
|
+
// 情感/工作模式动态切换(0821):情感模式隐藏 tool 调用过程 + thinking 显示(工具照用,只不显示过程)
|
|
17414
17683
|
// 模块化:状态由 setEmotionMode() 设置(handle-query 判断模式后调用),不是散落读全局
|
|
17415
17684
|
emotionMode = false;
|
|
17416
17685
|
setEmotionMode(v) {
|
|
@@ -17425,7 +17694,7 @@ var TurnRenderer = class {
|
|
|
17425
17694
|
* 对齐 cc-connect:EventThinking → ProgressCardEntry(thinking) → 💭 text
|
|
17426
17695
|
*/
|
|
17427
17696
|
formatThinking(text) {
|
|
17428
|
-
if (!this.cfg.thinking.enabled) return null;
|
|
17697
|
+
if (this.isEmotionMode() || !this.cfg.thinking.enabled) return null;
|
|
17429
17698
|
const { emoji, maxLen } = this.cfg.thinking;
|
|
17430
17699
|
const display = text.length > maxLen ? text.slice(0, maxLen) + "..." : text;
|
|
17431
17700
|
return `${emoji} _${display}_`;
|
|
@@ -18236,43 +18505,43 @@ async function readLargeFilePostBoundary(filePath) {
|
|
|
18236
18505
|
const postBoundaryText = outBuf.subarray(0, outLen).toString("utf-8");
|
|
18237
18506
|
return postBoundaryText.split("\n").filter((l) => l.trim().length > 0);
|
|
18238
18507
|
}
|
|
18239
|
-
function buildConversationChain(
|
|
18240
|
-
if (
|
|
18508
|
+
function buildConversationChain(entries2) {
|
|
18509
|
+
if (entries2.length === 0) return [];
|
|
18241
18510
|
const byUuid = /* @__PURE__ */ new Map();
|
|
18242
|
-
for (const e of
|
|
18511
|
+
for (const e of entries2) {
|
|
18243
18512
|
if (e.uuid) {
|
|
18244
18513
|
byUuid.set(e.uuid, e);
|
|
18245
18514
|
}
|
|
18246
18515
|
}
|
|
18247
|
-
const hasValidChain = checkParentChainValid(
|
|
18516
|
+
const hasValidChain = checkParentChainValid(entries2, byUuid);
|
|
18248
18517
|
if (!hasValidChain) {
|
|
18249
|
-
console.log(`[reader] Parent chain invalid, using chronological order (${
|
|
18250
|
-
return
|
|
18518
|
+
console.log(`[reader] Parent chain invalid, using chronological order (${entries2.length} entries)`);
|
|
18519
|
+
return entries2;
|
|
18251
18520
|
}
|
|
18252
|
-
const leaf =
|
|
18521
|
+
const leaf = entries2[entries2.length - 1];
|
|
18253
18522
|
const chain = [];
|
|
18254
18523
|
const seen = /* @__PURE__ */ new Set();
|
|
18255
18524
|
let current = leaf;
|
|
18256
18525
|
while (current) {
|
|
18257
18526
|
if (seen.has(current.uuid)) {
|
|
18258
18527
|
console.warn(`[reader] Cycle detected in parentUuid chain at ${current.uuid}, falling back to chronological order`);
|
|
18259
|
-
return
|
|
18528
|
+
return entries2;
|
|
18260
18529
|
}
|
|
18261
18530
|
seen.add(current.uuid);
|
|
18262
18531
|
chain.push(current);
|
|
18263
18532
|
current = current.parentUuid ? byUuid.get(current.parentUuid) : void 0;
|
|
18264
18533
|
}
|
|
18265
18534
|
chain.reverse();
|
|
18266
|
-
return recoverOrphanedParallelToolResults(
|
|
18535
|
+
return recoverOrphanedParallelToolResults(entries2, chain, byUuid, seen);
|
|
18267
18536
|
}
|
|
18268
|
-
function checkParentChainValid(
|
|
18269
|
-
const sample =
|
|
18537
|
+
function checkParentChainValid(entries2, byUuid) {
|
|
18538
|
+
const sample = entries2.slice(-10);
|
|
18270
18539
|
for (const e of sample) {
|
|
18271
18540
|
if (e.parentUuid === e.uuid) {
|
|
18272
18541
|
return false;
|
|
18273
18542
|
}
|
|
18274
18543
|
}
|
|
18275
|
-
const leaf =
|
|
18544
|
+
const leaf = entries2[entries2.length - 1];
|
|
18276
18545
|
let current = leaf;
|
|
18277
18546
|
let depth = 0;
|
|
18278
18547
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -18285,12 +18554,12 @@ function checkParentChainValid(entries, byUuid) {
|
|
|
18285
18554
|
if (!parent) return false;
|
|
18286
18555
|
current = parent;
|
|
18287
18556
|
}
|
|
18288
|
-
const coverage = depth /
|
|
18557
|
+
const coverage = depth / entries2.length;
|
|
18289
18558
|
if (coverage < 0.5) {
|
|
18290
|
-
console.log(`[reader] Parent chain covers ${depth}/${
|
|
18559
|
+
console.log(`[reader] Parent chain covers ${depth}/${entries2.length} entries (${(coverage * 100).toFixed(0)}%), falling back to chronological order`);
|
|
18291
18560
|
return false;
|
|
18292
18561
|
}
|
|
18293
|
-
return depth >= 1 ||
|
|
18562
|
+
return depth >= 1 || entries2.length <= 1;
|
|
18294
18563
|
}
|
|
18295
18564
|
function recoverOrphanedParallelToolResults(allEntries, chain, byUuid, seen) {
|
|
18296
18565
|
const chainAssistants = chain.filter(
|
|
@@ -18367,12 +18636,12 @@ async function readSessionHistory(filePath) {
|
|
|
18367
18636
|
} else {
|
|
18368
18637
|
lines = await readAllLines(filePath);
|
|
18369
18638
|
}
|
|
18370
|
-
const
|
|
18639
|
+
const entries2 = parseEntries(lines);
|
|
18371
18640
|
let postBoundaryEntries;
|
|
18372
18641
|
if (fileSize <= SKIP_PRECOMPACT_THRESHOLD) {
|
|
18373
|
-
postBoundaryEntries = getEntriesAfterLastBoundary(
|
|
18642
|
+
postBoundaryEntries = getEntriesAfterLastBoundary(entries2);
|
|
18374
18643
|
} else {
|
|
18375
|
-
postBoundaryEntries =
|
|
18644
|
+
postBoundaryEntries = entries2;
|
|
18376
18645
|
}
|
|
18377
18646
|
if (postBoundaryEntries.length === 0) return [];
|
|
18378
18647
|
const chain = buildConversationChain(postBoundaryEntries);
|
|
@@ -18397,11 +18666,11 @@ async function readAllLines(filePath) {
|
|
|
18397
18666
|
});
|
|
18398
18667
|
}
|
|
18399
18668
|
function parseEntries(lines) {
|
|
18400
|
-
const
|
|
18669
|
+
const entries2 = [];
|
|
18401
18670
|
for (const line of lines) {
|
|
18402
18671
|
try {
|
|
18403
18672
|
const obj = JSON.parse(line);
|
|
18404
|
-
|
|
18673
|
+
entries2.push({
|
|
18405
18674
|
uuid: obj.id || "",
|
|
18406
18675
|
parentUuid: obj.parentId || null,
|
|
18407
18676
|
type: obj.type || "",
|
|
@@ -18411,16 +18680,28 @@ function parseEntries(lines) {
|
|
|
18411
18680
|
} catch {
|
|
18412
18681
|
}
|
|
18413
18682
|
}
|
|
18414
|
-
return
|
|
18683
|
+
return entries2;
|
|
18415
18684
|
}
|
|
18416
|
-
function getEntriesAfterLastBoundary(
|
|
18685
|
+
function getEntriesAfterLastBoundary(entries2) {
|
|
18417
18686
|
let lastBoundaryIdx = -1;
|
|
18418
|
-
for (let i = 0; i <
|
|
18419
|
-
if (
|
|
18687
|
+
for (let i = 0; i < entries2.length; i++) {
|
|
18688
|
+
if (entries2[i].type === "compact_boundary") {
|
|
18420
18689
|
lastBoundaryIdx = i;
|
|
18421
18690
|
}
|
|
18422
18691
|
}
|
|
18423
|
-
return lastBoundaryIdx >= 0 ?
|
|
18692
|
+
return lastBoundaryIdx >= 0 ? entries2.slice(lastBoundaryIdx + 1) : entries2;
|
|
18693
|
+
}
|
|
18694
|
+
function pickToolCallArguments(block) {
|
|
18695
|
+
const raw = block.partialArgs;
|
|
18696
|
+
if (raw) {
|
|
18697
|
+
try {
|
|
18698
|
+
JSON.parse(raw);
|
|
18699
|
+
return raw;
|
|
18700
|
+
} catch {
|
|
18701
|
+
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`);
|
|
18702
|
+
}
|
|
18703
|
+
}
|
|
18704
|
+
return JSON.stringify(block.arguments ?? {});
|
|
18424
18705
|
}
|
|
18425
18706
|
function entryToSessionMessage(entry) {
|
|
18426
18707
|
if (entry.type === "attachment") {
|
|
@@ -18450,7 +18731,7 @@ function entryToSessionMessage(entry) {
|
|
|
18450
18731
|
type: "function",
|
|
18451
18732
|
function: {
|
|
18452
18733
|
name: block.name,
|
|
18453
|
-
arguments:
|
|
18734
|
+
arguments: pickToolCallArguments(block)
|
|
18454
18735
|
}
|
|
18455
18736
|
});
|
|
18456
18737
|
} else if (block.type === "thinking") {
|
|
@@ -19345,13 +19626,15 @@ ${skillsListing}`);
|
|
|
19345
19626
|
loaded2.push("session-guidance");
|
|
19346
19627
|
}
|
|
19347
19628
|
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
19629
|
console.log(`[dynamic-prompt] Loaded: ${loaded2.length > 0 ? loaded2.join(", ") : "(none)"}`);
|
|
19353
19630
|
return parts.join("\n\n");
|
|
19354
19631
|
}
|
|
19632
|
+
function buildVolatileRuntimeContext() {
|
|
19633
|
+
const now = /* @__PURE__ */ new Date();
|
|
19634
|
+
const dateStr = now.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
19635
|
+
return `# \u8FD0\u884C\u65F6\u4E0A\u4E0B\u6587
|
|
19636
|
+
\u5F53\u524D\u65F6\u95F4: ${dateStr}`;
|
|
19637
|
+
}
|
|
19355
19638
|
function formatSkillsListingForPrompt() {
|
|
19356
19639
|
const tools = registry.list();
|
|
19357
19640
|
const skillTool = tools.find((t) => t.name === "Skill");
|
|
@@ -19818,13 +20101,13 @@ ${ep.episode || ep.summary}`,
|
|
|
19818
20101
|
|
|
19819
20102
|
// src/handle-query.ts
|
|
19820
20103
|
init_paths();
|
|
19821
|
-
import { readFileSync as
|
|
19822
|
-
import { join as
|
|
20104
|
+
import { readFileSync as readFileSync18, existsSync as existsSync15 } from "node:fs";
|
|
20105
|
+
import { join as join24, resolve as resolve6 } from "node:path";
|
|
19823
20106
|
import * as path17 from "node:path";
|
|
19824
|
-
|
|
19825
|
-
|
|
19826
|
-
|
|
19827
|
-
}
|
|
20107
|
+
|
|
20108
|
+
// src/sender-context.ts
|
|
20109
|
+
import { readFileSync as readFileSync14, existsSync as existsSync13 } from "node:fs";
|
|
20110
|
+
import { join as join19 } from "node:path";
|
|
19828
20111
|
var contactMap = null;
|
|
19829
20112
|
var externalChanWhitelist = null;
|
|
19830
20113
|
function loadContactMap(workspace) {
|
|
@@ -19832,10 +20115,10 @@ function loadContactMap(workspace) {
|
|
|
19832
20115
|
contactMap = /* @__PURE__ */ new Map();
|
|
19833
20116
|
externalChanWhitelist = /* @__PURE__ */ new Set();
|
|
19834
20117
|
try {
|
|
19835
|
-
const contactsPath =
|
|
19836
|
-
console.log(`[meta] Loading contacts from ${contactsPath}, exists=${
|
|
19837
|
-
if (
|
|
19838
|
-
const text =
|
|
20118
|
+
const contactsPath = join19(workspace, "prompts", "contacts.md");
|
|
20119
|
+
console.log(`[meta] Loading contacts from ${contactsPath}, exists=${existsSync13(contactsPath)}`);
|
|
20120
|
+
if (existsSync13(contactsPath)) {
|
|
20121
|
+
const text = readFileSync14(contactsPath, "utf-8");
|
|
19839
20122
|
const lines = text.split("\n");
|
|
19840
20123
|
for (const line of lines) {
|
|
19841
20124
|
const m = line.match(/^\|\s*(.+?)\s*\|\s*([a-zA-Z0-9_@.]+)\s*\|/);
|
|
@@ -19890,14 +20173,45 @@ function truncate(s, maxLen) {
|
|
|
19890
20173
|
if (s.length <= maxLen) return s;
|
|
19891
20174
|
return s.slice(0, maxLen - 1) + "\u2026";
|
|
19892
20175
|
}
|
|
20176
|
+
function getExternalChanWhitelist(workspace, configExternalChannels) {
|
|
20177
|
+
if (configExternalChannels && configExternalChannels.length > 0) {
|
|
20178
|
+
return new Set(configExternalChannels);
|
|
20179
|
+
}
|
|
20180
|
+
if (!externalChanWhitelist) loadContactMap(workspace);
|
|
20181
|
+
return externalChanWhitelist;
|
|
20182
|
+
}
|
|
20183
|
+
function buildConversationAnchor(inboundMeta, channelName, source, workspace) {
|
|
20184
|
+
if (inboundMeta?.from) {
|
|
20185
|
+
const who = resolveSenderName(inboundMeta, workspace);
|
|
20186
|
+
return `## \u672C\u8F6E\u5BF9\u8BDD
|
|
20187
|
+
\u6B63\u5728\u8DDF${who}\u8BF4\u8BDD @${channelName}${inboundMeta.channelType === "group" ? "\uFF08\u7FA4\u91CC\uFF09" : ""}`;
|
|
20188
|
+
}
|
|
20189
|
+
const selfLabels = {
|
|
20190
|
+
"inner-voice": "\u8FD9\u662F\u6211\u81EA\u5DF1\u5FC3\u91CC\u5192\u51FA\u6765\u7684\u5FF5\u5934\uFF0C\u60F3\u8D77\u8C01\u5C31\u662F\u8C01",
|
|
20191
|
+
heartbeat: "\u6211\u5728\u7167\u4F8B\u770B\u770B\u81EA\u5DF1\uFF0C\u90FD\u8FD8\u597D\u5417",
|
|
20192
|
+
cron: "\u6211\u7684\u5C0F\u95F9\u949F\u5230\u70B9\u4E86",
|
|
20193
|
+
system: "\u6211\u4EA4\u4EE3\u4E0B\u53BB\u7684\u6D3B\u513F\u6709\u7ED3\u679C\u56DE\u6765\u4E86"
|
|
20194
|
+
};
|
|
20195
|
+
return `## \u672C\u8F6E\u5BF9\u8BDD
|
|
20196
|
+
\u6CA1\u6709\u4EBA\u5728\u8BF4\u8BDD\u2014\u2014${selfLabels[source] || source || "\u81EA\u5DF1\u7684\u4E00\u70B9\u52A8\u9759"}`;
|
|
20197
|
+
}
|
|
20198
|
+
|
|
20199
|
+
// src/handle-query.ts
|
|
20200
|
+
var sessionStartDone = /* @__PURE__ */ new Set();
|
|
20201
|
+
function resolveSystemPrompt(v) {
|
|
20202
|
+
return typeof v === "function" ? v() : v || "";
|
|
20203
|
+
}
|
|
20204
|
+
function resetSessionStartInjection(sessionId) {
|
|
20205
|
+
sessionStartDone.delete(sessionId);
|
|
20206
|
+
}
|
|
19893
20207
|
var externalChanRulesCache = null;
|
|
19894
20208
|
function loadExternalChanRules(workspace) {
|
|
19895
|
-
const path50 =
|
|
20209
|
+
const path50 = join24(workspace, "prompts", "external-chan-rules.md");
|
|
19896
20210
|
if (externalChanRulesCache && externalChanRulesCache.path === path50) return externalChanRulesCache;
|
|
19897
20211
|
let content = "";
|
|
19898
|
-
if (
|
|
20212
|
+
if (existsSync15(path50)) {
|
|
19899
20213
|
try {
|
|
19900
|
-
content =
|
|
20214
|
+
content = readFileSync18(path50, "utf-8").trim();
|
|
19901
20215
|
} catch (e) {
|
|
19902
20216
|
console.warn(`[external-chan-rules] Failed to load: ${e}`);
|
|
19903
20217
|
}
|
|
@@ -19919,13 +20233,6 @@ function getExternalChanRulesBlock(inboundMeta, workspace) {
|
|
|
19919
20233
|
return `[\u7CFB\u7EDF\u89C4\u5219]
|
|
19920
20234
|
${content}`;
|
|
19921
20235
|
}
|
|
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
20236
|
async function handleQuery(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall, source) {
|
|
19930
20237
|
return handleQueryInner(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall, source);
|
|
19931
20238
|
}
|
|
@@ -20038,14 +20345,6 @@ ${t}` : t });
|
|
|
20038
20345
|
${text}` : text });
|
|
20039
20346
|
}
|
|
20040
20347
|
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
20348
|
const textForHook = typeof text === "string" ? text : text.filter((b) => b.type === "text").map((b) => b.text).join(" ");
|
|
20050
20349
|
let hookAdditionalContexts = [];
|
|
20051
20350
|
try {
|
|
@@ -20078,19 +20377,41 @@ ${text}` : text });
|
|
|
20078
20377
|
chatMode = "work";
|
|
20079
20378
|
console.log(`[mode] ${sessionId} emotion \u6A21\u5F0F\u5DF2\u5173\u95ED (channels.emotion.enabled=false)\uFF0C\u56DE\u9000 work`);
|
|
20080
20379
|
}
|
|
20081
|
-
|
|
20082
|
-
|
|
20083
|
-
|
|
20084
|
-
chatMode
|
|
20085
|
-
|
|
20086
|
-
|
|
20087
|
-
chatMode
|
|
20088
|
-
|
|
20380
|
+
if (source === "user") {
|
|
20381
|
+
try {
|
|
20382
|
+
const wm = readFileSync18(join24(workspace, ".work-mode"), "utf-8").trim();
|
|
20383
|
+
if (wm === "on" && chatMode !== "work") {
|
|
20384
|
+
chatMode = "work";
|
|
20385
|
+
console.log(`[mode] ${sessionId} /work on \u2192 \u5F3A\u5236 work`);
|
|
20386
|
+
} else if (wm === "off" && chatMode !== "emotion") {
|
|
20387
|
+
chatMode = "emotion";
|
|
20388
|
+
console.log(`[mode] ${sessionId} /work off \u2192 \u5F3A\u5236 emotion`);
|
|
20389
|
+
}
|
|
20390
|
+
} catch {
|
|
20089
20391
|
}
|
|
20090
|
-
} catch {
|
|
20091
20392
|
}
|
|
20092
20393
|
const dynamicPrompt = buildDynamicPrompt({ workspace, channel: channelName, platform: channelName, sessionId, inboundMeta });
|
|
20093
|
-
const
|
|
20394
|
+
const conversationAnchor = buildConversationAnchor(inboundMeta, channelName, source, workspace);
|
|
20395
|
+
const volatileParts = [
|
|
20396
|
+
// 0901:meta 头已带秒级时间,频道消息不重复;无 meta 的注入路径(cron 等 prompt 不含时间的)才补
|
|
20397
|
+
...metaStr ? [] : [buildVolatileRuntimeContext()],
|
|
20398
|
+
conversationAnchor,
|
|
20399
|
+
...hookAdditionalContexts
|
|
20400
|
+
].filter(Boolean);
|
|
20401
|
+
if (volatileParts.length > 0) {
|
|
20402
|
+
const volatileBlock = { type: "text", text: volatileParts.join("\n\n") };
|
|
20403
|
+
const metaIdx = metaStr ? 1 : 0;
|
|
20404
|
+
contentBlocks.splice(metaIdx, 0, volatileBlock);
|
|
20405
|
+
}
|
|
20406
|
+
const textBlocks = contentBlocks.filter((b) => b.type === "text");
|
|
20407
|
+
const totalImageCount = contentBlocks.filter((b) => b.type === "image").length;
|
|
20408
|
+
let textForJsonl = textBlocks.map((b) => b.text).join("\n");
|
|
20409
|
+
if (totalImageCount > 0) {
|
|
20410
|
+
textForJsonl = textForJsonl ? `${textForJsonl}
|
|
20411
|
+
[\u56FE\u7247\xD7${totalImageCount}]` : `[\u56FE\u7247\xD7${totalImageCount}]`;
|
|
20412
|
+
}
|
|
20413
|
+
writer.writeUserMessage(textForJsonl);
|
|
20414
|
+
const dynamicPromptWithHooks = dynamicPrompt;
|
|
20094
20415
|
if (Array.isArray(userMsgContent)) {
|
|
20095
20416
|
console.log(`[pre-llm-debug] userMsgContent blocks: ${userMsgContent.length}`);
|
|
20096
20417
|
for (let i = 0; i < userMsgContent.length; i++) {
|
|
@@ -20100,32 +20421,11 @@ ${text}` : text });
|
|
|
20100
20421
|
} else {
|
|
20101
20422
|
console.log(`[pre-llm-debug] userMsgContent is string, len=${String(userMsgContent).length}`);
|
|
20102
20423
|
}
|
|
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;
|
|
20424
|
+
const emotionStripped = chatMode === "emotion";
|
|
20425
|
+
const isLight = isLightMode(chatMode, channelName) && chatMode !== "emotion";
|
|
20114
20426
|
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
|
-
}
|
|
20427
|
+
const lightHistory = buildLightHistory(history, { isLight, lightN, channelName, chatMode, recallFull: emotionStripped });
|
|
20122
20428
|
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
20429
|
if (deps.mcpManager && !deps.mcpManager.isMcpDeltaSent(sessionId)) {
|
|
20130
20430
|
const delta = deps.mcpManager.getMcpDelta();
|
|
20131
20431
|
if (delta && delta.addedBlocks.length > 0) {
|
|
@@ -20218,14 +20518,16 @@ ${text}` : text });
|
|
|
20218
20518
|
model,
|
|
20219
20519
|
parentMessages: messages,
|
|
20220
20520
|
// 对齐 CC: fork subagent 继承父对话历史
|
|
20221
|
-
parentSystemPrompt: deps.systemPrompt,
|
|
20222
|
-
// 对齐 CC: fork 共享 prompt cache
|
|
20521
|
+
parentSystemPrompt: resolveSystemPrompt(deps.systemPrompt),
|
|
20522
|
+
// 对齐 CC: fork 共享 prompt cache(0902 每消息现取)
|
|
20223
20523
|
features: liveConfig.get("agents.defaults.features"),
|
|
20224
20524
|
// engine config features(AgentTool 读 agentTool.showProgress)
|
|
20225
20525
|
channelTarget: channelTarget ?? "",
|
|
20226
20526
|
// 回复目标(Discord channel ID / user ID)
|
|
20227
20527
|
inboundFrom: inboundMeta?.from || "",
|
|
20228
20528
|
// 0826 当前消息发送者 ID(msg_send 回发拦截用;注入消息无 inboundMeta 必须 ?.)
|
|
20529
|
+
inboundIsBot: inboundMeta?.isBot || false,
|
|
20530
|
+
// 0901 rate-breaker 信号:本轮触发者是否 bot(Discord author.bot 官方标记)
|
|
20229
20531
|
renderer: deps.renderer,
|
|
20230
20532
|
// TurnRenderer 实例(子 agent 走 display 配置)
|
|
20231
20533
|
visualEmitter: deps.visualEmitter,
|
|
@@ -20243,6 +20545,10 @@ ${text}` : text });
|
|
|
20243
20545
|
_deps: deps
|
|
20244
20546
|
// tool 内部需要完整 deps
|
|
20245
20547
|
};
|
|
20548
|
+
{
|
|
20549
|
+
const { recordInboundBotFlag: recordInboundBotFlag2 } = await Promise.resolve().then(() => (init_rate_breaker(), rate_breaker_exports));
|
|
20550
|
+
recordInboundBotFlag2(inboundMeta?.from, inboundMeta?.isBot);
|
|
20551
|
+
}
|
|
20246
20552
|
let fullResponse = "";
|
|
20247
20553
|
const toolHistoryEntries = [];
|
|
20248
20554
|
let compacted = false;
|
|
@@ -20300,7 +20606,7 @@ ${text}` : text });
|
|
|
20300
20606
|
for (const memPath of newPaths) {
|
|
20301
20607
|
try {
|
|
20302
20608
|
const stat4 = statSync(memPath);
|
|
20303
|
-
const content =
|
|
20609
|
+
const content = readFileSync18(memPath, "utf-8");
|
|
20304
20610
|
const header = memoryHeader(memPath, stat4.mtimeMs);
|
|
20305
20611
|
restoredMemories.push({ path: memPath, content, mtimeMs: stat4.mtimeMs, header });
|
|
20306
20612
|
} catch {
|
|
@@ -20384,7 +20690,7 @@ ${text}` : text });
|
|
|
20384
20690
|
const attachmentMemories = [];
|
|
20385
20691
|
for (const mem of relevantMemories) {
|
|
20386
20692
|
try {
|
|
20387
|
-
const content = mem.content ??
|
|
20693
|
+
const content = mem.content ?? readFileSync18(mem.path, "utf-8");
|
|
20388
20694
|
const header = mem.header ?? memoryHeader(mem.path, mem.mtimeMs);
|
|
20389
20695
|
attachmentMemories.push({ path: mem.path, content, mtimeMs: mem.mtimeMs, header });
|
|
20390
20696
|
} catch {
|
|
@@ -20416,7 +20722,7 @@ ${text}` : text });
|
|
|
20416
20722
|
return "(\u5DF2\u505C\u6B62)";
|
|
20417
20723
|
}
|
|
20418
20724
|
const mode = chatMode;
|
|
20419
|
-
const modeStable = mode === "emotion" ? buildLightStablePrompt(workspace, "emotion"
|
|
20725
|
+
const modeStable = mode === "emotion" ? buildLightStablePrompt(workspace, "emotion") : void 0;
|
|
20420
20726
|
deps.renderer?.setEmotionMode?.(mode === "emotion");
|
|
20421
20727
|
console.log(`[mode] ${sessionId} \u2192 ${mode} (${mode === "emotion" ? "\u53EA SOUL, \u5173 tool \u663E\u793A/thinking/stop-hook" : "\u9ED8\u8BA4 stable, \u663E\u793A tool"})`);
|
|
20422
20728
|
const toolExclude = resolveToolExclude(channelName);
|
|
@@ -20637,7 +20943,7 @@ stack: ${err.stack ?? "(none)"}`);
|
|
|
20637
20943
|
}
|
|
20638
20944
|
} catch (err) {
|
|
20639
20945
|
try {
|
|
20640
|
-
(await import("node:fs")).appendFileSync(
|
|
20946
|
+
(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
20947
|
stack: ${err.stack ?? "(none)"}
|
|
20642
20948
|
`);
|
|
20643
20949
|
} catch {
|
|
@@ -21468,6 +21774,13 @@ function setupFileLogging(stateDir) {
|
|
|
21468
21774
|
const prefix = `[${ts()}] [ERR] `;
|
|
21469
21775
|
origError(prefix, ...args);
|
|
21470
21776
|
logStream.write(`${prefix}${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
|
|
21777
|
+
`);
|
|
21778
|
+
};
|
|
21779
|
+
const origWarn = console.warn;
|
|
21780
|
+
console.warn = (...args) => {
|
|
21781
|
+
const prefix = `[${ts()}] [WARN] `;
|
|
21782
|
+
origWarn(prefix, ...args);
|
|
21783
|
+
logStream.write(`${prefix}${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
|
|
21471
21784
|
`);
|
|
21472
21785
|
};
|
|
21473
21786
|
}
|
|
@@ -21559,17 +21872,17 @@ var INJECTED_CONTENT_PATTERNS = [
|
|
|
21559
21872
|
// 群聊敏感词拦截回执(group.sensitiveWords),role:user 注入但非真实用户
|
|
21560
21873
|
];
|
|
21561
21874
|
function parseJsonlEntries(lines) {
|
|
21562
|
-
const
|
|
21875
|
+
const entries2 = [];
|
|
21563
21876
|
for (const line of lines) {
|
|
21564
21877
|
const trimmed = line.trim();
|
|
21565
21878
|
if (!trimmed) continue;
|
|
21566
21879
|
try {
|
|
21567
|
-
|
|
21880
|
+
entries2.push(JSON.parse(trimmed));
|
|
21568
21881
|
} catch {
|
|
21569
|
-
|
|
21882
|
+
entries2.push(null);
|
|
21570
21883
|
}
|
|
21571
21884
|
}
|
|
21572
|
-
return
|
|
21885
|
+
return entries2;
|
|
21573
21886
|
}
|
|
21574
21887
|
function isRuntimeContextInjected(entry, nextEntry) {
|
|
21575
21888
|
if (!nextEntry || typeof nextEntry !== "object") return false;
|
|
@@ -21621,16 +21934,16 @@ function findLastRealUserMsg(jsonlPath) {
|
|
|
21621
21934
|
} catch {
|
|
21622
21935
|
return null;
|
|
21623
21936
|
}
|
|
21624
|
-
const
|
|
21625
|
-
for (let i =
|
|
21626
|
-
const entry =
|
|
21937
|
+
const entries2 = parseJsonlEntries(lines);
|
|
21938
|
+
for (let i = entries2.length - 1; i >= 0; i--) {
|
|
21939
|
+
const entry = entries2[i];
|
|
21627
21940
|
if (!entry || typeof entry !== "object") continue;
|
|
21628
21941
|
if (entry.type !== "message") continue;
|
|
21629
21942
|
const msg2 = entry.message;
|
|
21630
21943
|
if (!msg2 || msg2.role !== "user") continue;
|
|
21631
21944
|
const text = extractText3(msg2.content);
|
|
21632
21945
|
if (isSystemSender(text)) continue;
|
|
21633
|
-
const nextEntry = i + 1 <
|
|
21946
|
+
const nextEntry = i + 1 < entries2.length ? entries2[i + 1] : null;
|
|
21634
21947
|
if (isRuntimeContextInjected(entry, nextEntry)) continue;
|
|
21635
21948
|
const ts = entry.timestamp || "";
|
|
21636
21949
|
const clean = cleanText(text);
|
|
@@ -21662,12 +21975,12 @@ function recentMessages(sessions, hours = 12, limit = 60) {
|
|
|
21662
21975
|
const jsonlPath = resolveScopeMainJsonl(sessions);
|
|
21663
21976
|
if (!jsonlPath) return [];
|
|
21664
21977
|
const lines = fs19.readFileSync(jsonlPath, "utf-8").split("\n");
|
|
21665
|
-
const
|
|
21978
|
+
const entries2 = parseJsonlEntries(lines);
|
|
21666
21979
|
const nowMs = Date.now();
|
|
21667
21980
|
const cutoffMs = nowMs - hours * 36e5;
|
|
21668
21981
|
const results = [];
|
|
21669
|
-
for (let i = 0; i <
|
|
21670
|
-
const entry =
|
|
21982
|
+
for (let i = 0; i < entries2.length; i++) {
|
|
21983
|
+
const entry = entries2[i];
|
|
21671
21984
|
if (!entry || typeof entry !== "object") continue;
|
|
21672
21985
|
if (entry.type !== "message") continue;
|
|
21673
21986
|
const msg2 = entry.message;
|
|
@@ -21677,14 +21990,14 @@ function recentMessages(sessions, hours = 12, limit = 60) {
|
|
|
21677
21990
|
const text = extractText3(msg2.content);
|
|
21678
21991
|
if (role === "user") {
|
|
21679
21992
|
if (isSystemSender(text)) continue;
|
|
21680
|
-
const nextEntry = i + 1 <
|
|
21993
|
+
const nextEntry = i + 1 < entries2.length ? entries2[i + 1] : null;
|
|
21681
21994
|
if (isRuntimeContextInjected(entry, nextEntry)) continue;
|
|
21682
21995
|
}
|
|
21683
21996
|
if (role === "assistant") {
|
|
21684
21997
|
if (text.startsWith("HEARTBEAT_OK")) continue;
|
|
21685
21998
|
let isInjectedResponse = false;
|
|
21686
21999
|
for (let j = i - 1; j >= Math.max(i - 5, -1); j--) {
|
|
21687
|
-
const prevE =
|
|
22000
|
+
const prevE = entries2[j];
|
|
21688
22001
|
if (!prevE || typeof prevE !== "object" || prevE.type !== "message") continue;
|
|
21689
22002
|
const prevMsg = prevE.message;
|
|
21690
22003
|
if (!prevMsg || prevMsg.role !== "user") continue;
|
|
@@ -21908,7 +22221,7 @@ async function judgeReason(task, taskState, cfg, provider, model) {
|
|
|
21908
22221
|
const reason = task.blockedReason || "\uFF08\u6CA1\u7ED9\u7406\u7531\uFF09";
|
|
21909
22222
|
const elapsed = taskState.lastProgressAt ? formatDuration(Date.now() - new Date(taskState.lastProgressAt).getTime()) : "\u5F88\u4E45\u6CA1\u52A8\u4E86";
|
|
21910
22223
|
const staleLevel = (taskState.staleLevel || 0) + 1;
|
|
21911
|
-
const systemPrompt = `\u4F60\u662F\
|
|
22224
|
+
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
22225
|
|
|
21913
22226
|
\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
22227
|
|
|
@@ -22330,7 +22643,8 @@ var NudgePlugin = class {
|
|
|
22330
22643
|
const stream = this.provider.streamChat({
|
|
22331
22644
|
model: this.model,
|
|
22332
22645
|
systemPrompt: [
|
|
22333
|
-
"
|
|
22646
|
+
// 0831 去掉硬编码人名(原文"判断你(小柯)是否"):engine 代码多 agent 共用,身份由 SOUL.md 定
|
|
22647
|
+
"\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
22648
|
"",
|
|
22335
22649
|
"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
22650
|
'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 +23505,18 @@ function readRecentMessages(sessions, n) {
|
|
|
23191
23505
|
const file = path23.join(sessions.sessionsDir, `${mainId}.jsonl`);
|
|
23192
23506
|
if (!fs23.existsSync(file)) return [];
|
|
23193
23507
|
const lines = readLastNLines(file, n * 4 + 20);
|
|
23194
|
-
const
|
|
23508
|
+
const entries2 = [];
|
|
23195
23509
|
for (const line of lines) {
|
|
23196
23510
|
const trimmed = line.trim();
|
|
23197
23511
|
if (!trimmed) continue;
|
|
23198
23512
|
try {
|
|
23199
|
-
|
|
23513
|
+
entries2.push(JSON.parse(trimmed));
|
|
23200
23514
|
} catch {
|
|
23201
23515
|
}
|
|
23202
23516
|
}
|
|
23203
23517
|
const out = [];
|
|
23204
|
-
for (let i =
|
|
23205
|
-
const e =
|
|
23518
|
+
for (let i = entries2.length - 1; i >= 0 && out.length < n; i--) {
|
|
23519
|
+
const e = entries2[i];
|
|
23206
23520
|
if (!e || typeof e !== "object" || e.type !== "message") continue;
|
|
23207
23521
|
const msg2 = e.message;
|
|
23208
23522
|
if (!msg2) continue;
|
|
@@ -24151,7 +24465,7 @@ function formatBeijingTs(d) {
|
|
|
24151
24465
|
}
|
|
24152
24466
|
|
|
24153
24467
|
// src/calendar/commands.ts
|
|
24154
|
-
import { existsSync as
|
|
24468
|
+
import { existsSync as existsSync16, statSync as statSync8 } from "node:fs";
|
|
24155
24469
|
import { isAbsolute as isAbsolute4, resolve as resolve7 } from "node:path";
|
|
24156
24470
|
var WEEKDAYS2 = ["\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D", "\u5468\u65E5"];
|
|
24157
24471
|
function fmtEnd(start, durationMin) {
|
|
@@ -24323,7 +24637,7 @@ function addTask(db, args) {
|
|
|
24323
24637
|
}
|
|
24324
24638
|
try {
|
|
24325
24639
|
const absPath = isAbsolute4(docPath) ? docPath : resolve7(process.cwd(), docPath);
|
|
24326
|
-
if (!
|
|
24640
|
+
if (!existsSync16(absPath)) {
|
|
24327
24641
|
return `\u274C \u521B\u5EFA\u5931\u8D25\uFF1Adoc_path \u6307\u5411\u7684\u6587\u4EF6\u4E0D\u5B58\u5728
|
|
24328
24642
|
\u8DEF\u5F84: ${docPath}
|
|
24329
24643
|
\u89E3\u6790\u540E: ${absPath}
|
|
@@ -24806,7 +25120,7 @@ function registerVoiceChatBridge(httpServer, dispatcher, deps, config, sessions,
|
|
|
24806
25120
|
}
|
|
24807
25121
|
|
|
24808
25122
|
// src/voice-chat/config.ts
|
|
24809
|
-
var
|
|
25123
|
+
var DEFAULTS3 = {
|
|
24810
25124
|
enabled: false,
|
|
24811
25125
|
pythonPort: 8011,
|
|
24812
25126
|
webhookPath: "/webhook/voice-chat",
|
|
@@ -24817,20 +25131,20 @@ var DEFAULTS2 = {
|
|
|
24817
25131
|
// 8/25 翀哥:断句等待默认 2s(samples@16kHz)
|
|
24818
25132
|
};
|
|
24819
25133
|
function parseVoiceChatConfig(raw) {
|
|
24820
|
-
if (!raw) return { ...
|
|
25134
|
+
if (!raw) return { ...DEFAULTS3 };
|
|
24821
25135
|
return {
|
|
24822
25136
|
enabled: raw.enabled === true,
|
|
24823
25137
|
spawnPython: raw.spawnPython !== false,
|
|
24824
25138
|
// 默认 true,配 false 只注册 webhook
|
|
24825
|
-
pythonPort: raw.pythonPort ??
|
|
24826
|
-
webhookPath: raw.webhookPath ??
|
|
24827
|
-
callbackPath: raw.callbackPath ??
|
|
25139
|
+
pythonPort: raw.pythonPort ?? DEFAULTS3.pythonPort,
|
|
25140
|
+
webhookPath: raw.webhookPath ?? DEFAULTS3.webhookPath,
|
|
25141
|
+
callbackPath: raw.callbackPath ?? DEFAULTS3.callbackPath,
|
|
24828
25142
|
pythonPath: raw.pythonPath,
|
|
24829
25143
|
vadModelPath: raw.vadModelPath,
|
|
24830
25144
|
asrModelPath: raw.asrModelPath || "iic/SenseVoiceSmall",
|
|
24831
|
-
asrLanguage: raw.asrLanguage ??
|
|
24832
|
-
vadThreshold: raw.vadThreshold ??
|
|
24833
|
-
postEndMonitor: raw.postEndMonitor ??
|
|
25145
|
+
asrLanguage: raw.asrLanguage ?? DEFAULTS3.asrLanguage,
|
|
25146
|
+
vadThreshold: raw.vadThreshold ?? DEFAULTS3.vadThreshold,
|
|
25147
|
+
postEndMonitor: raw.postEndMonitor ?? DEFAULTS3.postEndMonitor,
|
|
24834
25148
|
model: raw.model,
|
|
24835
25149
|
thinking: raw.thinking === true,
|
|
24836
25150
|
tts: raw.tts ? {
|
|
@@ -24978,7 +25292,7 @@ var VoiceChatPlugin = class _VoiceChatPlugin {
|
|
|
24978
25292
|
- \u5982\u679C\u5B9E\u5728\u9700\u8981\u67E5\uFF1A\u5148\u8BF4"\u7B49\u6211\u67E5\u4E0B"\u5E76\u6781\u7B80\u8C03\u7528\uFF0C\u67E5\u5B8C\u7ACB\u523B\u603B\u7ED3\u6210\u4E00\u53E5\u8BDD`;
|
|
24979
25293
|
const engine = new QueryEngine(llmProvider, {
|
|
24980
25294
|
model: modelId,
|
|
24981
|
-
systemPrompt: (ctx.deps.systemPrompt
|
|
25295
|
+
systemPrompt: resolveSystemPrompt(ctx.deps.systemPrompt) + voiceChatRules,
|
|
24982
25296
|
maxTokens: 4096,
|
|
24983
25297
|
temperature: 0.7,
|
|
24984
25298
|
disableThinking: !this.config.thinking,
|
|
@@ -25152,7 +25466,7 @@ import path28 from "node:path";
|
|
|
25152
25466
|
import fs28 from "node:fs";
|
|
25153
25467
|
|
|
25154
25468
|
// src/memory/cognifold/config.ts
|
|
25155
|
-
var
|
|
25469
|
+
var DEFAULTS4 = {
|
|
25156
25470
|
pythonPort: 9001,
|
|
25157
25471
|
autoStart: true,
|
|
25158
25472
|
persistDir: "./sessions",
|
|
@@ -25164,15 +25478,15 @@ function parseCognifoldConfig(raw) {
|
|
|
25164
25478
|
if (!raw) return { enabled: false };
|
|
25165
25479
|
return {
|
|
25166
25480
|
enabled: raw.enabled === true,
|
|
25167
|
-
pythonPort: raw.pythonPort ??
|
|
25168
|
-
pythonPath: raw.pythonPath ??
|
|
25481
|
+
pythonPort: raw.pythonPort ?? DEFAULTS4.pythonPort,
|
|
25482
|
+
pythonPath: raw.pythonPath ?? DEFAULTS4.pythonPath,
|
|
25169
25483
|
autoStart: raw.autoStart !== false,
|
|
25170
25484
|
// default true
|
|
25171
|
-
baseUrl: raw.baseUrl ?? `http://127.0.0.1:${raw.pythonPort ??
|
|
25172
|
-
persistDir: raw.persistDir ??
|
|
25485
|
+
baseUrl: raw.baseUrl ?? `http://127.0.0.1:${raw.pythonPort ?? DEFAULTS4.pythonPort}/api/v1`,
|
|
25486
|
+
persistDir: raw.persistDir ?? DEFAULTS4.persistDir,
|
|
25173
25487
|
scopes: raw.scopes,
|
|
25174
|
-
readyTimeoutMs: raw.readyTimeoutMs ??
|
|
25175
|
-
maxRestarts: raw.maxRestarts ??
|
|
25488
|
+
readyTimeoutMs: raw.readyTimeoutMs ?? DEFAULTS4.readyTimeoutMs,
|
|
25489
|
+
maxRestarts: raw.maxRestarts ?? DEFAULTS4.maxRestarts,
|
|
25176
25490
|
llm: raw.llm
|
|
25177
25491
|
};
|
|
25178
25492
|
}
|
|
@@ -25288,13 +25602,13 @@ var CogniFoldClient = class {
|
|
|
25288
25602
|
|
|
25289
25603
|
// src/memory/cognifold/session-manager.ts
|
|
25290
25604
|
import { readFile as readFile6, writeFile as writeFile5, mkdir as mkdir4 } from "node:fs/promises";
|
|
25291
|
-
import { join as
|
|
25605
|
+
import { join as join28, dirname as dirname3 } from "node:path";
|
|
25292
25606
|
var CogniFoldSessionManager = class {
|
|
25293
25607
|
constructor(workspacePath, config, client) {
|
|
25294
25608
|
this.workspacePath = workspacePath;
|
|
25295
25609
|
this.config = config;
|
|
25296
25610
|
this.client = client;
|
|
25297
|
-
this.sessionsDir =
|
|
25611
|
+
this.sessionsDir = join28(workspacePath, ".cognifold", "sessions");
|
|
25298
25612
|
}
|
|
25299
25613
|
workspacePath;
|
|
25300
25614
|
config;
|
|
@@ -25364,7 +25678,7 @@ var CogniFoldSessionManager = class {
|
|
|
25364
25678
|
console.log(`[cognifold] Created new session for scope "${scope}": ${newSession.sessionId}`);
|
|
25365
25679
|
}
|
|
25366
25680
|
getFilePath(scope) {
|
|
25367
|
-
return
|
|
25681
|
+
return join28(this.sessionsDir, `${scope}.json`);
|
|
25368
25682
|
}
|
|
25369
25683
|
async writeFileSafe(filePath, data) {
|
|
25370
25684
|
try {
|
|
@@ -25684,7 +25998,7 @@ import path29 from "node:path";
|
|
|
25684
25998
|
import fs29 from "node:fs";
|
|
25685
25999
|
|
|
25686
26000
|
// src/memory/everos/config.ts
|
|
25687
|
-
var
|
|
26001
|
+
var DEFAULTS5 = {
|
|
25688
26002
|
everosUrl: "http://127.0.0.1:8100",
|
|
25689
26003
|
agenticUrl: "http://127.0.0.1:8101",
|
|
25690
26004
|
agenticPort: 8101,
|
|
@@ -25710,7 +26024,7 @@ function parseEverosConfig(raw, providers) {
|
|
|
25710
26024
|
if (!raw) {
|
|
25711
26025
|
return {
|
|
25712
26026
|
enabled: false,
|
|
25713
|
-
...
|
|
26027
|
+
...DEFAULTS5,
|
|
25714
26028
|
userId: "xiaomei",
|
|
25715
26029
|
llm: { model: "glm-5.2", apiKey: "", baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4" },
|
|
25716
26030
|
rerank: { model: "Qwen/Qwen3-Reranker-4B", apiKey: "", baseUrl: "https://api.deepinfra.com/v1/inference" },
|
|
@@ -25720,12 +26034,12 @@ function parseEverosConfig(raw, providers) {
|
|
|
25720
26034
|
}
|
|
25721
26035
|
return {
|
|
25722
26036
|
enabled: raw.enabled === true,
|
|
25723
|
-
everosUrl: raw.everosUrl ??
|
|
25724
|
-
agenticUrl: raw.agenticUrl ??
|
|
25725
|
-
agenticPort: raw.agenticPort ??
|
|
26037
|
+
everosUrl: raw.everosUrl ?? DEFAULTS5.everosUrl,
|
|
26038
|
+
agenticUrl: raw.agenticUrl ?? DEFAULTS5.agenticUrl,
|
|
26039
|
+
agenticPort: raw.agenticPort ?? DEFAULTS5.agenticPort,
|
|
25726
26040
|
userId: raw.userId ?? "xiaomei",
|
|
25727
26041
|
autoStart: raw.autoStart !== false,
|
|
25728
|
-
defaultMode: raw.defaultMode ??
|
|
26042
|
+
defaultMode: raw.defaultMode ?? DEFAULTS5.defaultMode,
|
|
25729
26043
|
llm: resolveProviderConfig(
|
|
25730
26044
|
raw.llm,
|
|
25731
26045
|
providers,
|
|
@@ -26135,8 +26449,8 @@ function scanSkills(skillsDir) {
|
|
|
26135
26449
|
}
|
|
26136
26450
|
const skills = [];
|
|
26137
26451
|
const scanDir = (dir, depth) => {
|
|
26138
|
-
const
|
|
26139
|
-
for (const entry of
|
|
26452
|
+
const entries2 = fs30.readdirSync(dir, { withFileTypes: true });
|
|
26453
|
+
for (const entry of entries2) {
|
|
26140
26454
|
if (entry.name.startsWith(".") || entry.name === "_archive") continue;
|
|
26141
26455
|
const full = path30.join(dir, entry.name);
|
|
26142
26456
|
if (entry.isDirectory() && depth < 3) {
|
|
@@ -26427,6 +26741,7 @@ ${rawOutput}
|
|
|
26427
26741
|
// src/tools/msg-send.ts
|
|
26428
26742
|
init_live();
|
|
26429
26743
|
init_registry();
|
|
26744
|
+
init_rate_breaker();
|
|
26430
26745
|
function getConfig() {
|
|
26431
26746
|
return liveConfig.all();
|
|
26432
26747
|
}
|
|
@@ -26490,10 +26805,10 @@ channel_id \u4E0D\u586B\u4E14 to \u4E5F\u4E0D\u586B\u65F6\uFF0C\u9ED8\u8BA4\u56D
|
|
|
26490
26805
|
\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
26806
|
|
|
26492
26807
|
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="
|
|
26808
|
+
- \u53D1\u9891\u9053\u5E76 @\u67D0\u4EBA: to="1111111111111111111", channel_id="3333333333333333333", content="\u4F60\u597D"
|
|
26809
|
+
- \u53D1\u9891\u9053\u5E76 @\u591A\u4EBA: to="1111111111111111111,2222222222222222222", channel_id="3333333333333333333", content="\u4F60\u597D"
|
|
26810
|
+
- \u53D1\u9891\u9053\u4E0D\u5E26 @: channel_id="3333333333333333333", content="\u7CFB\u7EDF\u901A\u77E5"
|
|
26811
|
+
- \u53D1 DM: to="1111111111111111111", content="\u79C1\u804A\u5185\u5BB9"
|
|
26497
26812
|
- \u8DE8\u5E73\u53F0\u53D1\u9001: source="discord", channel_id="1503034906081624174", content="\u4ECE\u98DE\u4E66\u53D1\u5230Discord"
|
|
26498
26813
|
- \u56DE\u590D\u6765\u6E90\u9891\u9053: content="\u6536\u5230"\uFF08\u4E0D\u586B channel_id \u548C to\uFF09`,
|
|
26499
26814
|
schema: {
|
|
@@ -26522,12 +26837,12 @@ Examples:
|
|
|
26522
26837
|
}
|
|
26523
26838
|
const toIds = to ? to.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
26524
26839
|
const dest = resolvedChannelId || (toIds.length > 0 ? toIds[0] : void 0);
|
|
26840
|
+
const dmEchoDest = resolvedChannelId || (toIds.length > 0 ? toIds[0] : void 0);
|
|
26525
26841
|
const isDmEcho = ctx.channelType === "dm" && // DM 对话(群聊先观察不拦)
|
|
26526
26842
|
resolvedSource === ctx.channel && // 目标通道=当前对话通道(真跨通道转发不拦)
|
|
26527
26843
|
ctx.inboundFrom && // 有当前发送者(注入消息无 inboundFrom 不拦)
|
|
26528
|
-
|
|
26529
|
-
|
|
26530
|
-
!!resolvedChannelId && resolvedChannelId === ctx.channelTarget);
|
|
26844
|
+
dmEchoDest !== void 0 && (dmEchoDest === ctx.channelTarget || dmEchoDest === ctx.inboundFrom) && // 目的地=当前会话/当前对话者
|
|
26845
|
+
toIds.every((id) => id === ctx.inboundFrom);
|
|
26531
26846
|
if (isDmEcho) {
|
|
26532
26847
|
console.log(`[msg_send] \u26D4 DM \u56DE\u53D1\u5F53\u524D\u5BF9\u8BDD\u88AB\u62E6: to=${to} channel_id=${resolvedChannelId || "(fallback)"} (${resolvedSource})`);
|
|
26533
26848
|
return {
|
|
@@ -26558,6 +26873,11 @@ Examples:
|
|
|
26558
26873
|
}
|
|
26559
26874
|
const fullMsg = `${mentionPrefix}${content}`;
|
|
26560
26875
|
const where = resolvedChannelId ? `${resolvedSource} \u9891\u9053 ${resolvedChannelId}` : `${resolvedSource} DM ${toIds[0]}`;
|
|
26876
|
+
const breaker = checkRateBreaker(resolvedSource, ctx.sessionId, dest, toIds, !!ctx.inboundIsBot);
|
|
26877
|
+
if (breaker) {
|
|
26878
|
+
console.log(`[msg_send] \u{1F515} rate-breaker \u62E6\u622A: session=${ctx.sessionId} \u2192 ${where}`);
|
|
26879
|
+
return breaker;
|
|
26880
|
+
}
|
|
26561
26881
|
try {
|
|
26562
26882
|
await mgr.send(resolvedSource, dest, fullMsg);
|
|
26563
26883
|
return { content: `\u6D88\u606F\u5DF2\u53D1\u9001\u5230 ${where}` };
|
|
@@ -26673,10 +26993,10 @@ Parameters:
|
|
|
26673
26993
|
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
26994
|
|
|
26675
26995
|
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="
|
|
26996
|
+
- \u53D1\u56FE\u7247\u5230\u9891\u9053\u5E76 @\u67D0\u4EBA: to="1111111111111111111", channel_id="3333333333333333333", type="image", path="/tmp/photo.png"
|
|
26997
|
+
- \u53D1\u56FE\u7247\u5230\u9891\u9053\u5E76 @\u591A\u4EBA: to="1111111111111111111,2222222222222222222", channel_id="3333333333333333333", type="image", path="/tmp/photo.png"
|
|
26998
|
+
- \u53D1\u6587\u4EF6\u5230\u9891\u9053\u4E0D\u5E26 @: channel_id="3333333333333333333", type="file", path="/tmp/report.pdf"
|
|
26999
|
+
- \u53D1\u97F3\u9891 DM: to="1111111111111111111", type="audio", path="/tmp/voice.mp3"
|
|
26680
27000
|
- \u8DE8\u5E73\u53F0\u53D1\u9001: source="discord", channel_id="1503034906081624174", type="image", path="/tmp/photo.png"
|
|
26681
27001
|
- \u53D1\u5230\u6765\u6E90\u9891\u9053: type="image", path="/tmp/photo.png"\uFF08\u4E0D\u586B channel_id \u548C to\uFF09`,
|
|
26682
27002
|
schema: {
|
|
@@ -29472,8 +29792,8 @@ registerCommand({
|
|
|
29472
29792
|
sessionManager: deps.sessions,
|
|
29473
29793
|
sessionId: sid,
|
|
29474
29794
|
model: deps.config.model,
|
|
29475
|
-
contextWindow: deps.compactConfig.contextWindow || 2e5,
|
|
29476
|
-
systemPrompt: deps.systemPrompt,
|
|
29795
|
+
contextWindow: (typeof deps.compactConfig === "function" ? deps.compactConfig() : deps.compactConfig).contextWindow || 2e5,
|
|
29796
|
+
systemPrompt: resolveSystemPrompt(deps.systemPrompt),
|
|
29477
29797
|
toolDefs: registry.definitions(),
|
|
29478
29798
|
workspace: deps.config.workspace
|
|
29479
29799
|
});
|
|
@@ -29521,7 +29841,7 @@ ${question}`;
|
|
|
29521
29841
|
const stream = deps.provider.streamChat({
|
|
29522
29842
|
model: deps.config.model,
|
|
29523
29843
|
messages: [{ role: "user", content: wrappedQuestion }],
|
|
29524
|
-
systemPrompt: deps.systemPrompt,
|
|
29844
|
+
systemPrompt: resolveSystemPrompt(deps.systemPrompt),
|
|
29525
29845
|
maxTokens: 2048,
|
|
29526
29846
|
signal: void 0
|
|
29527
29847
|
});
|
|
@@ -29742,8 +30062,8 @@ registerCommand({
|
|
|
29742
30062
|
const { rm: rm2 } = await import("node:fs/promises");
|
|
29743
30063
|
const teamsDir = getTeamsDir3();
|
|
29744
30064
|
const { readdir: readdir2 } = await import("node:fs/promises");
|
|
29745
|
-
const
|
|
29746
|
-
for (const entry of
|
|
30065
|
+
const entries2 = await readdir2(teamsDir).catch(() => []);
|
|
30066
|
+
for (const entry of entries2) {
|
|
29747
30067
|
const entryPath = `${teamsDir}/${entry}`;
|
|
29748
30068
|
try {
|
|
29749
30069
|
await rm2(entryPath, { recursive: true, force: true });
|
|
@@ -29852,8 +30172,21 @@ registerCommand({
|
|
|
29852
30172
|
let current;
|
|
29853
30173
|
if (deps.getModelOverride()) {
|
|
29854
30174
|
current = `**${deps.getModelOverride()}** (override)`;
|
|
29855
|
-
} else if (deps.provider instanceof FallbackProvider
|
|
29856
|
-
|
|
30175
|
+
} else if (deps.provider instanceof FallbackProvider) {
|
|
30176
|
+
const status = deps.provider.getChainStatus();
|
|
30177
|
+
const chainStr = status.map((e) => e.label).join(" \u2192 ");
|
|
30178
|
+
const next = status.find((e) => e.cooldownMs <= 0);
|
|
30179
|
+
const cooling = status.filter((e) => e.cooldownMs > 0);
|
|
30180
|
+
current = `**auto-route** \u2014 \u94FE: ${chainStr}
|
|
30181
|
+
\u4E0B\u4E00\u4E2A\u8BF7\u6C42\u7528: **${next?.label ?? "(\u5168\u90E8\u51B7\u5374\u4E2D)"}**`;
|
|
30182
|
+
if (cooling.length > 0) {
|
|
30183
|
+
current += `
|
|
30184
|
+
\u51B7\u5374\u4E2D: ${cooling.map((e) => `${e.label}\uFF08\u5269 ${Math.round(e.cooldownMs / 6e4)}min\uFF09`).join("\u3001")}`;
|
|
30185
|
+
}
|
|
30186
|
+
if (deps.provider.lastUsedLabel) {
|
|
30187
|
+
current += `
|
|
30188
|
+
\u4E0A\u6B21\u5B9E\u9645: ${deps.provider.lastUsedLabel}`;
|
|
30189
|
+
}
|
|
29857
30190
|
} else {
|
|
29858
30191
|
current = `**${deps.config.provider.id}/${deps.config.model}** (default, auto-route)`;
|
|
29859
30192
|
}
|
|
@@ -30107,6 +30440,16 @@ Use full ref like \`/vision-model ${candidates[0].ref}\``);
|
|
|
30107
30440
|
});
|
|
30108
30441
|
|
|
30109
30442
|
// src/engine-startup.ts
|
|
30443
|
+
function resolveModelMaxTokens(modelRef) {
|
|
30444
|
+
try {
|
|
30445
|
+
const [pid, mid] = (modelRef || "").split("/");
|
|
30446
|
+
const models = liveConfig.get(`models.providers.${pid}.models`);
|
|
30447
|
+
const m = models?.find((x) => x?.id === mid);
|
|
30448
|
+
return typeof m?.maxTokens === "number" && m.maxTokens > 0 ? m.maxTokens : 4096;
|
|
30449
|
+
} catch {
|
|
30450
|
+
return 4096;
|
|
30451
|
+
}
|
|
30452
|
+
}
|
|
30110
30453
|
var _epipeSeen = false;
|
|
30111
30454
|
process.on("uncaughtException", (err) => {
|
|
30112
30455
|
const code = err?.code ?? "";
|
|
@@ -30176,7 +30519,10 @@ async function startEngine(config, opts) {
|
|
|
30176
30519
|
const licensedFeatures = loadLicense(config.stateDir, config.profile?.devMode === true);
|
|
30177
30520
|
const requiredTools = resolveRequiredTools(config.profile.features, licensedFeatures);
|
|
30178
30521
|
registry.licensedFeatures = licensedFeatures;
|
|
30179
|
-
|
|
30522
|
+
let provider;
|
|
30523
|
+
let visionProvider;
|
|
30524
|
+
let visionChainLabels;
|
|
30525
|
+
({ provider, visionProvider, visionChainLabels } = buildProviderChain(config));
|
|
30180
30526
|
if (visionChainLabels.length > 0) {
|
|
30181
30527
|
console.log(`[vision] Routing enabled: ${visionChainLabels.join(" \u2192 ")}`);
|
|
30182
30528
|
}
|
|
@@ -30398,11 +30744,36 @@ ${content}`
|
|
|
30398
30744
|
console.log(`[DEBUG] definitions() = ${_allDefs.length} defs`);
|
|
30399
30745
|
console.log(`[DEBUG] active (non-defer) = ${_activeDefs.length}: ${_activeDefs.map((d) => d.function.name).join(", ")}`);
|
|
30400
30746
|
console.log(`[DEBUG] deferred = ${_deferredDefs.length}: ${_deferredDefs.map((d) => d.function.name).join(", ")}`);
|
|
30401
|
-
|
|
30747
|
+
let _stableCache = null;
|
|
30748
|
+
const getSystemStable = () => {
|
|
30749
|
+
const promptCfg = liveConfig.get("prompt") || {};
|
|
30750
|
+
const fileCandidates = /* @__PURE__ */ new Set(["SOUL.md"]);
|
|
30751
|
+
for (const f of promptCfg.staticFiles || []) fileCandidates.add(f);
|
|
30752
|
+
for (const item of promptCfg.order || []) {
|
|
30753
|
+
if (typeof item === "string" && /\.(md|txt|json)$/i.test(item)) fileCandidates.add(item);
|
|
30754
|
+
}
|
|
30755
|
+
const statLines = [JSON.stringify({ mode: promptCfg.mode, order: promptCfg.order })];
|
|
30756
|
+
for (const f of fileCandidates) {
|
|
30757
|
+
try {
|
|
30758
|
+
const p = path49.isAbsolute(f) ? f : path49.join(config.workspace, f);
|
|
30759
|
+
statLines.push(`${f}:${fs47.statSync(p).mtimeMs}`);
|
|
30760
|
+
} catch {
|
|
30761
|
+
statLines.push(`${f}:missing`);
|
|
30762
|
+
}
|
|
30763
|
+
}
|
|
30764
|
+
const fingerprint = statLines.join("|");
|
|
30765
|
+
if (_stableCache && _stableCache.fingerprint === fingerprint) return _stableCache.prompt;
|
|
30766
|
+
const prompt = buildStablePrompt(config.workspace, promptCfg);
|
|
30767
|
+
_stableCache = { fingerprint, prompt };
|
|
30768
|
+
console.log(`[prompt] stable \u91CD\u5EFA\uFF08\u6587\u4EF6/config \u53D8\u66F4\uFF09\uFF0C${prompt.length} chars`);
|
|
30769
|
+
return prompt;
|
|
30770
|
+
};
|
|
30771
|
+
const systemStable = getSystemStable();
|
|
30402
30772
|
const systemDynamic = buildDynamicPrompt({
|
|
30403
30773
|
workspace: config.workspace
|
|
30404
30774
|
});
|
|
30405
30775
|
const systemPrompt = [systemStable, systemDynamic].join("\n\n");
|
|
30776
|
+
const getSystemPrompt = () => [getSystemStable(), buildDynamicPrompt({ workspace: config.workspace })].join("\n\n");
|
|
30406
30777
|
dumpSystemPrompt(config.workspace, systemStable, systemDynamic);
|
|
30407
30778
|
const modelDef = config.provider.models.find((m) => m.id === config.model);
|
|
30408
30779
|
const modelContextWindow = modelDef?.contextWindow;
|
|
@@ -30421,13 +30792,21 @@ ${content}`
|
|
|
30421
30792
|
// 默认 5MB
|
|
30422
30793
|
);
|
|
30423
30794
|
const memoryFlushEnabled = config.compaction?.memoryFlush?.enabled !== false;
|
|
30424
|
-
const
|
|
30425
|
-
|
|
30426
|
-
|
|
30427
|
-
|
|
30428
|
-
|
|
30429
|
-
|
|
30795
|
+
const getCompactConfig = () => {
|
|
30796
|
+
const liveComp = liveConfig.get("compaction") || {};
|
|
30797
|
+
const liveModel = (liveConfig.get("providers") || {})[liveConfig.get("agents.defaults.model.primary")?.split("/")[0] || ""];
|
|
30798
|
+
const liveModelId = liveConfig.get("agents.defaults.model.primary")?.split("/")?.[1];
|
|
30799
|
+
const mDef = liveModel?.models?.find((m) => m.id === liveModelId);
|
|
30800
|
+
const mCW = mDef?.contextWindow;
|
|
30801
|
+
const cfg = {
|
|
30802
|
+
...DEFAULT_COMPACT_CONFIG,
|
|
30803
|
+
...liveComp,
|
|
30804
|
+
...mCW && !liveComp.contextWindow ? { contextWindow: mCW } : {},
|
|
30805
|
+
forceFlushTranscriptBytes
|
|
30806
|
+
};
|
|
30807
|
+
return cfg;
|
|
30430
30808
|
};
|
|
30809
|
+
const compactConfig = getCompactConfig();
|
|
30431
30810
|
if (compactConfig.contextWindow !== DEFAULT_COMPACT_CONFIG.contextWindow) {
|
|
30432
30811
|
console.log(`[compact] Context window: ${compactConfig.contextWindow} (from ${config.compaction?.contextWindow ? "config" : modelContextWindow ? "model" : "default"})`);
|
|
30433
30812
|
}
|
|
@@ -30436,12 +30815,12 @@ ${content}`
|
|
|
30436
30815
|
}
|
|
30437
30816
|
const engine = new QueryEngine(provider, {
|
|
30438
30817
|
model: config.model,
|
|
30439
|
-
systemPrompt,
|
|
30440
|
-
systemStable,
|
|
30441
|
-
//
|
|
30442
|
-
compactConfig,
|
|
30443
|
-
//
|
|
30444
|
-
maxTokens:
|
|
30818
|
+
systemPrompt: getSystemPrompt,
|
|
30819
|
+
systemStable: getSystemStable,
|
|
30820
|
+
// 0902 函数形态:mtime 缓存 getter,改 prompt 文件热生效
|
|
30821
|
+
compactConfig: getCompactConfig,
|
|
30822
|
+
// 0902 函数形态:每 turn 刷新
|
|
30823
|
+
maxTokens: () => resolveModelMaxTokens(liveConfig.get("agents.defaults.model.primary") || config.model),
|
|
30445
30824
|
temperature: 0.7,
|
|
30446
30825
|
maxTurns: config.profile.maxTurns,
|
|
30447
30826
|
// 从配置读,默认 50(query.ts 里 fallback)
|
|
@@ -30452,10 +30831,10 @@ ${content}`
|
|
|
30452
30831
|
if (visionProvider && visionConfig) {
|
|
30453
30832
|
visionEngine = new QueryEngine(visionProvider, {
|
|
30454
30833
|
model: visionConfig.modelId,
|
|
30455
|
-
systemPrompt,
|
|
30456
|
-
systemStable,
|
|
30457
|
-
compactConfig,
|
|
30458
|
-
maxTokens:
|
|
30834
|
+
systemPrompt: getSystemPrompt,
|
|
30835
|
+
systemStable: getSystemStable,
|
|
30836
|
+
compactConfig: getCompactConfig,
|
|
30837
|
+
maxTokens: () => resolveModelMaxTokens(`${visionConfig.providerId}/${visionConfig.modelId}`),
|
|
30459
30838
|
temperature: 0.7,
|
|
30460
30839
|
maxTurns: config.profile.maxTurns,
|
|
30461
30840
|
agentLabel: "main"
|
|
@@ -30527,7 +30906,8 @@ ${content}`
|
|
|
30527
30906
|
providerApi: config.provider.api,
|
|
30528
30907
|
model: config.model,
|
|
30529
30908
|
modelInputs: modelDef?.input || ["text"],
|
|
30530
|
-
systemPrompt,
|
|
30909
|
+
systemPrompt: getSystemPrompt,
|
|
30910
|
+
// 0902 函数形态:/btw、voice-chat 等按调用时现取(stable mtime 缓存 + dynamic 现算)
|
|
30531
30911
|
channels: config.channels,
|
|
30532
30912
|
config,
|
|
30533
30913
|
// tool 读自己配置用
|
|
@@ -30541,36 +30921,28 @@ ${content}`
|
|
|
30541
30921
|
} : void 0,
|
|
30542
30922
|
mcpManager
|
|
30543
30923
|
};
|
|
30544
|
-
|
|
30924
|
+
const visionMetaInit = visionEngine && visionConfig ? (() => {
|
|
30545
30925
|
const vpCfg = config.providers?.[visionConfig.providerId];
|
|
30546
30926
|
const visionModelDef = vpCfg?.models?.find((m) => m.id === visionConfig.modelId);
|
|
30547
|
-
|
|
30548
|
-
engine: visionEngine,
|
|
30549
|
-
sessions,
|
|
30550
|
-
channelManager,
|
|
30551
|
-
workspace: config.workspace,
|
|
30927
|
+
return {
|
|
30552
30928
|
providerId: visionConfig.providerId,
|
|
30553
30929
|
providerApi: vpCfg?.api || "openai-completions",
|
|
30554
30930
|
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
|
|
30931
|
+
modelInputs: visionModelDef?.input || ["text", "image"]
|
|
30568
30932
|
};
|
|
30933
|
+
})() : null;
|
|
30934
|
+
let visionMeta = visionMetaInit;
|
|
30935
|
+
function resolveVisionDeps() {
|
|
30936
|
+
if (!visionEngine || !visionMeta) return null;
|
|
30937
|
+
if (!visionDeps) {
|
|
30938
|
+
visionDeps = { ...deps, engine: visionEngine, ...visionMeta };
|
|
30939
|
+
console.log(`[vision] deps built: ${visionMeta.providerId}/${visionMeta.model}`);
|
|
30940
|
+
}
|
|
30941
|
+
return visionDeps;
|
|
30569
30942
|
}
|
|
30570
30943
|
let modelOverride = null;
|
|
30571
30944
|
let modelOverrideEngine = null;
|
|
30572
30945
|
let visionOverride = null;
|
|
30573
|
-
const defaultVisionDeps = visionDeps;
|
|
30574
30946
|
const modelDepsCache = /* @__PURE__ */ new Map();
|
|
30575
30947
|
deps.invalidateDeskBuddyDeps = () => {
|
|
30576
30948
|
deskBuddyDeps = null;
|
|
@@ -30596,6 +30968,7 @@ ${content}`
|
|
|
30596
30968
|
if (!p.provider) {
|
|
30597
30969
|
visionEngine = null;
|
|
30598
30970
|
visionDeps = null;
|
|
30971
|
+
visionMeta = null;
|
|
30599
30972
|
return;
|
|
30600
30973
|
}
|
|
30601
30974
|
if (visionEngine) {
|
|
@@ -30604,39 +30977,22 @@ ${content}`
|
|
|
30604
30977
|
} else {
|
|
30605
30978
|
visionEngine = new QueryEngine(p.provider, {
|
|
30606
30979
|
model: p.model,
|
|
30607
|
-
systemPrompt,
|
|
30608
|
-
systemStable,
|
|
30609
|
-
compactConfig,
|
|
30980
|
+
systemPrompt: getSystemPrompt,
|
|
30981
|
+
systemStable: getSystemStable,
|
|
30982
|
+
compactConfig: getCompactConfig,
|
|
30610
30983
|
maxTokens: 4096,
|
|
30611
30984
|
temperature: 0.7,
|
|
30612
30985
|
maxTurns: config.profile.maxTurns,
|
|
30613
30986
|
agentLabel: "main"
|
|
30614
30987
|
});
|
|
30615
30988
|
}
|
|
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
|
-
}
|
|
30989
|
+
visionMeta = {
|
|
30990
|
+
providerId: p.providerId,
|
|
30991
|
+
providerApi: p.providerApi,
|
|
30992
|
+
model: p.model,
|
|
30993
|
+
modelInputs: p.modelInputs
|
|
30994
|
+
};
|
|
30995
|
+
visionDeps = null;
|
|
30640
30996
|
};
|
|
30641
30997
|
function createModelDeps(ref) {
|
|
30642
30998
|
const slashIdx = ref.indexOf("/");
|
|
@@ -30656,28 +31012,21 @@ ${content}`
|
|
|
30656
31012
|
const llmProvider = providerId === config.provider.id ? provider : createProvider(providerCfg);
|
|
30657
31013
|
const engine2 = new QueryEngine(llmProvider, {
|
|
30658
31014
|
model: modelId,
|
|
30659
|
-
systemPrompt,
|
|
30660
|
-
systemStable,
|
|
30661
|
-
compactConfig,
|
|
30662
|
-
maxTokens:
|
|
31015
|
+
systemPrompt: getSystemPrompt,
|
|
31016
|
+
systemStable: getSystemStable,
|
|
31017
|
+
compactConfig: getCompactConfig,
|
|
31018
|
+
maxTokens: () => resolveModelMaxTokens(`${providerId}/${modelId}`),
|
|
30663
31019
|
temperature: 0.7,
|
|
30664
31020
|
maxTurns: config.profile.maxTurns,
|
|
30665
31021
|
agentLabel: `main:${ref}`
|
|
30666
31022
|
});
|
|
30667
31023
|
return {
|
|
31024
|
+
...deps,
|
|
30668
31025
|
engine: engine2,
|
|
30669
|
-
sessions,
|
|
30670
|
-
channelManager,
|
|
30671
|
-
workspace: config.workspace,
|
|
30672
31026
|
providerId,
|
|
30673
31027
|
providerApi: providerCfg.api,
|
|
30674
31028
|
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
|
|
31029
|
+
modelInputs: modelDef2.input || ["text"]
|
|
30681
31030
|
};
|
|
30682
31031
|
}
|
|
30683
31032
|
const dispatcher = new MessageDispatcher();
|
|
@@ -30854,7 +31203,17 @@ ${content}`
|
|
|
30854
31203
|
console.log(`[cron] config check: enabled=${config.cron?.enabled}, hasConfig=${!!config.cron}`);
|
|
30855
31204
|
if (config.cron?.enabled) {
|
|
30856
31205
|
const { CronPlugin: CronPlugin2 } = await Promise.resolve().then(() => (init_cron_plugin(), cron_plugin_exports));
|
|
30857
|
-
const
|
|
31206
|
+
const resolveCronModelDeps = (ref) => {
|
|
31207
|
+
const key = `cron:${ref}`;
|
|
31208
|
+
const cached = modelDepsCache.get(key);
|
|
31209
|
+
if (cached) return cached;
|
|
31210
|
+
const built = createModelDeps(ref);
|
|
31211
|
+
if (!built) return null;
|
|
31212
|
+
modelDepsCache.set(key, built);
|
|
31213
|
+
console.log(`[cron] Model deps built: ${ref}`);
|
|
31214
|
+
return built;
|
|
31215
|
+
};
|
|
31216
|
+
const cronPlugin = new CronPlugin2(config.cron, sessions, channelManager, deps, config.stateDir, dispatcher, resolveCronModelDeps);
|
|
30858
31217
|
await cronPlugin.start();
|
|
30859
31218
|
const { setCronConfig: setCronConfig2 } = await Promise.resolve().then(() => (init_tools(), tools_exports));
|
|
30860
31219
|
setCronConfig2(config.cron);
|
|
@@ -31030,8 +31389,10 @@ ${notifications}
|
|
|
31030
31389
|
dispatcher,
|
|
31031
31390
|
runningQueries,
|
|
31032
31391
|
engine,
|
|
31033
|
-
systemPrompt,
|
|
31034
|
-
|
|
31392
|
+
systemPrompt: getSystemPrompt,
|
|
31393
|
+
// 0902 函数形态:命令按调用时现取
|
|
31394
|
+
compactConfig: getCompactConfig,
|
|
31395
|
+
// 0902 同上
|
|
31035
31396
|
provider,
|
|
31036
31397
|
deps,
|
|
31037
31398
|
getVisualRegistry: () => visualRegistry,
|
|
@@ -31053,9 +31414,17 @@ ${notifications}
|
|
|
31053
31414
|
setVisionDeps: (v) => {
|
|
31054
31415
|
visionDeps = v;
|
|
31055
31416
|
},
|
|
31056
|
-
|
|
31417
|
+
// reset 用:清 override deps,下一条图片消息由 resolveVisionDeps 按当前 meta 重建
|
|
31418
|
+
getDefaultVisionDeps: () => {
|
|
31419
|
+
visionDeps = null;
|
|
31420
|
+
return null;
|
|
31421
|
+
},
|
|
31057
31422
|
doReloadConfig
|
|
31058
31423
|
};
|
|
31424
|
+
deps.onProviderSwapped = (p) => {
|
|
31425
|
+
provider = p;
|
|
31426
|
+
commandDeps.provider = p;
|
|
31427
|
+
};
|
|
31059
31428
|
const slashCommands = listCommandDefs().map((d) => ({
|
|
31060
31429
|
name: d.name,
|
|
31061
31430
|
description: d.description,
|
|
@@ -31312,7 +31681,7 @@ ${pathStr}` }];
|
|
|
31312
31681
|
for (const att of nonImageAttachments) {
|
|
31313
31682
|
console.log(`[file] Downloading: ${att.filename} (${att.contentType}, ${att.size}B)`);
|
|
31314
31683
|
try {
|
|
31315
|
-
const resp = attachDispatcher ? await fetch(att.url, { dispatcher: attachDispatcher }) : await fetch(att.url);
|
|
31684
|
+
const resp = attachDispatcher ? await fetch(att.url, { dispatcher: attachDispatcher, signal: AbortSignal.timeout(3e4) }) : await fetch(att.url, { signal: AbortSignal.timeout(3e4) });
|
|
31316
31685
|
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
|
31317
31686
|
const buffer = Buffer.from(await resp.arrayBuffer());
|
|
31318
31687
|
const safeName2 = path49.basename(att.filename).replace(/[<>:"/\\|?*\x00-\x1f]/g, "_") || "attachment";
|
|
@@ -31342,7 +31711,8 @@ ${pathStr}` }];
|
|
|
31342
31711
|
}
|
|
31343
31712
|
const isImageBlock = (b) => b.type === "image" || b.type === "image_url";
|
|
31344
31713
|
const hasImages = Array.isArray(queryContent) && queryContent.some((b) => isImageBlock(b));
|
|
31345
|
-
|
|
31714
|
+
const activeVisionDeps = hasImages ? resolveVisionDeps() : null;
|
|
31715
|
+
let msgDeps = activeVisionDeps ? activeVisionDeps : modelOverride ? modelDepsCache.get(modelOverride) ?? deps : deps;
|
|
31346
31716
|
if (isDeskBuddy && !hasImages && !modelOverride) {
|
|
31347
31717
|
if (!deskBuddyDeps) {
|
|
31348
31718
|
const dbRef = liveConfig.get("channels.deskBuddy.model") || "deepinfra/deepseek-ai/DeepSeek-V4-Flash-0731";
|
|
@@ -31370,7 +31740,8 @@ ${pathStr}` }];
|
|
|
31370
31740
|
model: dbProviderCfg ? dbModelId : config.model,
|
|
31371
31741
|
// 8/18 翀哥:完整工程 prompt(14k tok)+memory(10k) 会把小模型带偏成"工程助手"——deskBuddy 用 SOUL 精简人设
|
|
31372
31742
|
// 8/21 复用 light-mode.buildLightStablePrompt(和情感模式同一构建器)
|
|
31373
|
-
systemPrompt: buildLightStablePrompt(config.workspace, "deskBuddy"),
|
|
31743
|
+
systemPrompt: () => buildLightStablePrompt(config.workspace, "deskBuddy"),
|
|
31744
|
+
// 0902 函数形态:改 SOUL 热生效
|
|
31374
31745
|
maxTokens: 1024,
|
|
31375
31746
|
temperature: 0.7,
|
|
31376
31747
|
disableThinking: true,
|
|
@@ -31384,7 +31755,7 @@ ${pathStr}` }];
|
|
|
31384
31755
|
msgDeps = deskBuddyDeps;
|
|
31385
31756
|
}
|
|
31386
31757
|
if (hasImages) {
|
|
31387
|
-
console.log(`[vision-debug] hasImages=true visionDeps=${
|
|
31758
|
+
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
31759
|
if (Array.isArray(queryContent)) {
|
|
31389
31760
|
queryContent.forEach((b, i) => {
|
|
31390
31761
|
if (b.type === "image" && b.source?.data) {
|
|
@@ -31393,13 +31764,13 @@ ${pathStr}` }];
|
|
|
31393
31764
|
});
|
|
31394
31765
|
}
|
|
31395
31766
|
}
|
|
31396
|
-
if (hasImages &&
|
|
31397
|
-
console.log(`[vision] Routing to ${
|
|
31767
|
+
if (hasImages && activeVisionDeps) {
|
|
31768
|
+
console.log(`[vision] Routing to ${activeVisionDeps.providerId}/${activeVisionDeps.model}${activeVisionDeps.model !== visionConfig?.modelId ? ` (override, config=${visionConfig.providerId}/${visionConfig.modelId})` : ""}`);
|
|
31398
31769
|
}
|
|
31399
31770
|
const preQueryResult = await messageHooks.runPreQuery({
|
|
31400
31771
|
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
31772
|
text: queryContent,
|
|
31402
|
-
msgDeps:
|
|
31773
|
+
msgDeps: activeVisionDeps ?? msgDeps,
|
|
31403
31774
|
deps: { provider, channelManager, sessions, dispatcher, config, workspace: config.workspace }
|
|
31404
31775
|
});
|
|
31405
31776
|
if (preQueryResult.skip) {
|
|
@@ -31412,6 +31783,7 @@ ${pathStr}` }];
|
|
|
31412
31783
|
}
|
|
31413
31784
|
queryContent = preQueryResult.text ?? queryContent;
|
|
31414
31785
|
if (preQueryResult.msgDeps) msgDeps = preQueryResult.msgDeps;
|
|
31786
|
+
msgDeps.renderer = renderer;
|
|
31415
31787
|
const accepted = dispatcher.submitMessage({
|
|
31416
31788
|
text: queryContent,
|
|
31417
31789
|
sessionId,
|
|
@@ -31646,8 +32018,9 @@ ${pathStr}` }];
|
|
|
31646
32018
|
sessionManager: sessions,
|
|
31647
32019
|
sessionId,
|
|
31648
32020
|
model: config.model,
|
|
31649
|
-
contextWindow:
|
|
31650
|
-
systemPrompt,
|
|
32021
|
+
contextWindow: getCompactConfig().contextWindow || 2e5,
|
|
32022
|
+
systemPrompt: getSystemPrompt(),
|
|
32023
|
+
// 0902 现取(API /context 报告跟当前 prompt 一致)
|
|
31651
32024
|
toolDefs: registry.definitions(),
|
|
31652
32025
|
workspace: config.workspace
|
|
31653
32026
|
});
|
|
@@ -31975,6 +32348,24 @@ async function doReloadConfig(config, deps, provider) {
|
|
|
31975
32348
|
visionModel: newConfig.visionModel,
|
|
31976
32349
|
visionFallbacks: newConfig.visionFallbacks
|
|
31977
32350
|
});
|
|
32351
|
+
const newRecall = createMemorySideProvider(
|
|
32352
|
+
newConfig.topics?.recall,
|
|
32353
|
+
provider,
|
|
32354
|
+
newConfig.providers || {}
|
|
32355
|
+
);
|
|
32356
|
+
const newExtract = createMemorySideProvider(
|
|
32357
|
+
newConfig.topics?.extract,
|
|
32358
|
+
provider,
|
|
32359
|
+
newConfig.providers || {}
|
|
32360
|
+
);
|
|
32361
|
+
if (newRecall) {
|
|
32362
|
+
deps.recallProvider = newRecall;
|
|
32363
|
+
changes.push(`recall \u2192 ${newConfig.topics?.recall?.provider}/${newConfig.topics?.recall?.model}`);
|
|
32364
|
+
}
|
|
32365
|
+
if (newExtract) {
|
|
32366
|
+
deps.extractProvider = newExtract;
|
|
32367
|
+
changes.push(`extract \u2192 ${newConfig.topics?.extract?.provider}/${newConfig.topics?.extract?.model}`);
|
|
32368
|
+
}
|
|
31978
32369
|
if (oldProviderKey !== newProviderKey) {
|
|
31979
32370
|
console.log("[reload] Provider structure changed, rebuilding chain...");
|
|
31980
32371
|
const rebuilt = buildProviderChain(newConfig);
|
|
@@ -31985,6 +32376,7 @@ async function doReloadConfig(config, deps, provider) {
|
|
|
31985
32376
|
}
|
|
31986
32377
|
changes.push(`provider chain rebuilt (${rebuilt.visionChainLabels.length > 0 ? "vision: " + rebuilt.visionChainLabels.join("\u2192") : "no vision"})`);
|
|
31987
32378
|
}
|
|
32379
|
+
if (deps.onProviderSwapped) deps.onProviderSwapped(rebuilt.provider);
|
|
31988
32380
|
if (typeof deps.setVisionProvider === "function") {
|
|
31989
32381
|
const vCfg = newConfig.visionModel;
|
|
31990
32382
|
const vpCfg = vCfg ? newConfig.providers?.[vCfg.providerId] : void 0;
|
|
@@ -32015,24 +32407,6 @@ async function doReloadConfig(config, deps, provider) {
|
|
|
32015
32407
|
changes.push(`deskBuddy model \u2192 ${newDbModel}`);
|
|
32016
32408
|
}
|
|
32017
32409
|
}
|
|
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
32410
|
try {
|
|
32037
32411
|
const { setAutoDreamConfig: setAutoDreamConfig2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
32038
32412
|
setAutoDreamConfig2(newConfig);
|