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 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: { parts, noReply: true }
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(session.id, { chatId, placeholderId, feishuClient });
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: { parts }
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
- const { autoPrompt } = config;
99536
- if (autoPrompt.enabled && shouldReply) {
99537
- const ac = new AbortController();
99538
- activeAutoPrompts.set(sessionKey, ac);
99539
- log("info", "\u542F\u52A8\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF", { sessionKey, maxIterations: autoPrompt.maxIterations });
99540
- try {
99541
- for (let i = 0; i < autoPrompt.maxIterations; i++) {
99542
- await abortableSleep(autoPrompt.intervalSeconds * 1e3, ac.signal);
99543
- log("info", "\u53D1\u9001\u81EA\u52A8\u63D0\u793A", { sessionKey, iteration: i + 1 });
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: session.id },
99590
+ path: { id: newSession.id },
99546
99591
  query,
99547
- body: { parts: [{ type: "text", text: autoPrompt.message }] }
99592
+ body: retryBody
99548
99593
  });
99549
- const text2 = await pollForResponse(client, session.id, { timeout, pollInterval, stablePolls, query, signal: ac.signal });
99550
- if (text2) {
99551
- await sendTextMessage(feishuClient, chatId, text2);
99552
- }
99553
- }
99554
- log("info", "\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u7ED3\u675F\uFF08\u8FBE\u5230\u6700\u5927\u6B21\u6570\uFF09", { sessionKey });
99555
- } catch (err) {
99556
- if (err.name === "AbortError") {
99557
- log("info", "\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u88AB\u4E2D\u65AD", { sessionKey });
99558
- } else {
99559
- log("error", "\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u5F02\u5E38", {
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
- error: err instanceof Error ? err.message : String(err)
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
- } finally {
99565
- activeAutoPrompts.delete(sessionKey);
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: sessionKey.replace(/-[^-]+$/, "-***"),
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(session.id);
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") {