@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, _workDir, // 保留参数以备将来使用
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
- if (sessionId) {
52
- // 尝试恢复已有会话
53
- try {
54
- log.info(`Attempting to resume session: ${sessionId}`);
55
- session = unstable_v2_resumeSession(sessionId, sessionOptions);
56
- activeSessions.set(sessionId, session);
57
- log.info(`Successfully resumed session: ${sessionId}`);
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
- catch (err) {
61
- log.warn(`Failed to resume session ${sessionId}, creating new one: ${err}`);
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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wu529778790/open-im",
3
- "version": "1.8.1-beta.13",
3
+ "version": "1.8.1-beta.15",
4
4
  "description": "Multi-platform IM bridge for AI CLI tools (Claude, Codex, CodeBuddy)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",