engine7 7.1.37 → 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 +423 -144
- package/dist/main.mjs +344 -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) }
|
|
14517
|
+
});
|
|
14518
|
+
} else {
|
|
14519
|
+
await this.client.im.message.create({
|
|
14520
|
+
params: { receive_id_type: receiveIdType },
|
|
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 }) }
|
|
14443
14532
|
});
|
|
14444
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);
|
|
14445
14545
|
await this.client.im.message.create({
|
|
14446
14546
|
params: { receive_id_type: receiveIdType },
|
|
14447
|
-
data: { receive_id:
|
|
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();
|
|
@@ -17290,6 +17412,8 @@ var SessionManager = class {
|
|
|
17290
17412
|
platformMapPath;
|
|
17291
17413
|
/** 外部 hook:每次创建新 writer 时回调(如 EverOS sync 注入 onMessageWritten) */
|
|
17292
17414
|
onWriterCreated = null;
|
|
17415
|
+
/** 外部 hook:判断 session 是否有活跃 query(idle cleanup 据此跳过,防止误关 query 正持有的 writer) */
|
|
17416
|
+
onIsSessionActive = null;
|
|
17293
17417
|
histories = /* @__PURE__ */ new Map();
|
|
17294
17418
|
restoredRecallPaths = /* @__PURE__ */ new Map();
|
|
17295
17419
|
writers = /* @__PURE__ */ new Map();
|
|
@@ -17736,6 +17860,7 @@ var SessionManager = class {
|
|
|
17736
17860
|
const now = Date.now();
|
|
17737
17861
|
for (const [id, lastUsed] of this.lastUsed) {
|
|
17738
17862
|
if (now - lastUsed > idleMs) {
|
|
17863
|
+
if (this.onIsSessionActive?.(id)) continue;
|
|
17739
17864
|
const writer = this.writers.get(id);
|
|
17740
17865
|
if (writer) {
|
|
17741
17866
|
writer.close();
|
|
@@ -19086,8 +19211,11 @@ async function deepinfraRerank(query, episodes, rerankUrl, rerankApiKey, rerankM
|
|
|
19086
19211
|
}
|
|
19087
19212
|
var DEFAULT_MIN_SCORE2 = 0.5;
|
|
19088
19213
|
async function findRelevantMemoriesEveros(query, _memoryDir, alreadySurfaced = /* @__PURE__ */ new Set(), options) {
|
|
19089
|
-
const everosUrl = options?.everosUrl
|
|
19090
|
-
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
|
+
}
|
|
19091
19219
|
const topK = options?.topK ?? 3;
|
|
19092
19220
|
const minScore = options?.minScore ?? DEFAULT_MIN_SCORE2;
|
|
19093
19221
|
console.log(`[memdir] everos recall: query="${query.slice(0, 50)}..." url=${everosUrl} userId=${userId} topK=${topK}`);
|
|
@@ -19409,7 +19537,9 @@ ${text}` : text });
|
|
|
19409
19537
|
} else {
|
|
19410
19538
|
console.log(`[pre-llm-debug] userMsgContent is string, len=${String(userMsgContent).length}`);
|
|
19411
19539
|
}
|
|
19412
|
-
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)];
|
|
19413
19543
|
if (deps.mcpManager && !deps.mcpManager.isMcpDeltaSent(sessionId)) {
|
|
19414
19544
|
const delta = deps.mcpManager.getMcpDelta();
|
|
19415
19545
|
if (delta && delta.addedBlocks.length > 0) {
|
|
@@ -19578,7 +19708,7 @@ ${text}` : text });
|
|
|
19578
19708
|
const queryAbortController = new AbortController();
|
|
19579
19709
|
engine.setExternalAbort(queryAbortController);
|
|
19580
19710
|
setActiveQueryEngine(sessionId, engine);
|
|
19581
|
-
const shouldSkipRecall = skipRecall ?? channelName === "cron";
|
|
19711
|
+
const shouldSkipRecall = skipRecall ?? (channelName === "cron" || channelName === "voice-chat");
|
|
19582
19712
|
if (getFeature("topic-recall") !== false && !shouldSkipRecall) {
|
|
19583
19713
|
try {
|
|
19584
19714
|
const memoryDir = getAutoMemPath(workspace);
|
|
@@ -19626,31 +19756,49 @@ ${text}` : text });
|
|
|
19626
19756
|
const recallP = deps.recallProvider;
|
|
19627
19757
|
const recallMode = topics?.recall?.mode || "llm";
|
|
19628
19758
|
let relevantMemories;
|
|
19629
|
-
|
|
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") {
|
|
19630
19764
|
const everosCfg = deps?.everosCfg;
|
|
19631
|
-
|
|
19632
|
-
|
|
19633
|
-
|
|
19634
|
-
|
|
19635
|
-
|
|
19636
|
-
|
|
19637
|
-
|
|
19638
|
-
|
|
19639
|
-
|
|
19640
|
-
|
|
19641
|
-
|
|
19642
|
-
|
|
19643
|
-
|
|
19644
|
-
|
|
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
|
+
}
|
|
19645
19793
|
} else if (recallMode === "vector") {
|
|
19646
19794
|
relevantMemories = await findRelevantMemoriesVector(
|
|
19647
|
-
|
|
19795
|
+
effectiveText,
|
|
19648
19796
|
memoryDir,
|
|
19649
19797
|
surfaced.paths
|
|
19650
19798
|
);
|
|
19651
19799
|
} else {
|
|
19652
19800
|
relevantMemories = await findRelevantMemories(
|
|
19653
|
-
|
|
19801
|
+
effectiveText,
|
|
19654
19802
|
memoryDir,
|
|
19655
19803
|
recallP?.provider || provider,
|
|
19656
19804
|
recallP?.model || model,
|
|
@@ -20882,6 +21030,10 @@ var NudgePlugin = class {
|
|
|
20882
21030
|
registerCallbackHook("Stop", {
|
|
20883
21031
|
type: "callback",
|
|
20884
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
|
+
}
|
|
20885
21037
|
const mode = this.cfg.stopHookMode || "sync";
|
|
20886
21038
|
if (mode === "async") {
|
|
20887
21039
|
console.log("[stop-hook] async mode \u2014 firing judge in background, not blocking");
|
|
@@ -21156,26 +21308,28 @@ var NudgePlugin = class {
|
|
|
21156
21308
|
console.log(`[nudge] ${due.length} stop-hook notification(s) due: ${due.map((n) => n.id).join(", ")}`);
|
|
21157
21309
|
const items = due.map((n) => `[\u901A\u77E5ID: ${n.id}]
|
|
21158
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`;
|
|
21159
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
|
|
21160
21313
|
|
|
21161
21314
|
${items}
|
|
21162
21315
|
|
|
21163
|
-
\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
|
|
21164
21317
|
|
|
21165
21318
|
${items}
|
|
21166
21319
|
|
|
21167
|
-
\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`;
|
|
21168
21321
|
return { message: buildNudgeNotification("wake", desc), ids: due.map((n) => n.id) };
|
|
21169
21322
|
} catch (e) {
|
|
21170
21323
|
console.warn(`[nudge] collectDueStopHookNotifications error: ${e.message}`);
|
|
21171
21324
|
return null;
|
|
21172
21325
|
}
|
|
21173
21326
|
}
|
|
21174
|
-
/** 从回复文本里提取 "<id> 过期了" 的 wake id(一条回复可能处置多个)
|
|
21327
|
+
/** 从回复文本里提取 "<id> 过期了" 的 wake id(一条回复可能处置多个)
|
|
21328
|
+
* 0816 翀哥:关键字多样化——过期了/已落盘/resolved/已处理/已闭环/已归档 都认 */
|
|
21175
21329
|
extractWakeReplyIds(text) {
|
|
21176
21330
|
if (!text) return [];
|
|
21177
21331
|
const ids = [];
|
|
21178
|
-
const re = /(wake-\d+-[a-z0-9]+)\s
|
|
21332
|
+
const re = /(wake-\d+-[a-z0-9]+)\s*(?:过期了|已落盘|resolved|已处理|已闭环|已归档|已完成|done)/g;
|
|
21179
21333
|
let m;
|
|
21180
21334
|
while ((m = re.exec(text)) !== null) {
|
|
21181
21335
|
if (!ids.includes(m[1])) ids.push(m[1]);
|
|
@@ -23618,8 +23772,9 @@ var VoiceChatPlugin = class _VoiceChatPlugin {
|
|
|
23618
23772
|
- \u4FDD\u6301\u7B80\u77ED\u53E3\u8BED\u5316\uFF08\u901A\u5E38 1-3 \u53E5\uFF09\uFF0C\u4E0D\u8981\u957F\u7BC7\u5927\u8BBA
|
|
23619
23773
|
- \u7B2C\u4E00\u53E5\u63A7\u5236\u5728 4-6 \u4E2A\u5B57\uFF0C\u5C3D\u5FEB\u89E6\u53D1 TTS \u5408\u6210
|
|
23620
23774
|
- \u8BF4\u8BDD\u50CF\u4EBA\u2014\u2014\u77ED\u53E5\uFF0C\u53BB\u670D\u52A1\u611F
|
|
23621
|
-
- \
|
|
23622
|
-
- \
|
|
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`;
|
|
23623
23778
|
const engine = new QueryEngine(llmProvider, {
|
|
23624
23779
|
model: modelId,
|
|
23625
23780
|
systemPrompt: (ctx.deps.systemPrompt || "") + voiceChatRules,
|
|
@@ -24333,7 +24488,22 @@ var DEFAULTS4 = {
|
|
|
24333
24488
|
autoStart: true,
|
|
24334
24489
|
defaultMode: "hybrid_agentic"
|
|
24335
24490
|
};
|
|
24336
|
-
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) {
|
|
24337
24507
|
if (!raw) {
|
|
24338
24508
|
return {
|
|
24339
24509
|
enabled: false,
|
|
@@ -24353,8 +24523,20 @@ function parseEverosConfig(raw) {
|
|
|
24353
24523
|
userId: raw.userId ?? "xiaomei",
|
|
24354
24524
|
autoStart: raw.autoStart !== false,
|
|
24355
24525
|
defaultMode: raw.defaultMode ?? DEFAULTS4.defaultMode,
|
|
24356
|
-
llm:
|
|
24357
|
-
|
|
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
|
+
),
|
|
24358
24540
|
lancedbPath: raw.lancedbPath ?? "",
|
|
24359
24541
|
sqlitePath: raw.sqlitePath ?? "",
|
|
24360
24542
|
minScore: raw.minScore
|
|
@@ -24405,14 +24587,14 @@ var EverosSearchClient = class {
|
|
|
24405
24587
|
const url = useAgentic ? `${this.agenticUrl}/api/v1/search` : `${this.everosUrl}/api/v1/memory/search`;
|
|
24406
24588
|
const body = useAgentic ? JSON.stringify({
|
|
24407
24589
|
query: params.query,
|
|
24408
|
-
user_id: params.userId
|
|
24590
|
+
user_id: params.userId,
|
|
24409
24591
|
mode,
|
|
24410
24592
|
top_k: params.topK ?? 5,
|
|
24411
24593
|
strategy: params.strategy || "multi_query"
|
|
24412
24594
|
}) : JSON.stringify({
|
|
24413
24595
|
query: params.query,
|
|
24414
|
-
user_id: params.userId
|
|
24415
|
-
app_id:
|
|
24596
|
+
user_id: params.userId,
|
|
24597
|
+
app_id: params.appId,
|
|
24416
24598
|
project_id: "default",
|
|
24417
24599
|
top_k: params.topK ?? 5
|
|
24418
24600
|
});
|
|
@@ -24450,8 +24632,8 @@ var EverosPlugin = class {
|
|
|
24450
24632
|
healthTimer = null;
|
|
24451
24633
|
weStartedAgentic = false;
|
|
24452
24634
|
// 我们拉起的才管
|
|
24453
|
-
constructor(rawConfig) {
|
|
24454
|
-
this.config = parseEverosConfig(rawConfig);
|
|
24635
|
+
constructor(rawConfig, providers) {
|
|
24636
|
+
this.config = parseEverosConfig(rawConfig, providers);
|
|
24455
24637
|
this.client = new EverosSearchClient(this.config.agenticUrl, this.config.everosUrl);
|
|
24456
24638
|
}
|
|
24457
24639
|
static shouldEnable(config2) {
|
|
@@ -25588,6 +25770,47 @@ async function compressWav(wavPath) {
|
|
|
25588
25770
|
return wavPath;
|
|
25589
25771
|
}
|
|
25590
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
|
+
}
|
|
25591
25814
|
registry.register({
|
|
25592
25815
|
name: "my_voice",
|
|
25593
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.",
|
|
@@ -25625,9 +25848,6 @@ registry.register({
|
|
|
25625
25848
|
vc.workspaceId,
|
|
25626
25849
|
vc.instruction
|
|
25627
25850
|
);
|
|
25628
|
-
if (audioPath.endsWith(".wav")) {
|
|
25629
|
-
audioPath = await compressWav(audioPath);
|
|
25630
|
-
}
|
|
25631
25851
|
} catch (e) {
|
|
25632
25852
|
console.warn(`[my-voice] cosyvoice failed (${e.message}), falling back to edge-tts`);
|
|
25633
25853
|
audioPath = await ttsEdge(text);
|
|
@@ -25636,9 +25856,6 @@ registry.register({
|
|
|
25636
25856
|
} else if (useEngine === "gptsovits") {
|
|
25637
25857
|
try {
|
|
25638
25858
|
audioPath = await ttsGptsovits(text);
|
|
25639
|
-
if (audioPath.endsWith(".wav")) {
|
|
25640
|
-
audioPath = await compressWav(audioPath);
|
|
25641
|
-
}
|
|
25642
25859
|
} catch (e) {
|
|
25643
25860
|
console.warn(`[my-voice] gptsovits failed (${e.message}), falling back to edge-tts`);
|
|
25644
25861
|
audioPath = await ttsEdge(text);
|
|
@@ -25650,12 +25867,30 @@ registry.register({
|
|
|
25650
25867
|
} catch (e) {
|
|
25651
25868
|
return { content: `TTS failed: ${e.message}`, isError: true };
|
|
25652
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
|
+
}
|
|
25653
25890
|
const ext = path31.extname(audioPath).toLowerCase();
|
|
25654
|
-
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" };
|
|
25655
25892
|
const mimeType = mimeMap[ext] || "audio/mpeg";
|
|
25656
25893
|
const sizeKB = fs30.statSync(audioPath).size / 1024;
|
|
25657
|
-
const resolvedChannel = args2.channel || ctx.channel || "feishu";
|
|
25658
|
-
const target = ctx.channelTarget || ctx.from;
|
|
25659
25894
|
try {
|
|
25660
25895
|
await mgr.sendFile(resolvedChannel, target, caption, {
|
|
25661
25896
|
path: audioPath,
|
|
@@ -25693,9 +25928,12 @@ function getProxyDispatcher2() {
|
|
|
25693
25928
|
return void 0;
|
|
25694
25929
|
}
|
|
25695
25930
|
}
|
|
25696
|
-
var FAL_KEY = "3b848fc6-bee5-46e5-8db7-ae81ac16dc28:2f1f15cf394db2d32c9bb9a4f23f3bee";
|
|
25697
25931
|
var FAL_ENDPOINT = "https://fal.run/xai/grok-imagine-image/edit";
|
|
25698
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
|
+
}
|
|
25699
25937
|
var DEFAULT_REFERENCES = [
|
|
25700
25938
|
{ name: "default", p: "images/xiaomei_clean_v2.png" },
|
|
25701
25939
|
{ name: "v3", p: "images/xiaomei_clean_v3.png" },
|
|
@@ -25738,6 +25976,7 @@ function detectMode(input) {
|
|
|
25738
25976
|
return "direct";
|
|
25739
25977
|
}
|
|
25740
25978
|
async function generateWithFal(imageB64, prompt, resolution) {
|
|
25979
|
+
const cfg = liveConfig.all();
|
|
25741
25980
|
let aspectRatio;
|
|
25742
25981
|
try {
|
|
25743
25982
|
const refBuf = Buffer.from(imageB64, "base64");
|
|
@@ -25762,8 +26001,7 @@ async function generateWithFal(imageB64, prompt, resolution) {
|
|
|
25762
26001
|
}
|
|
25763
26002
|
}
|
|
25764
26003
|
if (w > 0 && h > 0) {
|
|
25765
|
-
|
|
25766
|
-
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`);
|
|
25767
26005
|
}
|
|
25768
26006
|
} catch (e) {
|
|
25769
26007
|
console.warn(`[my-selfie] Failed to read ref dimensions: ${e.message}`);
|
|
@@ -25778,7 +26016,7 @@ async function generateWithFal(imageB64, prompt, resolution) {
|
|
|
25778
26016
|
if (aspectRatio) body.aspect_ratio = aspectRatio;
|
|
25779
26017
|
const res = await fetch(FAL_ENDPOINT, {
|
|
25780
26018
|
method: "POST",
|
|
25781
|
-
headers: { "Authorization": `Key ${
|
|
26019
|
+
headers: { "Authorization": `Key ${getFalKey(cfg)}`, "Content-Type": "application/json" },
|
|
25782
26020
|
body: JSON.stringify(body)
|
|
25783
26021
|
});
|
|
25784
26022
|
if (!res.ok) {
|
|
@@ -28190,7 +28428,12 @@ ${content}`
|
|
|
28190
28428
|
// tool 读自己配置用
|
|
28191
28429
|
recallProvider: memoryRecallProvider || void 0,
|
|
28192
28430
|
extractProvider: memoryExtractProvider || void 0,
|
|
28193
|
-
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,
|
|
28194
28437
|
mcpManager
|
|
28195
28438
|
};
|
|
28196
28439
|
if (visionEngine && visionConfig) {
|
|
@@ -28210,7 +28453,13 @@ ${content}`
|
|
|
28210
28453
|
config: config2,
|
|
28211
28454
|
// tool 读自己配置用
|
|
28212
28455
|
recallProvider: memoryRecallProvider || void 0,
|
|
28213
|
-
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
|
|
28214
28463
|
};
|
|
28215
28464
|
}
|
|
28216
28465
|
let modelOverride = null;
|
|
@@ -28259,7 +28508,8 @@ ${content}`
|
|
|
28259
28508
|
channels: config2.channels,
|
|
28260
28509
|
config: config2,
|
|
28261
28510
|
recallProvider: memoryRecallProvider || void 0,
|
|
28262
|
-
extractProvider: memoryExtractProvider || void 0
|
|
28511
|
+
extractProvider: memoryExtractProvider || void 0,
|
|
28512
|
+
everosCfg: deps?.everosCfg
|
|
28263
28513
|
};
|
|
28264
28514
|
}
|
|
28265
28515
|
};
|
|
@@ -28301,11 +28551,13 @@ ${content}`
|
|
|
28301
28551
|
systemPrompt,
|
|
28302
28552
|
channels: config2.channels,
|
|
28303
28553
|
recallProvider: memoryRecallProvider || void 0,
|
|
28304
|
-
extractProvider: memoryExtractProvider || void 0
|
|
28554
|
+
extractProvider: memoryExtractProvider || void 0,
|
|
28555
|
+
everosCfg: deps?.everosCfg
|
|
28305
28556
|
};
|
|
28306
28557
|
}
|
|
28307
28558
|
const dispatcher = new MessageDispatcher();
|
|
28308
28559
|
deps.dispatcher = dispatcher;
|
|
28560
|
+
sessions.onIsSessionActive = (sid) => dispatcher.isActive(sid);
|
|
28309
28561
|
setNotificationCallback((notif, route) => {
|
|
28310
28562
|
const sid = route?.sessionId || sessions.getSessionId("scope:main") || "main";
|
|
28311
28563
|
const chan = route?.channel;
|
|
@@ -28676,6 +28928,10 @@ ${notifications}
|
|
|
28676
28928
|
{ name: "session-memory", description: "\u67E5/\u5207 session-memory\uFF08on/off/\u7559\u7A7A=\u67E5\uFF09", options: [
|
|
28677
28929
|
{ name: "state", description: "on \u6216 off\uFF0C\u7559\u7A7A=\u53EA\u67E5\u72B6\u6001", type: "string", required: false }
|
|
28678
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
|
+
] },
|
|
28679
28935
|
(() => {
|
|
28680
28936
|
const aliases = config2.modelAliases || {};
|
|
28681
28937
|
const aliasByRef = /* @__PURE__ */ new Map();
|
|
@@ -29104,6 +29360,29 @@ ${result.changes.map((c) => `- ${c}`).join("\n")}` : `\u274C Reload failed: ${re
|
|
|
29104
29360
|
await ctx.reply(`\u2705 ${key}: **${cur}** \u2192 **${rawState}**`);
|
|
29105
29361
|
return;
|
|
29106
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
|
+
}
|
|
29107
29386
|
if (ctx.command === "model") {
|
|
29108
29387
|
const input = (ctx.args.model || "").trim();
|
|
29109
29388
|
const aliases = config2.modelAliases || {};
|
|
@@ -29829,7 +30108,7 @@ ${pathStr}` }];
|
|
|
29829
30108
|
}
|
|
29830
30109
|
}
|
|
29831
30110
|
if (config2.everos?.enabled) {
|
|
29832
|
-
pluginManager.register(new EverosPlugin(config2.everos));
|
|
30111
|
+
pluginManager.register(new EverosPlugin(config2.everos, config2.providers));
|
|
29833
30112
|
console.log("[everos] Plugin registered");
|
|
29834
30113
|
}
|
|
29835
30114
|
globalThis.__pluginManager = pluginManager;
|