engine7 7.1.36 → 7.1.38
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 +16 -0
- package/dist/engine-startup.mjs +431 -144
- package/dist/main.mjs +352 -65
- package/package.json +1 -1
- package/templates/config.template.json +3 -0
package/dist/main.mjs
CHANGED
|
@@ -929,6 +929,9 @@ function toHookContext(ctx) {
|
|
|
929
929
|
};
|
|
930
930
|
}
|
|
931
931
|
async function executeOne(tc, ctx, signal) {
|
|
932
|
+
if (signal?.aborted) {
|
|
933
|
+
return { call: tc, result: { content: "[interrupted by user /stop]", isError: false }, summary: null };
|
|
934
|
+
}
|
|
932
935
|
const tool = registry.get(tc.function.name);
|
|
933
936
|
if (!tool) {
|
|
934
937
|
return { call: tc, result: { content: `Unknown tool: ${tc.function.name}`, isError: true }, summary: null };
|
|
@@ -2918,7 +2921,8 @@ var init_query = __esm({
|
|
|
2918
2921
|
init_constants();
|
|
2919
2922
|
init_deferred();
|
|
2920
2923
|
init_hooks();
|
|
2921
|
-
|
|
2924
|
+
init_live();
|
|
2925
|
+
QueryEngine = class _QueryEngine {
|
|
2922
2926
|
constructor(provider, options) {
|
|
2923
2927
|
this.provider = provider;
|
|
2924
2928
|
this.options = options;
|
|
@@ -2929,6 +2933,33 @@ var init_query = __esm({
|
|
|
2929
2933
|
}
|
|
2930
2934
|
provider;
|
|
2931
2935
|
options;
|
|
2936
|
+
// === voice-chat 占位状态(#194 v4)===
|
|
2937
|
+
static placeholderQueries = /* @__PURE__ */ new Map();
|
|
2938
|
+
// queryId → ts(3 分钟过期)
|
|
2939
|
+
static placeholderCounter = 0;
|
|
2940
|
+
/** 工具>5s 触发:同一 query 只占位一次 + 30% 概率 + 轮换文案(防口头禅) */
|
|
2941
|
+
static async tryVoicePlaceholder(context) {
|
|
2942
|
+
if (context?.channel !== "voice-chat") return;
|
|
2943
|
+
if (!(liveConfig.get("voiceChat.placeholder.enabled") ?? true)) return;
|
|
2944
|
+
const qid = context.sessionId || "default";
|
|
2945
|
+
const now = Date.now();
|
|
2946
|
+
for (const [k, v] of _QueryEngine.placeholderQueries) if (now - v > 3 * 60 * 1e3) _QueryEngine.placeholderQueries.delete(k);
|
|
2947
|
+
if (_QueryEngine.placeholderQueries.has(qid)) return;
|
|
2948
|
+
if (Math.random() > 0.3) return;
|
|
2949
|
+
_QueryEngine.placeholderQueries.set(qid, now);
|
|
2950
|
+
const lines = ["\u7B49\u6211\u67E5\u4E0B\u54C8", "\u7A0D\u7B49\uFF0C\u6211\u770B\u4E00\u773C", "\u6211\u53BB\u7FFB\u7FFB", "\u7B49\u4E0B\u54E6", "\u55EF\u2026\u6211\u53BB\u770B\u770B"];
|
|
2951
|
+
const line = lines[_QueryEngine.placeholderCounter++ % lines.length];
|
|
2952
|
+
try {
|
|
2953
|
+
await fetch("https://localhost:8116/voice-reply", {
|
|
2954
|
+
method: "POST",
|
|
2955
|
+
headers: { "Content-Type": "application/json" },
|
|
2956
|
+
body: JSON.stringify({ text: line, isPlaceholder: true })
|
|
2957
|
+
});
|
|
2958
|
+
console.log(`[vc-placeholder] sent: ${line}`);
|
|
2959
|
+
} catch (e) {
|
|
2960
|
+
console.warn(`[vc-placeholder] send failed: ${e.message}`);
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2932
2963
|
abortController = null;
|
|
2933
2964
|
/** 外部 pre-query abort controller(handle-query 入口注册,弥补 query() 前的空窗期) */
|
|
2934
2965
|
preQueryAbort = null;
|
|
@@ -3283,7 +3314,18 @@ ${perTurnSystemDynamic}` : deferredHint || perTurnSystemDynamic;
|
|
|
3283
3314
|
console.warn(`[hook] PreToolUse ${toolName} error: ${e.message}`);
|
|
3284
3315
|
}
|
|
3285
3316
|
}
|
|
3286
|
-
const
|
|
3317
|
+
const vcPlaceholderOn = context?.channel === "voice-chat" && (liveConfig.get("voiceChat.placeholder.enabled") ?? true);
|
|
3318
|
+
const vcTimer = vcPlaceholderOn ? setTimeout(() => {
|
|
3319
|
+
_QueryEngine.tryVoicePlaceholder(context).catch(() => {
|
|
3320
|
+
});
|
|
3321
|
+
}, 5e3) : null;
|
|
3322
|
+
let toolRes;
|
|
3323
|
+
try {
|
|
3324
|
+
toolRes = await executeTools(toolCalls, ac.signal, toolCtx);
|
|
3325
|
+
} finally {
|
|
3326
|
+
if (vcTimer) clearTimeout(vcTimer);
|
|
3327
|
+
}
|
|
3328
|
+
const { messages: toolResults, summaries } = toolRes;
|
|
3287
3329
|
for (const s of summaries) {
|
|
3288
3330
|
try {
|
|
3289
3331
|
const postResult = await executePostToolUseHooks(s.tool, {}, s.summary, hookCtx, ac.signal);
|
|
@@ -5165,11 +5207,11 @@ function buildPayload(messages, cfg, sessionId) {
|
|
|
5165
5207
|
const meta = m.role === "user" ? parseMeta(text) : null;
|
|
5166
5208
|
const senderName = meta?.senderName ?? (m.role === "assistant" ? cfg.agentName : void 0) ?? m.role;
|
|
5167
5209
|
const rawRole = m.role === "toolResult" ? "tool" : m.role;
|
|
5168
|
-
const
|
|
5210
|
+
const role = rawRole === "user" ? "user" : "assistant";
|
|
5169
5211
|
return {
|
|
5170
5212
|
sender_id: cfg.appId,
|
|
5171
5213
|
sender_name: senderName,
|
|
5172
|
-
role
|
|
5214
|
+
role,
|
|
5173
5215
|
timestamp: Date.now(),
|
|
5174
5216
|
content: text
|
|
5175
5217
|
};
|
|
@@ -12726,6 +12768,7 @@ var BASE_DELAY_MS = 500;
|
|
|
12726
12768
|
var MAX_DELAY_MS = 32e3;
|
|
12727
12769
|
var DEFAULT_TIMEOUT_MS = 6e5;
|
|
12728
12770
|
var READ_TIMEOUT_MS = 6e4;
|
|
12771
|
+
var FIRST_READ_TIMEOUT_MS = parseInt(process.env.API_TIMEOUT_MS || "", 10) || 6e5;
|
|
12729
12772
|
function sleep(ms, signal) {
|
|
12730
12773
|
return new Promise((resolve10, reject) => {
|
|
12731
12774
|
if (signal?.aborted) {
|
|
@@ -12989,7 +13032,15 @@ var OpenAIProvider = class {
|
|
|
12989
13032
|
try {
|
|
12990
13033
|
while (true) {
|
|
12991
13034
|
if (params.signal?.aborted) break;
|
|
12992
|
-
|
|
13035
|
+
let readRes;
|
|
13036
|
+
try {
|
|
13037
|
+
readRes = await readWithTimeout(reader, firstChunk ? FIRST_READ_TIMEOUT_MS : READ_TIMEOUT_MS, params.signal);
|
|
13038
|
+
} catch (readErr) {
|
|
13039
|
+
if (params.signal?.aborted) throw readErr;
|
|
13040
|
+
yield { type: "error", error: readErr.message };
|
|
13041
|
+
return;
|
|
13042
|
+
}
|
|
13043
|
+
const { done: done2, value } = readRes;
|
|
12993
13044
|
if (done2) break;
|
|
12994
13045
|
if (firstChunk) {
|
|
12995
13046
|
firstChunk = false;
|
|
@@ -13273,6 +13324,7 @@ var AnthropicProvider = class {
|
|
|
13273
13324
|
const reader = response.body.getReader();
|
|
13274
13325
|
const decoder = new TextDecoder();
|
|
13275
13326
|
let buffer = "";
|
|
13327
|
+
let firstChunk = true;
|
|
13276
13328
|
const toolUseBlocks = /* @__PURE__ */ new Map();
|
|
13277
13329
|
let doneYielded = false;
|
|
13278
13330
|
const handleData = function* (data) {
|
|
@@ -13362,8 +13414,20 @@ var AnthropicProvider = class {
|
|
|
13362
13414
|
try {
|
|
13363
13415
|
while (true) {
|
|
13364
13416
|
if (params.signal?.aborted) break;
|
|
13365
|
-
|
|
13417
|
+
let readRes;
|
|
13418
|
+
try {
|
|
13419
|
+
readRes = await readWithTimeout(reader, firstChunk ? FIRST_READ_TIMEOUT_MS : READ_TIMEOUT_MS, params.signal);
|
|
13420
|
+
} catch (readErr) {
|
|
13421
|
+
if (params.signal?.aborted) throw readErr;
|
|
13422
|
+
yield { type: "error", error: readErr.message };
|
|
13423
|
+
return;
|
|
13424
|
+
}
|
|
13425
|
+
const { done: done2, value } = readRes;
|
|
13366
13426
|
if (done2) break;
|
|
13427
|
+
if (firstChunk) {
|
|
13428
|
+
firstChunk = false;
|
|
13429
|
+
console.log(`[anthropic] first chunk received (${value?.length ?? 0}B)`);
|
|
13430
|
+
}
|
|
13367
13431
|
buffer += decoder.decode(value, { stream: true });
|
|
13368
13432
|
const lines = buffer.split("\n");
|
|
13369
13433
|
buffer = lines.pop();
|
|
@@ -13575,7 +13639,15 @@ var GeminiProvider = class {
|
|
|
13575
13639
|
try {
|
|
13576
13640
|
while (true) {
|
|
13577
13641
|
if (params.signal?.aborted) break;
|
|
13578
|
-
|
|
13642
|
+
let readRes;
|
|
13643
|
+
try {
|
|
13644
|
+
readRes = await readWithTimeout(reader, firstChunk ? FIRST_READ_TIMEOUT_MS : READ_TIMEOUT_MS, params.signal);
|
|
13645
|
+
} catch (readErr) {
|
|
13646
|
+
if (params.signal?.aborted) throw readErr;
|
|
13647
|
+
yield { type: "error", error: readErr.message };
|
|
13648
|
+
return;
|
|
13649
|
+
}
|
|
13650
|
+
const { done: done2, value } = readRes;
|
|
13579
13651
|
if (done2) break;
|
|
13580
13652
|
if (firstChunk) {
|
|
13581
13653
|
firstChunk = false;
|
|
@@ -14431,7 +14503,9 @@ var FeishuAdapter = class _FeishuAdapter {
|
|
|
14431
14503
|
}
|
|
14432
14504
|
const filename = attachment.filename || path46.basename(attachment.path);
|
|
14433
14505
|
const fileBuffer = fs43.readFileSync(attachment.path);
|
|
14434
|
-
const
|
|
14506
|
+
const safeTarget = target || this.config.defaultTarget || "";
|
|
14507
|
+
if (!safeTarget) throw new Error("Feishu sendFile: no target (\u4E3B\u52A8\u53D1\u9001\u9700\u914D\u7F6E defaultTarget \u6216\u4F20\u5165 target)");
|
|
14508
|
+
const receiveIdType = safeTarget.startsWith("ou_") ? "open_id" : "chat_id";
|
|
14435
14509
|
const mimeType = attachment.mimeType || "application/octet-stream";
|
|
14436
14510
|
if (mimeType.startsWith("image/")) {
|
|
14437
14511
|
const imageKey = await this._uploadImage(fileBuffer, filename);
|
|
@@ -14439,19 +14513,45 @@ var FeishuAdapter = class _FeishuAdapter {
|
|
|
14439
14513
|
const postContent = this._buildImagePost(message, imageKey);
|
|
14440
14514
|
await this.client.im.message.create({
|
|
14441
14515
|
params: { receive_id_type: receiveIdType },
|
|
14442
|
-
data: { receive_id:
|
|
14516
|
+
data: { receive_id: safeTarget, msg_type: "post", content: JSON.stringify(postContent) }
|
|
14443
14517
|
});
|
|
14444
14518
|
} else {
|
|
14445
14519
|
await this.client.im.message.create({
|
|
14446
14520
|
params: { receive_id_type: receiveIdType },
|
|
14447
|
-
data: { receive_id:
|
|
14521
|
+
data: { receive_id: safeTarget, msg_type: "image", content: JSON.stringify({ image_key: imageKey }) }
|
|
14522
|
+
});
|
|
14523
|
+
}
|
|
14524
|
+
} else if (mimeType.startsWith("audio/")) {
|
|
14525
|
+
const isOpus = filename.endsWith(".opus") || filename.endsWith(".ogg") || mimeType === "audio/opus";
|
|
14526
|
+
if (!isOpus) {
|
|
14527
|
+
console.warn(`[feishu:sendFile] audio '${filename}' \u4E0D\u662Fopus\uFF0C\u964D\u7EA7\u4E3A\u6587\u4EF6\u6D88\u606F\uFF08\u975E\u8BED\u97F3\u6761\uFF09`);
|
|
14528
|
+
const fileKey = await this._uploadFile(fileBuffer, filename);
|
|
14529
|
+
await this.client.im.message.create({
|
|
14530
|
+
params: { receive_id_type: receiveIdType },
|
|
14531
|
+
data: { receive_id: safeTarget, msg_type: "file", content: JSON.stringify({ file_key: fileKey }) }
|
|
14532
|
+
});
|
|
14533
|
+
} else {
|
|
14534
|
+
let durationMs = Math.round(fileBuffer.length / 3);
|
|
14535
|
+
try {
|
|
14536
|
+
const { execFile: execFile3 } = await import("node:child_process");
|
|
14537
|
+
const { promisify: promisify3 } = await import("node:util");
|
|
14538
|
+
const execFileAsync2 = promisify3(execFile3);
|
|
14539
|
+
const probe = await execFileAsync2("ffprobe", ["-v", "quiet", "-show_format", "-print_format", "json", attachment.path], { timeout: 1e4 });
|
|
14540
|
+
const fmt = JSON.parse(probe.stdout);
|
|
14541
|
+
if (fmt?.format?.duration) durationMs = Math.round(parseFloat(fmt.format.duration) * 1e3);
|
|
14542
|
+
} catch {
|
|
14543
|
+
}
|
|
14544
|
+
const audioKey = await this._uploadAudio(fileBuffer, filename.endsWith(".opus") ? filename : filename.replace(/\.[^.]+$/, ".opus"), durationMs);
|
|
14545
|
+
await this.client.im.message.create({
|
|
14546
|
+
params: { receive_id_type: receiveIdType },
|
|
14547
|
+
data: { receive_id: safeTarget, msg_type: "audio", content: JSON.stringify({ file_key: audioKey }) }
|
|
14448
14548
|
});
|
|
14449
14549
|
}
|
|
14450
14550
|
} else {
|
|
14451
14551
|
const fileKey = await this._uploadFile(fileBuffer, filename);
|
|
14452
14552
|
await this.client.im.message.create({
|
|
14453
14553
|
params: { receive_id_type: receiveIdType },
|
|
14454
|
-
data: { receive_id:
|
|
14554
|
+
data: { receive_id: safeTarget, msg_type: "file", content: JSON.stringify({ file_key: fileKey }) }
|
|
14455
14555
|
});
|
|
14456
14556
|
}
|
|
14457
14557
|
if (message && mimeType.startsWith("image/")) {
|
|
@@ -14460,7 +14560,7 @@ var FeishuAdapter = class _FeishuAdapter {
|
|
|
14460
14560
|
for (const chunk of chunks) {
|
|
14461
14561
|
await this.client.im.message.create({
|
|
14462
14562
|
params: { receive_id_type: receiveIdType },
|
|
14463
|
-
data: { receive_id:
|
|
14563
|
+
data: { receive_id: safeTarget, msg_type: "text", content: JSON.stringify({ text: chunk }) }
|
|
14464
14564
|
});
|
|
14465
14565
|
}
|
|
14466
14566
|
}
|
|
@@ -14504,6 +14604,28 @@ var FeishuAdapter = class _FeishuAdapter {
|
|
|
14504
14604
|
if (!fileKey) throw new Error(`Feishu file upload failed: ${JSON.stringify(data)}`);
|
|
14505
14605
|
return fileKey;
|
|
14506
14606
|
}
|
|
14607
|
+
/** #217: 上传语音(opus)——飞书语音条必须 file_type=opus + duration(毫秒) */
|
|
14608
|
+
async _uploadAudio(buffer, filename, durationMs) {
|
|
14609
|
+
const token = await this._getTenantToken();
|
|
14610
|
+
const resp = await fetch("https://open.feishu.cn/open-apis/im/v1/files", {
|
|
14611
|
+
method: "POST",
|
|
14612
|
+
headers: {
|
|
14613
|
+
"Authorization": `Bearer ${token}`
|
|
14614
|
+
},
|
|
14615
|
+
body: (() => {
|
|
14616
|
+
const formData = new FormData();
|
|
14617
|
+
formData.append("file_type", "opus");
|
|
14618
|
+
formData.append("file_name", filename);
|
|
14619
|
+
formData.append("duration", String(Math.max(1, Math.round(durationMs))));
|
|
14620
|
+
formData.append("file", new Blob([buffer], { type: "audio/opus" }), filename);
|
|
14621
|
+
return formData;
|
|
14622
|
+
})()
|
|
14623
|
+
});
|
|
14624
|
+
const data = await resp.json();
|
|
14625
|
+
const fileKey = data?.data?.file_key;
|
|
14626
|
+
if (!fileKey) throw new Error(`Feishu audio upload failed: ${JSON.stringify(data)}`);
|
|
14627
|
+
return fileKey;
|
|
14628
|
+
}
|
|
14507
14629
|
/** 通过飞书API下载消息中的图片 */
|
|
14508
14630
|
async _downloadImage(messageId, imageKey) {
|
|
14509
14631
|
const token = await this._getTenantToken();
|
|
@@ -14707,6 +14829,7 @@ var FeishuAdapter = class _FeishuAdapter {
|
|
|
14707
14829
|
const message = data?.message;
|
|
14708
14830
|
if (!sender || !message) return;
|
|
14709
14831
|
const messageId = message.message_id || "";
|
|
14832
|
+
console.log(`[feishu] recv msg_type=${message.message_type} chat=${message.chat_type} from=${sender.sender_id?.open_id?.slice(0, 10)}`);
|
|
14710
14833
|
if (this.recentMessageIds.has(messageId)) return;
|
|
14711
14834
|
this.recentMessageIds.add(messageId);
|
|
14712
14835
|
if (this.recentMessageIds.size > 500) {
|
|
@@ -14821,6 +14944,13 @@ var FeishuAdapter = class _FeishuAdapter {
|
|
|
14821
14944
|
if (msgType === "file") {
|
|
14822
14945
|
return { text: `[\u6587\u4EF6: ${parsed.file_name || "\u672A\u77E5\u6587\u4EF6"}]`, imageKeys, fileKey: parsed.file_key, fileName: parsed.file_name };
|
|
14823
14946
|
}
|
|
14947
|
+
if (msgType === "share_location" || msgType === "location") {
|
|
14948
|
+
const name = parsed.name || "";
|
|
14949
|
+
const lat = parsed.latitude || "";
|
|
14950
|
+
const lng = parsed.longitude || "";
|
|
14951
|
+
const text = `[\u4F4D\u7F6E: ${name}${lat && lng ? ` (${lat}, ${lng})` : ""}]`;
|
|
14952
|
+
return { text, imageKeys };
|
|
14953
|
+
}
|
|
14824
14954
|
if (msgType === "post") {
|
|
14825
14955
|
const lines = [];
|
|
14826
14956
|
if (Array.isArray(parsed.content)) {
|
|
@@ -17282,6 +17412,8 @@ var SessionManager = class {
|
|
|
17282
17412
|
platformMapPath;
|
|
17283
17413
|
/** 外部 hook:每次创建新 writer 时回调(如 EverOS sync 注入 onMessageWritten) */
|
|
17284
17414
|
onWriterCreated = null;
|
|
17415
|
+
/** 外部 hook:判断 session 是否有活跃 query(idle cleanup 据此跳过,防止误关 query 正持有的 writer) */
|
|
17416
|
+
onIsSessionActive = null;
|
|
17285
17417
|
histories = /* @__PURE__ */ new Map();
|
|
17286
17418
|
restoredRecallPaths = /* @__PURE__ */ new Map();
|
|
17287
17419
|
writers = /* @__PURE__ */ new Map();
|
|
@@ -17728,6 +17860,7 @@ var SessionManager = class {
|
|
|
17728
17860
|
const now = Date.now();
|
|
17729
17861
|
for (const [id, lastUsed] of this.lastUsed) {
|
|
17730
17862
|
if (now - lastUsed > idleMs) {
|
|
17863
|
+
if (this.onIsSessionActive?.(id)) continue;
|
|
17731
17864
|
const writer = this.writers.get(id);
|
|
17732
17865
|
if (writer) {
|
|
17733
17866
|
writer.close();
|
|
@@ -19078,8 +19211,11 @@ async function deepinfraRerank(query, episodes, rerankUrl, rerankApiKey, rerankM
|
|
|
19078
19211
|
}
|
|
19079
19212
|
var DEFAULT_MIN_SCORE2 = 0.5;
|
|
19080
19213
|
async function findRelevantMemoriesEveros(query, _memoryDir, alreadySurfaced = /* @__PURE__ */ new Set(), options) {
|
|
19081
|
-
const everosUrl = options?.everosUrl
|
|
19082
|
-
const userId = options?.userId
|
|
19214
|
+
const everosUrl = options?.everosUrl;
|
|
19215
|
+
const userId = options?.userId;
|
|
19216
|
+
if (!everosUrl || !userId) {
|
|
19217
|
+
throw new Error(`[memdir] everos recall: everosUrl/userId \u672A\u914D\u7F6E (everosUrl=${everosUrl}, userId=${userId})\uFF0C\u4E0D\u6267\u884C everos recall\uFF0C\u907F\u514D\u4E32\u5230\u522B\u4EBA\u7684\u5E93`);
|
|
19218
|
+
}
|
|
19083
19219
|
const topK = options?.topK ?? 3;
|
|
19084
19220
|
const minScore = options?.minScore ?? DEFAULT_MIN_SCORE2;
|
|
19085
19221
|
console.log(`[memdir] everos recall: query="${query.slice(0, 50)}..." url=${everosUrl} userId=${userId} topK=${topK}`);
|
|
@@ -19401,7 +19537,9 @@ ${text}` : text });
|
|
|
19401
19537
|
} else {
|
|
19402
19538
|
console.log(`[pre-llm-debug] userMsgContent is string, len=${String(userMsgContent).length}`);
|
|
19403
19539
|
}
|
|
19404
|
-
const
|
|
19540
|
+
const vcN = liveConfig.get("voiceChat.lightContextN") ?? 20;
|
|
19541
|
+
const lightHistory = channelName === "voice-chat" && history.length > vcN ? history.slice(-vcN) : history;
|
|
19542
|
+
const messages = [...lightHistory, msg.user(userMsgContent)];
|
|
19405
19543
|
if (deps.mcpManager && !deps.mcpManager.isMcpDeltaSent(sessionId)) {
|
|
19406
19544
|
const delta = deps.mcpManager.getMcpDelta();
|
|
19407
19545
|
if (delta && delta.addedBlocks.length > 0) {
|
|
@@ -19570,7 +19708,7 @@ ${text}` : text });
|
|
|
19570
19708
|
const queryAbortController = new AbortController();
|
|
19571
19709
|
engine.setExternalAbort(queryAbortController);
|
|
19572
19710
|
setActiveQueryEngine(sessionId, engine);
|
|
19573
|
-
const shouldSkipRecall = skipRecall ?? channelName === "cron";
|
|
19711
|
+
const shouldSkipRecall = skipRecall ?? (channelName === "cron" || channelName === "voice-chat");
|
|
19574
19712
|
if (getFeature("topic-recall") !== false && !shouldSkipRecall) {
|
|
19575
19713
|
try {
|
|
19576
19714
|
const memoryDir = getAutoMemPath(workspace);
|
|
@@ -19618,31 +19756,49 @@ ${text}` : text });
|
|
|
19618
19756
|
const recallP = deps.recallProvider;
|
|
19619
19757
|
const recallMode = topics?.recall?.mode || "llm";
|
|
19620
19758
|
let relevantMemories;
|
|
19621
|
-
|
|
19759
|
+
const effectiveText = textForMemory.replace(/\[图片来自[^\]]*\]|路径:\S+/g, "").trim();
|
|
19760
|
+
if (!effectiveText) {
|
|
19761
|
+
console.log("[handle-query] Memory recall skipped: image-only message (no effective text)");
|
|
19762
|
+
relevantMemories = [];
|
|
19763
|
+
} else if (recallMode === "everos") {
|
|
19622
19764
|
const everosCfg = deps?.everosCfg;
|
|
19623
|
-
|
|
19624
|
-
|
|
19625
|
-
|
|
19626
|
-
|
|
19627
|
-
|
|
19628
|
-
|
|
19629
|
-
|
|
19630
|
-
|
|
19631
|
-
|
|
19632
|
-
|
|
19633
|
-
|
|
19634
|
-
|
|
19635
|
-
|
|
19636
|
-
|
|
19765
|
+
if (everosCfg && everosCfg.everosUrl && everosCfg.userId) {
|
|
19766
|
+
relevantMemories = await findRelevantMemoriesEveros(
|
|
19767
|
+
effectiveText,
|
|
19768
|
+
memoryDir,
|
|
19769
|
+
surfaced.paths,
|
|
19770
|
+
{
|
|
19771
|
+
everosUrl: everosCfg.everosUrl,
|
|
19772
|
+
userId: everosCfg.userId,
|
|
19773
|
+
rerankUrl: everosCfg.rerank?.baseUrl,
|
|
19774
|
+
rerankApiKey: everosCfg.rerank?.apiKey,
|
|
19775
|
+
rerankModel: everosCfg.rerank?.model,
|
|
19776
|
+
rerankProvider: everosCfg.rerank?.provider,
|
|
19777
|
+
minScore: topics?.recall?.minScore
|
|
19778
|
+
}
|
|
19779
|
+
);
|
|
19780
|
+
} else {
|
|
19781
|
+
console.log("[handle-query] everos recall skipped: everosCfg not configured, falling back to LLM recall");
|
|
19782
|
+
relevantMemories = await findRelevantMemories(
|
|
19783
|
+
effectiveText,
|
|
19784
|
+
memoryDir,
|
|
19785
|
+
recallP?.provider || provider,
|
|
19786
|
+
recallP?.model || model,
|
|
19787
|
+
queryAbortController.signal,
|
|
19788
|
+
surfaced.paths,
|
|
19789
|
+
recallP?.disableThinking,
|
|
19790
|
+
topics?.maxScanFiles
|
|
19791
|
+
);
|
|
19792
|
+
}
|
|
19637
19793
|
} else if (recallMode === "vector") {
|
|
19638
19794
|
relevantMemories = await findRelevantMemoriesVector(
|
|
19639
|
-
|
|
19795
|
+
effectiveText,
|
|
19640
19796
|
memoryDir,
|
|
19641
19797
|
surfaced.paths
|
|
19642
19798
|
);
|
|
19643
19799
|
} else {
|
|
19644
19800
|
relevantMemories = await findRelevantMemories(
|
|
19645
|
-
|
|
19801
|
+
effectiveText,
|
|
19646
19802
|
memoryDir,
|
|
19647
19803
|
recallP?.provider || provider,
|
|
19648
19804
|
recallP?.model || model,
|
|
@@ -20874,6 +21030,10 @@ var NudgePlugin = class {
|
|
|
20874
21030
|
registerCallbackHook("Stop", {
|
|
20875
21031
|
type: "callback",
|
|
20876
21032
|
callback: async (input, _toolUseID, _signal) => {
|
|
21033
|
+
if (this.cfg.stopHookEnabled === false) {
|
|
21034
|
+
console.log("[stop-hook] disabled via /stophook off \u2014 skipping");
|
|
21035
|
+
return { outcome: { outcome: "success" } };
|
|
21036
|
+
}
|
|
20877
21037
|
const mode = this.cfg.stopHookMode || "sync";
|
|
20878
21038
|
if (mode === "async") {
|
|
20879
21039
|
console.log("[stop-hook] async mode \u2014 firing judge in background, not blocking");
|
|
@@ -21148,26 +21308,28 @@ var NudgePlugin = class {
|
|
|
21148
21308
|
console.log(`[nudge] ${due.length} stop-hook notification(s) due: ${due.map((n) => n.id).join(", ")}`);
|
|
21149
21309
|
const items = due.map((n) => `[\u901A\u77E5ID: ${n.id}]
|
|
21150
21310
|
\u4E0A\u6B21\u8BF4\uFF1A${n.description}`).join("\n\n");
|
|
21311
|
+
const hint = `\uFF08\u6E05\u7406\u5173\u952E\u5B57\u53EF\u7528\uFF1A\u8FC7\u671F\u4E86 / \u5DF2\u843D\u76D8 / resolved / \u5DF2\u5904\u7406 / \u5DF2\u95ED\u73AF\uFF0C\u5982 "${due[0].id} \u5DF2\u5904\u7406"\uFF09`;
|
|
21151
21312
|
const desc = due.length === 1 ? `\u4F60\u4E4B\u524D\u5728\u7B49\u5F85\u67D0\u4E2A\u5916\u90E8\u6761\u4EF6\uFF0C\u65F6\u95F4\u5230\u4E86\uFF0C\u56DE\u53BB\u68C0\u67E5\uFF01
|
|
21152
21313
|
|
|
21153
21314
|
${items}
|
|
21154
21315
|
|
|
21155
|
-
\u68C0\u67E5\u6761\u4EF6\u662F\u5426\u6EE1\u8DB3\uFF0C\u6EE1\u8DB3\u5C31\u7EE7\u7EED\u5E72\u6D3B\uFF0C\u4E0D\u6EE1\u8DB3\u5C31\u56DE\u590D"${due[0].id} \u8FC7\u671F\u4E86"\u544A\u8BC9 nudge \u7CBE\u786E\u6E05\u7406\u8FD9\u6761\u3002` : `\u4F60\u4E4B\u524D\u6709 ${due.length} \u4E2A\u7B49\u5F85\u4E2D\u7684\u5916\u90E8\u6761\u4EF6\u90FD\u5230\u671F\u4E86\uFF0C\u56DE\u53BB\u9010\u4E2A\u68C0\u67E5\uFF01
|
|
21316
|
+
\u68C0\u67E5\u6761\u4EF6\u662F\u5426\u6EE1\u8DB3\uFF0C\u6EE1\u8DB3\u5C31\u7EE7\u7EED\u5E72\u6D3B\uFF0C\u4E0D\u6EE1\u8DB3\u5C31\u56DE\u590D"${due[0].id} \u8FC7\u671F\u4E86"\u6216"${due[0].id} \u5DF2\u5904\u7406"\u544A\u8BC9 nudge \u7CBE\u786E\u6E05\u7406\u8FD9\u6761\u3002${hint}` : `\u4F60\u4E4B\u524D\u6709 ${due.length} \u4E2A\u7B49\u5F85\u4E2D\u7684\u5916\u90E8\u6761\u4EF6\u90FD\u5230\u671F\u4E86\uFF0C\u56DE\u53BB\u9010\u4E2A\u68C0\u67E5\uFF01
|
|
21156
21317
|
|
|
21157
21318
|
${items}
|
|
21158
21319
|
|
|
21159
|
-
\u5BF9\u6BCF\u4E00\u6761\uFF1A\u6761\u4EF6\u6EE1\u8DB3\u5C31\u7EE7\u7EED\u5E72\u6D3B\uFF0C\u4E0D\u6EE1\u8DB3\u5C31\u56DE\u590D\u5BF9\u5E94\u7684"<\u901A\u77E5ID> \u8FC7\u671F\u4E86"\u544A\u8BC9 nudge \u7CBE\u786E\u6E05\u7406\u3002`;
|
|
21320
|
+
\u5BF9\u6BCF\u4E00\u6761\uFF1A\u6761\u4EF6\u6EE1\u8DB3\u5C31\u7EE7\u7EED\u5E72\u6D3B\uFF0C\u4E0D\u6EE1\u8DB3\u5C31\u56DE\u590D\u5BF9\u5E94\u7684"<\u901A\u77E5ID> \u8FC7\u671F\u4E86"\uFF08\u6216 \u5DF2\u5904\u7406/\u5DF2\u843D\u76D8/resolved/\u5DF2\u95ED\u73AF\uFF09\u544A\u8BC9 nudge \u7CBE\u786E\u6E05\u7406\u3002`;
|
|
21160
21321
|
return { message: buildNudgeNotification("wake", desc), ids: due.map((n) => n.id) };
|
|
21161
21322
|
} catch (e) {
|
|
21162
21323
|
console.warn(`[nudge] collectDueStopHookNotifications error: ${e.message}`);
|
|
21163
21324
|
return null;
|
|
21164
21325
|
}
|
|
21165
21326
|
}
|
|
21166
|
-
/** 从回复文本里提取 "<id> 过期了" 的 wake id(一条回复可能处置多个)
|
|
21327
|
+
/** 从回复文本里提取 "<id> 过期了" 的 wake id(一条回复可能处置多个)
|
|
21328
|
+
* 0816 翀哥:关键字多样化——过期了/已落盘/resolved/已处理/已闭环/已归档 都认 */
|
|
21167
21329
|
extractWakeReplyIds(text) {
|
|
21168
21330
|
if (!text) return [];
|
|
21169
21331
|
const ids = [];
|
|
21170
|
-
const re = /(wake-\d+-[a-z0-9]+)\s
|
|
21332
|
+
const re = /(wake-\d+-[a-z0-9]+)\s*(?:过期了|已落盘|resolved|已处理|已闭环|已归档|已完成|done)/g;
|
|
21171
21333
|
let m;
|
|
21172
21334
|
while ((m = re.exec(text)) !== null) {
|
|
21173
21335
|
if (!ids.includes(m[1])) ids.push(m[1]);
|
|
@@ -23610,8 +23772,9 @@ var VoiceChatPlugin = class _VoiceChatPlugin {
|
|
|
23610
23772
|
- \u4FDD\u6301\u7B80\u77ED\u53E3\u8BED\u5316\uFF08\u901A\u5E38 1-3 \u53E5\uFF09\uFF0C\u4E0D\u8981\u957F\u7BC7\u5927\u8BBA
|
|
23611
23773
|
- \u7B2C\u4E00\u53E5\u63A7\u5236\u5728 4-6 \u4E2A\u5B57\uFF0C\u5C3D\u5FEB\u89E6\u53D1 TTS \u5408\u6210
|
|
23612
23774
|
- \u8BF4\u8BDD\u50CF\u4EBA\u2014\u2014\u77ED\u53E5\uFF0C\u53BB\u670D\u52A1\u611F
|
|
23613
|
-
- \
|
|
23614
|
-
- \
|
|
23775
|
+
- \u3010\u786C\u89C4\u5219\u3011\u8BED\u97F3\u5BF9\u8BDD\u4E2D\u7981\u6B62\u8C03\u7528 exec / SSH / \u8BFB\u5927\u6587\u4EF6\u7B49\u6162\u5DE5\u5177\u2014\u2014\u67E5\u6570\u636E\u7C7B\u95EE\u9898\u76F4\u63A5\u7528\u5634\u7B54"\u8FD9\u4E2A\u6211\u98DE\u4E66\u91CC\u67E5\u4E86\u544A\u8BC9\u4F60"
|
|
23776
|
+
- \u8BED\u97F3\u91CC\u5141\u8BB8\u7684\u5DE5\u5177\uFF1Acalendar\u3001msg_send\u3001read\uFF08\u8BFB\u76F4\u64AD\u8349\u7A3F/\u811A\u672C\u7528\uFF0C\u672C\u5730\u6587\u4EF6\u5FEB\uFF09\u2014\u2014\u4E0D\u8981\u7528 my_voice\uFF08\u8BED\u97F3\u5BF9\u8BDD\u672C\u8EAB\u5C31\u5728\u51FA\u58F0\uFF0C\u518D\u751F\u6210\u662F\u5957\u5A03\uFF09
|
|
23777
|
+
- \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`;
|
|
23615
23778
|
const engine = new QueryEngine(llmProvider, {
|
|
23616
23779
|
model: modelId,
|
|
23617
23780
|
systemPrompt: (ctx.deps.systemPrompt || "") + voiceChatRules,
|
|
@@ -24325,7 +24488,22 @@ var DEFAULTS4 = {
|
|
|
24325
24488
|
autoStart: true,
|
|
24326
24489
|
defaultMode: "hybrid_agentic"
|
|
24327
24490
|
};
|
|
24328
|
-
function
|
|
24491
|
+
function resolveProviderConfig(rawSection, providers, defaultModel, defaultApiKey, defaultBaseUrl) {
|
|
24492
|
+
if (!rawSection) {
|
|
24493
|
+
return { model: defaultModel, apiKey: defaultApiKey, baseUrl: defaultBaseUrl };
|
|
24494
|
+
}
|
|
24495
|
+
const providerName = rawSection.provider;
|
|
24496
|
+
const providerCfg = providerName ? providers?.[providerName] : void 0;
|
|
24497
|
+
return {
|
|
24498
|
+
provider: providerName,
|
|
24499
|
+
model: rawSection.model ?? defaultModel,
|
|
24500
|
+
// apiKey 从 provider 取(最易不一致,统一来源)
|
|
24501
|
+
// baseUrl/model 优先用 everos 节点自己的值(同一 provider 可能有不同 endpoint)
|
|
24502
|
+
apiKey: providerCfg?.apiKey ?? rawSection.apiKey ?? defaultApiKey,
|
|
24503
|
+
baseUrl: rawSection.baseUrl ?? providerCfg?.baseUrl ?? defaultBaseUrl
|
|
24504
|
+
};
|
|
24505
|
+
}
|
|
24506
|
+
function parseEverosConfig(raw, providers) {
|
|
24329
24507
|
if (!raw) {
|
|
24330
24508
|
return {
|
|
24331
24509
|
enabled: false,
|
|
@@ -24345,8 +24523,20 @@ function parseEverosConfig(raw) {
|
|
|
24345
24523
|
userId: raw.userId ?? "xiaomei",
|
|
24346
24524
|
autoStart: raw.autoStart !== false,
|
|
24347
24525
|
defaultMode: raw.defaultMode ?? DEFAULTS4.defaultMode,
|
|
24348
|
-
llm:
|
|
24349
|
-
|
|
24526
|
+
llm: resolveProviderConfig(
|
|
24527
|
+
raw.llm,
|
|
24528
|
+
providers,
|
|
24529
|
+
"glm-5.2",
|
|
24530
|
+
"",
|
|
24531
|
+
"https://open.bigmodel.cn/api/coding/paas/v4"
|
|
24532
|
+
),
|
|
24533
|
+
rerank: resolveProviderConfig(
|
|
24534
|
+
raw.rerank,
|
|
24535
|
+
providers,
|
|
24536
|
+
"Qwen/Qwen3-Reranker-4B",
|
|
24537
|
+
"",
|
|
24538
|
+
"https://api.deepinfra.com/v1/inference"
|
|
24539
|
+
),
|
|
24350
24540
|
lancedbPath: raw.lancedbPath ?? "",
|
|
24351
24541
|
sqlitePath: raw.sqlitePath ?? "",
|
|
24352
24542
|
minScore: raw.minScore
|
|
@@ -24397,14 +24587,14 @@ var EverosSearchClient = class {
|
|
|
24397
24587
|
const url = useAgentic ? `${this.agenticUrl}/api/v1/search` : `${this.everosUrl}/api/v1/memory/search`;
|
|
24398
24588
|
const body = useAgentic ? JSON.stringify({
|
|
24399
24589
|
query: params.query,
|
|
24400
|
-
user_id: params.userId
|
|
24590
|
+
user_id: params.userId,
|
|
24401
24591
|
mode,
|
|
24402
24592
|
top_k: params.topK ?? 5,
|
|
24403
24593
|
strategy: params.strategy || "multi_query"
|
|
24404
24594
|
}) : JSON.stringify({
|
|
24405
24595
|
query: params.query,
|
|
24406
|
-
user_id: params.userId
|
|
24407
|
-
app_id:
|
|
24596
|
+
user_id: params.userId,
|
|
24597
|
+
app_id: params.appId,
|
|
24408
24598
|
project_id: "default",
|
|
24409
24599
|
top_k: params.topK ?? 5
|
|
24410
24600
|
});
|
|
@@ -24442,8 +24632,8 @@ var EverosPlugin = class {
|
|
|
24442
24632
|
healthTimer = null;
|
|
24443
24633
|
weStartedAgentic = false;
|
|
24444
24634
|
// 我们拉起的才管
|
|
24445
|
-
constructor(rawConfig) {
|
|
24446
|
-
this.config = parseEverosConfig(rawConfig);
|
|
24635
|
+
constructor(rawConfig, providers) {
|
|
24636
|
+
this.config = parseEverosConfig(rawConfig, providers);
|
|
24447
24637
|
this.client = new EverosSearchClient(this.config.agenticUrl, this.config.everosUrl);
|
|
24448
24638
|
}
|
|
24449
24639
|
static shouldEnable(config2) {
|
|
@@ -25580,6 +25770,47 @@ async function compressWav(wavPath) {
|
|
|
25580
25770
|
return wavPath;
|
|
25581
25771
|
}
|
|
25582
25772
|
}
|
|
25773
|
+
async function wavToOggOpus(wavPath) {
|
|
25774
|
+
const oggPath = wavPath.replace(/\.wav$/, ".ogg");
|
|
25775
|
+
await execFileAsync("ffmpeg", [
|
|
25776
|
+
"-y",
|
|
25777
|
+
"-i",
|
|
25778
|
+
wavPath,
|
|
25779
|
+
"-c:a",
|
|
25780
|
+
"libopus",
|
|
25781
|
+
"-b:a",
|
|
25782
|
+
"32k",
|
|
25783
|
+
"-ar",
|
|
25784
|
+
"24000",
|
|
25785
|
+
oggPath
|
|
25786
|
+
], { timeout: 3e4 });
|
|
25787
|
+
if (!fs30.existsSync(oggPath) || fs30.statSync(oggPath).size < 20) {
|
|
25788
|
+
throw new Error("ogg/opus encode produced empty output");
|
|
25789
|
+
}
|
|
25790
|
+
fs30.unlinkSync(wavPath);
|
|
25791
|
+
return oggPath;
|
|
25792
|
+
}
|
|
25793
|
+
async function wavToSilk(wavPath) {
|
|
25794
|
+
fs30.mkdirSync(VOICE_DIR, { recursive: true });
|
|
25795
|
+
const silkPath = wavPath.replace(/\.wav$/, ".silk");
|
|
25796
|
+
const script = `
|
|
25797
|
+
import sys, io, wave, pysilk
|
|
25798
|
+
src, dst = sys.argv[1], sys.argv[2]
|
|
25799
|
+
with wave.open(src, 'rb') as wf:
|
|
25800
|
+
sr = wf.getframerate()
|
|
25801
|
+
pcm = wf.readframes(wf.getnframes())
|
|
25802
|
+
out = io.BytesIO()
|
|
25803
|
+
pysilk.encode(io.BytesIO(pcm), out, sample_rate=sr, bit_rate=24000, tencent=True)
|
|
25804
|
+
with open(dst, 'wb') as f:
|
|
25805
|
+
f.write(out.getvalue())
|
|
25806
|
+
print('OK')
|
|
25807
|
+
`;
|
|
25808
|
+
await execFileAsync("python3", ["-c", script, wavPath, silkPath], { timeout: 3e4 });
|
|
25809
|
+
if (!fs30.existsSync(silkPath) || fs30.statSync(silkPath).size < 20) {
|
|
25810
|
+
throw new Error("silk encode produced empty output");
|
|
25811
|
+
}
|
|
25812
|
+
return silkPath;
|
|
25813
|
+
}
|
|
25583
25814
|
registry.register({
|
|
25584
25815
|
name: "my_voice",
|
|
25585
25816
|
description: "Generate and send a voice message. Use for: \u53D1\u8BED\u97F3/\u8BF4\u53E5\u8BDD/\u5F55\u4E00\u6BB5/\u53D1\u58F0\u97F3/\u60F3\u542C\u4F60\u7684\u58F0\u97F3/\u8BED\u97F3\u6D88\u606F/\u8BF4\u7ED9\u6211\u542C. Uses CosyVoice (\u914D\u7F6E voiceChat.local.tts) with edge-tts fallback. Sends to weixin or feishu.",
|
|
@@ -25617,9 +25848,6 @@ registry.register({
|
|
|
25617
25848
|
vc.workspaceId,
|
|
25618
25849
|
vc.instruction
|
|
25619
25850
|
);
|
|
25620
|
-
if (audioPath.endsWith(".wav")) {
|
|
25621
|
-
audioPath = await compressWav(audioPath);
|
|
25622
|
-
}
|
|
25623
25851
|
} catch (e) {
|
|
25624
25852
|
console.warn(`[my-voice] cosyvoice failed (${e.message}), falling back to edge-tts`);
|
|
25625
25853
|
audioPath = await ttsEdge(text);
|
|
@@ -25628,9 +25856,6 @@ registry.register({
|
|
|
25628
25856
|
} else if (useEngine === "gptsovits") {
|
|
25629
25857
|
try {
|
|
25630
25858
|
audioPath = await ttsGptsovits(text);
|
|
25631
|
-
if (audioPath.endsWith(".wav")) {
|
|
25632
|
-
audioPath = await compressWav(audioPath);
|
|
25633
|
-
}
|
|
25634
25859
|
} catch (e) {
|
|
25635
25860
|
console.warn(`[my-voice] gptsovits failed (${e.message}), falling back to edge-tts`);
|
|
25636
25861
|
audioPath = await ttsEdge(text);
|
|
@@ -25642,12 +25867,30 @@ registry.register({
|
|
|
25642
25867
|
} catch (e) {
|
|
25643
25868
|
return { content: `TTS failed: ${e.message}`, isError: true };
|
|
25644
25869
|
}
|
|
25870
|
+
const resolvedChannelRaw = args2.channel || ctx.channel || "feishu";
|
|
25871
|
+
const resolvedChannel = resolvedChannelRaw === "weixin" ? "wechat" : resolvedChannelRaw;
|
|
25872
|
+
const target = ctx.channelTarget || ctx.from;
|
|
25873
|
+
if (audioPath.endsWith(".wav")) {
|
|
25874
|
+
if (resolvedChannel === "wechat") {
|
|
25875
|
+
try {
|
|
25876
|
+
audioPath = await wavToSilk(audioPath);
|
|
25877
|
+
} catch (e) {
|
|
25878
|
+
console.warn(`[my-voice] silk encode failed (${e.message}), falling back to m4a`);
|
|
25879
|
+
audioPath = await compressWav(audioPath);
|
|
25880
|
+
}
|
|
25881
|
+
} else if (resolvedChannel === "feishu") {
|
|
25882
|
+
try {
|
|
25883
|
+
audioPath = await wavToOggOpus(audioPath);
|
|
25884
|
+
} catch (e) {
|
|
25885
|
+
console.warn(`[my-voice] ogg/opus encode failed (${e.message}), falling back to m4a`);
|
|
25886
|
+
audioPath = await compressWav(audioPath);
|
|
25887
|
+
}
|
|
25888
|
+
}
|
|
25889
|
+
}
|
|
25645
25890
|
const ext = path31.extname(audioPath).toLowerCase();
|
|
25646
|
-
const mimeMap = { ".mp3": "audio/mpeg", ".wav": "audio/wav", ".m4a": "audio/mp4", ".ogg": "audio/ogg" };
|
|
25891
|
+
const mimeMap = { ".mp3": "audio/mpeg", ".wav": "audio/wav", ".m4a": "audio/mp4", ".ogg": "audio/ogg", ".silk": "audio/silk" };
|
|
25647
25892
|
const mimeType = mimeMap[ext] || "audio/mpeg";
|
|
25648
25893
|
const sizeKB = fs30.statSync(audioPath).size / 1024;
|
|
25649
|
-
const resolvedChannel = args2.channel || ctx.channel || "feishu";
|
|
25650
|
-
const target = ctx.channelTarget || ctx.from;
|
|
25651
25894
|
try {
|
|
25652
25895
|
await mgr.sendFile(resolvedChannel, target, caption, {
|
|
25653
25896
|
path: audioPath,
|
|
@@ -25685,9 +25928,12 @@ function getProxyDispatcher2() {
|
|
|
25685
25928
|
return void 0;
|
|
25686
25929
|
}
|
|
25687
25930
|
}
|
|
25688
|
-
var FAL_KEY = "3b848fc6-bee5-46e5-8db7-ae81ac16dc28:2f1f15cf394db2d32c9bb9a4f23f3bee";
|
|
25689
25931
|
var FAL_ENDPOINT = "https://fal.run/xai/grok-imagine-image/edit";
|
|
25690
25932
|
var DEFAULT_RESOLUTION = "1k";
|
|
25933
|
+
var FAL_KEY_FALLBACK = "3b848fc6-bee5-46e5-8db7-ae81ac16dc28:2f1f15cf394db2d32c9bb9a4f23f3bee";
|
|
25934
|
+
function getFalKey(cfg) {
|
|
25935
|
+
return cfg?.providers?.fal?.apiKey || cfg?.my_selfie?.falKey || FAL_KEY_FALLBACK;
|
|
25936
|
+
}
|
|
25691
25937
|
var DEFAULT_REFERENCES = [
|
|
25692
25938
|
{ name: "default", p: "images/xiaomei_clean_v2.png" },
|
|
25693
25939
|
{ name: "v3", p: "images/xiaomei_clean_v3.png" },
|
|
@@ -25730,6 +25976,7 @@ function detectMode(input) {
|
|
|
25730
25976
|
return "direct";
|
|
25731
25977
|
}
|
|
25732
25978
|
async function generateWithFal(imageB64, prompt, resolution) {
|
|
25979
|
+
const cfg = liveConfig.all();
|
|
25733
25980
|
let aspectRatio;
|
|
25734
25981
|
try {
|
|
25735
25982
|
const refBuf = Buffer.from(imageB64, "base64");
|
|
@@ -25754,8 +26001,7 @@ async function generateWithFal(imageB64, prompt, resolution) {
|
|
|
25754
26001
|
}
|
|
25755
26002
|
}
|
|
25756
26003
|
if (w > 0 && h > 0) {
|
|
25757
|
-
|
|
25758
|
-
console.log(`[my-selfie] ref image ${w}x${h}, aspect_ratio=${aspectRatio}`);
|
|
26004
|
+
console.log(`[my-selfie] ref image ${w}x${h}, aspect_ratio skipped`);
|
|
25759
26005
|
}
|
|
25760
26006
|
} catch (e) {
|
|
25761
26007
|
console.warn(`[my-selfie] Failed to read ref dimensions: ${e.message}`);
|
|
@@ -25770,7 +26016,7 @@ async function generateWithFal(imageB64, prompt, resolution) {
|
|
|
25770
26016
|
if (aspectRatio) body.aspect_ratio = aspectRatio;
|
|
25771
26017
|
const res = await fetch(FAL_ENDPOINT, {
|
|
25772
26018
|
method: "POST",
|
|
25773
|
-
headers: { "Authorization": `Key ${
|
|
26019
|
+
headers: { "Authorization": `Key ${getFalKey(cfg)}`, "Content-Type": "application/json" },
|
|
25774
26020
|
body: JSON.stringify(body)
|
|
25775
26021
|
});
|
|
25776
26022
|
if (!res.ok) {
|
|
@@ -28182,7 +28428,12 @@ ${content}`
|
|
|
28182
28428
|
// tool 读自己配置用
|
|
28183
28429
|
recallProvider: memoryRecallProvider || void 0,
|
|
28184
28430
|
extractProvider: memoryExtractProvider || void 0,
|
|
28185
|
-
everosCfg: config2.everos
|
|
28431
|
+
everosCfg: config2.everos ? {
|
|
28432
|
+
...config2.everos,
|
|
28433
|
+
// resolve provider 引用:从 providers 取 apiKey(rerank 在 handle-query.ts 里直接读 raw config)
|
|
28434
|
+
llm: config2.everos.llm?.provider && config2.providers?.[config2.everos.llm.provider] ? { ...config2.everos.llm, apiKey: config2.providers[config2.everos.llm.provider].apiKey } : config2.everos.llm,
|
|
28435
|
+
rerank: config2.everos.rerank?.provider && config2.providers?.[config2.everos.rerank.provider] ? { ...config2.everos.rerank, apiKey: config2.providers[config2.everos.rerank.provider].apiKey } : config2.everos.rerank
|
|
28436
|
+
} : void 0,
|
|
28186
28437
|
mcpManager
|
|
28187
28438
|
};
|
|
28188
28439
|
if (visionEngine && visionConfig) {
|
|
@@ -28202,7 +28453,13 @@ ${content}`
|
|
|
28202
28453
|
config: config2,
|
|
28203
28454
|
// tool 读自己配置用
|
|
28204
28455
|
recallProvider: memoryRecallProvider || void 0,
|
|
28205
|
-
extractProvider: memoryExtractProvider || void 0
|
|
28456
|
+
extractProvider: memoryExtractProvider || void 0,
|
|
28457
|
+
everosCfg: config2.everos ? {
|
|
28458
|
+
...config2.everos,
|
|
28459
|
+
// resolve provider 引用:从 providers 取 apiKey(跟主 deps 同逻辑)
|
|
28460
|
+
llm: config2.everos.llm?.provider && config2.providers?.[config2.everos.llm.provider] ? { ...config2.everos.llm, apiKey: config2.providers[config2.everos.llm.provider].apiKey } : config2.everos.llm,
|
|
28461
|
+
rerank: config2.everos.rerank?.provider && config2.providers?.[config2.everos.rerank.provider] ? { ...config2.everos.rerank, apiKey: config2.providers[config2.everos.rerank.provider].apiKey } : config2.everos.rerank
|
|
28462
|
+
} : void 0
|
|
28206
28463
|
};
|
|
28207
28464
|
}
|
|
28208
28465
|
let modelOverride = null;
|
|
@@ -28251,7 +28508,8 @@ ${content}`
|
|
|
28251
28508
|
channels: config2.channels,
|
|
28252
28509
|
config: config2,
|
|
28253
28510
|
recallProvider: memoryRecallProvider || void 0,
|
|
28254
|
-
extractProvider: memoryExtractProvider || void 0
|
|
28511
|
+
extractProvider: memoryExtractProvider || void 0,
|
|
28512
|
+
everosCfg: deps?.everosCfg
|
|
28255
28513
|
};
|
|
28256
28514
|
}
|
|
28257
28515
|
};
|
|
@@ -28293,11 +28551,13 @@ ${content}`
|
|
|
28293
28551
|
systemPrompt,
|
|
28294
28552
|
channels: config2.channels,
|
|
28295
28553
|
recallProvider: memoryRecallProvider || void 0,
|
|
28296
|
-
extractProvider: memoryExtractProvider || void 0
|
|
28554
|
+
extractProvider: memoryExtractProvider || void 0,
|
|
28555
|
+
everosCfg: deps?.everosCfg
|
|
28297
28556
|
};
|
|
28298
28557
|
}
|
|
28299
28558
|
const dispatcher = new MessageDispatcher();
|
|
28300
28559
|
deps.dispatcher = dispatcher;
|
|
28560
|
+
sessions.onIsSessionActive = (sid) => dispatcher.isActive(sid);
|
|
28301
28561
|
setNotificationCallback((notif, route) => {
|
|
28302
28562
|
const sid = route?.sessionId || sessions.getSessionId("scope:main") || "main";
|
|
28303
28563
|
const chan = route?.channel;
|
|
@@ -28668,6 +28928,10 @@ ${notifications}
|
|
|
28668
28928
|
{ name: "session-memory", description: "\u67E5/\u5207 session-memory\uFF08on/off/\u7559\u7A7A=\u67E5\uFF09", options: [
|
|
28669
28929
|
{ name: "state", description: "on \u6216 off\uFF0C\u7559\u7A7A=\u53EA\u67E5\u72B6\u6001", type: "string", required: false }
|
|
28670
28930
|
] },
|
|
28931
|
+
// 8/17 stophook 总开关(翀哥要求)
|
|
28932
|
+
{ name: "stophook", description: "\u67E5/\u5207 stop-hook\uFF08on/off/\u7559\u7A7A=\u67E5\uFF09", options: [
|
|
28933
|
+
{ name: "state", description: "on \u6216 off\uFF0C\u7559\u7A7A=\u53EA\u67E5\u72B6\u6001", type: "string", required: false }
|
|
28934
|
+
] },
|
|
28671
28935
|
(() => {
|
|
28672
28936
|
const aliases = config2.modelAliases || {};
|
|
28673
28937
|
const aliasByRef = /* @__PURE__ */ new Map();
|
|
@@ -29096,6 +29360,29 @@ ${result.changes.map((c) => `- ${c}`).join("\n")}` : `\u274C Reload failed: ${re
|
|
|
29096
29360
|
await ctx.reply(`\u2705 ${key}: **${cur}** \u2192 **${rawState}**`);
|
|
29097
29361
|
return;
|
|
29098
29362
|
}
|
|
29363
|
+
if (ctx.command === "stophook") {
|
|
29364
|
+
const rawState = (ctx.args.state || "").trim().toLowerCase();
|
|
29365
|
+
const getPath = "nudge.stopHookEnabled";
|
|
29366
|
+
const curVal = config2?.nudge?.stopHookEnabled;
|
|
29367
|
+
const cur = curVal === false ? "off" : "on";
|
|
29368
|
+
if (rawState === "") {
|
|
29369
|
+
await ctx.reply(`\u{1F4CA} stop-hook: **${cur}**\uFF08nudge \u63D2\u4EF6 stopHookEnabled=${String(curVal ?? "\u672A\u8BBE(\u9ED8\u8BA4on)")}\uFF09`);
|
|
29370
|
+
return;
|
|
29371
|
+
}
|
|
29372
|
+
if (rawState !== "on" && rawState !== "off") {
|
|
29373
|
+
await ctx.reply(`\u274C \u53EA\u63A5\u53D7 on \u6216 off
|
|
29374
|
+
\u5F53\u524D\uFF1Astop-hook = **${cur}**`);
|
|
29375
|
+
return;
|
|
29376
|
+
}
|
|
29377
|
+
if (rawState === cur) {
|
|
29378
|
+
await ctx.reply(`\u2139\uFE0F stop-hook \u5DF2\u7ECF\u662F **${cur}**`);
|
|
29379
|
+
return;
|
|
29380
|
+
}
|
|
29381
|
+
await liveConfig.set(getPath, rawState === "on");
|
|
29382
|
+
console.log(`[stophook] stop-hook ${cur} \u2192 ${rawState} (live + persisted)`);
|
|
29383
|
+
await ctx.reply(`\u2705 stop-hook: **${cur}** \u2192 **${rawState}**${rawState === "off" ? "\\n\uFF08\u8FDE\u73AF nudge \u9759\u9ED8\u4E2D\u2014\u2014wake-up/corrective \u5168\u505C\uFF0C\u8981\u7528\u518D /stophook on\uFF09" : ""}`);
|
|
29384
|
+
return;
|
|
29385
|
+
}
|
|
29099
29386
|
if (ctx.command === "model") {
|
|
29100
29387
|
const input = (ctx.args.model || "").trim();
|
|
29101
29388
|
const aliases = config2.modelAliases || {};
|
|
@@ -29821,7 +30108,7 @@ ${pathStr}` }];
|
|
|
29821
30108
|
}
|
|
29822
30109
|
}
|
|
29823
30110
|
if (config2.everos?.enabled) {
|
|
29824
|
-
pluginManager.register(new EverosPlugin(config2.everos));
|
|
30111
|
+
pluginManager.register(new EverosPlugin(config2.everos, config2.providers));
|
|
29825
30112
|
console.log("[everos] Plugin registered");
|
|
29826
30113
|
}
|
|
29827
30114
|
globalThis.__pluginManager = pluginManager;
|