dooers-agents-client 0.1.0

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/README.md ADDED
@@ -0,0 +1,261 @@
1
+ # dooers-agents-client
2
+
3
+ React agents client SDK for agents server SDK.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install dooers-agents-client
9
+ ```
10
+
11
+ Peer dependency: `react >= 18`
12
+
13
+ ## Quick Start
14
+
15
+ Wrap your component tree in `WorkerProvider`, then use hooks anywhere inside.
16
+
17
+ ```tsx
18
+ import { WorkerProvider, useConnection, useThreadDetails, useMessage } from "dooers-agents-client"
19
+
20
+ function App() {
21
+ return (
22
+ <WorkerProvider
23
+ url="ws://localhost:8000/ws"
24
+ workerId="worker-1"
25
+ userId="user-1"
26
+ userName="Alice"
27
+ >
28
+ <Chat threadId="thread-1" />
29
+ </WorkerProvider>
30
+ )
31
+ }
32
+
33
+ function Chat({ threadId }: { threadId: string }) {
34
+ const { status } = useConnection()
35
+ const { events, isWaiting } = useThreadDetails(threadId)
36
+ const { send } = useMessage()
37
+
38
+ return (
39
+ <div>
40
+ <p>{status}</p>
41
+
42
+ {events.map((event) => (
43
+ <div key={event.id}>
44
+ <strong>{event.actor}</strong>
45
+ {event.content?.map((part, i) =>
46
+ part.type === "text" ? <p key={i}>{part.text}</p> : null
47
+ )}
48
+ </div>
49
+ ))}
50
+
51
+ {isWaiting && <p>Thinking...</p>}
52
+
53
+ <button onClick={() => send({ text: "Hello!", threadId })}>
54
+ Send
55
+ </button>
56
+ </div>
57
+ )
58
+ }
59
+ ```
60
+
61
+ ## Provider
62
+
63
+ `WorkerProvider` manages the WebSocket lifecycle. On mount it connects, on unmount it disconnects. When any prop changes, the connection resets.
64
+
65
+ ```tsx
66
+ <WorkerProvider
67
+ url="ws://localhost:8000/ws" // WebSocket endpoint
68
+ workerId="worker-1" // Worker to connect to
69
+ organizationId="org-1" // Context passed to handler
70
+ workspaceId="ws-1"
71
+ userId="user-1"
72
+ userName="Alice"
73
+ userEmail="alice@example.com"
74
+ userRole="member"
75
+ authToken="sk-..." // Optional authentication
76
+ onError={(err) => console.error(err.code, err.message)}
77
+ >
78
+ {children}
79
+ </WorkerProvider>
80
+ ```
81
+
82
+ All props except `children` and `onError` are primitive strings, so React can diff them cheaply.
83
+
84
+ ## Hooks
85
+
86
+ All hooks must be called inside a `WorkerProvider`.
87
+
88
+ ### useConnection
89
+
90
+ ```tsx
91
+ const { status, error, reconnectFailed, reconnect } = useConnection()
92
+
93
+ // status: "idle" | "connecting" | "connected" | "disconnected" | "error"
94
+ ```
95
+
96
+ Auto-reconnects up to 5 times with exponential backoff (1s, 2s, 4s, 8s, 16s). When all attempts fail, `reconnectFailed` becomes `true` and you can offer a manual retry button calling `reconnect()`.
97
+
98
+ ### useThreadDetails
99
+
100
+ ```tsx
101
+ const { thread, events, runs, isLoading, isWaiting } = useThreadDetails(threadId)
102
+ ```
103
+
104
+ Subscribes on mount, unsubscribes on unmount. Refcounted internally, so multiple components subscribing to the same thread share a single WebSocket subscription.
105
+
106
+ `events` includes optimistic messages that appear instantly when sending, then get replaced by the server-confirmed version. `isWaiting` is `true` while any run has status `"running"`.
107
+
108
+ ### useThreadEvents
109
+
110
+ Load older messages beyond the initial snapshot.
111
+
112
+ ```tsx
113
+ const { hasOlderEvents, loadOlderEvents } = useThreadEvents(threadId)
114
+
115
+ if (hasOlderEvents) {
116
+ loadOlderEvents(50) // load 50 more
117
+ }
118
+ ```
119
+
120
+ ### useThreadsList
121
+
122
+ Read-only thread list for the connected worker.
123
+
124
+ ```tsx
125
+ const { threads, isLoading, hasMore, totalCount } = useThreadsList()
126
+ ```
127
+
128
+ Threads load automatically on connect. `totalCount` reflects the server-reported total.
129
+
130
+ ### useThreadsActions
131
+
132
+ Thread mutations (delete, load more).
133
+
134
+ ```tsx
135
+ const { deleteThread, loadMore } = useThreadsActions()
136
+ ```
137
+
138
+ `loadMore()` for cursor-based pagination. `deleteThread(id)` broadcasts deletion to all connected clients.
139
+
140
+ ### useMessage
141
+
142
+ ```tsx
143
+ const { send } = useMessage()
144
+
145
+ // Existing thread
146
+ send({ text: "Hello!", threadId: "t-1" })
147
+
148
+ // New thread (omit threadId — returns the created thread ID)
149
+ const { threadId } = await send({ text: "Start a conversation" })
150
+
151
+ // Rich content
152
+ send({
153
+ threadId: "t-1",
154
+ content: [
155
+ { type: "text", text: "Check this out:" },
156
+ { type: "image", url: "https://...", mimeType: "image/png" },
157
+ ],
158
+ })
159
+ ```
160
+
161
+ ### useFeedback
162
+
163
+ ```tsx
164
+ const { feedback, like, dislike } = useFeedback(eventId, "event")
165
+
166
+ // feedback: "like" | "dislike" | null
167
+ like()
168
+ dislike("The response was incorrect")
169
+ ```
170
+
171
+ Second argument is the target type: `"event"`, `"run"`, or `"thread"`.
172
+
173
+ ### useAnalytics
174
+
175
+ Live event stream. Subscribe and unsubscribe explicitly.
176
+
177
+ ```tsx
178
+ const { events, counters, subscribe, unsubscribe } = useAnalytics()
179
+
180
+ useEffect(() => {
181
+ subscribe()
182
+ return () => unsubscribe()
183
+ }, [subscribe, unsubscribe])
184
+
185
+ // events: AnalyticsEvent[] — latest events
186
+ // counters: Record<string, number> — aggregated counters
187
+ ```
188
+
189
+ ### useSettings
190
+
191
+ Worker settings with real-time sync across clients.
192
+
193
+ ```tsx
194
+ const { fields, updatedAt, isLoading, subscribe, unsubscribe, patchField } = useSettings()
195
+
196
+ // fields: (SettingsField | SettingsFieldGroup)[]
197
+ patchField("model", "gpt-4o")
198
+ ```
199
+
200
+ Subscribe explicitly — designed so you only receive updates when the settings UI is visible:
201
+
202
+ ```tsx
203
+ useEffect(() => {
204
+ subscribe()
205
+ return () => unsubscribe()
206
+ }, [subscribe, unsubscribe])
207
+ ```
208
+
209
+ ## Types
210
+
211
+ All public types are camelCase. The SDK transforms the wire format (snake_case) internally.
212
+
213
+ ```typescript
214
+ import type {
215
+ Thread, // { id, workerId, title, createdAt, updatedAt, lastEventAt, metadata }
216
+ ThreadEvent, // { id, threadId, type, actor, author, content, data, createdAt, ... }
217
+ Run, // { id, threadId, agentId, status, startedAt, endedAt, error }
218
+ ContentPart, // TextPart | ImagePart | DocumentPart
219
+ Metadata, // { userId, userName, userEmail, userRole, organizationId, workspaceId }
220
+ ConnectionStatus, // "idle" | "connecting" | "connected" | "disconnected" | "error"
221
+ Actor, // "user" | "assistant" | "system" | "tool"
222
+ EventType, // "message" | "tool.call" | "tool.result" | "tool.transaction" | ...
223
+ RunStatus, // "running" | "succeeded" | "failed" | "canceled"
224
+ ThreadState, // { thread, events, runs, isLoading, isWaiting }
225
+ SettingsField,
226
+ SettingsFieldGroup,
227
+ SettingsItem, // SettingsField | SettingsFieldGroup
228
+ AnalyticsEvent,
229
+ FeedbackType, // "like" | "dislike"
230
+ FeedbackTarget, // "event" | "run" | "thread"
231
+ } from "dooers-agents-client"
232
+
233
+ import { isSettingsFieldGroup } from "dooers-agents-client" // type guard
234
+ ```
235
+
236
+ ## Architecture
237
+
238
+ ```
239
+ WorkerProvider
240
+ ├── WorkerClient WebSocket lifecycle, reconnection, optimistic events
241
+ └── WorkerStore Zustand vanilla store
242
+ ├── connection { status, error, reconnectFailed }
243
+ ├── threads Record<id, Thread>
244
+ ├── events Record<threadId, ThreadEvent[]>
245
+ ├── optimistic Record<threadId, ThreadEvent[]> (merged into events by useThreadDetails)
246
+ ├── runs Record<threadId, Run[]>
247
+ ├── settings { fields, updatedAt, isLoading }
248
+ ├── analytics { events, counters }
249
+ └── feedback Record<targetId, FeedbackType>
250
+ ```
251
+
252
+ The client handles:
253
+
254
+ - **Optimistic messages** — shown immediately on send, reconciled when the server confirms via `clientEventId`
255
+ - **Refcounted subscriptions** — multiple `useThreadDetails(id)` calls share one WebSocket subscription
256
+ - **Gap recovery** — on reconnect, sends `afterEventId` to resume from where the client left off
257
+ - **Exponential backoff** — reconnects at 1s, 2s, 4s, 8s, 16s intervals before giving up
258
+
259
+ ## See Also
260
+
261
+ - [dooers-agents-server](https://github.com/Dooers-ai/dooers-agents-server) — Python SDK for building the agents this client connects to