opencode-feishu 0.7.5 → 0.7.8
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/index.js +280 -205
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -99176,6 +99176,140 @@ async function updateMessage(client, messageId, text2) {
|
|
|
99176
99176
|
}
|
|
99177
99177
|
}
|
|
99178
99178
|
|
|
99179
|
+
// src/handler/event.ts
|
|
99180
|
+
var pendingBySession = /* @__PURE__ */ new Map();
|
|
99181
|
+
var sessionErrors = /* @__PURE__ */ new Map();
|
|
99182
|
+
var sessionErrorTimeouts = /* @__PURE__ */ new Map();
|
|
99183
|
+
var SESSION_ERROR_TTL_MS = 3e4;
|
|
99184
|
+
var forkAttempts = /* @__PURE__ */ new Map();
|
|
99185
|
+
var forkAttemptTimeouts = /* @__PURE__ */ new Map();
|
|
99186
|
+
var MAX_FORK_ATTEMPTS = 2;
|
|
99187
|
+
var FORK_ATTEMPTS_TTL_MS = 36e5;
|
|
99188
|
+
function clearForkAttempts(sessionKey) {
|
|
99189
|
+
forkAttempts.delete(sessionKey);
|
|
99190
|
+
const timer = forkAttemptTimeouts.get(sessionKey);
|
|
99191
|
+
if (timer) {
|
|
99192
|
+
clearTimeout(timer);
|
|
99193
|
+
forkAttemptTimeouts.delete(sessionKey);
|
|
99194
|
+
}
|
|
99195
|
+
}
|
|
99196
|
+
function getForkAttempts(sessionKey) {
|
|
99197
|
+
return forkAttempts.get(sessionKey) ?? 0;
|
|
99198
|
+
}
|
|
99199
|
+
function setForkAttempts(sessionKey, count) {
|
|
99200
|
+
forkAttempts.set(sessionKey, count);
|
|
99201
|
+
const existing = forkAttemptTimeouts.get(sessionKey);
|
|
99202
|
+
if (existing) clearTimeout(existing);
|
|
99203
|
+
const timeoutId = setTimeout(() => {
|
|
99204
|
+
forkAttempts.delete(sessionKey);
|
|
99205
|
+
forkAttemptTimeouts.delete(sessionKey);
|
|
99206
|
+
}, FORK_ATTEMPTS_TTL_MS);
|
|
99207
|
+
forkAttemptTimeouts.set(sessionKey, timeoutId);
|
|
99208
|
+
}
|
|
99209
|
+
function getSessionError(sessionId) {
|
|
99210
|
+
return sessionErrors.get(sessionId);
|
|
99211
|
+
}
|
|
99212
|
+
function clearSessionError(sessionId) {
|
|
99213
|
+
const timer = sessionErrorTimeouts.get(sessionId);
|
|
99214
|
+
if (timer) {
|
|
99215
|
+
clearTimeout(timer);
|
|
99216
|
+
sessionErrorTimeouts.delete(sessionId);
|
|
99217
|
+
}
|
|
99218
|
+
sessionErrors.delete(sessionId);
|
|
99219
|
+
}
|
|
99220
|
+
function setSessionError(sessionId, message, fields) {
|
|
99221
|
+
const existing = sessionErrorTimeouts.get(sessionId);
|
|
99222
|
+
if (existing) {
|
|
99223
|
+
clearTimeout(existing);
|
|
99224
|
+
}
|
|
99225
|
+
sessionErrors.set(sessionId, { message, fields });
|
|
99226
|
+
const timeoutId = setTimeout(() => {
|
|
99227
|
+
sessionErrors.delete(sessionId);
|
|
99228
|
+
sessionErrorTimeouts.delete(sessionId);
|
|
99229
|
+
}, SESSION_ERROR_TTL_MS);
|
|
99230
|
+
sessionErrorTimeouts.set(sessionId, timeoutId);
|
|
99231
|
+
}
|
|
99232
|
+
function registerPending(sessionId, payload) {
|
|
99233
|
+
pendingBySession.set(sessionId, { ...payload, textBuffer: "" });
|
|
99234
|
+
}
|
|
99235
|
+
function unregisterPending(sessionId) {
|
|
99236
|
+
pendingBySession.delete(sessionId);
|
|
99237
|
+
}
|
|
99238
|
+
function extractErrorFields(error) {
|
|
99239
|
+
if (typeof error === "string") return [error];
|
|
99240
|
+
if (error && typeof error === "object") {
|
|
99241
|
+
const e = error;
|
|
99242
|
+
const fields = [e.type, e.name, e.message].filter(Boolean).map(String);
|
|
99243
|
+
if (e.data && typeof e.data === "object" && "message" in e.data) {
|
|
99244
|
+
const dataMsg = e.data.message;
|
|
99245
|
+
if (dataMsg) fields.push(String(dataMsg));
|
|
99246
|
+
}
|
|
99247
|
+
return fields;
|
|
99248
|
+
}
|
|
99249
|
+
return [String(error)];
|
|
99250
|
+
}
|
|
99251
|
+
function isModelError(fields) {
|
|
99252
|
+
const check = (s) => {
|
|
99253
|
+
const l = s.toLowerCase();
|
|
99254
|
+
return l.includes("model not found") || l.includes("modelnotfound");
|
|
99255
|
+
};
|
|
99256
|
+
return fields.some(check);
|
|
99257
|
+
}
|
|
99258
|
+
async function handleEvent(event, deps) {
|
|
99259
|
+
switch (event.type) {
|
|
99260
|
+
case "message.part.updated": {
|
|
99261
|
+
const part = event.properties.part;
|
|
99262
|
+
if (!part) break;
|
|
99263
|
+
const sessionId = part.sessionID;
|
|
99264
|
+
if (!sessionId) break;
|
|
99265
|
+
const payload = pendingBySession.get(sessionId);
|
|
99266
|
+
if (!payload) break;
|
|
99267
|
+
const delta = event.properties.delta;
|
|
99268
|
+
if (delta) {
|
|
99269
|
+
payload.textBuffer += delta;
|
|
99270
|
+
} else {
|
|
99271
|
+
const fullText = extractPartText(part);
|
|
99272
|
+
if (fullText) {
|
|
99273
|
+
payload.textBuffer = fullText;
|
|
99274
|
+
}
|
|
99275
|
+
}
|
|
99276
|
+
if (payload.textBuffer) {
|
|
99277
|
+
const res = await updateMessage(payload.feishuClient, payload.placeholderId, payload.textBuffer.trim());
|
|
99278
|
+
if (!res.ok) ;
|
|
99279
|
+
}
|
|
99280
|
+
break;
|
|
99281
|
+
}
|
|
99282
|
+
case "session.error": {
|
|
99283
|
+
const props = event.properties;
|
|
99284
|
+
const sessionId = props.sessionID;
|
|
99285
|
+
if (!sessionId) break;
|
|
99286
|
+
const error = props.error;
|
|
99287
|
+
let errMsg;
|
|
99288
|
+
if (typeof error === "string") {
|
|
99289
|
+
errMsg = error;
|
|
99290
|
+
} else if (error && typeof error === "object") {
|
|
99291
|
+
const e = error;
|
|
99292
|
+
const rawDataMsg = e.data && typeof e.data === "object" && "message" in e.data ? e.data.message : void 0;
|
|
99293
|
+
const dataMsg = rawDataMsg != null ? String(rawDataMsg) : void 0;
|
|
99294
|
+
errMsg = String(e.message ?? dataMsg ?? e.type ?? e.name ?? "An unexpected error occurred");
|
|
99295
|
+
} else {
|
|
99296
|
+
errMsg = String(error);
|
|
99297
|
+
}
|
|
99298
|
+
const fields = extractErrorFields(error);
|
|
99299
|
+
deps.log("warn", "\u6536\u5230 session.error \u4E8B\u4EF6", { sessionId, errMsg });
|
|
99300
|
+
setSessionError(sessionId, errMsg, fields);
|
|
99301
|
+
break;
|
|
99302
|
+
}
|
|
99303
|
+
}
|
|
99304
|
+
}
|
|
99305
|
+
function extractPartText(part) {
|
|
99306
|
+
if (part.type === "text") return part.text ?? "";
|
|
99307
|
+
if (part.type === "reasoning" && part.text) return `\u{1F914} \u601D\u8003: ${part.text}
|
|
99308
|
+
|
|
99309
|
+
`;
|
|
99310
|
+
return "";
|
|
99311
|
+
}
|
|
99312
|
+
|
|
99179
99313
|
// src/session.ts
|
|
99180
99314
|
var SESSION_KEY_PREFIX = "feishu";
|
|
99181
99315
|
var TITLE_PREFIX = "Feishu";
|
|
@@ -99291,175 +99425,6 @@ async function forkOrCreateSession(client, oldSessionId, sessionKey, directory,
|
|
|
99291
99425
|
}
|
|
99292
99426
|
}
|
|
99293
99427
|
|
|
99294
|
-
// src/handler/event.ts
|
|
99295
|
-
var pendingBySession = /* @__PURE__ */ new Map();
|
|
99296
|
-
var sessionErrors = /* @__PURE__ */ new Map();
|
|
99297
|
-
var sessionErrorTimeouts = /* @__PURE__ */ new Map();
|
|
99298
|
-
var SESSION_ERROR_TTL_MS = 3e4;
|
|
99299
|
-
var forkAttempts = /* @__PURE__ */ new Map();
|
|
99300
|
-
var forkAttemptTimeouts = /* @__PURE__ */ new Map();
|
|
99301
|
-
var MAX_FORK_ATTEMPTS = 2;
|
|
99302
|
-
var FORK_ATTEMPTS_TTL_MS = 36e5;
|
|
99303
|
-
function clearForkAttempts(sessionKey) {
|
|
99304
|
-
forkAttempts.delete(sessionKey);
|
|
99305
|
-
const timer = forkAttemptTimeouts.get(sessionKey);
|
|
99306
|
-
if (timer) {
|
|
99307
|
-
clearTimeout(timer);
|
|
99308
|
-
forkAttemptTimeouts.delete(sessionKey);
|
|
99309
|
-
}
|
|
99310
|
-
}
|
|
99311
|
-
function setForkAttempts(sessionKey, count) {
|
|
99312
|
-
forkAttempts.set(sessionKey, count);
|
|
99313
|
-
const existing = forkAttemptTimeouts.get(sessionKey);
|
|
99314
|
-
if (existing) clearTimeout(existing);
|
|
99315
|
-
const timeoutId = setTimeout(() => {
|
|
99316
|
-
forkAttempts.delete(sessionKey);
|
|
99317
|
-
forkAttemptTimeouts.delete(sessionKey);
|
|
99318
|
-
}, FORK_ATTEMPTS_TTL_MS);
|
|
99319
|
-
forkAttemptTimeouts.set(sessionKey, timeoutId);
|
|
99320
|
-
}
|
|
99321
|
-
function getSessionError(sessionId) {
|
|
99322
|
-
return sessionErrors.get(sessionId);
|
|
99323
|
-
}
|
|
99324
|
-
function clearSessionError(sessionId) {
|
|
99325
|
-
const timer = sessionErrorTimeouts.get(sessionId);
|
|
99326
|
-
if (timer) {
|
|
99327
|
-
clearTimeout(timer);
|
|
99328
|
-
sessionErrorTimeouts.delete(sessionId);
|
|
99329
|
-
}
|
|
99330
|
-
sessionErrors.delete(sessionId);
|
|
99331
|
-
}
|
|
99332
|
-
function setSessionError(sessionId, errMsg) {
|
|
99333
|
-
const existing = sessionErrorTimeouts.get(sessionId);
|
|
99334
|
-
if (existing) {
|
|
99335
|
-
clearTimeout(existing);
|
|
99336
|
-
}
|
|
99337
|
-
sessionErrors.set(sessionId, errMsg);
|
|
99338
|
-
const timeoutId = setTimeout(() => {
|
|
99339
|
-
sessionErrors.delete(sessionId);
|
|
99340
|
-
sessionErrorTimeouts.delete(sessionId);
|
|
99341
|
-
}, SESSION_ERROR_TTL_MS);
|
|
99342
|
-
sessionErrorTimeouts.set(sessionId, timeoutId);
|
|
99343
|
-
}
|
|
99344
|
-
function registerPending(sessionId, payload) {
|
|
99345
|
-
pendingBySession.set(sessionId, { ...payload, textBuffer: "" });
|
|
99346
|
-
}
|
|
99347
|
-
function unregisterPending(sessionId) {
|
|
99348
|
-
pendingBySession.delete(sessionId);
|
|
99349
|
-
}
|
|
99350
|
-
function migratePending(oldSessionId, newSessionId) {
|
|
99351
|
-
const payload = pendingBySession.get(oldSessionId);
|
|
99352
|
-
if (payload) {
|
|
99353
|
-
pendingBySession.delete(oldSessionId);
|
|
99354
|
-
pendingBySession.set(newSessionId, payload);
|
|
99355
|
-
}
|
|
99356
|
-
}
|
|
99357
|
-
function isModelError(errMsg, rawError) {
|
|
99358
|
-
if (errMsg.includes("ModelNotFound") || errMsg.includes("ProviderModelNotFound")) {
|
|
99359
|
-
return true;
|
|
99360
|
-
}
|
|
99361
|
-
if (rawError && typeof rawError === "object") {
|
|
99362
|
-
const e = rawError;
|
|
99363
|
-
const fields = [e.type, e.name, e.message].filter(Boolean).map(String);
|
|
99364
|
-
if (e.data && typeof e.data === "object" && "message" in e.data) {
|
|
99365
|
-
const dataMsg = e.data.message;
|
|
99366
|
-
if (dataMsg) fields.push(String(dataMsg));
|
|
99367
|
-
}
|
|
99368
|
-
return fields.some((f) => f.includes("ModelNotFound") || f.includes("ProviderModelNotFound"));
|
|
99369
|
-
}
|
|
99370
|
-
return false;
|
|
99371
|
-
}
|
|
99372
|
-
async function handleEvent(event, deps) {
|
|
99373
|
-
switch (event.type) {
|
|
99374
|
-
case "message.part.updated": {
|
|
99375
|
-
const part = event.properties.part;
|
|
99376
|
-
if (!part) break;
|
|
99377
|
-
const sessionId = part.sessionID;
|
|
99378
|
-
if (!sessionId) break;
|
|
99379
|
-
const payload = pendingBySession.get(sessionId);
|
|
99380
|
-
if (!payload) break;
|
|
99381
|
-
const delta = event.properties.delta;
|
|
99382
|
-
if (delta) {
|
|
99383
|
-
payload.textBuffer += delta;
|
|
99384
|
-
} else {
|
|
99385
|
-
const fullText = extractPartText(part);
|
|
99386
|
-
if (fullText) {
|
|
99387
|
-
payload.textBuffer = fullText;
|
|
99388
|
-
}
|
|
99389
|
-
}
|
|
99390
|
-
if (payload.textBuffer) {
|
|
99391
|
-
const res = await updateMessage(payload.feishuClient, payload.placeholderId, payload.textBuffer.trim());
|
|
99392
|
-
if (!res.ok) ;
|
|
99393
|
-
}
|
|
99394
|
-
break;
|
|
99395
|
-
}
|
|
99396
|
-
case "session.error": {
|
|
99397
|
-
const props = event.properties;
|
|
99398
|
-
const sessionId = props.sessionID;
|
|
99399
|
-
if (!sessionId) break;
|
|
99400
|
-
const error = props.error;
|
|
99401
|
-
let errMsg;
|
|
99402
|
-
if (typeof error === "string") {
|
|
99403
|
-
errMsg = error;
|
|
99404
|
-
} else if (error && typeof error === "object") {
|
|
99405
|
-
const e = error;
|
|
99406
|
-
const rawDataMsg = e.data && typeof e.data === "object" && "message" in e.data ? e.data.message : void 0;
|
|
99407
|
-
const dataMsg = rawDataMsg != null ? String(rawDataMsg) : void 0;
|
|
99408
|
-
errMsg = String(e.message ?? dataMsg ?? e.type ?? e.name ?? "An unexpected error occurred");
|
|
99409
|
-
} else {
|
|
99410
|
-
errMsg = String(error);
|
|
99411
|
-
}
|
|
99412
|
-
const safeErrorFields = error && typeof error === "object" ? {
|
|
99413
|
-
type: error.type,
|
|
99414
|
-
name: error.name,
|
|
99415
|
-
dataMessage: error.data?.message
|
|
99416
|
-
} : { raw: String(error) };
|
|
99417
|
-
deps.log("warn", "\u6536\u5230 session.error \u4E8B\u4EF6", {
|
|
99418
|
-
sessionId,
|
|
99419
|
-
error: safeErrorFields,
|
|
99420
|
-
extractedMsg: errMsg
|
|
99421
|
-
});
|
|
99422
|
-
setSessionError(sessionId, errMsg);
|
|
99423
|
-
if (isModelError(errMsg, props.error)) {
|
|
99424
|
-
const sessionKey = invalidateCachedSession(sessionId);
|
|
99425
|
-
if (sessionKey) {
|
|
99426
|
-
const attempts = forkAttempts.get(sessionKey) ?? 0;
|
|
99427
|
-
if (attempts >= MAX_FORK_ATTEMPTS) {
|
|
99428
|
-
deps.log("warn", "\u5DF2\u8FBE fork \u4E0A\u9650\uFF0C\u653E\u5F03\u6062\u590D", { sessionKey, attempts });
|
|
99429
|
-
} else {
|
|
99430
|
-
setForkAttempts(sessionKey, attempts + 1);
|
|
99431
|
-
try {
|
|
99432
|
-
const newSession = await forkOrCreateSession(deps.client, sessionId, sessionKey, deps.directory, deps.log);
|
|
99433
|
-
setCachedSession(sessionKey, newSession);
|
|
99434
|
-
deps.log("warn", "\u6A21\u578B\u4E0D\u517C\u5BB9\uFF0C\u5DF2\u6062\u590D\u4F1A\u8BDD", {
|
|
99435
|
-
oldSessionId: sessionId,
|
|
99436
|
-
newSessionId: newSession.id,
|
|
99437
|
-
sessionKey,
|
|
99438
|
-
forkAttempt: attempts + 1
|
|
99439
|
-
});
|
|
99440
|
-
migratePending(sessionId, newSession.id);
|
|
99441
|
-
} catch (recoverErr) {
|
|
99442
|
-
deps.log("error", "\u4F1A\u8BDD\u6062\u590D\u5931\u8D25", {
|
|
99443
|
-
sessionId,
|
|
99444
|
-
sessionKey,
|
|
99445
|
-
error: recoverErr instanceof Error ? recoverErr.message : String(recoverErr)
|
|
99446
|
-
});
|
|
99447
|
-
}
|
|
99448
|
-
}
|
|
99449
|
-
}
|
|
99450
|
-
}
|
|
99451
|
-
break;
|
|
99452
|
-
}
|
|
99453
|
-
}
|
|
99454
|
-
}
|
|
99455
|
-
function extractPartText(part) {
|
|
99456
|
-
if (part.type === "text") return part.text ?? "";
|
|
99457
|
-
if (part.type === "reasoning" && part.text) return `\u{1F914} \u601D\u8003: ${part.text}
|
|
99458
|
-
|
|
99459
|
-
`;
|
|
99460
|
-
return "";
|
|
99461
|
-
}
|
|
99462
|
-
|
|
99463
99428
|
// src/handler/chat.ts
|
|
99464
99429
|
var SSE_RACE_WAIT_MS = 100;
|
|
99465
99430
|
var activeAutoPrompts = /* @__PURE__ */ new Map();
|
|
@@ -99485,14 +99450,16 @@ async function handleChat(ctx, deps) {
|
|
|
99485
99450
|
senderId,
|
|
99486
99451
|
messageType,
|
|
99487
99452
|
shouldReply,
|
|
99453
|
+
content,
|
|
99488
99454
|
parts
|
|
99489
99455
|
});
|
|
99456
|
+
const baseBody = { parts };
|
|
99490
99457
|
if (!shouldReply) {
|
|
99491
99458
|
try {
|
|
99492
99459
|
await client.session.prompt({
|
|
99493
99460
|
path: { id: session.id },
|
|
99494
99461
|
query,
|
|
99495
|
-
body: {
|
|
99462
|
+
body: { ...baseBody, noReply: true }
|
|
99496
99463
|
});
|
|
99497
99464
|
} catch (err) {
|
|
99498
99465
|
log("warn", "\u9759\u9ED8\u8F6C\u53D1\u5931\u8D25", {
|
|
@@ -99507,6 +99474,7 @@ async function handleChat(ctx, deps) {
|
|
|
99507
99474
|
const stablePolls = config.stablePolls;
|
|
99508
99475
|
let placeholderId = "";
|
|
99509
99476
|
let done = false;
|
|
99477
|
+
let activeSessionId = session.id;
|
|
99510
99478
|
const timer = thinkingDelay > 0 ? setTimeout(async () => {
|
|
99511
99479
|
if (done) return;
|
|
99512
99480
|
try {
|
|
@@ -99514,7 +99482,7 @@ async function handleChat(ctx, deps) {
|
|
|
99514
99482
|
if (done) return;
|
|
99515
99483
|
if (res.ok && res.messageId) {
|
|
99516
99484
|
placeholderId = res.messageId;
|
|
99517
|
-
registerPending(
|
|
99485
|
+
registerPending(activeSessionId, { chatId, placeholderId, feishuClient });
|
|
99518
99486
|
}
|
|
99519
99487
|
} catch (err) {
|
|
99520
99488
|
log("warn", "\u53D1\u9001\u5360\u4F4D\u6D88\u606F\u5931\u8D25", {
|
|
@@ -99523,68 +99491,175 @@ async function handleChat(ctx, deps) {
|
|
|
99523
99491
|
});
|
|
99524
99492
|
}
|
|
99525
99493
|
}, thinkingDelay) : null;
|
|
99494
|
+
async function runAutoPromptLoop(activeId) {
|
|
99495
|
+
const { autoPrompt } = config;
|
|
99496
|
+
if (!autoPrompt.enabled || !shouldReply) return;
|
|
99497
|
+
const ac = new AbortController();
|
|
99498
|
+
activeAutoPrompts.set(sessionKey, ac);
|
|
99499
|
+
log("info", "\u542F\u52A8\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF", { sessionKey, maxIterations: autoPrompt.maxIterations });
|
|
99500
|
+
try {
|
|
99501
|
+
for (let i = 0; i < autoPrompt.maxIterations; i++) {
|
|
99502
|
+
await abortableSleep(autoPrompt.intervalSeconds * 1e3, ac.signal);
|
|
99503
|
+
log("info", "\u53D1\u9001\u81EA\u52A8\u63D0\u793A", { sessionKey, iteration: i + 1 });
|
|
99504
|
+
await client.session.prompt({
|
|
99505
|
+
path: { id: activeId },
|
|
99506
|
+
query,
|
|
99507
|
+
body: { parts: [{ type: "text", text: autoPrompt.message }] }
|
|
99508
|
+
});
|
|
99509
|
+
const text2 = await pollForResponse(client, activeId, { timeout, pollInterval, stablePolls, query, signal: ac.signal });
|
|
99510
|
+
if (text2) {
|
|
99511
|
+
log("info", "\u81EA\u52A8\u63D0\u793A\u54CD\u5E94", {
|
|
99512
|
+
sessionKey,
|
|
99513
|
+
iteration: i + 1,
|
|
99514
|
+
output: text2
|
|
99515
|
+
});
|
|
99516
|
+
await sendTextMessage(feishuClient, chatId, text2);
|
|
99517
|
+
}
|
|
99518
|
+
}
|
|
99519
|
+
log("info", "\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u7ED3\u675F\uFF08\u8FBE\u5230\u6700\u5927\u6B21\u6570\uFF09", { sessionKey });
|
|
99520
|
+
} catch (loopErr) {
|
|
99521
|
+
if (loopErr.name === "AbortError") {
|
|
99522
|
+
log("info", "\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u88AB\u4E2D\u65AD", { sessionKey });
|
|
99523
|
+
} else {
|
|
99524
|
+
log("error", "\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u5F02\u5E38", {
|
|
99525
|
+
sessionKey,
|
|
99526
|
+
error: loopErr instanceof Error ? loopErr.message : String(loopErr)
|
|
99527
|
+
});
|
|
99528
|
+
}
|
|
99529
|
+
} finally {
|
|
99530
|
+
activeAutoPrompts.delete(sessionKey);
|
|
99531
|
+
}
|
|
99532
|
+
}
|
|
99526
99533
|
try {
|
|
99527
99534
|
await client.session.prompt({
|
|
99528
99535
|
path: { id: session.id },
|
|
99529
99536
|
query,
|
|
99530
|
-
body:
|
|
99537
|
+
body: baseBody
|
|
99531
99538
|
});
|
|
99532
99539
|
const finalText = await pollForResponse(client, session.id, { timeout, pollInterval, stablePolls, query });
|
|
99540
|
+
log("info", "\u6A21\u578B\u54CD\u5E94\u5B8C\u6210", {
|
|
99541
|
+
sessionKey,
|
|
99542
|
+
sessionId: session.id,
|
|
99543
|
+
output: finalText || "(empty)"
|
|
99544
|
+
});
|
|
99533
99545
|
clearForkAttempts(sessionKey);
|
|
99534
99546
|
await replyOrUpdate(feishuClient, chatId, placeholderId, finalText || "\u26A0\uFE0F \u54CD\u5E94\u8D85\u65F6");
|
|
99535
|
-
|
|
99536
|
-
|
|
99537
|
-
|
|
99538
|
-
|
|
99539
|
-
|
|
99540
|
-
|
|
99541
|
-
|
|
99542
|
-
|
|
99543
|
-
|
|
99547
|
+
await runAutoPromptLoop(session.id);
|
|
99548
|
+
} catch (err) {
|
|
99549
|
+
await new Promise((r) => setTimeout(r, SSE_RACE_WAIT_MS));
|
|
99550
|
+
let sessionError = getSessionError(session.id);
|
|
99551
|
+
clearSessionError(session.id);
|
|
99552
|
+
if (!sessionError) {
|
|
99553
|
+
const thrownFields = extractErrorFields(err);
|
|
99554
|
+
if (isModelError(thrownFields)) {
|
|
99555
|
+
const thrownMsg = err instanceof Error ? err.message : String(err);
|
|
99556
|
+
sessionError = { message: thrownMsg, fields: thrownFields };
|
|
99557
|
+
}
|
|
99558
|
+
}
|
|
99559
|
+
if (sessionError && isModelError(sessionError.fields)) {
|
|
99560
|
+
const attempts = getForkAttempts(sessionKey);
|
|
99561
|
+
if (attempts < MAX_FORK_ATTEMPTS) {
|
|
99562
|
+
setForkAttempts(sessionKey, attempts + 1);
|
|
99563
|
+
try {
|
|
99564
|
+
invalidateCachedSession(session.id);
|
|
99565
|
+
const newSession = await forkOrCreateSession(client, session.id, sessionKey, directory, log);
|
|
99566
|
+
setCachedSession(sessionKey, newSession);
|
|
99567
|
+
activeSessionId = newSession.id;
|
|
99568
|
+
let modelOverride;
|
|
99569
|
+
try {
|
|
99570
|
+
modelOverride = await resolveLatestModel(client, sessionError.fields, directory);
|
|
99571
|
+
if (modelOverride) {
|
|
99572
|
+
log("info", "\u5DF2\u89E3\u6790\u964D\u7EA7\u6A21\u578B", {
|
|
99573
|
+
sessionKey,
|
|
99574
|
+
providerID: modelOverride.providerID,
|
|
99575
|
+
modelID: modelOverride.modelID
|
|
99576
|
+
});
|
|
99577
|
+
}
|
|
99578
|
+
} catch (modelErr) {
|
|
99579
|
+
log("warn", "\u89E3\u6790\u964D\u7EA7\u6A21\u578B\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u9ED8\u8BA4\u6A21\u578B", {
|
|
99580
|
+
sessionKey,
|
|
99581
|
+
error: modelErr instanceof Error ? modelErr.message : String(modelErr)
|
|
99582
|
+
});
|
|
99583
|
+
}
|
|
99584
|
+
unregisterPending(session.id);
|
|
99585
|
+
if (placeholderId) {
|
|
99586
|
+
registerPending(newSession.id, { chatId, placeholderId, feishuClient });
|
|
99587
|
+
}
|
|
99588
|
+
const retryBody = { ...baseBody, ...modelOverride ? { model: modelOverride } : {} };
|
|
99544
99589
|
await client.session.prompt({
|
|
99545
|
-
path: { id:
|
|
99590
|
+
path: { id: newSession.id },
|
|
99546
99591
|
query,
|
|
99547
|
-
body:
|
|
99592
|
+
body: retryBody
|
|
99548
99593
|
});
|
|
99549
|
-
const
|
|
99550
|
-
|
|
99551
|
-
|
|
99552
|
-
|
|
99553
|
-
|
|
99554
|
-
|
|
99555
|
-
|
|
99556
|
-
|
|
99557
|
-
log("info", "\
|
|
99558
|
-
|
|
99559
|
-
|
|
99594
|
+
const finalText = await pollForResponse(client, newSession.id, { timeout, pollInterval, stablePolls, query });
|
|
99595
|
+
log("info", "\u6062\u590D\u540E\u6A21\u578B\u54CD\u5E94\u5B8C\u6210", {
|
|
99596
|
+
sessionKey,
|
|
99597
|
+
newSessionId: newSession.id,
|
|
99598
|
+
output: finalText || "(empty)"
|
|
99599
|
+
});
|
|
99600
|
+
clearForkAttempts(sessionKey);
|
|
99601
|
+
await replyOrUpdate(feishuClient, chatId, placeholderId, finalText || "\u26A0\uFE0F \u54CD\u5E94\u8D85\u65F6");
|
|
99602
|
+
log("info", "\u6A21\u578B\u4E0D\u517C\u5BB9\u6062\u590D\u6210\u529F", {
|
|
99603
|
+
oldSessionId: session.id,
|
|
99604
|
+
newSessionId: newSession.id,
|
|
99560
99605
|
sessionKey,
|
|
99561
|
-
|
|
99606
|
+
forkAttempt: attempts + 1
|
|
99607
|
+
});
|
|
99608
|
+
await runAutoPromptLoop(newSession.id);
|
|
99609
|
+
return;
|
|
99610
|
+
} catch (recoveryErr) {
|
|
99611
|
+
const recoveryErrMsg = recoveryErr instanceof Error ? recoveryErr.message : String(recoveryErr);
|
|
99612
|
+
const newSessionError = activeSessionId !== session.id ? getSessionError(activeSessionId) : void 0;
|
|
99613
|
+
if (newSessionError) clearSessionError(activeSessionId);
|
|
99614
|
+
sessionError = newSessionError ?? { message: recoveryErrMsg, fields: [] };
|
|
99615
|
+
log("error", "\u6A21\u578B\u6062\u590D\u5931\u8D25", {
|
|
99616
|
+
sessionId: session.id,
|
|
99617
|
+
sessionKey,
|
|
99618
|
+
error: recoveryErrMsg
|
|
99562
99619
|
});
|
|
99563
99620
|
}
|
|
99564
|
-
}
|
|
99565
|
-
|
|
99621
|
+
} else {
|
|
99622
|
+
log("warn", "\u5DF2\u8FBE fork \u4E0A\u9650\uFF0C\u653E\u5F03\u6062\u590D", {
|
|
99623
|
+
sessionKey,
|
|
99624
|
+
attempts
|
|
99625
|
+
});
|
|
99566
99626
|
}
|
|
99567
99627
|
}
|
|
99568
|
-
} catch (err) {
|
|
99569
|
-
await new Promise((r) => setTimeout(r, SSE_RACE_WAIT_MS));
|
|
99570
|
-
const sessionError = getSessionError(session.id);
|
|
99571
|
-
clearSessionError(session.id);
|
|
99572
99628
|
const thrownError = err instanceof Error ? err.message : String(err);
|
|
99573
|
-
const errorMessage = sessionError || thrownError;
|
|
99629
|
+
const errorMessage = sessionError?.message || thrownError;
|
|
99574
99630
|
log("error", "\u5BF9\u8BDD\u5904\u7406\u5931\u8D25", {
|
|
99575
99631
|
sessionId: session.id,
|
|
99576
|
-
sessionKey
|
|
99632
|
+
sessionKey,
|
|
99577
99633
|
chatType,
|
|
99578
99634
|
error: thrownError,
|
|
99579
|
-
...sessionError ? { sessionError } : { sseRaceMiss: true }
|
|
99635
|
+
...sessionError ? { sessionError: sessionError.message } : { sseRaceMiss: true }
|
|
99580
99636
|
});
|
|
99581
99637
|
const msg = "\u274C " + errorMessage;
|
|
99582
99638
|
await replyOrUpdate(feishuClient, chatId, placeholderId, msg);
|
|
99583
99639
|
} finally {
|
|
99584
99640
|
done = true;
|
|
99585
99641
|
if (timer) clearTimeout(timer);
|
|
99586
|
-
unregisterPending(
|
|
99642
|
+
unregisterPending(activeSessionId);
|
|
99643
|
+
}
|
|
99644
|
+
}
|
|
99645
|
+
async function resolveLatestModel(client, errorFields, directory) {
|
|
99646
|
+
const pattern = /model not found:?\s*(\w[\w-]*)\//i;
|
|
99647
|
+
const rawProviderID = errorFields.map((f) => pattern.exec(f)?.[1]).find(Boolean);
|
|
99648
|
+
if (!rawProviderID) return void 0;
|
|
99649
|
+
const providerID = rawProviderID.toLowerCase();
|
|
99650
|
+
const query = directory ? { directory } : void 0;
|
|
99651
|
+
const { data } = await client.provider.list({ query });
|
|
99652
|
+
if (!data) return void 0;
|
|
99653
|
+
const defaultModelID = data.default?.[providerID];
|
|
99654
|
+
if (defaultModelID) {
|
|
99655
|
+
return { providerID, modelID: defaultModelID };
|
|
99587
99656
|
}
|
|
99657
|
+
const provider = data.all?.find((p) => p.id === providerID);
|
|
99658
|
+
if (!provider?.models) return void 0;
|
|
99659
|
+
const sortedModels = Object.values(provider.models).filter((m) => m.status !== "deprecated").sort((a, b) => b.release_date.localeCompare(a.release_date));
|
|
99660
|
+
if (sortedModels.length === 0) return void 0;
|
|
99661
|
+
const best = sortedModels.find((m) => m.tool_call) ?? sortedModels[0];
|
|
99662
|
+
return { providerID, modelID: best.id };
|
|
99588
99663
|
}
|
|
99589
99664
|
async function buildPromptParts(feishuClient, messageId, messageType, rawContent, textContent, chatType, senderId, log) {
|
|
99590
99665
|
if (messageType === "text") {
|