@wu529778790/open-im 1.3.0 → 1.3.1-beta.1

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.
@@ -117,9 +117,18 @@ export class ClaudeSDKAdapter {
117
117
  queryClosed = true;
118
118
  const m = msg;
119
119
  const success = m.subtype === 'success';
120
+ const errs = m.errors ?? [];
121
+ const noConvErr = errs.find((e) => e.includes('No conversation found with session ID'));
122
+ if (!success && noConvErr) {
123
+ log.warn(`SDK session invalid: ${noConvErr}`);
124
+ callbacks.onSessionInvalid?.();
125
+ callbacks.onError('会话已过期,请发送 /new 开始新会话');
126
+ return;
127
+ }
128
+ const resultText = m.result ?? '';
120
129
  const result = {
121
130
  success,
122
- result: m.result ?? '',
131
+ result: resultText,
123
132
  accumulated: success ? accumulated : '',
124
133
  cost: m.total_cost_usd ?? 0,
125
134
  durationMs: m.duration_ms ?? 0,
@@ -128,6 +137,17 @@ export class ClaudeSDKAdapter {
128
137
  };
129
138
  if (!result.accumulated && result.result)
130
139
  result.accumulated = result.result;
140
+ if (!result.accumulated && !result.result && accumulated) {
141
+ log.debug(`Result event had no text but accumulated=${accumulated.length} chars, using accumulated`);
142
+ result.accumulated = accumulated;
143
+ result.result = accumulated;
144
+ }
145
+ if (!result.accumulated && !result.result) {
146
+ const errMsg = errs[0] ?? '未知错误';
147
+ log.warn(`SDK result empty: subtype=${m.subtype}, errors=${JSON.stringify(errs)}`);
148
+ callbacks.onError(errMsg);
149
+ return;
150
+ }
131
151
  callbacks.onComplete(result);
132
152
  return;
133
153
  }
@@ -18,6 +18,8 @@ export interface RunCallbacks {
18
18
  onComplete: (result: ParsedResult) => void;
19
19
  onError: (error: string) => void;
20
20
  onSessionId?: (sessionId: string) => void;
21
+ /** SDK 报 "No conversation found" 时调用,用于清除无效 session */
22
+ onSessionInvalid?: () => void;
21
23
  }
22
24
  export interface RunOptions {
23
25
  skipPermissions?: boolean;
@@ -7,6 +7,8 @@ export declare class SessionManager {
7
7
  constructor(defaultWorkDir: string, allowedBaseDirs: string[]);
8
8
  getSessionIdForConv(userId: string, convId: string): string | undefined;
9
9
  setSessionIdForConv(userId: string, convId: string, sessionId: string): void;
10
+ /** 清除指定会话的 sessionId(用于 SDK 报 "No conversation found" 时) */
11
+ clearSessionForConv(userId: string, convId: string): void;
10
12
  getSessionIdForThread(_userId: string, _threadId: string): string | undefined;
11
13
  setSessionIdForThread(userId: string, threadId: string, sessionId: string): void;
12
14
  getWorkDir(userId: string): string;
@@ -33,6 +33,16 @@ export class SessionManager {
33
33
  this.convSessionMap.set(`${userId}:${convId}`, sessionId);
34
34
  }
35
35
  }
36
+ /** 清除指定会话的 sessionId(用于 SDK 报 "No conversation found" 时) */
37
+ clearSessionForConv(userId, convId) {
38
+ const s = this.sessions.get(userId);
39
+ if (s?.activeConvId === convId) {
40
+ s.sessionId = undefined;
41
+ this.save();
42
+ }
43
+ this.convSessionMap.delete(`${userId}:${convId}`);
44
+ log.info(`Cleared session for user ${userId}, convId=${convId}`);
45
+ }
36
46
  getSessionIdForThread(_userId, _threadId) {
37
47
  return undefined;
38
48
  }
@@ -103,6 +103,10 @@ export function runAITask(deps, ctx, prompt, toolAdapter, platformAdapter) {
103
103
  else if (ctx.convId)
104
104
  sessionManager.setSessionIdForConv(ctx.userId, ctx.convId, id);
105
105
  },
106
+ onSessionInvalid: () => {
107
+ if (ctx.convId)
108
+ sessionManager.clearSessionForConv(ctx.userId, ctx.convId);
109
+ },
106
110
  onThinking: (t) => {
107
111
  if (!firstContentLogged) {
108
112
  firstContentLogged = true;
@@ -147,9 +151,19 @@ export function runAITask(deps, ctx, prompt, toolAdapter, platformAdapter) {
147
151
  pendingUpdate = null;
148
152
  }
149
153
  const note = buildCompletionNote(result, sessionManager, ctx, mode);
150
- const finalContent = result.accumulated || result.result || '(无输出)';
154
+ // 优先用 adapter 返回的 accumulated/result;若为空则用流式期间累积的 latestContent(SDK 有时 result 事件不携带文本)
155
+ const output = result.accumulated ||
156
+ result.result ||
157
+ taskState.latestContent ||
158
+ '(无输出)';
159
+ if (!result.accumulated && !result.result && taskState.latestContent) {
160
+ log.warn(`Empty AI output from adapter but had streamed content (${taskState.latestContent.length} chars), using latestContent. platform=${ctx.platform}, taskKey=${ctx.taskKey}`);
161
+ }
162
+ else if (!output || output === '(无输出)') {
163
+ log.warn(`Empty AI output for user ${ctx.userId}, platform=${ctx.platform}, taskKey=${ctx.taskKey}`);
164
+ }
151
165
  try {
152
- await platformAdapter.sendComplete(finalContent, note, thinkingText || undefined);
166
+ await platformAdapter.sendComplete(output, note, thinkingText || undefined);
153
167
  }
154
168
  catch (err) {
155
169
  log.error('Failed to send complete:', err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wu529778790/open-im",
3
- "version": "1.3.0",
3
+ "version": "1.3.1-beta.1",
4
4
  "description": "Multi-platform IM bridge for AI CLI tools (Claude, Codex, Cursor)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",