dooers-agents-client 0.3.0 → 0.4.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 +16 -16
- package/dist/main.cjs +76 -38
- package/dist/main.cjs.map +1 -1
- package/dist/main.d.cts +30 -8
- package/dist/main.d.ts +30 -8
- package/dist/main.js +75 -38
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,21 +12,21 @@ Peer dependency: `react >= 18`
|
|
|
12
12
|
|
|
13
13
|
## Quick Start
|
|
14
14
|
|
|
15
|
-
Wrap your component tree in `
|
|
15
|
+
Wrap your component tree in `AgentProvider`, then use hooks anywhere inside.
|
|
16
16
|
|
|
17
17
|
```tsx
|
|
18
|
-
import {
|
|
18
|
+
import { AgentProvider, useConnection, useThreadDetails, useMessage } from "dooers-agents-client"
|
|
19
19
|
|
|
20
20
|
function App() {
|
|
21
21
|
return (
|
|
22
|
-
<
|
|
22
|
+
<AgentProvider
|
|
23
23
|
url="ws://localhost:8000/ws"
|
|
24
|
-
|
|
24
|
+
agentId="agent-1"
|
|
25
25
|
userId="user-1"
|
|
26
26
|
userName="Alice"
|
|
27
27
|
>
|
|
28
28
|
<Chat threadId="thread-1" />
|
|
29
|
-
</
|
|
29
|
+
</AgentProvider>
|
|
30
30
|
)
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -60,12 +60,12 @@ function Chat({ threadId }: { threadId: string }) {
|
|
|
60
60
|
|
|
61
61
|
## Provider
|
|
62
62
|
|
|
63
|
-
`
|
|
63
|
+
`AgentProvider` manages the WebSocket lifecycle. On mount it connects, on unmount it disconnects. When any prop changes, the connection resets.
|
|
64
64
|
|
|
65
65
|
```tsx
|
|
66
|
-
<
|
|
66
|
+
<AgentProvider
|
|
67
67
|
url="ws://localhost:8000/ws" // WebSocket endpoint
|
|
68
|
-
|
|
68
|
+
agentId="agent-1" // Agent to connect to
|
|
69
69
|
organizationId="org-1" // Context passed to handler
|
|
70
70
|
workspaceId="ws-1"
|
|
71
71
|
userId="user-1"
|
|
@@ -78,14 +78,14 @@ function Chat({ threadId }: { threadId: string }) {
|
|
|
78
78
|
onError={(err) => console.error(err.code, err.message)}
|
|
79
79
|
>
|
|
80
80
|
{children}
|
|
81
|
-
</
|
|
81
|
+
</AgentProvider>
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
All props except `children` and `onError` are primitive strings, so React can diff them cheaply.
|
|
85
85
|
|
|
86
86
|
## Hooks
|
|
87
87
|
|
|
88
|
-
All hooks must be called inside a `
|
|
88
|
+
All hooks must be called inside a `AgentProvider`.
|
|
89
89
|
|
|
90
90
|
### useConnection
|
|
91
91
|
|
|
@@ -121,7 +121,7 @@ if (hasOlderEvents) {
|
|
|
121
121
|
|
|
122
122
|
### useThreadsList
|
|
123
123
|
|
|
124
|
-
Read-only thread list for the connected
|
|
124
|
+
Read-only thread list for the connected agent.
|
|
125
125
|
|
|
126
126
|
```tsx
|
|
127
127
|
const { threads, isLoading, hasMore, totalCount } = useThreadsList()
|
|
@@ -190,7 +190,7 @@ useEffect(() => {
|
|
|
190
190
|
|
|
191
191
|
### useSettings
|
|
192
192
|
|
|
193
|
-
|
|
193
|
+
Agent settings with real-time sync across clients.
|
|
194
194
|
|
|
195
195
|
```tsx
|
|
196
196
|
const { fields, updatedAt, isLoading, subscribe, unsubscribe, patchField } = useSettings()
|
|
@@ -215,7 +215,7 @@ All public types are camelCase. The SDK transforms the wire format (snake_case)
|
|
|
215
215
|
```typescript
|
|
216
216
|
import type {
|
|
217
217
|
User, // { userId, userName, userEmail, systemRole, organizationRole, workspaceRole }
|
|
218
|
-
Thread, // { id,
|
|
218
|
+
Thread, // { id, agentId, organizationId, workspaceId, owner, users, title, ... }
|
|
219
219
|
ThreadEvent, // { id, threadId, type, actor, author, user, content, data, createdAt, ... }
|
|
220
220
|
Run, // { id, threadId, agentId, status, startedAt, endedAt, error }
|
|
221
221
|
ContentPart, // TextPart | ImagePart | DocumentPart
|
|
@@ -238,9 +238,9 @@ import { isSettingsFieldGroup } from "dooers-agents-client" // type guard
|
|
|
238
238
|
## Architecture
|
|
239
239
|
|
|
240
240
|
```
|
|
241
|
-
|
|
242
|
-
├──
|
|
243
|
-
└──
|
|
241
|
+
AgentProvider
|
|
242
|
+
├── AgentClient WebSocket lifecycle, reconnection, optimistic events
|
|
243
|
+
└── AgentStore Zustand vanilla store
|
|
244
244
|
├── connection { status, error, reconnectFailed }
|
|
245
245
|
├── threads Record<id, Thread>
|
|
246
246
|
├── events Record<threadId, ThreadEvent[]>
|
package/dist/main.cjs
CHANGED
|
@@ -27,7 +27,7 @@ function toUser(w) {
|
|
|
27
27
|
function toThread(w) {
|
|
28
28
|
return {
|
|
29
29
|
id: w.id,
|
|
30
|
-
|
|
30
|
+
agentId: w.agent_id,
|
|
31
31
|
organizationId: w.organization_id,
|
|
32
32
|
workspaceId: w.workspace_id,
|
|
33
33
|
owner: toUser(w.owner),
|
|
@@ -187,7 +187,9 @@ function toSettingsField(w) {
|
|
|
187
187
|
rows: w.rows,
|
|
188
188
|
src: w.src,
|
|
189
189
|
width: w.width,
|
|
190
|
-
height: w.height
|
|
190
|
+
height: w.height,
|
|
191
|
+
uploadUrl: w.upload_url,
|
|
192
|
+
accept: w.accept
|
|
191
193
|
};
|
|
192
194
|
}
|
|
193
195
|
function toSettingsItem(w) {
|
|
@@ -206,7 +208,7 @@ function toAnalyticsEvent(w) {
|
|
|
206
208
|
return {
|
|
207
209
|
event: w.event,
|
|
208
210
|
timestamp: w.timestamp,
|
|
209
|
-
|
|
211
|
+
agentId: w.agent_id,
|
|
210
212
|
threadId: w.thread_id,
|
|
211
213
|
userId: w.user_id,
|
|
212
214
|
runId: w.run_id,
|
|
@@ -231,13 +233,13 @@ function toWireContentPart(p) {
|
|
|
231
233
|
var MAX_RECONNECT_ATTEMPTS = 5;
|
|
232
234
|
var RECONNECT_DELAYS = [1e3, 2e3, 4e3, 8e3, 16e3];
|
|
233
235
|
var SEND_MESSAGE_TIMEOUT = 3e4;
|
|
234
|
-
var
|
|
236
|
+
var AgentClient = class {
|
|
235
237
|
ws = null;
|
|
236
238
|
callbacks;
|
|
237
239
|
onError = null;
|
|
238
240
|
url = "";
|
|
239
241
|
httpBaseUrl = "";
|
|
240
|
-
|
|
242
|
+
agentId = "";
|
|
241
243
|
uploadUrl;
|
|
242
244
|
config = {
|
|
243
245
|
organizationId: "",
|
|
@@ -296,9 +298,9 @@ var WorkerClient = class {
|
|
|
296
298
|
sizeBytes: result.size_bytes
|
|
297
299
|
};
|
|
298
300
|
}
|
|
299
|
-
connect(url,
|
|
301
|
+
connect(url, agentId, config) {
|
|
300
302
|
this.url = url;
|
|
301
|
-
this.
|
|
303
|
+
this.agentId = agentId;
|
|
302
304
|
this.config = config ?? { organizationId: "", workspaceId: "", userId: "" };
|
|
303
305
|
this.isIntentionallyClosed = false;
|
|
304
306
|
try {
|
|
@@ -384,10 +386,10 @@ var WorkerClient = class {
|
|
|
384
386
|
}
|
|
385
387
|
// --- Settings ---
|
|
386
388
|
subscribeSettings() {
|
|
387
|
-
this.send("settings.subscribe", {
|
|
389
|
+
this.send("settings.subscribe", { agent_id: this.agentId });
|
|
388
390
|
}
|
|
389
391
|
unsubscribeSettings() {
|
|
390
|
-
this.send("settings.unsubscribe", {
|
|
392
|
+
this.send("settings.unsubscribe", { agent_id: this.agentId });
|
|
391
393
|
}
|
|
392
394
|
patchSetting(fieldId, value) {
|
|
393
395
|
this.send("settings.patch", { field_id: fieldId, value });
|
|
@@ -404,10 +406,10 @@ var WorkerClient = class {
|
|
|
404
406
|
}
|
|
405
407
|
// --- Analytics ---
|
|
406
408
|
subscribeAnalytics() {
|
|
407
|
-
this.send("analytics.subscribe", {
|
|
409
|
+
this.send("analytics.subscribe", { agent_id: this.agentId });
|
|
408
410
|
}
|
|
409
411
|
unsubscribeAnalytics() {
|
|
410
|
-
this.send("analytics.unsubscribe", {
|
|
412
|
+
this.send("analytics.unsubscribe", { agent_id: this.agentId });
|
|
411
413
|
}
|
|
412
414
|
// --- Messaging ---
|
|
413
415
|
sendMessage(params) {
|
|
@@ -527,7 +529,7 @@ var WorkerClient = class {
|
|
|
527
529
|
id: this.connectFrameId,
|
|
528
530
|
type: "connect",
|
|
529
531
|
payload: {
|
|
530
|
-
|
|
532
|
+
agent_id: this.agentId,
|
|
531
533
|
organization_id: this.config.organizationId ?? "",
|
|
532
534
|
workspace_id: this.config.workspaceId ?? "",
|
|
533
535
|
user: {
|
|
@@ -726,7 +728,7 @@ function extractFormStates(events, existing) {
|
|
|
726
728
|
}
|
|
727
729
|
return result;
|
|
728
730
|
}
|
|
729
|
-
function
|
|
731
|
+
function createAgentStore() {
|
|
730
732
|
return vanilla.createStore()((set) => ({
|
|
731
733
|
connection: {
|
|
732
734
|
status: "idle",
|
|
@@ -1063,10 +1065,10 @@ function createWorkerStore() {
|
|
|
1063
1065
|
}
|
|
1064
1066
|
}));
|
|
1065
1067
|
}
|
|
1066
|
-
var
|
|
1067
|
-
function
|
|
1068
|
+
var AgentContext = react.createContext(null);
|
|
1069
|
+
function AgentProvider({
|
|
1068
1070
|
url,
|
|
1069
|
-
|
|
1071
|
+
agentId,
|
|
1070
1072
|
organizationId,
|
|
1071
1073
|
workspaceId,
|
|
1072
1074
|
userId,
|
|
@@ -1084,8 +1086,8 @@ function WorkerProvider({
|
|
|
1084
1086
|
const storeRef = react.useRef(void 0);
|
|
1085
1087
|
const clientRef = react.useRef(void 0);
|
|
1086
1088
|
if (!storeRef.current) {
|
|
1087
|
-
storeRef.current =
|
|
1088
|
-
clientRef.current = new
|
|
1089
|
+
storeRef.current = createAgentStore();
|
|
1090
|
+
clientRef.current = new AgentClient(storeRef.current.getState().actions);
|
|
1089
1091
|
}
|
|
1090
1092
|
react.useEffect(() => {
|
|
1091
1093
|
clientRef.current?.setOnError(onError ?? null);
|
|
@@ -1095,8 +1097,8 @@ function WorkerProvider({
|
|
|
1095
1097
|
}, [uploadUrl]);
|
|
1096
1098
|
const identityIdsKey = identityIds?.join(",") ?? "";
|
|
1097
1099
|
react.useEffect(() => {
|
|
1098
|
-
if (!url || !
|
|
1099
|
-
clientRef.current?.connect(url,
|
|
1100
|
+
if (!url || !agentId) return;
|
|
1101
|
+
clientRef.current?.connect(url, agentId, {
|
|
1100
1102
|
organizationId,
|
|
1101
1103
|
workspaceId,
|
|
1102
1104
|
userId,
|
|
@@ -1111,7 +1113,7 @@ function WorkerProvider({
|
|
|
1111
1113
|
return () => clientRef.current?.disconnect();
|
|
1112
1114
|
}, [
|
|
1113
1115
|
url,
|
|
1114
|
-
|
|
1116
|
+
agentId,
|
|
1115
1117
|
organizationId,
|
|
1116
1118
|
workspaceId,
|
|
1117
1119
|
userId,
|
|
@@ -1127,27 +1129,27 @@ function WorkerProvider({
|
|
|
1127
1129
|
() => ({ store: storeRef.current, client: clientRef.current }),
|
|
1128
1130
|
[]
|
|
1129
1131
|
);
|
|
1130
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1132
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AgentContext.Provider, { value: contextValue, children });
|
|
1131
1133
|
}
|
|
1132
|
-
function
|
|
1133
|
-
const ctx = react.useContext(
|
|
1134
|
+
function useAgentContext() {
|
|
1135
|
+
const ctx = react.useContext(AgentContext);
|
|
1134
1136
|
if (!ctx) {
|
|
1135
|
-
throw new Error("
|
|
1137
|
+
throw new Error("useAgentContext must be used within a <AgentProvider>");
|
|
1136
1138
|
}
|
|
1137
1139
|
return ctx;
|
|
1138
1140
|
}
|
|
1139
1141
|
function useStore(selector) {
|
|
1140
|
-
const { store } =
|
|
1142
|
+
const { store } = useAgentContext();
|
|
1141
1143
|
return zustand.useStore(store, selector);
|
|
1142
1144
|
}
|
|
1143
1145
|
function useShallowStore(selector) {
|
|
1144
|
-
const { store } =
|
|
1146
|
+
const { store } = useAgentContext();
|
|
1145
1147
|
return zustand.useStore(store, shallow.useShallow(selector));
|
|
1146
1148
|
}
|
|
1147
1149
|
|
|
1148
1150
|
// src/hooks/use-analytics.ts
|
|
1149
1151
|
function useAnalytics() {
|
|
1150
|
-
const { client } =
|
|
1152
|
+
const { client } = useAgentContext();
|
|
1151
1153
|
const events = useShallowStore((s) => s.analytics.events);
|
|
1152
1154
|
const counters = useShallowStore((s) => s.analytics.counters);
|
|
1153
1155
|
const subscribe = react.useCallback(() => client.subscribeAnalytics(), [client]);
|
|
@@ -1259,7 +1261,7 @@ function useAudioRecorder() {
|
|
|
1259
1261
|
return { start, stop, cancel, isRecording, duration, analyserNode: analyserRef.current };
|
|
1260
1262
|
}
|
|
1261
1263
|
function useConnection() {
|
|
1262
|
-
const { client } =
|
|
1264
|
+
const { client } = useAgentContext();
|
|
1263
1265
|
const status = useStore((s) => s.connection.status);
|
|
1264
1266
|
const error = useStore((s) => s.connection.error);
|
|
1265
1267
|
const reconnectFailed = useStore((s) => s.connection.reconnectFailed);
|
|
@@ -1267,7 +1269,7 @@ function useConnection() {
|
|
|
1267
1269
|
return { status, error, reconnectFailed, reconnect };
|
|
1268
1270
|
}
|
|
1269
1271
|
function useFeedback(targetId, targetType = "event") {
|
|
1270
|
-
const { client } =
|
|
1272
|
+
const { client } = useAgentContext();
|
|
1271
1273
|
const feedback = useStore((s) => s.feedback[targetId] ?? null);
|
|
1272
1274
|
const like = react.useCallback(
|
|
1273
1275
|
(reason, classification) => client.sendFeedback(targetType, targetId, "like", reason, classification),
|
|
@@ -1280,7 +1282,7 @@ function useFeedback(targetId, targetType = "event") {
|
|
|
1280
1282
|
return { feedback, like, dislike };
|
|
1281
1283
|
}
|
|
1282
1284
|
function useForm(formEventId, formData, threadId) {
|
|
1283
|
-
const { client, store } =
|
|
1285
|
+
const { client, store } = useAgentContext();
|
|
1284
1286
|
const formState = useStore((s) => s.formStates[formEventId]);
|
|
1285
1287
|
const [errors, setErrors] = react.useState({});
|
|
1286
1288
|
react.useEffect(() => {
|
|
@@ -1422,7 +1424,7 @@ function useFormFileUpload() {
|
|
|
1422
1424
|
return { upload, isUploading, error, clearError };
|
|
1423
1425
|
}
|
|
1424
1426
|
function useMessage() {
|
|
1425
|
-
const { client } =
|
|
1427
|
+
const { client } = useAgentContext();
|
|
1426
1428
|
const send = react.useCallback(
|
|
1427
1429
|
(params) => {
|
|
1428
1430
|
return client.sendMessage(params);
|
|
@@ -1432,7 +1434,7 @@ function useMessage() {
|
|
|
1432
1434
|
return { send };
|
|
1433
1435
|
}
|
|
1434
1436
|
function useSettings() {
|
|
1435
|
-
const { client } =
|
|
1437
|
+
const { client } = useAgentContext();
|
|
1436
1438
|
const fields = useShallowStore((s) => s.settings.fields);
|
|
1437
1439
|
const updatedAt = useStore((s) => s.settings.updatedAt);
|
|
1438
1440
|
const isLoading = useStore((s) => s.settings.isLoading);
|
|
@@ -1444,10 +1446,45 @@ function useSettings() {
|
|
|
1444
1446
|
);
|
|
1445
1447
|
return { fields, updatedAt, isLoading, subscribe, unsubscribe, patchField };
|
|
1446
1448
|
}
|
|
1449
|
+
function useSettingsFileUpload() {
|
|
1450
|
+
const [isUploading, setIsUploading] = react.useState(false);
|
|
1451
|
+
const [error, setError] = react.useState(null);
|
|
1452
|
+
const upload = react.useCallback(
|
|
1453
|
+
async (params) => {
|
|
1454
|
+
setIsUploading(true);
|
|
1455
|
+
setError(null);
|
|
1456
|
+
try {
|
|
1457
|
+
const formData = new FormData();
|
|
1458
|
+
formData.append("file", params.file);
|
|
1459
|
+
formData.append("source", "settings");
|
|
1460
|
+
formData.append("field_id", params.fieldId);
|
|
1461
|
+
formData.append("agent_id", params.agentId);
|
|
1462
|
+
const response = await fetch(params.uploadUrl, {
|
|
1463
|
+
method: "POST",
|
|
1464
|
+
body: formData
|
|
1465
|
+
});
|
|
1466
|
+
if (!response.ok) {
|
|
1467
|
+
const detail = await response.text().catch(() => "");
|
|
1468
|
+
throw new Error(detail || `Upload failed (${response.status})`);
|
|
1469
|
+
}
|
|
1470
|
+
return await response.json();
|
|
1471
|
+
} catch (err) {
|
|
1472
|
+
const message = err instanceof Error ? err.message : "Upload failed";
|
|
1473
|
+
setError(message);
|
|
1474
|
+
throw err;
|
|
1475
|
+
} finally {
|
|
1476
|
+
setIsUploading(false);
|
|
1477
|
+
}
|
|
1478
|
+
},
|
|
1479
|
+
[]
|
|
1480
|
+
);
|
|
1481
|
+
const clearError = react.useCallback(() => setError(null), []);
|
|
1482
|
+
return { upload, isUploading, error, clearError };
|
|
1483
|
+
}
|
|
1447
1484
|
var EMPTY_ARRAY = [];
|
|
1448
1485
|
var EMPTY_RUNS = [];
|
|
1449
1486
|
function useThreadDetails(threadId) {
|
|
1450
|
-
const { client } =
|
|
1487
|
+
const { client } = useAgentContext();
|
|
1451
1488
|
react.useEffect(() => {
|
|
1452
1489
|
if (!threadId) return;
|
|
1453
1490
|
client.subscribe(threadId);
|
|
@@ -1472,7 +1509,7 @@ function useThreadDetails(threadId) {
|
|
|
1472
1509
|
return { thread, events, runs, isLoading, isWaiting };
|
|
1473
1510
|
}
|
|
1474
1511
|
function useThreadEvents(threadId) {
|
|
1475
|
-
const { client } =
|
|
1512
|
+
const { client } = useAgentContext();
|
|
1476
1513
|
const hasOlderEvents = useStore(
|
|
1477
1514
|
(s) => threadId ? s.eventPagination[threadId]?.hasMore ?? false : false
|
|
1478
1515
|
);
|
|
@@ -1500,13 +1537,13 @@ function useThreadsList() {
|
|
|
1500
1537
|
return { threads, isLoading, hasMore, totalCount };
|
|
1501
1538
|
}
|
|
1502
1539
|
function useThreadsActions() {
|
|
1503
|
-
const { client } =
|
|
1540
|
+
const { client } = useAgentContext();
|
|
1504
1541
|
const deleteThread = react.useCallback((threadId) => client.deleteThread(threadId), [client]);
|
|
1505
1542
|
const loadMore = react.useCallback((limit) => client.loadMoreThreads(limit), [client]);
|
|
1506
1543
|
return { deleteThread, loadMore };
|
|
1507
1544
|
}
|
|
1508
1545
|
function useUpload() {
|
|
1509
|
-
const { client } =
|
|
1546
|
+
const { client } = useAgentContext();
|
|
1510
1547
|
const [isUploading, setIsUploading] = react.useState(false);
|
|
1511
1548
|
const activeCountRef = react.useRef(0);
|
|
1512
1549
|
const upload = react.useCallback(
|
|
@@ -1527,7 +1564,7 @@ function useUpload() {
|
|
|
1527
1564
|
return { upload, isUploading };
|
|
1528
1565
|
}
|
|
1529
1566
|
|
|
1530
|
-
exports.
|
|
1567
|
+
exports.AgentProvider = AgentProvider;
|
|
1531
1568
|
exports.isSettingsFieldGroup = isSettingsFieldGroup;
|
|
1532
1569
|
exports.toFormElement = toFormElement;
|
|
1533
1570
|
exports.toFormEventData = toFormEventData;
|
|
@@ -1540,6 +1577,7 @@ exports.useForm = useForm;
|
|
|
1540
1577
|
exports.useFormFileUpload = useFormFileUpload;
|
|
1541
1578
|
exports.useMessage = useMessage;
|
|
1542
1579
|
exports.useSettings = useSettings;
|
|
1580
|
+
exports.useSettingsFileUpload = useSettingsFileUpload;
|
|
1543
1581
|
exports.useThreadDetails = useThreadDetails;
|
|
1544
1582
|
exports.useThreadEvents = useThreadEvents;
|
|
1545
1583
|
exports.useThreadsActions = useThreadsActions;
|