@aliwey/bmo 2.0.7 → 2.0.9

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.
package/cli.py CHANGED
@@ -1120,6 +1120,20 @@ def main_run():
1120
1120
  warnings.filterwarnings("ignore", category=ResourceWarning)
1121
1121
  # Suppress Windows asyncio pipe ValueError on exit (closed pipe repr noise)
1122
1122
  warnings.filterwarnings("ignore", message=".*I/O operation on closed pipe.*")
1123
+
1124
+ # Set up basic logging for the CLI TUI to write to ~/.bmo/logs/cli.log
1125
+ try:
1126
+ from config.settings import LOGS_DIR
1127
+ LOGS_DIR.mkdir(parents=True, exist_ok=True)
1128
+ logging.basicConfig(
1129
+ filename=LOGS_DIR / "cli.log",
1130
+ filemode="a",
1131
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
1132
+ level=logging.INFO
1133
+ )
1134
+ except Exception:
1135
+ pass
1136
+
1123
1137
  try:
1124
1138
  asyncio.run(main())
1125
1139
  except KeyboardInterrupt:
@@ -57,7 +57,8 @@ class OpenCodeBotClient:
57
57
  try:
58
58
  r = await self._http.get("/session", timeout=5.0)
59
59
  return r.status_code < 500
60
- except Exception:
60
+ except Exception as e:
61
+ logger.error("is_alive check failed for %s: %s", OPENCODE_BASE_URL, e)
61
62
  return False
62
63
 
63
64
  async def connect(self) -> bool:
@@ -75,19 +76,23 @@ class OpenCodeBotClient:
75
76
  self.is_connected = False
76
77
  return self.is_connected
77
78
 
78
- async def create_session(self, provider_id: Optional[str] = None, model_id: Optional[str] = None, env: Optional[dict] = None) -> Optional[str]:
79
+ async def create_session(self, provider_id: Optional[str] = None, model_id: Optional[str] = None, env: Optional[dict] = None, agent: str = "build") -> Optional[str]:
79
80
  """Create a new opencode session and return its ID."""
80
81
  try:
81
82
  # Force defaults if None/Empty
82
83
  p_id = provider_id if (provider_id and str(provider_id) != "None") else "opencode"
83
84
  m_id = model_id if (model_id and str(model_id) != "None") else "big-pickle"
84
85
 
85
- # The server expects a nested 'model' object
86
+ # Map 'default' to 'build' to prevent server-side agent switching errors
87
+ agent_to_use = "build" if (not agent or agent == "default") else agent
88
+
89
+ # The server expects a nested 'model' object and optional 'agent'
86
90
  payload = {
87
91
  "model": {
88
92
  "id": m_id,
89
93
  "providerID": p_id
90
- }
94
+ },
95
+ "agent": agent_to_use
91
96
  }
92
97
 
93
98
  if env:
@@ -103,7 +108,7 @@ class OpenCodeBotClient:
103
108
  session_id = data.get("id")
104
109
 
105
110
  actual_model = data.get("model", {}).get("id", "Unknown")
106
- logger.info("--- OPENCODE SESSION CREATED: %s (Model: %s) ---", session_id, actual_model)
111
+ logger.info("--- OPENCODE SESSION CREATED: %s (Model: %s, Agent: %s) ---", session_id, actual_model, agent_to_use)
107
112
  return session_id
108
113
  except Exception as e:
109
114
  logger.error("Failed to create session: %s", e)
@@ -281,7 +286,7 @@ class OpenCodeBotClient:
281
286
 
282
287
  if not session_id:
283
288
  # Inject keys if provided
284
- session_id = await self.create_session(provider_id, model_id, env=provider_env)
289
+ session_id = await self.create_session(provider_id, model_id, env=provider_env, agent=active_agent)
285
290
  if not session_id:
286
291
  return "Error: Could not create OpenCode session."
287
292
 
@@ -410,7 +415,7 @@ class OpenCodeBotClient:
410
415
  # Automatic recovery: if session not found, clear it and try once more
411
416
  if r is not None and r.status_code == 404:
412
417
  logger.warning("Session %s not found on server. Creating new session...", session_id)
