myagent-ai 1.47.13 → 1.47.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.
@@ -241,24 +241,63 @@ def _ensure_display() -> Optional[Dict[str, Any]]:
241
241
  logger.info(f"VNC 远程桌面已在运行,复用显示: {display}")
242
242
  return {"display": display, "vnc": True, "xvfb_standalone": False}
243
243
 
244
- # VNC 未运行 → 尝试自动启动
244
+ # [v1.47.13] VNC 未运行 → 尝试自动启动
245
+ # 重要修复: 不能在 ThreadPoolExecutor + asyncio.run 中调用 vnc.start()!
246
+ # 因为 vnc.start() 内部用 asyncio.Lock(),跨 event loop 会导致死锁。
247
+ # 正确做法:在单独线程中用 asyncio.run 启动,然后轮询等待 VNC 就绪。
245
248
  try:
246
249
  import asyncio
247
- loop = asyncio.get_event_loop()
248
- if loop.is_running():
249
- import concurrent.futures
250
- with concurrent.futures.ThreadPoolExecutor() as pool:
251
- result = pool.submit(asyncio.run, vnc.start()).result(timeout=30)
252
- else:
253
- result = asyncio.run(vnc.start())
250
+ import concurrent.futures
251
+
252
+ # 在独立线程中启动 VNC(新的 event loop,避免冲突)
253
+ def _start_vnc_in_thread():
254
+ try:
255
+ return asyncio.run(vnc.start())
256
+ except Exception as e:
257
+ return {"success": False, "message": str(e)}
258
+
259
+ with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
260
+ future = pool.submit(_start_vnc_in_thread)
261
+
262
+ # 轮询等待 VNC 就绪(同时等待 future 完成)
263
+ max_wait = 60 # 最多等60秒
264
+ check_interval = 2
265
+ elapsed = 0
266
+ while elapsed < max_wait:
267
+ # 先检查 VNC 是否已就绪
268
+ if vnc.is_running:
269
+ display = vnc.display
270
+ os.environ["DISPLAY"] = display
271
+ logger.info(f"VNC 远程桌面已启动,复用显示: {display}")
272
+ return {"display": display, "vnc": True, "xvfb_standalone": False}
273
+
274
+ # 检查启动线程是否已完成(非阻塞)
275
+ try:
276
+ result = future.result(timeout=0.1)
277
+ # 线程完成了,检查结果
278
+ if result.get("success") or vnc.is_running:
279
+ display = vnc.display
280
+ os.environ["DISPLAY"] = display
281
+ logger.info(f"VNC 远程桌面已启动,显示: {display}")
282
+ return {"display": display, "vnc": True, "xvfb_standalone": False}
283
+ else:
284
+ logger.warning(f"VNC 启动失败: {result.get('message', '')}")
285
+ break
286
+ except concurrent.futures.TimeoutError:
287
+ # 线程还在运行,继续等待
288
+ pass
289
+
290
+ import time
291
+ time.sleep(check_interval)
292
+ elapsed += check_interval
293
+
294
+ # 超时后再做最后检查
295
+ if vnc.is_running:
296
+ display = vnc.display
297
+ os.environ["DISPLAY"] = display
298
+ logger.info(f"VNC 远程桌面已启动(等待后),显示: {display}")
299
+ return {"display": display, "vnc": True, "xvfb_standalone": False}
254
300
 
255
- if result.get("success"):
256
- display = vnc.display
257
- os.environ["DISPLAY"] = display
258
- logger.info(f"VNC 远程桌面已自动启动,显示: {display},可在 VNC 中查看浏览器操作")
259
- return {"display": display, "vnc": True, "xvfb_standalone": False}
260
- else:
261
- logger.warning(f"VNC 自动启动失败: {result.get('message', '')}")
262
301
  except Exception as e:
263
302
  logger.warning(f"VNC 自动启动异常: {e}")
264
303
 
@@ -189,27 +189,55 @@ def _ensure_display() -> Optional[str]:
189
189
  logger.info(f"VNC 远程桌面已在运行,复用显示: {display}")
190
190
  return display
191
191
 
