@economic/agents-react 1.6.0 → 1.6.1
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/dist/index.mjs +156 -68
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
1
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
2
2
|
import { useAgent as useAgent$1 } from "agents/react";
|
|
3
3
|
import { getToolApproval, getToolCallId, getToolPartState, useAgentChat } from "@cloudflare/ai-chat/react";
|
|
4
4
|
//#region src/util.ts
|
|
@@ -11,6 +11,16 @@ function getTokenArgs(authToken) {
|
|
|
11
11
|
}
|
|
12
12
|
//#endregion
|
|
13
13
|
//#region src/useAgent.ts
|
|
14
|
+
function getAgentConnectionStatus(state, connectionId) {
|
|
15
|
+
return state.connectionId === connectionId ? state.status : "connecting";
|
|
16
|
+
}
|
|
17
|
+
function getNextAgentConnectionState(state, connectionId, status) {
|
|
18
|
+
if (status === "disconnected" && state.connectionId === connectionId && state.status === "error") return state;
|
|
19
|
+
return {
|
|
20
|
+
connectionId,
|
|
21
|
+
status
|
|
22
|
+
};
|
|
23
|
+
}
|
|
14
24
|
function getCloseReason(code) {
|
|
15
25
|
return {
|
|
16
26
|
1e3: "normal-closure",
|
|
@@ -29,93 +39,168 @@ function getCloseReason(code) {
|
|
|
29
39
|
1015: "tls-handshake-failure"
|
|
30
40
|
}[code] ?? "unknown";
|
|
31
41
|
}
|
|
42
|
+
function getDefaultHost() {
|
|
43
|
+
return globalThis.window?.location?.host || "dummy-domain.com";
|
|
44
|
+
}
|
|
45
|
+
function normalizeAgentName(name) {
|
|
46
|
+
if (name === name.toUpperCase() && name !== name.toLowerCase()) return name.toLowerCase().replace(/_/g, "-");
|
|
47
|
+
const kebabified = name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
48
|
+
return (kebabified.startsWith("-") ? kebabified.slice(1) : kebabified).replace(/_/g, "-").replace(/-$/, "");
|
|
49
|
+
}
|
|
50
|
+
function getSubPath(sub) {
|
|
51
|
+
if (!sub?.length) return void 0;
|
|
52
|
+
return sub.flatMap(({ agent, name }) => [
|
|
53
|
+
"sub",
|
|
54
|
+
normalizeAgentName(agent),
|
|
55
|
+
encodeURIComponent(name)
|
|
56
|
+
]).join("/");
|
|
57
|
+
}
|
|
58
|
+
function getAgentConnectionTarget(options) {
|
|
59
|
+
const effectiveName = options.actorId ? `${options.actorId}:${options.name}` : options.name;
|
|
60
|
+
return {
|
|
61
|
+
enabled: options.enabled ?? true,
|
|
62
|
+
host: options.host || getDefaultHost(),
|
|
63
|
+
agentName: normalizeAgentName(options.agentName),
|
|
64
|
+
name: effectiveName || "default",
|
|
65
|
+
subPath: getSubPath(options.sub),
|
|
66
|
+
authToken: options.authToken || void 0
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function getAgentSocketTarget(agent) {
|
|
70
|
+
const options = agent.partySocketOptions;
|
|
71
|
+
if (typeof options?.host !== "string" || typeof options.party !== "string" || typeof options.room !== "string") return;
|
|
72
|
+
const protocols = options.protocols;
|
|
73
|
+
const authToken = Array.isArray(protocols) && protocols[0] === "bearer" && typeof protocols[1] === "string" ? protocols[1] : void 0;
|
|
74
|
+
return {
|
|
75
|
+
host: options.host,
|
|
76
|
+
agentName: options.party,
|
|
77
|
+
name: options.room,
|
|
78
|
+
subPath: typeof options.path === "string" && options.path ? options.path : void 0,
|
|
79
|
+
authToken
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function isSameSocketTarget(a, b) {
|
|
83
|
+
return a.host === b.host && a.agentName === b.agentName && a.name === b.name && a.subPath === b.subPath && a.authToken === b.authToken;
|
|
84
|
+
}
|
|
32
85
|
function isSameConnectionTarget(a, b) {
|
|
33
|
-
|
|
34
|
-
|
|
86
|
+
return a.enabled === b.enabled && isSameSocketTarget(a, b);
|
|
87
|
+
}
|
|
88
|
+
function getNextAgentConnection(connection, target) {
|
|
89
|
+
return isSameConnectionTarget(connection.target, target) ? connection : {
|
|
90
|
+
id: connection.id + 1,
|
|
91
|
+
target
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function isCurrentAgentSocketTarget(eventConnection, connection) {
|
|
95
|
+
if (!connection.target.enabled || !eventConnection) return false;
|
|
96
|
+
const socketTarget = getAgentSocketTarget(eventConnection);
|
|
97
|
+
return socketTarget !== void 0 && isSameSocketTarget(socketTarget, connection.target);
|
|
35
98
|
}
|
|
36
99
|
function useAgent(options) {
|
|
37
100
|
const { enabled, host, agentName, actorId, name, sub, authToken, onError, onOpen, onClose, onMessage } = options;
|
|
38
|
-
const target =
|
|
39
|
-
|
|
40
|
-
host,
|
|
41
|
-
agentName,
|
|
42
|
-
actorId,
|
|
43
|
-
name,
|
|
44
|
-
sub,
|
|
45
|
-
authToken
|
|
46
|
-
};
|
|
47
|
-
const connection = useRef({
|
|
101
|
+
const target = getAgentConnectionTarget(options);
|
|
102
|
+
const [connection, setConnection] = useState({
|
|
48
103
|
id: 0,
|
|
49
104
|
target
|
|
50
105
|
});
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
};
|
|
55
|
-
const connectionId = connection.current.id;
|
|
56
|
-
const currentConnectionId = useRef(connectionId);
|
|
57
|
-
currentConnectionId.current = connectionId;
|
|
106
|
+
const nextConnection = getNextAgentConnection(connection, target);
|
|
107
|
+
if (nextConnection !== connection) setConnection(nextConnection);
|
|
108
|
+
const connectionId = nextConnection.id;
|
|
58
109
|
const [connectionType, setConnectionType] = useState();
|
|
59
110
|
const type = connectionType?.connectionId === connectionId ? connectionType.type : void 0;
|
|
60
111
|
const [connectionStatus, setConnectionStatus] = useState({
|
|
61
112
|
connectionId,
|
|
62
113
|
status: "connecting"
|
|
63
114
|
});
|
|
64
|
-
const status = connectionStatus
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
115
|
+
const status = getAgentConnectionStatus(connectionStatus, connectionId);
|
|
116
|
+
const committedConnection = useRef({
|
|
117
|
+
connection: nextConnection,
|
|
118
|
+
callbacks: {
|
|
119
|
+
onOpen,
|
|
120
|
+
onClose,
|
|
121
|
+
onError,
|
|
122
|
+
onMessage
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
const setStatus = useCallback((nextStatus) => {
|
|
126
|
+
const committedConnectionId = committedConnection.current.connection.id;
|
|
127
|
+
setConnectionStatus((current) => getNextAgentConnectionState(current, committedConnectionId, nextStatus));
|
|
128
|
+
}, []);
|
|
129
|
+
const setType = useCallback((nextType) => {
|
|
130
|
+
setConnectionType({
|
|
131
|
+
connectionId: committedConnection.current.connection.id,
|
|
78
132
|
type: nextType
|
|
79
133
|
});
|
|
80
|
-
};
|
|
81
|
-
const
|
|
134
|
+
}, []);
|
|
135
|
+
const isCurrentConnectionEvent = useCallback((event) => {
|
|
136
|
+
const committed = committedConnection.current;
|
|
137
|
+
return isCurrentAgentSocketTarget(event.target, committed.connection);
|
|
138
|
+
}, []);
|
|
139
|
+
const handleOpen = useCallback((event) => {
|
|
140
|
+
if (!isCurrentConnectionEvent(event)) return;
|
|
141
|
+
committedConnection.current.callbacks.onOpen?.(event);
|
|
142
|
+
}, [isCurrentConnectionEvent]);
|
|
143
|
+
const handleClose = useCallback((event) => {
|
|
144
|
+
if (!isCurrentConnectionEvent(event)) return;
|
|
145
|
+
if (event.code >= 3e3) setStatus("unauthorized");
|
|
146
|
+
else {
|
|
147
|
+
setStatus("disconnected");
|
|
148
|
+
if (!event.wasClean) console.warn("[useAgent] WebSocket closed unexpectedly", {
|
|
149
|
+
code: event.code,
|
|
150
|
+
reason: event.reason,
|
|
151
|
+
closeReason: getCloseReason(event.code),
|
|
152
|
+
wasClean: event.wasClean
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
committedConnection.current.callbacks.onClose?.(event);
|
|
156
|
+
}, [isCurrentConnectionEvent, setStatus]);
|
|
157
|
+
const handleMessage = useCallback((event) => {
|
|
158
|
+
if (!isCurrentConnectionEvent(event)) return;
|
|
159
|
+
const msg = JSON.parse(event.data);
|
|
160
|
+
if (msg.type === "connection_status") {
|
|
161
|
+
if (msg.status === "unauthorized") event.target.close(3e3);
|
|
162
|
+
setStatus(msg.status);
|
|
163
|
+
} else if (msg.type === "agent_type") setType(msg.agentType);
|
|
164
|
+
committedConnection.current.callbacks.onMessage?.(event);
|
|
165
|
+
}, [
|
|
166
|
+
isCurrentConnectionEvent,
|
|
167
|
+
setStatus,
|
|
168
|
+
setType
|
|
169
|
+
]);
|
|
170
|
+
const handleError = useCallback((event) => {
|
|
171
|
+
if (!isCurrentConnectionEvent(event)) return;
|
|
172
|
+
setStatus("error");
|
|
173
|
+
committedConnection.current.callbacks.onError?.(event);
|
|
174
|
+
}, [isCurrentConnectionEvent, setStatus]);
|
|
82
175
|
const agent = useAgent$1({
|
|
83
176
|
enabled,
|
|
84
177
|
host,
|
|
85
178
|
agent: agentName,
|
|
86
179
|
name: actorId ? `${actorId}:${name}` : name,
|
|
87
180
|
sub,
|
|
88
|
-
onOpen:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (!event.target || event.target === currentAgent.current) if (event.code >= 3e3) setStatus("unauthorized");
|
|
93
|
-
else {
|
|
94
|
-
setStatus("disconnected");
|
|
95
|
-
if (!event.wasClean) console.warn("[useAgent] WebSocket closed unexpectedly", {
|
|
96
|
-
code: event.code,
|
|
97
|
-
reason: event.reason,
|
|
98
|
-
closeReason: getCloseReason(event.code),
|
|
99
|
-
wasClean: event.wasClean
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
onClose?.(event);
|
|
103
|
-
},
|
|
104
|
-
onMessage: (event) => {
|
|
105
|
-
const msg = JSON.parse(event.data);
|
|
106
|
-
if (msg.type === "connection_status" && (!event.target || event.target === currentAgent.current)) {
|
|
107
|
-
if (msg.status === "unauthorized") agent.close(3e3);
|
|
108
|
-
setStatus(msg.status);
|
|
109
|
-
} else if (msg.type === "agent_type" && (!event.target || event.target === currentAgent.current)) setType(msg.agentType);
|
|
110
|
-
onMessage?.(event);
|
|
111
|
-
},
|
|
112
|
-
onError: (event) => {
|
|
113
|
-
if (!event.target || event.target === currentAgent.current) setStatus("error");
|
|
114
|
-
onError?.(event);
|
|
115
|
-
},
|
|
181
|
+
onOpen: handleOpen,
|
|
182
|
+
onClose: handleClose,
|
|
183
|
+
onMessage: handleMessage,
|
|
184
|
+
onError: handleError,
|
|
116
185
|
...getTokenArgs(authToken)
|
|
117
186
|
});
|
|
118
|
-
|
|
187
|
+
useLayoutEffect(() => {
|
|
188
|
+
committedConnection.current = {
|
|
189
|
+
connection: nextConnection,
|
|
190
|
+
callbacks: {
|
|
191
|
+
onOpen,
|
|
192
|
+
onClose,
|
|
193
|
+
onError,
|
|
194
|
+
onMessage
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}, [
|
|
198
|
+
nextConnection,
|
|
199
|
+
onOpen,
|
|
200
|
+
onClose,
|
|
201
|
+
onError,
|
|
202
|
+
onMessage
|
|
203
|
+
]);
|
|
119
204
|
return {
|
|
120
205
|
agent,
|
|
121
206
|
status,
|
|
@@ -173,6 +258,11 @@ function useChat(options) {
|
|
|
173
258
|
}
|
|
174
259
|
//#endregion
|
|
175
260
|
//#region src/useAssistant.ts
|
|
261
|
+
function getAssistantConnectionStatus(currentChatName, isChatEnabled, assistantStatus, chatStatus) {
|
|
262
|
+
if (currentChatName === void 0) return assistantStatus;
|
|
263
|
+
if (!isChatEnabled) return assistantStatus === "connected" ? "connecting" : assistantStatus;
|
|
264
|
+
return chatStatus;
|
|
265
|
+
}
|
|
176
266
|
function useAssistant(options) {
|
|
177
267
|
const { toolContext, welcomeMessage, initialChatId, ...agentOptions } = options;
|
|
178
268
|
const [chatId, setChatId] = useState(initialChatId);
|
|
@@ -262,7 +352,6 @@ function useAssistant(options) {
|
|
|
262
352
|
await refreshChats();
|
|
263
353
|
};
|
|
264
354
|
const isChatEnabled = !!chatId && !!subAgentName;
|
|
265
|
-
const isChatReady = isChatEnabled && assistantStatus === "connected";
|
|
266
355
|
const chat = useChat({
|
|
267
356
|
...agentOptions,
|
|
268
357
|
enabled: isChatEnabled,
|
|
@@ -270,7 +359,6 @@ function useAssistant(options) {
|
|
|
270
359
|
agent: subAgentName,
|
|
271
360
|
name: chatId
|
|
272
361
|
}] : void 0,
|
|
273
|
-
getInitialMessages: isChatReady ? void 0 : null,
|
|
274
362
|
toolContext,
|
|
275
363
|
welcomeMessage
|
|
276
364
|
});
|
|
@@ -287,7 +375,7 @@ function useAssistant(options) {
|
|
|
287
375
|
draftChat,
|
|
288
376
|
scheduleDraftRefresh
|
|
289
377
|
]);
|
|
290
|
-
const status = isChatEnabled
|
|
378
|
+
const status = getAssistantConnectionStatus(chatId, isChatEnabled, assistantStatus, chat.status);
|
|
291
379
|
return {
|
|
292
380
|
currentChatName: chatId,
|
|
293
381
|
chats: draftChat && draftChat.name === chatId && !chats.some((persistedChat) => persistedChat.name === draftChat.name) ? [draftChat, ...chats] : chats,
|