@wu529778790/open-im 1.8.1-beta.13 → 1.8.1-beta.15
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.
|
@@ -37,39 +37,51 @@ function isResult(msg) {
|
|
|
37
37
|
* @param permissionMode 权限模式
|
|
38
38
|
* @returns SDKSession 对象和实际的 sessionId
|
|
39
39
|
*/
|
|
40
|
-
async function getOrCreateSession(sessionId,
|
|
41
|
-
model, permissionMode) {
|
|
40
|
+
async function getOrCreateSession(sessionId, workDir, model, permissionMode) {
|
|
42
41
|
const resolvedModel = model?.trim() || 'claude-opus-4-5';
|
|
43
42
|
const sessionOptions = {
|
|
44
43
|
model: resolvedModel,
|
|
45
44
|
permissionMode,
|
|
46
|
-
// 可以添加其他选项,如 hooks, allowedTools 等
|
|
47
45
|
};
|
|
48
46
|
const baseUrl = process.env.ANTHROPIC_BASE_URL ?? '(default)';
|
|
49
|
-
log.info(`[V2] getOrCreateSession model param=${String(model ?? '')} resolved=${resolvedModel} baseUrl=${baseUrl}`);
|
|
47
|
+
log.info(`[V2] getOrCreateSession model param=${String(model ?? '')} resolved=${resolvedModel} baseUrl=${baseUrl} workDir=${workDir}`);
|
|
50
48
|
let session;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return { session, sessionId };
|
|
49
|
+
// SDK V2 的 SDKSessionOptions 没有 cwd 字段,
|
|
50
|
+
// 需要通过 process.chdir() 临时切换工作目录。
|
|
51
|
+
// 因为 createSession/resumeSession 是同步调用,且 JS 单线程,所以是安全的。
|
|
52
|
+
const originalCwd = process.cwd();
|
|
53
|
+
try {
|
|
54
|
+
if (workDir && workDir !== originalCwd) {
|
|
55
|
+
process.chdir(workDir);
|
|
59
56
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
if (sessionId) {
|
|
58
|
+
// 尝试恢复已有会话
|
|
59
|
+
try {
|
|
60
|
+
log.info(`Attempting to resume session: ${sessionId}`);
|
|
61
|
+
session = unstable_v2_resumeSession(sessionId, sessionOptions);
|
|
62
|
+
activeSessions.set(sessionId, session);
|
|
63
|
+
log.info(`Successfully resumed session: ${sessionId}`);
|
|
64
|
+
return { session, sessionId };
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
log.warn(`Failed to resume session ${sessionId}, creating new one: ${err}`);
|
|
68
|
+
// 恢复失败,创建新会话
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// 创建新会话
|
|
72
|
+
session = unstable_v2_createSession(sessionOptions);
|
|
73
|
+
// 新会话的 sessionId 需要从第一个消息中获取
|
|
74
|
+
// 暂时返回 undefined,稍后在 init 消息中获取
|
|
75
|
+
const tempId = `pending-${Date.now()}`;
|
|
76
|
+
activeSessions.set(tempId, session);
|
|
77
|
+
log.info(`Created new session (tempId: ${tempId})`);
|
|
78
|
+
return { session, sessionId: tempId };
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
if (workDir && workDir !== originalCwd) {
|
|
82
|
+
process.chdir(originalCwd);
|
|
63
83
|
}
|
|
64
84
|
}
|
|
65
|
-
// 创建新会话
|
|
66
|
-
session = unstable_v2_createSession(sessionOptions);
|
|
67
|
-
// 新会话的 sessionId 需要从第一个消息中获取
|
|
68
|
-
// 暂时返回 undefined,稍后在 init 消息中获取
|
|
69
|
-
const tempId = `pending-${Date.now()}`;
|
|
70
|
-
activeSessions.set(tempId, session);
|
|
71
|
-
log.info(`Created new session (tempId: ${tempId})`);
|
|
72
|
-
return { session, sessionId: tempId };
|
|
73
85
|
}
|
|
74
86
|
export class ClaudeSDKAdapter {
|
|
75
87
|
toolId = 'claude-sdk';
|
|
@@ -21,6 +21,8 @@ import { buildMediaContext } from '../shared/media-context.js';
|
|
|
21
21
|
import { buildErrorNote, buildProgressNote } from '../shared/message-note.js';
|
|
22
22
|
const log = createLogger('WeWorkHandler');
|
|
23
23
|
const WEWORK_MEDIA_TIMEOUT_MS = 60_000;
|
|
24
|
+
// Safety timeout: abort hung tasks before stream expires (5 min TTL → 4.5 min safety)
|
|
25
|
+
const WEWORK_TASK_SAFETY_TIMEOUT_MS = 4.5 * 60 * 1000;
|
|
24
26
|
async function saveWeWorkUrlMedia(payload, fallbackExtension) {
|
|
25
27
|
if (!payload.url) {
|
|
26
28
|
throw new Error("Missing WeWork media URL");
|
|
@@ -199,6 +201,24 @@ export function setupWeWorkHandlers(config, sessionManager) {
|
|
|
199
201
|
}
|
|
200
202
|
const stopTyping = startTypingLoop(chatId);
|
|
201
203
|
const taskKey = `${userId}:${msgId}`;
|
|
204
|
+
// Safety timeout: abort hung tasks before stream expires, unblocking the queue
|
|
205
|
+
let safetyTimer = setTimeout(() => {
|
|
206
|
+
safetyTimer = null;
|
|
207
|
+
const state = runningTasks.get(taskKey);
|
|
208
|
+
if (state) {
|
|
209
|
+
log.warn(`[SAFETY_TIMEOUT] Task ${taskKey} exceeded ${WEWORK_TASK_SAFETY_TIMEOUT_MS}ms, aborting`);
|
|
210
|
+
state.handle.abort();
|
|
211
|
+
runningTasks.delete(taskKey);
|
|
212
|
+
stopTyping();
|
|
213
|
+
sendTextReply(chatId, `AI 处理超时(${Math.round(WEWORK_TASK_SAFETY_TIMEOUT_MS / 1000)}s),已自动取消。请重试。`, reqId).catch(() => { });
|
|
214
|
+
}
|
|
215
|
+
}, WEWORK_TASK_SAFETY_TIMEOUT_MS);
|
|
216
|
+
const clearSafetyTimer = () => {
|
|
217
|
+
if (safetyTimer) {
|
|
218
|
+
clearTimeout(safetyTimer);
|
|
219
|
+
safetyTimer = null;
|
|
220
|
+
}
|
|
221
|
+
};
|
|
202
222
|
await runAITask({ config, sessionManager }, { userId, chatId, workDir, sessionId, convId, platform: 'wework', taskKey }, prompt, toolAdapter, {
|
|
203
223
|
throttleMs: WEWORK_THROTTLE_MS,
|
|
204
224
|
streamUpdate: async (content, toolNote) => {
|
|
@@ -217,6 +237,7 @@ export function setupWeWorkHandlers(config, sessionManager) {
|
|
|
217
237
|
await updateMessage(chatId, msgId, `Error: ${error}`, 'error', buildErrorNote(), toolId, reqId);
|
|
218
238
|
},
|
|
219
239
|
extraCleanup: () => {
|
|
240
|
+
clearSafetyTimer();
|
|
220
241
|
stopTyping();
|
|
221
242
|
runningTasks.delete(taskKey);
|
|
222
243
|
},
|
|
@@ -187,6 +187,12 @@ export async function updateMessage(chatId, streamId, content, status, note, too
|
|
|
187
187
|
return;
|
|
188
188
|
if (Date.now() - state.createdAt >= STREAM_SAFE_TTL_MS) {
|
|
189
189
|
markExpired(state, streamId);
|
|
190
|
+
// Stream expired - fall back to text delivery for errors and final states
|
|
191
|
+
if (status === 'error' || status === 'done') {
|
|
192
|
+
const reqIdUsed = getReqId(reqId);
|
|
193
|
+
sendText(reqIdUsed, message);
|
|
194
|
+
log.info(`Stream expired, sent ${status} via text fallback: streamId=${streamId}`);
|
|
195
|
+
}
|
|
190
196
|
return;
|
|
191
197
|
}
|
|
192
198
|
state.pendingUpdate = { message, status, reqId };
|