opencode-feishu 0.7.6 → 0.7.10
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 +215 -260
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -99176,157 +99176,35 @@ 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
|
|
99303
|
-
var
|
|
99304
|
-
var
|
|
99305
|
-
var
|
|
99306
|
-
|
|
99307
|
-
|
|
99308
|
-
|
|
99309
|
-
const timer = forkAttemptTimeouts.get(sessionKey);
|
|
99184
|
+
var retryAttempts = /* @__PURE__ */ new Map();
|
|
99185
|
+
var retryAttemptTimeouts = /* @__PURE__ */ new Map();
|
|
99186
|
+
var MAX_RETRY_ATTEMPTS = 2;
|
|
99187
|
+
var RETRY_ATTEMPTS_TTL_MS = 36e5;
|
|
99188
|
+
function clearRetryAttempts(sessionKey) {
|
|
99189
|
+
retryAttempts.delete(sessionKey);
|
|
99190
|
+
const timer = retryAttemptTimeouts.get(sessionKey);
|
|
99310
99191
|
if (timer) {
|
|
99311
99192
|
clearTimeout(timer);
|
|
99312
|
-
|
|
99193
|
+
retryAttemptTimeouts.delete(sessionKey);
|
|
99313
99194
|
}
|
|
99314
99195
|
}
|
|
99315
|
-
function
|
|
99316
|
-
|
|
99317
|
-
|
|
99196
|
+
function getRetryAttempts(sessionKey) {
|
|
99197
|
+
return retryAttempts.get(sessionKey) ?? 0;
|
|
99198
|
+
}
|
|
99199
|
+
function setRetryAttempts(sessionKey, count) {
|
|
99200
|
+
retryAttempts.set(sessionKey, count);
|
|
99201
|
+
const existing = retryAttemptTimeouts.get(sessionKey);
|
|
99318
99202
|
if (existing) clearTimeout(existing);
|
|
99319
99203
|
const timeoutId = setTimeout(() => {
|
|
99320
|
-
|
|
99321
|
-
|
|
99322
|
-
},
|
|
99323
|
-
|
|
99324
|
-
}
|
|
99325
|
-
function getModelOverride(sessionKey) {
|
|
99326
|
-
return modelOverrides.get(sessionKey);
|
|
99327
|
-
}
|
|
99328
|
-
function clearModelOverride(sessionKey) {
|
|
99329
|
-
modelOverrides.delete(sessionKey);
|
|
99204
|
+
retryAttempts.delete(sessionKey);
|
|
99205
|
+
retryAttemptTimeouts.delete(sessionKey);
|
|
99206
|
+
}, RETRY_ATTEMPTS_TTL_MS);
|
|
99207
|
+
retryAttemptTimeouts.set(sessionKey, timeoutId);
|
|
99330
99208
|
}
|
|
99331
99209
|
function getSessionError(sessionId) {
|
|
99332
99210
|
return sessionErrors.get(sessionId);
|
|
@@ -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,56 @@ 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
|
+
function setCachedSession(sessionKey, session) {
|
|
99318
|
+
sessionCache.set(sessionKey, session);
|
|
99319
|
+
}
|
|
99320
|
+
function buildSessionKey(chatType, id) {
|
|
99321
|
+
return `${SESSION_KEY_PREFIX}-${chatType}-${id}`;
|
|
99322
|
+
}
|
|
99323
|
+
function generateSessionTitle(sessionKey) {
|
|
99324
|
+
return `${TITLE_PREFIX}-${sessionKey}-${Date.now()}`;
|
|
99325
|
+
}
|
|
99326
|
+
async function getOrCreateSession(client, sessionKey, directory) {
|
|
99327
|
+
const cached = sessionCache.get(sessionKey);
|
|
99328
|
+
if (cached) return cached;
|
|
99329
|
+
const titlePrefix = `${TITLE_PREFIX}-${sessionKey}-`;
|
|
99330
|
+
const query = directory ? { directory } : void 0;
|
|
99331
|
+
const { data: sessions } = await client.session.list({ query });
|
|
99332
|
+
if (Array.isArray(sessions)) {
|
|
99333
|
+
const candidates = sessions.filter(
|
|
99334
|
+
(s) => s.title && s.title.startsWith(titlePrefix)
|
|
99335
|
+
);
|
|
99336
|
+
if (candidates.length > 0) {
|
|
99337
|
+
candidates.sort((a, b) => {
|
|
99338
|
+
const ca = a.time?.created ?? 0;
|
|
99339
|
+
const cb = b.time?.created ?? 0;
|
|
99340
|
+
return cb - ca;
|
|
99341
|
+
});
|
|
99342
|
+
const best = candidates[0];
|
|
99343
|
+
if (best?.id) {
|
|
99344
|
+
const session2 = { id: best.id, title: best.title };
|
|
99345
|
+
setCachedSession(sessionKey, session2);
|
|
99346
|
+
return session2;
|
|
99347
|
+
}
|
|
99348
|
+
}
|
|
99349
|
+
}
|
|
99350
|
+
const title = generateSessionTitle(sessionKey);
|
|
99351
|
+
const createResp = await client.session.create({ query, body: { title } });
|
|
99352
|
+
if (!createResp?.data?.id) {
|
|
99353
|
+
const err = createResp?.error;
|
|
99354
|
+
throw new Error(
|
|
99355
|
+
`\u521B\u5EFA OpenCode \u4F1A\u8BDD\u5931\u8D25: ${err ? JSON.stringify(err) : "unknown"}`
|
|
99356
|
+
);
|
|
99357
|
+
}
|
|
99358
|
+
const session = { id: createResp.data.id, title: createResp.data.title };
|
|
99359
|
+
setCachedSession(sessionKey, session);
|
|
99360
|
+
return session;
|
|
99361
|
+
}
|
|
99362
|
+
|
|
99508
99363
|
// src/handler/chat.ts
|
|
99509
99364
|
var SSE_RACE_WAIT_MS = 100;
|
|
99510
99365
|
var activeAutoPrompts = /* @__PURE__ */ new Map();
|
|
@@ -99530,10 +99385,10 @@ async function handleChat(ctx, deps) {
|
|
|
99530
99385
|
senderId,
|
|
99531
99386
|
messageType,
|
|
99532
99387
|
shouldReply,
|
|
99388
|
+
content,
|
|
99533
99389
|
parts
|
|
99534
99390
|
});
|
|
99535
|
-
const
|
|
99536
|
-
const baseBody = { parts, ...modelOverride ? { model: modelOverride } : {} };
|
|
99391
|
+
const baseBody = { parts };
|
|
99537
99392
|
if (!shouldReply) {
|
|
99538
99393
|
try {
|
|
99539
99394
|
await client.session.prompt({
|
|
@@ -99554,6 +99409,7 @@ async function handleChat(ctx, deps) {
|
|
|
99554
99409
|
const stablePolls = config.stablePolls;
|
|
99555
99410
|
let placeholderId = "";
|
|
99556
99411
|
let done = false;
|
|
99412
|
+
let activeSessionId = session.id;
|
|
99557
99413
|
const timer = thinkingDelay > 0 ? setTimeout(async () => {
|
|
99558
99414
|
if (done) return;
|
|
99559
99415
|
try {
|
|
@@ -99561,7 +99417,7 @@ async function handleChat(ctx, deps) {
|
|
|
99561
99417
|
if (done) return;
|
|
99562
99418
|
if (res.ok && res.messageId) {
|
|
99563
99419
|
placeholderId = res.messageId;
|
|
99564
|
-
registerPending(
|
|
99420
|
+
registerPending(activeSessionId, { chatId, placeholderId, feishuClient });
|
|
99565
99421
|
}
|
|
99566
99422
|
} catch (err) {
|
|
99567
99423
|
log("warn", "\u53D1\u9001\u5360\u4F4D\u6D88\u606F\u5931\u8D25", {
|
|
@@ -99570,6 +99426,45 @@ async function handleChat(ctx, deps) {
|
|
|
99570
99426
|
});
|
|
99571
99427
|
}
|
|
99572
99428
|
}, thinkingDelay) : null;
|
|
99429
|
+
async function runAutoPromptLoop(activeId) {
|
|
99430
|
+
const { autoPrompt } = config;
|
|
99431
|
+
if (!autoPrompt.enabled || !shouldReply) return;
|
|
99432
|
+
const ac = new AbortController();
|
|
99433
|
+
activeAutoPrompts.set(sessionKey, ac);
|
|
99434
|
+
log("info", "\u542F\u52A8\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF", { sessionKey, maxIterations: autoPrompt.maxIterations });
|
|
99435
|
+
try {
|
|
99436
|
+
for (let i = 0; i < autoPrompt.maxIterations; i++) {
|
|
99437
|
+
await abortableSleep(autoPrompt.intervalSeconds * 1e3, ac.signal);
|
|
99438
|
+
log("info", "\u53D1\u9001\u81EA\u52A8\u63D0\u793A", { sessionKey, iteration: i + 1 });
|
|
99439
|
+
await client.session.prompt({
|
|
99440
|
+
path: { id: activeId },
|
|
99441
|
+
query,
|
|
99442
|
+
body: { parts: [{ type: "text", text: autoPrompt.message }] }
|
|
99443
|
+
});
|
|
99444
|
+
const text2 = await pollForResponse(client, activeId, { timeout, pollInterval, stablePolls, query, signal: ac.signal });
|
|
99445
|
+
if (text2) {
|
|
99446
|
+
log("info", "\u81EA\u52A8\u63D0\u793A\u54CD\u5E94", {
|
|
99447
|
+
sessionKey,
|
|
99448
|
+
iteration: i + 1,
|
|
99449
|
+
output: text2
|
|
99450
|
+
});
|
|
99451
|
+
await sendTextMessage(feishuClient, chatId, text2);
|
|
99452
|
+
}
|
|
99453
|
+
}
|
|
99454
|
+
log("info", "\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u7ED3\u675F\uFF08\u8FBE\u5230\u6700\u5927\u6B21\u6570\uFF09", { sessionKey });
|
|
99455
|
+
} catch (loopErr) {
|
|
99456
|
+
if (loopErr.name === "AbortError") {
|
|
99457
|
+
log("info", "\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u88AB\u4E2D\u65AD", { sessionKey });
|
|
99458
|
+
} else {
|
|
99459
|
+
log("error", "\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u5F02\u5E38", {
|
|
99460
|
+
sessionKey,
|
|
99461
|
+
error: loopErr instanceof Error ? loopErr.message : String(loopErr)
|
|
99462
|
+
});
|
|
99463
|
+
}
|
|
99464
|
+
} finally {
|
|
99465
|
+
activeAutoPrompts.delete(sessionKey);
|
|
99466
|
+
}
|
|
99467
|
+
}
|
|
99573
99468
|
try {
|
|
99574
99469
|
await client.session.prompt({
|
|
99575
99470
|
path: { id: session.id },
|
|
@@ -99577,62 +99472,122 @@ async function handleChat(ctx, deps) {
|
|
|
99577
99472
|
body: baseBody
|
|
99578
99473
|
});
|
|
99579
99474
|
const finalText = await pollForResponse(client, session.id, { timeout, pollInterval, stablePolls, query });
|
|
99580
|
-
|
|
99581
|
-
|
|
99475
|
+
log("info", "\u6A21\u578B\u54CD\u5E94\u5B8C\u6210", {
|
|
99476
|
+
sessionKey,
|
|
99477
|
+
sessionId: session.id,
|
|
99478
|
+
output: finalText || "(empty)"
|
|
99479
|
+
});
|
|
99480
|
+
clearRetryAttempts(sessionKey);
|
|
99582
99481
|
await replyOrUpdate(feishuClient, chatId, placeholderId, finalText || "\u26A0\uFE0F \u54CD\u5E94\u8D85\u65F6");
|
|
99583
|
-
|
|
99584
|
-
|
|
99585
|
-
|
|
99586
|
-
|
|
99587
|
-
|
|
99588
|
-
|
|
99589
|
-
|
|
99590
|
-
|
|
99591
|
-
|
|
99592
|
-
|
|
99593
|
-
|
|
99594
|
-
|
|
99595
|
-
|
|
99596
|
-
|
|
99597
|
-
|
|
99598
|
-
|
|
99599
|
-
|
|
99482
|
+
await runAutoPromptLoop(session.id);
|
|
99483
|
+
} catch (err) {
|
|
99484
|
+
await new Promise((r) => setTimeout(r, SSE_RACE_WAIT_MS));
|
|
99485
|
+
let sessionError = getSessionError(session.id);
|
|
99486
|
+
clearSessionError(session.id);
|
|
99487
|
+
if (!sessionError) {
|
|
99488
|
+
const thrownFields = extractErrorFields(err);
|
|
99489
|
+
if (isModelError(thrownFields)) {
|
|
99490
|
+
const thrownMsg = err instanceof Error ? err.message : String(err);
|
|
99491
|
+
sessionError = { message: thrownMsg, fields: thrownFields };
|
|
99492
|
+
}
|
|
99493
|
+
}
|
|
99494
|
+
if (sessionError && isModelError(sessionError.fields)) {
|
|
99495
|
+
const attempts = getRetryAttempts(sessionKey);
|
|
99496
|
+
if (attempts < MAX_RETRY_ATTEMPTS) {
|
|
99497
|
+
try {
|
|
99498
|
+
const modelOverride = await resolveLatestModel(client, sessionError.fields, directory);
|
|
99499
|
+
if (!modelOverride) {
|
|
99500
|
+
log("warn", "\u65E0\u4EFB\u4F55\u5DF2\u8FDE\u63A5 provider \u6709\u53EF\u7528\u6A21\u578B\uFF0C\u653E\u5F03\u6062\u590D", { sessionKey });
|
|
99501
|
+
} else {
|
|
99502
|
+
setRetryAttempts(sessionKey, attempts + 1);
|
|
99503
|
+
log("info", "\u5DF2\u89E3\u6790\u53EF\u7528\u6A21\u578B\uFF0C\u5728\u540C\u4E00 session \u4E0A\u91CD\u8BD5", {
|
|
99504
|
+
sessionKey,
|
|
99505
|
+
providerID: modelOverride.providerID,
|
|
99506
|
+
modelID: modelOverride.modelID
|
|
99507
|
+
});
|
|
99508
|
+
await client.session.prompt({
|
|
99509
|
+
path: { id: session.id },
|
|
99510
|
+
query,
|
|
99511
|
+
body: { ...baseBody, model: modelOverride }
|
|
99512
|
+
});
|
|
99513
|
+
const finalText = await pollForResponse(client, session.id, { timeout, pollInterval, stablePolls, query });
|
|
99514
|
+
log("info", "\u6A21\u578B\u6062\u590D\u540E\u54CD\u5E94\u5B8C\u6210", {
|
|
99515
|
+
sessionKey,
|
|
99516
|
+
sessionId: session.id,
|
|
99517
|
+
output: finalText || "(empty)"
|
|
99518
|
+
});
|
|
99519
|
+
clearRetryAttempts(sessionKey);
|
|
99520
|
+
await replyOrUpdate(feishuClient, chatId, placeholderId, finalText || "\u26A0\uFE0F \u54CD\u5E94\u8D85\u65F6");
|
|
99521
|
+
log("info", "\u6A21\u578B\u4E0D\u517C\u5BB9\u6062\u590D\u6210\u529F", {
|
|
99522
|
+
sessionId: session.id,
|
|
99523
|
+
sessionKey,
|
|
99524
|
+
model: `${modelOverride.providerID}/${modelOverride.modelID}`,
|
|
99525
|
+
attempt: attempts + 1
|
|
99526
|
+
});
|
|
99527
|
+
await runAutoPromptLoop(session.id);
|
|
99528
|
+
return;
|
|
99600
99529
|
}
|
|
99601
|
-
}
|
|
99602
|
-
|
|
99603
|
-
|
|
99604
|
-
|
|
99605
|
-
|
|
99606
|
-
|
|
99607
|
-
|
|
99530
|
+
} catch (recoveryErr) {
|
|
99531
|
+
const recoveryErrMsg = recoveryErr instanceof Error ? recoveryErr.message : String(recoveryErr);
|
|
99532
|
+
const retryError = getSessionError(session.id);
|
|
99533
|
+
if (retryError) clearSessionError(session.id);
|
|
99534
|
+
sessionError = retryError ?? { message: recoveryErrMsg, fields: [] };
|
|
99535
|
+
log("error", "\u6A21\u578B\u6062\u590D\u5931\u8D25", {
|
|
99536
|
+
sessionId: session.id,
|
|
99608
99537
|
sessionKey,
|
|
99609
|
-
error:
|
|
99538
|
+
error: recoveryErrMsg
|
|
99610
99539
|
});
|
|
99611
99540
|
}
|
|
99612
|
-
}
|
|
99613
|
-
|
|
99541
|
+
} else {
|
|
99542
|
+
log("warn", "\u5DF2\u8FBE\u91CD\u8BD5\u4E0A\u9650\uFF0C\u653E\u5F03\u6062\u590D", {
|
|
99543
|
+
sessionKey,
|
|
99544
|
+
attempts
|
|
99545
|
+
});
|
|
99614
99546
|
}
|
|
99615
99547
|
}
|
|
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
99548
|
const thrownError = err instanceof Error ? err.message : String(err);
|
|
99621
|
-
const errorMessage = sessionError || thrownError;
|
|
99549
|
+
const errorMessage = sessionError?.message || thrownError;
|
|
99622
99550
|
log("error", "\u5BF9\u8BDD\u5904\u7406\u5931\u8D25", {
|
|
99623
99551
|
sessionId: session.id,
|
|
99624
|
-
sessionKey
|
|
99552
|
+
sessionKey,
|
|
99625
99553
|
chatType,
|
|
99626
99554
|
error: thrownError,
|
|
99627
|
-
...sessionError ? { sessionError } : { sseRaceMiss: true }
|
|
99555
|
+
...sessionError ? { sessionError: sessionError.message } : { sseRaceMiss: true }
|
|
99628
99556
|
});
|
|
99629
99557
|
const msg = "\u274C " + errorMessage;
|
|
99630
99558
|
await replyOrUpdate(feishuClient, chatId, placeholderId, msg);
|
|
99631
99559
|
} finally {
|
|
99632
99560
|
done = true;
|
|
99633
99561
|
if (timer) clearTimeout(timer);
|
|
99634
|
-
unregisterPending(
|
|
99562
|
+
unregisterPending(activeSessionId);
|
|
99563
|
+
}
|
|
99564
|
+
}
|
|
99565
|
+
async function resolveLatestModel(client, errorFields, directory) {
|
|
99566
|
+
const pattern = /model\s*not\s*found:?\s*(\w[\w-]*)\/(\S+)/i;
|
|
99567
|
+
const match = errorFields.map((f) => pattern.exec(f)).find(Boolean);
|
|
99568
|
+
const failedProviderID = match?.[1]?.toLowerCase();
|
|
99569
|
+
const failedModelID = match?.[2]?.replace(/\.$/, "");
|
|
99570
|
+
const query = directory ? { directory } : void 0;
|
|
99571
|
+
const { data } = await client.provider.list({ query });
|
|
99572
|
+
if (!data) return void 0;
|
|
99573
|
+
const connectedProviders = data.connected ?? [];
|
|
99574
|
+
if (connectedProviders.length === 0) return void 0;
|
|
99575
|
+
for (const pid of connectedProviders) {
|
|
99576
|
+
const defaultModelID = data.default?.[pid];
|
|
99577
|
+
if (defaultModelID && !(pid === failedProviderID && defaultModelID === failedModelID)) {
|
|
99578
|
+
return { providerID: pid, modelID: defaultModelID };
|
|
99579
|
+
}
|
|
99580
|
+
}
|
|
99581
|
+
for (const pid of connectedProviders) {
|
|
99582
|
+
const provider = data.all?.find((p) => p.id === pid);
|
|
99583
|
+
if (!provider?.models) continue;
|
|
99584
|
+
const candidates = Object.values(provider.models).filter((m) => m.status !== "deprecated" && !(pid === failedProviderID && m.id === failedModelID)).sort((a, b) => b.release_date.localeCompare(a.release_date));
|
|
99585
|
+
if (candidates.length > 0) {
|
|
99586
|
+
const best = candidates.find((m) => m.tool_call) ?? candidates[0];
|
|
99587
|
+
return { providerID: pid, modelID: best.id };
|
|
99588
|
+
}
|
|
99635
99589
|
}
|
|
99590
|
+
return void 0;
|
|
99636
99591
|
}
|
|
99637
99592
|
async function buildPromptParts(feishuClient, messageId, messageType, rawContent, textContent, chatType, senderId, log) {
|
|
99638
99593
|
if (messageType === "text") {
|
|
@@ -99905,7 +99860,7 @@ var FeishuPlugin = async (ctx) => {
|
|
|
99905
99860
|
const hooks = {
|
|
99906
99861
|
event: async ({ event }) => {
|
|
99907
99862
|
if (!gateway) return;
|
|
99908
|
-
await handleEvent(event, {
|
|
99863
|
+
await handleEvent(event, { log, directory: resolvedConfig.directory });
|
|
99909
99864
|
}
|
|
99910
99865
|
};
|
|
99911
99866
|
return hooks;
|