Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.7] - 2026-06-07

### Fixed

- 🔄 **Messages no longer disappear after sending.** Fixed a race condition where the frontend needed a separate GET request after sending a message, creating a window where socket events were dropped. The POST now returns the created messages directly, eliminating the round-trip and the race.

## [0.1.6] - 2026-06-07

### Added
Expand Down
2 changes: 1 addition & 1 deletion cptr/frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "frontend",
"private": true,
"version": "0.1.4",
"version": "0.1.7",
"type": "module",
"scripts": {
"dev": "vite dev",
Expand Down
2 changes: 2 additions & 0 deletions cptr/frontend/src/lib/apis/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export interface SendMessageResult {
chat_id: string;
message_id: string;
queued?: boolean;
user_message?: ChatMessageRow;
assistant_message?: ChatMessageRow;
}

// ── Queries ─────────────────────────────────────────────────
Expand Down
27 changes: 22 additions & 5 deletions cptr/frontend/src/lib/components/chat/ChatPanel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -541,8 +541,9 @@
created_at: Date.now()
}
];
} else {
await loadChat(result.chat_id);
} else if (result.assistant_message) {
allMessages = [...allMessages, result.assistant_message];
currentMessageId = result.message_id;
}
} catch (e) {
console.error('[chat] send (queue) error', e);
Expand Down Expand Up @@ -588,7 +589,17 @@
undefined,
files
);
await loadChat(result.chat_id);

// Swap optimistic temp msg with real messages from backend.
chatId = result.chat_id;
const withoutTemp = allMessages.filter((m) => m.id !== tempId);
if (result.user_message && result.assistant_message) {
allMessages = [...withoutTemp, result.user_message, result.assistant_message];
} else if (result.assistant_message) {
allMessages = [...withoutTemp, result.assistant_message];
}
currentMessageId = result.message_id;

if (isNew && tabId) {
updateTab(tabId, result.chat_id, text.slice(0, 40) || 'Chat');
}
Expand Down Expand Up @@ -737,7 +748,10 @@
tool_approval_mode: mode,
plan_mode: get(planMode)
});
await loadChat(result.chat_id);
if (result.assistant_message) {
allMessages = [...allMessages, result.assistant_message];
currentMessageId = result.message_id;
}
} catch (e) {
console.error('[chat] regenerate error', e);
}
Expand Down Expand Up @@ -777,7 +791,10 @@
msg.parent_id,
{ tool_approval_mode: get(toolApprovalMode), plan_mode: get(planMode) }
);
await loadChat(result.chat_id);
if (result.user_message && result.assistant_message) {
allMessages = [...allMessages, result.user_message, result.assistant_message];
currentMessageId = result.message_id;
}
} catch (e) {
console.error('[chat] edit-send error', e);
}
Expand Down
11 changes: 10 additions & 1 deletion cptr/routers/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,16 @@ async def send_message(body: SendMessageRequest, request: Request):
regeneration_prompt=body.regeneration_prompt,
)

return {"chat_id": chat.id, "message_id": assistant_msg.id}
# Return the created messages so the frontend can append them
# directly — no separate GET needed, no client-side construction.
resp: dict = {
"chat_id": chat.id,
"message_id": assistant_msg.id,
"assistant_message": _message_dict(assistant_msg),
}
if parent_msg is None or parent_msg.role != "user":
resp["user_message"] = _message_dict(user_msg)
return resp


# ── Approve / reject a pending tool call ────────────────────
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "cptr"
version = "0.1.6"
version = "0.1.7"
description = "Your computer, from anywhere. Code, manage, and control your machine from the web."
license = {file = "LICENSE"}
readme = "README.md"
Expand Down
Loading