diff --git a/api/src/app.ts b/api/src/app.ts index 1af6646..902d744 100644 --- a/api/src/app.ts +++ b/api/src/app.ts @@ -48,7 +48,8 @@ const PORT = Number(process.env.PORT) || 18802; : colors.yellow('[gateway] initial connection failed, will use CLI fallback') ); - const server = app.listen(PORT, () => console.log(colors.green(`running on port ${PORT}`))); + const API_HOST = process.env.API_HOST || '0.0.0.0'; + const server = app.listen(PORT, API_HOST, () => console.log(colors.green(`running on ${API_HOST}:${PORT}`))); attachPtyWebSocket(server); startUpdateChecker(); } catch (error) { diff --git a/api/src/routes/message/controller.ts b/api/src/routes/message/controller.ts index 93ebe2f..7649512 100644 --- a/api/src/routes/message/controller.ts +++ b/api/src/routes/message/controller.ts @@ -200,42 +200,19 @@ const chat: Chat = async (req, res, next) => { .catch(() => {}); } - // Fetch messages from JSONL to get externalIds + // Link the user message to its JSONL externalId so the poll + // endpoint can recognise it later and skip duplicate inserts. + // Assistant message persistence is left entirely to the poll + // endpoint which has robust dedup logic; saving it here as well + // caused duplicate rows because the JSONL externalId can shift + // between reads (gateway v4 multi-pass writes). try { const jsonlMessages = ocService.getSessionMessages(agentIdForFiles, sessionKey); if (jsonlMessages.length) { const lastUserJsonl = [...jsonlMessages].reverse().find((m) => m.role === 'user'); - const lastAssistantJsonl = [...jsonlMessages].reverse().find((m) => m.role === 'assistant'); - if (lastUserJsonl?.externalId) { await msgRepo.update(savedUser._id, { externalId: lastUserJsonl.externalId }); } - - // Save assistant message from JSONL - if (lastAssistantJsonl) { - const assistantText = stripWrapperTags(lastAssistantJsonl.text).trim(); - const assistantThinking = lastAssistantJsonl.thinking - ? stripWrapperTags(lastAssistantJsonl.thinking).trim() - : null; - const assistantToolSteps = lastAssistantJsonl.toolSteps ?? null; - - /* Persist if we got any signal: real text, thinking, or tool calls - * — the last produces a compact tool-stub bubble in the UI. */ - if (assistantText || assistantThinking || (assistantToolSteps && assistantToolSteps.length > 0)) { - const assistantMessage = msgRepo.create({ - conversationId: Number(conversationId), - externalId: lastAssistantJsonl.externalId || null, - text: assistantText, - thinking: assistantThinking || null, - toolSteps: - assistantToolSteps && assistantToolSteps.length > 0 ? assistantToolSteps : null, - role: 'assistant' as const, - createdBy: req.user!._id, - createdAt: new Date(), - }); - await msgRepo.save(assistantMessage); - } - } } } catch { // Non-critical