opencode-feishu 0.7.6 → 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 +268 -241
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -99176,130 +99176,11 @@ async function updateMessage(client, messageId, text2) {
|
|
|
99176
99176
|
}
|
|
99177
99177
|
}
|
|
99178
99178
|
|
|
99179
|
-
// src/session.ts
|
|
99180
|
-
var SESSION_KEY_PREFIX = "feishu";
|
|
99181
|
-
var TITLE_PREFIX = "Feishu";
|
|
99182
|
-
var sessionCache = /* @__PURE__ */ new Map();
|
|
99183
|
-
var sessionIdToKeyCache = /* @__PURE__ */ new Map();
|
|
99184
|
-
function setCachedSession(sessionKey, session) {
|
|
99185
|
-
const oldSession = sessionCache.get(sessionKey);
|
|
99186
|
-
if (oldSession && oldSession.id !== session.id) {
|
|
99187
|
-
sessionIdToKeyCache.delete(oldSession.id);
|
|
99188
|
-
}
|
|
99189
|
-
sessionCache.set(sessionKey, session);
|
|
99190
|
-
sessionIdToKeyCache.set(session.id, sessionKey);
|
|
99191
|
-
}
|
|
99192
|
-
function invalidateCachedSession(sessionId) {
|
|
99193
|
-
const sessionKey = sessionIdToKeyCache.get(sessionId);
|
|
99194
|
-
if (sessionKey) {
|
|
99195
|
-
sessionCache.delete(sessionKey);
|
|
99196
|
-
sessionIdToKeyCache.delete(sessionId);
|
|
99197
|
-
}
|
|
99198
|
-
return sessionKey;
|
|
99199
|
-
}
|
|
99200
|
-
function buildSessionKey(chatType, id) {
|
|
99201
|
-
return `${SESSION_KEY_PREFIX}-${chatType}-${id}`;
|
|
99202
|
-
}
|
|
99203
|
-
function generateSessionTitle(sessionKey) {
|
|
99204
|
-
return `${TITLE_PREFIX}-${sessionKey}-${Date.now()}`;
|
|
99205
|
-
}
|
|
99206
|
-
async function getOrCreateSession(client, sessionKey, directory) {
|
|
99207
|
-
const cached = sessionCache.get(sessionKey);
|
|
99208
|
-
if (cached) return cached;
|
|
99209
|
-
const titlePrefix = `${TITLE_PREFIX}-${sessionKey}-`;
|
|
99210
|
-
const query = directory ? { directory } : void 0;
|
|
99211
|
-
const { data: sessions } = await client.session.list({ query });
|
|
99212
|
-
if (Array.isArray(sessions)) {
|
|
99213
|
-
const candidates = sessions.filter(
|
|
99214
|
-
(s) => s.title && s.title.startsWith(titlePrefix)
|
|
99215
|
-
);
|
|
99216
|
-
if (candidates.length > 0) {
|
|
99217
|
-
candidates.sort((a, b) => {
|
|
99218
|
-
const ca = a.time?.created ?? 0;
|
|
99219
|
-
const cb = b.time?.created ?? 0;
|
|
99220
|
-
return cb - ca;
|
|
99221
|
-
});
|
|
99222
|
-
const best = candidates[0];
|
|
99223
|
-
if (best?.id) {
|
|
99224
|
-
const session2 = { id: best.id, title: best.title };
|
|
99225
|
-
setCachedSession(sessionKey, session2);
|
|
99226
|
-
return session2;
|
|
99227
|
-
}
|
|
99228
|
-
}
|
|
99229
|
-
}
|
|
99230
|
-
const title = generateSessionTitle(sessionKey);
|
|
99231
|
-
const createResp = await client.session.create({ query, body: { title } });
|
|
99232
|
-
if (!createResp?.data?.id) {
|
|
99233
|
-
const err = createResp?.error;
|
|
99234
|
-
throw new Error(
|
|
99235
|
-
`\u521B\u5EFA OpenCode \u4F1A\u8BDD\u5931\u8D25: ${err ? JSON.stringify(err) : "unknown"}`
|
|
99236
|
-
);
|
|
99237
|
-
}
|
|
99238
|
-
const session = { id: createResp.data.id, title: createResp.data.title };
|
|
99239
|
-
setCachedSession(sessionKey, session);
|
|
99240
|
-
return session;
|
|
99241
|
-
}
|
|
99242
|
-
async function forkSession(client, oldSessionId, sessionKey, directory) {
|
|
99243
|
-
const query = directory ? { directory } : void 0;
|
|
99244
|
-
const resp = await client.session.fork({
|
|
99245
|
-
path: { id: oldSessionId },
|
|
99246
|
-
query,
|
|
99247
|
-
body: {}
|
|
99248
|
-
});
|
|
99249
|
-
if (!resp?.data?.id) {
|
|
99250
|
-
const err = resp?.error;
|
|
99251
|
-
throw new Error(
|
|
99252
|
-
`Fork \u4F1A\u8BDD\u5931\u8D25: ${err ? JSON.stringify(err) : "unknown"}`
|
|
99253
|
-
);
|
|
99254
|
-
}
|
|
99255
|
-
const title = generateSessionTitle(sessionKey);
|
|
99256
|
-
const updateResp = await client.session.update({
|
|
99257
|
-
path: { id: resp.data.id },
|
|
99258
|
-
query,
|
|
99259
|
-
body: { title }
|
|
99260
|
-
});
|
|
99261
|
-
if (!updateResp?.data?.id) {
|
|
99262
|
-
const err = updateResp?.error;
|
|
99263
|
-
throw new Error(
|
|
99264
|
-
`\u66F4\u65B0 forked session \u6807\u9898\u5931\u8D25: ${err ? JSON.stringify(err) : "unknown"}`
|
|
99265
|
-
);
|
|
99266
|
-
}
|
|
99267
|
-
return { id: resp.data.id, title };
|
|
99268
|
-
}
|
|
99269
|
-
async function createFreshSession(client, sessionKey, directory) {
|
|
99270
|
-
const query = directory ? { directory } : void 0;
|
|
99271
|
-
const title = generateSessionTitle(sessionKey);
|
|
99272
|
-
const resp = await client.session.create({ query, body: { title } });
|
|
99273
|
-
if (!resp?.data?.id) {
|
|
99274
|
-
const err = resp?.error;
|
|
99275
|
-
throw new Error(
|
|
99276
|
-
`\u521B\u5EFA\u65B0\u4F1A\u8BDD\u5931\u8D25: ${err ? JSON.stringify(err) : "unknown"}`
|
|
99277
|
-
);
|
|
99278
|
-
}
|
|
99279
|
-
return { id: resp.data.id, title: resp.data.title };
|
|
99280
|
-
}
|
|
99281
|
-
async function forkOrCreateSession(client, oldSessionId, sessionKey, directory, log) {
|
|
99282
|
-
try {
|
|
99283
|
-
return await forkSession(client, oldSessionId, sessionKey, directory);
|
|
99284
|
-
} catch (forkErr) {
|
|
99285
|
-
log?.("warn", "Fork \u5931\u8D25\uFF0C\u56DE\u9000\u5230\u521B\u5EFA\u65B0\u4F1A\u8BDD", {
|
|
99286
|
-
oldSessionId,
|
|
99287
|
-
sessionKey,
|
|
99288
|
-
error: forkErr instanceof Error ? forkErr.message : String(forkErr)
|
|
99289
|
-
});
|
|
99290
|
-
return await createFreshSession(client, sessionKey, directory);
|
|
99291
|
-
}
|
|
99292
|
-
}
|
|
99293
|
-
|
|
99294
99179
|
// src/handler/event.ts
|
|
99295
|
-
function maskKey(sessionKey) {
|
|
99296
|
-
return sessionKey.replace(/-[^-]+$/, "-***");
|
|
99297
|
-
}
|
|
99298
99180
|
var pendingBySession = /* @__PURE__ */ new Map();
|
|
99299
99181
|
var sessionErrors = /* @__PURE__ */ new Map();
|
|
99300
99182
|
var sessionErrorTimeouts = /* @__PURE__ */ new Map();
|
|
99301
99183
|
var SESSION_ERROR_TTL_MS = 3e4;
|
|
99302
|
-
var modelOverrides = /* @__PURE__ */ new Map();
|
|
99303
99184
|
var forkAttempts = /* @__PURE__ */ new Map();
|
|
99304
99185
|
var forkAttemptTimeouts = /* @__PURE__ */ new Map();
|
|
99305
99186
|
var MAX_FORK_ATTEMPTS = 2;
|
|
@@ -99312,6 +99193,9 @@ function clearForkAttempts(sessionKey) {
|
|
|
99312
99193
|
forkAttemptTimeouts.delete(sessionKey);
|
|
99313
99194
|
}
|
|
99314
99195
|
}
|
|
99196
|
+
function getForkAttempts(sessionKey) {
|
|
99197
|
+
return forkAttempts.get(sessionKey) ?? 0;
|
|
99198
|
+
}
|
|
99315
99199
|
function setForkAttempts(sessionKey, count) {
|
|
99316
99200
|
forkAttempts.set(sessionKey, count);
|
|
99317
99201
|
const existing = forkAttemptTimeouts.get(sessionKey);
|
|
@@ -99322,12 +99206,6 @@ function setForkAttempts(sessionKey, count) {
|
|
|
99322
99206
|
}, FORK_ATTEMPTS_TTL_MS);
|
|
99323
99207
|
forkAttemptTimeouts.set(sessionKey, timeoutId);
|
|
99324
99208
|
}
|
|
99325
|
-
function getModelOverride(sessionKey) {
|
|
99326
|
-
return modelOverrides.get(sessionKey);
|
|
99327
|
-
}
|
|
99328
|
-
function clearModelOverride(sessionKey) {
|
|
99329
|
-
modelOverrides.delete(sessionKey);
|
|
99330
|
-
}
|
|
99331
99209
|
function getSessionError(sessionId) {
|
|
99332
99210
|
return sessionErrors.get(sessionId);
|
|
99333
99211
|
}
|
|
@@ -99339,12 +99217,12 @@ function clearSessionError(sessionId) {
|
|
|
99339
99217
|
}
|
|
99340
99218
|
sessionErrors.delete(sessionId);
|
|
99341
99219
|
}
|
|
99342
|
-
function setSessionError(sessionId,
|
|
99220
|
+
function setSessionError(sessionId, message, fields) {
|
|
99343
99221
|
const existing = sessionErrorTimeouts.get(sessionId);
|
|
99344
99222
|
if (existing) {
|
|
99345
99223
|
clearTimeout(existing);
|
|
99346
99224
|
}
|
|
99347
|
-
sessionErrors.set(sessionId,
|
|
99225
|
+
sessionErrors.set(sessionId, { message, fields });
|
|
99348
99226
|
const timeoutId = setTimeout(() => {
|
|
99349
99227
|
sessionErrors.delete(sessionId);
|
|
99350
99228
|
sessionErrorTimeouts.delete(sessionId);
|
|
@@ -99357,13 +99235,6 @@ function registerPending(sessionId, payload) {
|
|
|
99357
99235
|
function unregisterPending(sessionId) {
|
|
99358
99236
|
pendingBySession.delete(sessionId);
|
|
99359
99237
|
}
|
|
99360
|
-
function migratePending(oldSessionId, newSessionId) {
|
|
99361
|
-
const payload = pendingBySession.get(oldSessionId);
|
|
99362
|
-
if (payload) {
|
|
99363
|
-
pendingBySession.delete(oldSessionId);
|
|
99364
|
-
pendingBySession.set(newSessionId, payload);
|
|
99365
|
-
}
|
|
99366
|
-
}
|
|
99367
99238
|
function extractErrorFields(error) {
|
|
99368
99239
|
if (typeof error === "string") return [error];
|
|
99369
99240
|
if (error && typeof error === "object") {
|
|
@@ -99377,14 +99248,12 @@ function extractErrorFields(error) {
|
|
|
99377
99248
|
}
|
|
99378
99249
|
return [String(error)];
|
|
99379
99250
|
}
|
|
99380
|
-
function isModelError(
|
|
99251
|
+
function isModelError(fields) {
|
|
99381
99252
|
const check = (s) => {
|
|
99382
99253
|
const l = s.toLowerCase();
|
|
99383
99254
|
return l.includes("model not found") || l.includes("modelnotfound");
|
|
99384
99255
|
};
|
|
99385
|
-
|
|
99386
|
-
if (rawError) return extractErrorFields(rawError).some(check);
|
|
99387
|
-
return false;
|
|
99256
|
+
return fields.some(check);
|
|
99388
99257
|
}
|
|
99389
99258
|
async function handleEvent(event, deps) {
|
|
99390
99259
|
switch (event.type) {
|
|
@@ -99426,77 +99295,13 @@ async function handleEvent(event, deps) {
|
|
|
99426
99295
|
} else {
|
|
99427
99296
|
errMsg = String(error);
|
|
99428
99297
|
}
|
|
99298
|
+
const fields = extractErrorFields(error);
|
|
99429
99299
|
deps.log("warn", "\u6536\u5230 session.error \u4E8B\u4EF6", { sessionId, errMsg });
|
|
99430
|
-
setSessionError(sessionId, errMsg);
|
|
99431
|
-
if (isModelError(errMsg, props.error)) {
|
|
99432
|
-
const sessionKey = invalidateCachedSession(sessionId);
|
|
99433
|
-
if (sessionKey) {
|
|
99434
|
-
const attempts = forkAttempts.get(sessionKey) ?? 0;
|
|
99435
|
-
if (attempts >= MAX_FORK_ATTEMPTS) {
|
|
99436
|
-
deps.log("warn", "\u5DF2\u8FBE fork \u4E0A\u9650\uFF0C\u653E\u5F03\u6062\u590D", { sessionKey: maskKey(sessionKey), attempts });
|
|
99437
|
-
} else {
|
|
99438
|
-
setForkAttempts(sessionKey, attempts + 1);
|
|
99439
|
-
try {
|
|
99440
|
-
const newSession = await forkOrCreateSession(deps.client, sessionId, sessionKey, deps.directory, deps.log);
|
|
99441
|
-
setCachedSession(sessionKey, newSession);
|
|
99442
|
-
modelOverrides.delete(sessionKey);
|
|
99443
|
-
try {
|
|
99444
|
-
const fallbackModel = await resolveLatestModel(deps.client, props.error ?? errMsg, deps.directory);
|
|
99445
|
-
if (fallbackModel) {
|
|
99446
|
-
modelOverrides.set(sessionKey, fallbackModel);
|
|
99447
|
-
deps.log("info", "\u5DF2\u89E3\u6790\u964D\u7EA7\u6A21\u578B", {
|
|
99448
|
-
sessionKey: maskKey(sessionKey),
|
|
99449
|
-
providerID: fallbackModel.providerID,
|
|
99450
|
-
modelID: fallbackModel.modelID
|
|
99451
|
-
});
|
|
99452
|
-
}
|
|
99453
|
-
} catch (modelErr) {
|
|
99454
|
-
deps.log("warn", "\u89E3\u6790\u964D\u7EA7\u6A21\u578B\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u9ED8\u8BA4\u6A21\u578B", {
|
|
99455
|
-
sessionKey: maskKey(sessionKey),
|
|
99456
|
-
error: modelErr instanceof Error ? modelErr.message : String(modelErr)
|
|
99457
|
-
});
|
|
99458
|
-
}
|
|
99459
|
-
deps.log("warn", "\u6A21\u578B\u4E0D\u517C\u5BB9\uFF0C\u5DF2\u6062\u590D\u4F1A\u8BDD", {
|
|
99460
|
-
oldSessionId: sessionId,
|
|
99461
|
-
newSessionId: newSession.id,
|
|
99462
|
-
sessionKey: maskKey(sessionKey),
|
|
99463
|
-
forkAttempt: attempts + 1
|
|
99464
|
-
});
|
|
99465
|
-
migratePending(sessionId, newSession.id);
|
|
99466
|
-
} catch (recoverErr) {
|
|
99467
|
-
deps.log("error", "\u4F1A\u8BDD\u6062\u590D\u5931\u8D25", {
|
|
99468
|
-
sessionId,
|
|
99469
|
-
sessionKey: maskKey(sessionKey),
|
|
99470
|
-
error: recoverErr instanceof Error ? recoverErr.message : String(recoverErr)
|
|
99471
|
-
});
|
|
99472
|
-
}
|
|
99473
|
-
}
|
|
99474
|
-
}
|
|
99475
|
-
}
|
|
99300
|
+
setSessionError(sessionId, errMsg, fields);
|
|
99476
99301
|
break;
|
|
99477
99302
|
}
|
|
99478
99303
|
}
|
|
99479
99304
|
}
|
|
99480
|
-
async function resolveLatestModel(client, rawError, directory) {
|
|
99481
|
-
const pattern = /model not found:?\s*(\w[\w-]*)\//i;
|
|
99482
|
-
const fields = extractErrorFields(rawError);
|
|
99483
|
-
const rawProviderID = fields.map((f) => pattern.exec(f)?.[1]).find(Boolean);
|
|
99484
|
-
if (!rawProviderID) return void 0;
|
|
99485
|
-
const providerID = rawProviderID.toLowerCase();
|
|
99486
|
-
const query = directory ? { directory } : void 0;
|
|
99487
|
-
const { data } = await client.provider.list({ query });
|
|
99488
|
-
if (!data) return void 0;
|
|
99489
|
-
const defaultModelID = data.default?.[providerID];
|
|
99490
|
-
if (defaultModelID) {
|
|
99491
|
-
return { providerID, modelID: defaultModelID };
|
|
99492
|
-
}
|
|
99493
|
-
const provider = data.all?.find((p) => p.id === providerID);
|
|
99494
|
-
if (!provider?.models) return void 0;
|
|
99495
|
-
const sortedModels = Object.values(provider.models).filter((m) => m.status !== "deprecated").sort((a, b) => b.release_date.localeCompare(a.release_date));
|
|
99496
|
-
if (sortedModels.length === 0) return void 0;
|
|
99497
|
-
const best = sortedModels.find((m) => m.tool_call) ?? sortedModels[0];
|
|
99498
|
-
return { providerID, modelID: best.id };
|
|
99499
|
-
}
|
|
99500
99305
|
function extractPartText(part) {
|
|
99501
99306
|
if (part.type === "text") return part.text ?? "";
|
|
99502
99307
|
if (part.type === "reasoning" && part.text) return `\u{1F914} \u601D\u8003: ${part.text}
|
|
@@ -99505,6 +99310,121 @@ function extractPartText(part) {
|
|
|
99505
99310
|
return "";
|
|
99506
99311
|
}
|
|
99507
99312
|
|
|
99313
|
+
// src/session.ts
|
|
99314
|
+
var SESSION_KEY_PREFIX = "feishu";
|
|
99315
|
+
var TITLE_PREFIX = "Feishu";
|
|
99316
|
+
var sessionCache = /* @__PURE__ */ new Map();
|
|
99317
|
+
var sessionIdToKeyCache = /* @__PURE__ */ new Map();
|
|
99318
|
+
function setCachedSession(sessionKey, session) {
|
|
99319
|
+
const oldSession = sessionCache.get(sessionKey);
|
|
99320
|
+
if (oldSession && oldSession.id !== session.id) {
|
|
99321
|
+
sessionIdToKeyCache.delete(oldSession.id);
|
|
99322
|
+
}
|
|
99323
|
+
sessionCache.set(sessionKey, session);
|
|
99324
|
+
sessionIdToKeyCache.set(session.id, sessionKey);
|
|
99325
|
+
}
|
|
99326
|
+
function invalidateCachedSession(sessionId) {
|
|
99327
|
+
const sessionKey = sessionIdToKeyCache.get(sessionId);
|
|
99328
|
+
if (sessionKey) {
|
|
99329
|
+
sessionCache.delete(sessionKey);
|
|
99330
|
+
sessionIdToKeyCache.delete(sessionId);
|
|
99331
|
+
}
|
|
99332
|
+
return sessionKey;
|
|
99333
|
+
}
|
|
99334
|
+
function buildSessionKey(chatType, id) {
|
|
99335
|
+
return `${SESSION_KEY_PREFIX}-${chatType}-${id}`;
|
|
99336
|
+
}
|
|
99337
|
+
function generateSessionTitle(sessionKey) {
|
|
99338
|
+
return `${TITLE_PREFIX}-${sessionKey}-${Date.now()}`;
|
|
99339
|
+
}
|
|
99340
|
+
async function getOrCreateSession(client, sessionKey, directory) {
|
|
99341
|
+
const cached = sessionCache.get(sessionKey);
|
|
99342
|
+
if (cached) return cached;
|
|
99343
|
+
const titlePrefix = `${TITLE_PREFIX}-${sessionKey}-`;
|
|
99344
|
+
const query = directory ? { directory } : void 0;
|
|
99345
|
+
const { data: sessions } = await client.session.list({ query });
|
|
99346
|
+
if (Array.isArray(sessions)) {
|
|
99347
|
+
const candidates = sessions.filter(
|
|
99348
|
+
(s) => s.title && s.title.startsWith(titlePrefix)
|
|
99349
|
+
);
|
|
99350
|
+
if (candidates.length > 0) {
|
|
99351
|
+
candidates.sort((a, b) => {
|
|
99352
|
+
const ca = a.time?.created ?? 0;
|
|
99353
|
+
const cb = b.time?.created ?? 0;
|
|
99354
|
+
return cb - ca;
|
|
99355
|
+
});
|
|
99356
|
+
const best = candidates[0];
|
|
99357
|
+
if (best?.id) {
|
|
99358
|
+
const session2 = { id: best.id, title: best.title };
|
|
99359
|
+
setCachedSession(sessionKey, session2);
|
|
99360
|
+
return session2;
|
|
99361
|
+
}
|
|
99362
|
+
}
|
|
99363
|
+
}
|
|
99364
|
+
const title = generateSessionTitle(sessionKey);
|
|
99365
|
+
const createResp = await client.session.create({ query, body: { title } });
|
|
99366
|
+
if (!createResp?.data?.id) {
|
|
99367
|
+
const err = createResp?.error;
|
|
99368
|
+
throw new Error(
|
|
99369
|
+
`\u521B\u5EFA OpenCode \u4F1A\u8BDD\u5931\u8D25: ${err ? JSON.stringify(err) : "unknown"}`
|
|
99370
|
+
);
|
|
99371
|
+
}
|
|
99372
|
+
const session = { id: createResp.data.id, title: createResp.data.title };
|
|
99373
|
+
setCachedSession(sessionKey, session);
|
|
99374
|
+
return session;
|
|
99375
|
+
}
|
|
99376
|
+
async function forkSession(client, oldSessionId, sessionKey, directory) {
|
|
99377
|
+
const query = directory ? { directory } : void 0;
|
|
99378
|
+
const resp = await client.session.fork({
|
|
99379
|
+
path: { id: oldSessionId },
|
|
99380
|
+
query,
|
|
99381
|
+
body: {}
|
|
99382
|
+
});
|
|
99383
|
+
if (!resp?.data?.id) {
|
|
99384
|
+
const err = resp?.error;
|
|
99385
|
+
throw new Error(
|
|
99386
|
+
`Fork \u4F1A\u8BDD\u5931\u8D25: ${err ? JSON.stringify(err) : "unknown"}`
|
|
99387
|
+
);
|
|
99388
|
+
}
|
|
99389
|
+
const title = generateSessionTitle(sessionKey);
|
|
99390
|
+
const updateResp = await client.session.update({
|
|
99391
|
+
path: { id: resp.data.id },
|
|
99392
|
+
query,
|
|
99393
|
+
body: { title }
|
|
99394
|
+
});
|
|
99395
|
+
if (!updateResp?.data?.id) {
|
|
99396
|
+
const err = updateResp?.error;
|
|
99397
|
+
throw new Error(
|
|
99398
|
+
`\u66F4\u65B0 forked session \u6807\u9898\u5931\u8D25: ${err ? JSON.stringify(err) : "unknown"}`
|
|
99399
|
+
);
|
|
99400
|
+
}
|
|
99401
|
+
return { id: resp.data.id, title };
|
|
99402
|
+
}
|
|
99403
|
+
async function createFreshSession(client, sessionKey, directory) {
|
|
99404
|
+
const query = directory ? { directory } : void 0;
|
|
99405
|
+
const title = generateSessionTitle(sessionKey);
|
|
99406
|
+
const resp = await client.session.create({ query, body: { title } });
|
|
99407
|
+
if (!resp?.data?.id) {
|
|
99408
|
+
const err = resp?.error;
|
|
99409
|
+
throw new Error(
|
|
99410
|
+
`\u521B\u5EFA\u65B0\u4F1A\u8BDD\u5931\u8D25: ${err ? JSON.stringify(err) : "unknown"}`
|
|
99411
|
+
);
|
|
99412
|
+
}
|
|
99413
|
+
return { id: resp.data.id, title: resp.data.title };
|
|
99414
|
+
}
|
|
99415
|
+
async function forkOrCreateSession(client, oldSessionId, sessionKey, directory, log) {
|
|
99416
|
+
try {
|
|
99417
|
+
return await forkSession(client, oldSessionId, sessionKey, directory);
|
|
99418
|
+
} catch (forkErr) {
|
|
99419
|
+
log?.("warn", "Fork \u5931\u8D25\uFF0C\u56DE\u9000\u5230\u521B\u5EFA\u65B0\u4F1A\u8BDD", {
|
|
99420
|
+
oldSessionId,
|
|
99421
|
+
sessionKey,
|
|
99422
|
+
error: forkErr instanceof Error ? forkErr.message : String(forkErr)
|
|
99423
|
+
});
|
|
99424
|
+
return await createFreshSession(client, sessionKey, directory);
|
|
99425
|
+
}
|
|
99426
|
+
}
|
|
99427
|
+
|
|
99508
99428
|
// src/handler/chat.ts
|
|
99509
99429
|
var SSE_RACE_WAIT_MS = 100;
|
|
99510
99430
|
var activeAutoPrompts = /* @__PURE__ */ new Map();
|
|
@@ -99530,10 +99450,10 @@ async function handleChat(ctx, deps) {
|
|
|
99530
99450
|
senderId,
|
|
99531
99451
|
messageType,
|
|
99532
99452
|
shouldReply,
|
|
99453
|
+
content,
|
|
99533
99454
|
parts
|
|
99534
99455
|
});
|
|
99535
|
-
const
|
|
99536
|
-
const baseBody = { parts, ...modelOverride ? { model: modelOverride } : {} };
|
|
99456
|
+
const baseBody = { parts };
|
|
99537
99457
|
if (!shouldReply) {
|
|
99538
99458
|
try {
|
|
99539
99459
|
await client.session.prompt({
|
|
@@ -99554,6 +99474,7 @@ async function handleChat(ctx, deps) {
|
|
|
99554
99474
|
const stablePolls = config.stablePolls;
|
|
99555
99475
|
let placeholderId = "";
|
|
99556
99476
|
let done = false;
|
|
99477
|
+
let activeSessionId = session.id;
|
|
99557
99478
|
const timer = thinkingDelay > 0 ? setTimeout(async () => {
|
|
99558
99479
|
if (done) return;
|
|
99559
99480
|
try {
|
|
@@ -99561,7 +99482,7 @@ async function handleChat(ctx, deps) {
|
|
|
99561
99482
|
if (done) return;
|
|
99562
99483
|
if (res.ok && res.messageId) {
|
|
99563
99484
|
placeholderId = res.messageId;
|
|
99564
|
-
registerPending(
|
|
99485
|
+
registerPending(activeSessionId, { chatId, placeholderId, feishuClient });
|
|
99565
99486
|
}
|
|
99566
99487
|
} catch (err) {
|
|
99567
99488
|
log("warn", "\u53D1\u9001\u5360\u4F4D\u6D88\u606F\u5931\u8D25", {
|
|
@@ -99570,6 +99491,45 @@ async function handleChat(ctx, deps) {
|
|
|
99570
99491
|
});
|
|
99571
99492
|
}
|
|
99572
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
|
+
}
|
|
99573
99533
|
try {
|
|
99574
99534
|
await client.session.prompt({
|
|
99575
99535
|
path: { id: session.id },
|
|
@@ -99577,62 +99537,129 @@ async function handleChat(ctx, deps) {
|
|
|
99577
99537
|
body: baseBody
|
|
99578
99538
|
});
|
|
99579
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
|
+
});
|
|
99580
99545
|
clearForkAttempts(sessionKey);
|
|
99581
|
-
clearModelOverride(sessionKey);
|
|
99582
99546
|
await replyOrUpdate(feishuClient, chatId, placeholderId, finalText || "\u26A0\uFE0F \u54CD\u5E94\u8D85\u65F6");
|
|
99583
|
-
|
|
99584
|
-
|
|
99585
|
-
|
|
99586
|
-
|
|
99587
|
-
|
|
99588
|
-
|
|
99589
|
-
|
|
99590
|
-
|
|
99591
|
-
|
|
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 } : {} };
|
|
99592
99589
|
await client.session.prompt({
|
|
99593
|
-
path: { id:
|
|
99590
|
+
path: { id: newSession.id },
|
|
99594
99591
|
query,
|
|
99595
|
-
body:
|
|
99592
|
+
body: retryBody
|
|
99596
99593
|
});
|
|
99597
|
-
const
|
|
99598
|
-
|
|
99599
|
-
|
|
99600
|
-
|
|
99601
|
-
|
|
99602
|
-
|
|
99603
|
-
|
|
99604
|
-
|
|
99605
|
-
log("info", "\
|
|
99606
|
-
|
|
99607
|
-
|
|
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,
|
|
99605
|
+
sessionKey,
|
|
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,
|
|
99608
99617
|
sessionKey,
|
|
99609
|
-
error:
|
|
99618
|
+
error: recoveryErrMsg
|
|
99610
99619
|
});
|
|
99611
99620
|
}
|
|
99612
|
-
}
|
|
99613
|
-
|
|
99621
|
+
} else {
|
|
99622
|
+
log("warn", "\u5DF2\u8FBE fork \u4E0A\u9650\uFF0C\u653E\u5F03\u6062\u590D", {
|
|
99623
|
+
sessionKey,
|
|
99624
|
+
attempts
|
|
99625
|
+
});
|
|
99614
99626
|
}
|
|
99615
99627
|
}
|
|
99616
|
-
} catch (err) {
|
|
99617
|
-
await new Promise((r) => setTimeout(r, SSE_RACE_WAIT_MS));
|
|
99618
|
-
const sessionError = getSessionError(session.id);
|
|
99619
|
-
clearSessionError(session.id);
|
|
99620
99628
|
const thrownError = err instanceof Error ? err.message : String(err);
|
|
99621
|
-
const errorMessage = sessionError || thrownError;
|
|
99629
|
+
const errorMessage = sessionError?.message || thrownError;
|
|
99622
99630
|
log("error", "\u5BF9\u8BDD\u5904\u7406\u5931\u8D25", {
|
|
99623
99631
|
sessionId: session.id,
|
|
99624
|
-
sessionKey
|
|
99632
|
+
sessionKey,
|
|
99625
99633
|
chatType,
|
|
99626
99634
|
error: thrownError,
|
|
99627
|
-
...sessionError ? { sessionError } : { sseRaceMiss: true }
|
|
99635
|
+
...sessionError ? { sessionError: sessionError.message } : { sseRaceMiss: true }
|
|
99628
99636
|
});
|
|
99629
99637
|
const msg = "\u274C " + errorMessage;
|
|
99630
99638
|
await replyOrUpdate(feishuClient, chatId, placeholderId, msg);
|
|
99631
99639
|
} finally {
|
|
99632
99640
|
done = true;
|
|
99633
99641
|
if (timer) clearTimeout(timer);
|
|
99634
|
-
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 };
|
|
99635
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 };
|
|
99636
99663
|
}
|
|
99637
99664
|
async function buildPromptParts(feishuClient, messageId, messageType, rawContent, textContent, chatType, senderId, log) {
|
|
99638
99665
|
if (messageType === "text") {
|