192
- # VNC 未运行 → 尝试自动启动
192
+ # [v1.47.13] VNC 未运行 → 尝试自动启动
193
+ # 修复: 不能在 ThreadPoolExecutor + asyncio.run 中直接调用 vnc.start()!
194
+ # 因为 vnc.start() 内部用 asyncio.Lock(),跨 event loop 会导致死锁。
195
+ # 正确做法:在单独线程中用 asyncio.run 启动,然后轮询等待 VNC 就绪。
193
196
  try:
194
197
  import asyncio
195
- try:
196
- loop = asyncio.get_event_loop()
197
- if loop.is_running():
198
- import concurrent.futures
199
- with concurrent.futures.ThreadPoolExecutor() as pool:
200
- result = pool.submit(asyncio.run, vnc.start()).result(timeout=30)
201
- else:
202
- result = asyncio.run(vnc.start())
203
- except RuntimeError:
204
- result = asyncio.run(vnc.start())
205
-
206
- if result.get("success"):
207
- display = vnc.display
208
- os.environ["DISPLAY"] = display
209
- logger.info(f"VNC 远程桌面已自动启动,显示: {display}")
210
- return display
211
- else:
212
- logger.warning(f"VNC 自动启动失败: {result.get('message', '')}")
198
+ import concurrent.futures
199
+
200
+ def _start_vnc_in_thread():
201
+ try:
202
+ return asyncio.run(vnc.start())
203
+ except Exception as e:
204
+ return {"success": False, "message": str(e)}
205
+
206
+ with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
207
+ future = pool.submit(_start_vnc_in_thread)
208
+
209
+ max_wait = 60
210
+ check_interval = 2
211
+ elapsed = 0
212
+ while elapsed < max_wait:
213
+ if vnc.is_running:
214
+ display = vnc.display
215
+ os.environ["DISPLAY"] = display
216
+ logger.info(f"VNC 远程桌面已启动,复用显示: {display}")
217
+ return display
218
+
219
+ try:
220
+ result = future.result(timeout=0.1)
221
+ if result.get("success") or vnc.is_running:
222
+ display = vnc.display
223
+ os.environ["DISPLAY"] = display
224
+ logger.info(f"VNC 远程桌面已启动,显示: {display}")
225
+ return display
226
+ else:
227
+ logger.warning(f"VNC 启动失败: {result.get('message', '')}")
228
+ break
229
+ except concurrent.futures.TimeoutError:
230
+ pass
231
+
232
+ import time
233
+ time.sleep(check_interval)
234
+ elapsed += check_interval
235
+
236
+ if vnc.is_running:
237
+ display = vnc.display
238
+ os.environ["DISPLAY"] = display
239
+ return display
240
+
213
241
  except Exception as e:
214
242
  logger.warning(f"VNC 自动启动异常: {e}")
215
243
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.47.13",
3
+ "version": "1.47.15",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {
@@ -1374,8 +1374,11 @@ async function sendMessage(opts) {
1374
1374
 
1375
1375
  // [v1.23.35] Create session if needed — 使用 UUID 防止 ID 重复/乱串
1376
1376
  let sessionId = state.activeSessionId;
1377
- // [v1.25.5] 如果 activeSessionId 为空,等待 loadSessions 完成确保会话绑定正确
1378
- if ((!sessionId || sessionId === '__new__') && typeof loadSessions === 'function' && state.activeAgent) {
1377
+ // [v1.47.14] 修复:新对话状态下不调用 loadSessions()!
1378
+ // loadSessions() 会通过 _pendingSessionRestore URL 参数 auto-select
1379
+ // 历史会话,导致用户在"新对话"页面发消息时被跳转到历史会话。
1380
+ // 只有 activeSessionId 完全为空(不是 '__new__')时才需要等待 loadSessions。
1381
+ if (!sessionId && sessionId !== '__new__' && typeof loadSessions === 'function' && state.activeAgent) {
1379
1382
  console.log('[sendMessage] activeSessionId is empty, waiting for loadSessions...');
1380
1383
  await loadSessions();
1381
1384
  sessionId = state.activeSessionId;