openclaw-navigator 5.7.4 → 5.7.5

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.
Files changed (2) hide show
  1. package/cli.mjs +38 -12
  2. package/package.json +1 -1
package/cli.mjs CHANGED
@@ -1193,10 +1193,28 @@ function handleRequest(req, res) {
1193
1193
 
1194
1194
  // 3. Background: relay to BFF (port 4000) /api/chat for AI processing
1195
1195
  // Format: OpenAI-compatible chat completions with message history
1196
- const chatHistory = session.messages.map((m) => ({
1197
- role: m.role,
1198
- content: m.content,
1199
- }));
1196
+ // Sanitize: BFF expects alternating user/assistant roles, starting with user.
1197
+ // Drop orphaned assistant messages at the start (from web UI SSE tap).
1198
+ let chatHistory = session.messages
1199
+ .map((m) => ({ role: m.role, content: m.content }))
1200
+ .filter((m) => m.role === "user" || m.role === "assistant" || m.role === "system");
1201
+ // Ensure first non-system message is 'user' — drop leading assistant messages
1202
+ while (chatHistory.length > 0 && chatHistory[0].role === "assistant") {
1203
+ chatHistory.shift();
1204
+ }
1205
+ // Collapse consecutive same-role messages (merge them)
1206
+ chatHistory = chatHistory.reduce((acc, msg) => {
1207
+ if (acc.length > 0 && acc[acc.length - 1].role === msg.role) {
1208
+ acc[acc.length - 1].content += "\n" + msg.content;
1209
+ } else {
1210
+ acc.push({ ...msg });
1211
+ }
1212
+ return acc;
1213
+ }, []);
1214
+ // Must have at least one user message
1215
+ if (chatHistory.length === 0 || chatHistory[0].role !== "user") {
1216
+ chatHistory.unshift({ role: "user", content: message });
1217
+ }
1200
1218
  const proxyBody = JSON.stringify({
1201
1219
  messages: chatHistory,
1202
1220
  stream: true,
@@ -1217,6 +1235,7 @@ function handleRequest(req, res) {
1217
1235
  };
1218
1236
 
1219
1237
  console.log(` ${DIM}→ Relaying to BFF /api/chat (port ${ocUIPort}) with ${chatHistory.length} messages${relayCookie ? " +cookie" : " NO-COOKIE"}${RESET}`);
1238
+ console.log(` ${DIM} Body: ${proxyBody.substring(0, 300)}${proxyBody.length > 300 ? "..." : ""}${RESET}`);
1220
1239
 
1221
1240
  // Helper: send the relay request (with optional retry after login)
1222
1241
  function sendBFFRelay(opts, body, retryCount = 0) {
@@ -1341,7 +1360,15 @@ function handleRequest(req, res) {
1341
1360
  if (!isSSE && sseBuffer) {
1342
1361
  try {
1343
1362
  const jsonBody = JSON.parse(sseBuffer);
1344
- fullText = jsonBody.choices?.[0]?.message?.content || jsonBody.response || jsonBody.message || jsonBody.text || "";
1363
+ // Check for error response (BFF returns 400/500 with error details)
1364
+ if (proxyRes.statusCode >= 400) {
1365
+ const errMsg = jsonBody.error?.message || jsonBody.error || jsonBody.message || JSON.stringify(jsonBody);
1366
+ console.log(` ${RED}✗${RESET} BFF error ${proxyRes.statusCode}: ${String(errMsg).substring(0, 300)}`);
1367
+ // Send error to Navigator so user sees it
1368
+ fullText = `⚠️ Error from AI: ${String(errMsg).substring(0, 200)}`;
1369
+ } else {
1370
+ fullText = jsonBody.choices?.[0]?.message?.content || jsonBody.response || jsonBody.message || jsonBody.text || "";
1371
+ }
1345
1372
  } catch {
1346
1373
  console.log(` ${DIM}BFF returned non-JSON (${sseBuffer.length} bytes): ${sseBuffer.substring(0, 200)}${RESET}`);
1347
1374
  }
@@ -1359,7 +1386,7 @@ function handleRequest(req, res) {
1359
1386
  });
1360
1387
  console.log(` ${GREEN}✓${RESET} AI response (${fullText.length} chars): ${fullText.substring(0, 80)}...`);
1361
1388
  } else {
1362
- console.log(` ${DIM}BFF returned no content${RESET}`);
1389
+ console.log(` ${DIM}BFF returned no content (status ${proxyRes.statusCode})${RESET}`);
1363
1390
  }
1364
1391
  });
1365
1392
 
@@ -1640,16 +1667,15 @@ function handleRequest(req, res) {
1640
1667
  res.writeHead(200, headers);
1641
1668
  res.end(JSON.stringify(result));
1642
1669
 
1643
- // Store in bridge session + broadcast via WebSocket
1670
+ // Broadcast via WebSocket for live updates (but DON'T store in
1671
+ // bridge chat session — web UI manages its own conversation state
1672
+ // separately from sidepane chat. Mixing them causes 400 errors from
1673
+ // the BFF due to malformed message history.)
1644
1674
  if (fullText) {
1645
- const session = getChatSession("main");
1646
- session.messages.push({ role: "assistant", content: fullText, timestamp: Date.now() });
1647
-
1648
1675
  broadcastToWS({
1649
- type: "chat.final",
1676
+ type: "chat.webui",
1650
1677
  text: fullText,
1651
1678
  content: fullText,
1652
- sessionKey: "main",
1653
1679
  role: "assistant",
1654
1680
  timestamp: Date.now(),
1655
1681
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-navigator",
3
- "version": "5.7.4",
3
+ "version": "5.7.5",
4
4
  "description": "One-command bridge + tunnel for the Navigator browser — works on any machine, any OS",
5
5
  "keywords": [
6
6
  "browser",