You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 3e47c58
Browse filesBrowse the repository at this point in the historyBrowse files
refactor: move pkg/client to internal/api and use domain types
- Delete pkg/client/ - internal packages shouldn't be imported externally
- Add api.NewServices() constructor that creates client internally
- Update all callers to use services instead of client directly
- Use domain.AccountID for SetAccountID throughout
- Use domain.WorkosOrganizationID for RefreshTokenWithOrganization
- Use domain.OrganizationID in auth command's org struct
- Extract timeout constant in workos/client.go
8.Upload queue (background) persists messages to GraphQL for durability
62
66
63
-
The client never calls Claude directly. It writes messages and renders responses. The control plane handles everything in between.
67
+
**Key principle: the client is the exclusive writer.** SQLite is the source of truth. The Chat API never persists anything—it's a pure function from messages to response stream. The upload queue handles durability by persisting to the server via GraphQL, but this is decoupled from the chat flow.
68
+
69
+
**Why client sends full history:** The Chat API is 100% stateless. No database lookups, no sync concerns, no race conditions. What you send is what the model sees. The upload queue can be behind—doesn't matter for chat correctness. It only matters for durability and multi-device sync.
64
70
65
71
---
66
72
@@ -107,11 +113,12 @@ For background processes (goroutines, channels), you need a bridge to convert Go
107
113
3. The bridge returns the message as a `tea.Cmd` so Bubbletea propagates it
108
114
4. Any component in the tree can handle the message
109
115
110
-
For example, when a user message starts uploading:
116
+
For example, streaming chat responses:
111
117
112
-
1.`upload.Uploader` (background goroutine) emits `MessageProcessingEvent` to a channel
113
-
2.`database.Uploader` (TUI bridge) receives it and emits `database.UploadEventMsg`
114
-
3.`chat.Messages` handles the message and shows the spinner
118
+
1. User submits message, TUI starts a goroutine that calls Chat API
119
+
2. Chat API streams SSE events, goroutine converts each to a `tea.Msg`
120
+
3. TUI receives messages and updates UI in real-time (text deltas, thinking blocks, etc.)
121
+
4. When stream completes, TUI writes the assistant message to SQLite
115
122
116
123
The bridge is transparent—it converts and emits. Producers don't know about consumers. Consumers import the message type from the producer package. Dependencies point in the natural direction.
117
124
@@ -228,13 +235,18 @@ Views are tool results. When the AI creates a view, it's a tool call with a view
228
235
229
236
When the user submits:
230
237
231
-
1. Client writes user message to local SQLite
232
-
2. PowerSync syncs to control plane
233
-
3. Control plane processes (calls Claude, etc.)
234
-
4. Assistant message syncs back
235
-
5. Client renders it
238
+
1. Client creates conversation in SQLite (if new)
239
+
2. Client writes user message to SQLite immediately
240
+
3. Client reads full message history from SQLite
241
+
4. Client calls Chat API directly with history + new message
242
+
5. Chat API streams response blocks via SSE
243
+
6. Client renders blocks in real-time as they arrive (ephemeral UI state)
244
+
7. When stream completes, client writes assistant message to SQLite
245
+
8. UI re-renders from SQLite (now showing persisted message)
236
246
237
-
The client doesn't wait for a response. It writes optimistically and renders when data arrives via sync.
247
+
The Chat API is stateless—it doesn't store messages. The client sends the full conversation history on every request. This eliminates sync concerns: what you send is what the model sees.
248
+
249
+
The upload queue runs independently, persisting messages to GraphQL for durability. It doesn't block chat—if it's behind, chat still works correctly.
238
250
239
251
---
240
252
@@ -306,17 +318,22 @@ This is why navigation is instant. Filtering, sorting, searching—all local que
0 commit comments