@wu529778790/open-im 1.9.3-beta.2 → 1.9.3-beta.3
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.
|
@@ -69,7 +69,13 @@ async function getOrCreateSession(sessionId, workDir, model, permissionMode) {
|
|
|
69
69
|
process.chdir(workDir);
|
|
70
70
|
}
|
|
71
71
|
if (sessionId) {
|
|
72
|
-
//
|
|
72
|
+
// 优先复用内存中已有的 SDKSession,避免每次都启动新进程
|
|
73
|
+
const existing = activeSessions.get(sessionId);
|
|
74
|
+
if (existing) {
|
|
75
|
+
log.info(`Reusing existing in-memory session: ${sessionId}`);
|
|
76
|
+
return { session: existing, sessionId };
|
|
77
|
+
}
|
|
78
|
+
// 内存中没有,尝试通过 resume 恢复(会启动新 CLI 进程)
|
|
73
79
|
try {
|
|
74
80
|
log.info(`Attempting to resume session: ${sessionId}`);
|
|
75
81
|
session = unstable_v2_resumeSession(sessionId, sessionOptions);
|
|
@@ -173,8 +179,10 @@ export class ClaudeSDKAdapter {
|
|
|
173
179
|
const newSessionId = msg.session_id;
|
|
174
180
|
if (newSessionId && newSessionId !== actualSessionId) {
|
|
175
181
|
// 更新 sessionId 映射
|
|
176
|
-
|
|
177
|
-
|
|
182
|
+
// 清理 pending 临时 ID(actualSessionId 尚未赋值时用 pendingTempId)
|
|
183
|
+
const idToClean = actualSessionId ?? pendingTempId;
|
|
184
|
+
if (idToClean?.startsWith('pending-')) {
|
|
185
|
+
activeSessions.delete(idToClean);
|
|
178
186
|
}
|
|
179
187
|
activeSessions.set(newSessionId, session);
|
|
180
188
|
actualSessionId = newSessionId;
|
|
@@ -149,13 +149,23 @@ export class WorkBuddyCentrifugeClient {
|
|
|
149
149
|
// The WorkBuddy server uses the registered channelId as the WeChat KF send_msg
|
|
150
150
|
// `touser`. Re-register the channel with the current WeChat user's externalUserId
|
|
151
151
|
// so that the server sends the reply to the correct customer.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
152
|
+
const externalUserId = sessionId.includes('::') ? sessionId.split('::')[0] : null;
|
|
153
|
+
if (this.config.registerChannelFn && externalUserId) {
|
|
154
|
+
// Retry registerChannelFn up to 3 times on network failure
|
|
155
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
156
|
+
try {
|
|
157
|
+
await this.config.registerChannelFn(externalUserId);
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
if (attempt < 3) {
|
|
162
|
+
log.warn(`${this.logPrefix} registerChannelFn attempt ${attempt} failed, retrying in 2s:`, err);
|
|
163
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
log.warn(`${this.logPrefix} registerChannelFn failed after 3 attempts (reply may go to wrong user):`, err);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
159
169
|
}
|
|
160
170
|
}
|
|
161
171
|
const httpPayload = {
|
|
@@ -172,31 +182,44 @@ export class WorkBuddyCentrifugeClient {
|
|
|
172
182
|
};
|
|
173
183
|
const url = `${this.config.httpBaseUrl}/v2/backgroundagent/wecom/local-proxy/receive`;
|
|
174
184
|
log.debug(`${this.logPrefix} HTTP COPILOT_RESPONSE → ${url} chatId=${sessionId} msgLen=${message.length}`);
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
185
|
+
// Retry COPILOT_RESPONSE up to 3 times on network failure
|
|
186
|
+
let sent = false;
|
|
187
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
188
|
+
try {
|
|
189
|
+
const res = await fetch(url, {
|
|
190
|
+
method: 'POST',
|
|
191
|
+
headers: {
|
|
192
|
+
'Content-Type': 'application/json',
|
|
193
|
+
Authorization: `Bearer ${this.config.httpAccessToken}`,
|
|
194
|
+
},
|
|
195
|
+
body: JSON.stringify(httpPayload),
|
|
196
|
+
signal: AbortSignal.timeout(30_000),
|
|
197
|
+
});
|
|
198
|
+
const body = await res.text().catch(() => '');
|
|
199
|
+
if (!res.ok) {
|
|
200
|
+
log.error(`${this.logPrefix} HTTP COPILOT_RESPONSE failed: ${res.status} ${body.substring(0, 300)}`);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
log.info(`${this.logPrefix} HTTP COPILOT_RESPONSE ok: ${res.status} ${body.substring(0, 200)}`);
|
|
204
|
+
}
|
|
205
|
+
sent = true;
|
|
206
|
+
break;
|
|
188
207
|
}
|
|
189
|
-
|
|
190
|
-
|
|
208
|
+
catch (err) {
|
|
209
|
+
if (attempt < 3) {
|
|
210
|
+
log.warn(`${this.logPrefix} HTTP COPILOT_RESPONSE attempt ${attempt} failed, retrying in 2s:`, err);
|
|
211
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
log.error(`${this.logPrefix} HTTP COPILOT_RESPONSE error after 3 attempts:`, err);
|
|
215
|
+
}
|
|
191
216
|
}
|
|
192
217
|
}
|
|
193
|
-
|
|
194
|
-
log.error(`${this.logPrefix}
|
|
195
|
-
}
|
|
196
|
-
finally {
|
|
197
|
-
// Release the heartbeat lock so the periodic registration can resume
|
|
198
|
-
this.config.releaseChannelLockFn?.();
|
|
218
|
+
if (!sent) {
|
|
219
|
+
log.error(`${this.logPrefix} Failed to send COPILOT_RESPONSE after retries`);
|
|
199
220
|
}
|
|
221
|
+
// Release the heartbeat lock so the periodic registration can resume
|
|
222
|
+
this.config.releaseChannelLockFn?.();
|
|
200
223
|
return;
|
|
201
224
|
}
|
|
202
225
|
this.sendEnvelope('session.promptResponse', payload, _guid, _userId);
|