413
- session_id = await self.create_session(provider_id, model_id, env=provider_env)
418
+ session_id = await self.create_session(provider_id, model_id, env=provider_env, agent=active_agent)
414
419
  if not session_id:
415
420
  return "Error: Session lost and could not create a new one."
416
421
  self.last_session_id = session_id
@@ -575,7 +580,7 @@ class OpenCodeBotClient:
575
580
  return "❌ Error: Could not connect to OpenCode backend. Is the server running?"
576
581
 
577
582
  if not session_id:
578
- session_id = await self.create_session(provider_id, model_id, env=provider_env)
583
+ session_id = await self.create_session(provider_id, model_id, env=provider_env, agent=active_agent)
579
584
  if not session_id:
580
585
  return "Error: Could not create OpenCode session."
581
586
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aliwey/bmo",
3
- "version": "2.0.7",
3
+ "version": "2.0.9",
4
4
  "description": "BMO — AI coding assistant with Telegram, CLI & Web sync. One command, all frontends.",
5
5
  "keywords": ["ai", "coding-assistant", "telegram-bot", "cli", "opencode", "bfp"],
6
6
  "homepage": "https://github.com/aliwey/bmo",
package/webchat/server.js CHANGED
@@ -327,7 +327,10 @@ app.post('/api/switch-model', express.json(), async (req, res) => {
327
327
  const parts = (model || 'big-pickle').split('/');
328
328
  const bareModelId = parts.pop();
329
329
  const providerId = provider || parts.pop() || 'opencode';
330
- const payload = { model: { id: bareModelId, providerID: providerId } };
330
+ const payload = {
331
+ model: { id: bareModelId, providerID: providerId },
332
+ agent: 'build'
333
+ };
331
334
  const r = await fetch(`${OPENCODE_URL}/session`, {
332
335
  method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload)
333
336
  });
@@ -353,9 +356,10 @@ function generateTitle(firstMessage) {
353
356
  return words.slice(0, 6).join(' ') + '…';
354
357
  }
355
358
 
356
- async function createOpenCodeSession(modelId = 'qwen3.6-plus-free', providerId = 'opencode') {
359
+ async function createOpenCodeSession(modelId = 'qwen3.6-plus-free', providerId = 'opencode', agent = 'build') {
357
360
  const payload = {
358
- model: { id: modelId, providerID: providerId }
361
+ model: { id: modelId, providerID: providerId },
362
+ agent: agent === 'default' ? 'build' : agent
359
363
  };
360
364
  const controller = new AbortController();
361
365
  const timeout = setTimeout(() => controller.abort(), 10000);
@@ -371,7 +375,7 @@ async function createOpenCodeSession(modelId = 'qwen3.6-plus-free', providerId =
371
375
  }
372
376
  const data = await r.json();
373
377
  const sessionId = data.id;
374
- console.log('[OpenCode] Created session:', sessionId, 'model:', modelId);
378
+ console.log('[OpenCode] Created session:', sessionId, 'model:', modelId, 'agent:', payload.agent);
375
379
  return sessionId;
376
380
  } catch (e) {
377
381
  clearTimeout(timeout);
@@ -412,14 +416,14 @@ async function callOpenCode(text, sessionId, mode = 'execute', agent = 'default'
412
416
 
413
417
  if (r && r.status === 404) {
414
418
  console.log('[OpenCode] Session not found, creating new one...');
415
- sessionId = await createOpenCodeSession(modelId || 'qwen3.6-plus-free', providerId || 'opencode');
419
+ sessionId = await createOpenCodeSession(modelId || 'qwen3.6-plus-free', providerId || 'opencode', agent);
416
420
  r = await doSend(sessionId);
417
421
  }
418
422
 
419
423
  // 500 with a recoverable model → create new session and retry
420
424
  if (r && r.status === 500 && modelId) {
421
425
  console.log('[OpenCode] Session 500, creating new session with model:', modelId);
422
- sessionId = await createOpenCodeSession(modelId, providerId);
426
+ sessionId = await createOpenCodeSession(modelId, providerId, agent);
423
427
  r = await doSend(sessionId, 30000);
424
428
  }
425
429