bs-agent 0.0.9 → 0.0.12
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 +496 -410
- package/dist/core/index.cjs +520 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +133 -0
- package/dist/core/index.d.ts +133 -0
- package/dist/core/index.js +489 -0
- package/dist/core/index.js.map +1 -0
- package/dist/react/index.cjs +1318 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +287 -0
- package/dist/react/index.d.ts +287 -0
- package/dist/react/index.js +1286 -0
- package/dist/react/index.js.map +1 -0
- package/dist/types-DryLPWU9.d.cts +160 -0
- package/dist/types-DryLPWU9.d.ts +160 -0
- package/package.json +26 -11
- package/dist/index.cjs +0 -915
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -196
- package/dist/index.d.ts +0 -196
- package/dist/index.js +0 -886
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,1318 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/react/index.ts
|
|
21
|
+
var react_exports = {};
|
|
22
|
+
__export(react_exports, {
|
|
23
|
+
AGENT_DEBUG_DATA_KEY: () => AGENT_DEBUG_DATA_KEY,
|
|
24
|
+
AGENT_SESSIONS_KEY: () => AGENT_SESSIONS_KEY,
|
|
25
|
+
AgentContextProvider: () => AgentContextProvider,
|
|
26
|
+
AgentToolContext: () => AgentToolContext,
|
|
27
|
+
DEFAULT_SESSION_NAME: () => DEFAULT_SESSION_NAME,
|
|
28
|
+
TEMPORARY_SESSION_ID: () => TEMPORARY_SESSION_ID,
|
|
29
|
+
ToolRenderer: () => ToolRenderer,
|
|
30
|
+
cleanSchema: () => cleanSchema,
|
|
31
|
+
tryParseJSON: () => tryParseJSON,
|
|
32
|
+
updateAgentMessageParts: () => updateAgentMessageParts,
|
|
33
|
+
useAgent: () => useAgent,
|
|
34
|
+
useAgentContext: () => useAgentContext,
|
|
35
|
+
useAgentGlobalState: () => useAgentGlobalState,
|
|
36
|
+
useClientTool: () => useClientTool
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(react_exports);
|
|
39
|
+
|
|
40
|
+
// src/react/agent-context.tsx
|
|
41
|
+
var import_react5 = require("react");
|
|
42
|
+
|
|
43
|
+
// src/react/use-agent.ts
|
|
44
|
+
var import_react3 = require("react");
|
|
45
|
+
|
|
46
|
+
// src/react/constants.ts
|
|
47
|
+
var AGENT_SESSIONS_KEY = "buildship:agent:conversations";
|
|
48
|
+
var AGENT_DEBUG_DATA_KEY = "buildship:agent:debug";
|
|
49
|
+
var DEFAULT_SESSION_NAME = "New Chat";
|
|
50
|
+
var TEMPORARY_SESSION_ID = "sess_temp";
|
|
51
|
+
|
|
52
|
+
// src/react/session-utils.ts
|
|
53
|
+
var import_react = require("react");
|
|
54
|
+
var useSessionUtils = (agentId, allSessions, setAllSessions, currentSessionId, setCurrentSessionId, messagesRef) => {
|
|
55
|
+
const agentSessions = (0, import_react.useMemo)(() => allSessions[agentId] || {}, [agentId, allSessions]);
|
|
56
|
+
const syncSessionRef = (0, import_react.useRef)();
|
|
57
|
+
syncSessionRef.current = (updatedMessages) => {
|
|
58
|
+
if (!currentSessionId || currentSessionId === TEMPORARY_SESSION_ID) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
setAllSessions((prev) => ({
|
|
62
|
+
...prev,
|
|
63
|
+
[agentId]: {
|
|
64
|
+
...prev[agentId],
|
|
65
|
+
[currentSessionId]: {
|
|
66
|
+
...prev[agentId]?.[currentSessionId],
|
|
67
|
+
messages: updatedMessages ?? messagesRef.current,
|
|
68
|
+
updatedAt: Date.now()
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}));
|
|
72
|
+
};
|
|
73
|
+
const getInitialSessionId = () => {
|
|
74
|
+
const sessions = Object.values(agentSessions);
|
|
75
|
+
if (sessions.length > 0) {
|
|
76
|
+
return sessions.sort((a, b) => b.updatedAt - a.updatedAt)[0].id;
|
|
77
|
+
}
|
|
78
|
+
return TEMPORARY_SESSION_ID;
|
|
79
|
+
};
|
|
80
|
+
const switchSession = (0, import_react.useCallback)(
|
|
81
|
+
(sessionId = TEMPORARY_SESSION_ID) => {
|
|
82
|
+
setCurrentSessionId(sessionId);
|
|
83
|
+
},
|
|
84
|
+
[setCurrentSessionId]
|
|
85
|
+
);
|
|
86
|
+
const deleteSession = (0, import_react.useCallback)(
|
|
87
|
+
(sessionId) => {
|
|
88
|
+
if (!sessionId || sessionId === TEMPORARY_SESSION_ID) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
setAllSessions((prev) => {
|
|
92
|
+
const updatedAgentSessions = { ...prev[agentId] };
|
|
93
|
+
delete updatedAgentSessions[sessionId];
|
|
94
|
+
return {
|
|
95
|
+
...prev,
|
|
96
|
+
[agentId]: updatedAgentSessions
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
if (sessionId === currentSessionId) {
|
|
100
|
+
const remainingSessions = Object.values(agentSessions).filter((s) => s.id !== sessionId);
|
|
101
|
+
if (remainingSessions.length > 0) {
|
|
102
|
+
const mostRecent = remainingSessions.sort((a, b) => b.updatedAt - a.updatedAt)[0];
|
|
103
|
+
setCurrentSessionId(mostRecent.id);
|
|
104
|
+
} else {
|
|
105
|
+
setCurrentSessionId(TEMPORARY_SESSION_ID);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
[agentId, currentSessionId, agentSessions, setAllSessions, setCurrentSessionId]
|
|
110
|
+
);
|
|
111
|
+
const sessionsList = (0, import_react.useMemo)(
|
|
112
|
+
() => Object.values(agentSessions).sort((a, b) => b.updatedAt - a.updatedAt),
|
|
113
|
+
[agentSessions]
|
|
114
|
+
);
|
|
115
|
+
const createSessionFromResponse = (sessionId, sessionName, currentMessages) => {
|
|
116
|
+
setAllSessions((prev) => ({
|
|
117
|
+
...prev,
|
|
118
|
+
[agentId]: {
|
|
119
|
+
...prev[agentId],
|
|
120
|
+
[sessionId]: {
|
|
121
|
+
id: sessionId,
|
|
122
|
+
createdAt: Date.now(),
|
|
123
|
+
updatedAt: Date.now(),
|
|
124
|
+
messages: currentMessages,
|
|
125
|
+
name: sessionName
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}));
|
|
129
|
+
};
|
|
130
|
+
return {
|
|
131
|
+
agentSessions,
|
|
132
|
+
syncSessionRef,
|
|
133
|
+
getInitialSessionId,
|
|
134
|
+
switchSession,
|
|
135
|
+
deleteSession,
|
|
136
|
+
sessionsList,
|
|
137
|
+
createSessionFromResponse
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// src/react/debug-handlers.ts
|
|
142
|
+
var createDebugHandlers = (setDebugData) => {
|
|
143
|
+
const handleStreamEvent = (event) => {
|
|
144
|
+
const executionId = event.meta.executionId;
|
|
145
|
+
switch (event.type) {
|
|
146
|
+
case "tool_call_start": {
|
|
147
|
+
const { callId, toolName, toolType, inputs, serverName } = event.data;
|
|
148
|
+
setDebugData((prev) => ({
|
|
149
|
+
...prev,
|
|
150
|
+
[executionId]: [
|
|
151
|
+
...prev[executionId] || [],
|
|
152
|
+
{
|
|
153
|
+
itemType: "tool_call",
|
|
154
|
+
toolName,
|
|
155
|
+
callId,
|
|
156
|
+
toolType,
|
|
157
|
+
status: "progress",
|
|
158
|
+
inputs,
|
|
159
|
+
serverName
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
}));
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
case "tool_call_end": {
|
|
166
|
+
const { callId, result, error } = event.data;
|
|
167
|
+
setDebugData((prev) => {
|
|
168
|
+
const currentData = [...prev[executionId] || []];
|
|
169
|
+
for (let i = currentData.length - 1; i >= 0; i--) {
|
|
170
|
+
if (currentData[i].itemType === "tool_call") {
|
|
171
|
+
const toolItem = currentData[i];
|
|
172
|
+
if (toolItem.callId === callId) {
|
|
173
|
+
currentData[i] = {
|
|
174
|
+
...toolItem,
|
|
175
|
+
status: error ? "error" : "complete",
|
|
176
|
+
output: result,
|
|
177
|
+
error
|
|
178
|
+
};
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
...prev,
|
|
185
|
+
[executionId]: currentData
|
|
186
|
+
};
|
|
187
|
+
});
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
case "reasoning_delta": {
|
|
191
|
+
const { delta, index } = event.data;
|
|
192
|
+
setDebugData((prev) => {
|
|
193
|
+
const currentData = [...prev[executionId] || []];
|
|
194
|
+
let existingItemIndex = -1;
|
|
195
|
+
for (let i = currentData.length - 1; i >= 0; i--) {
|
|
196
|
+
const item = currentData[i];
|
|
197
|
+
if (item.itemType === "reasoning" && item.index === index) {
|
|
198
|
+
existingItemIndex = i;
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (existingItemIndex === -1) {
|
|
203
|
+
currentData.push({ itemType: "reasoning", reasoning: delta, index });
|
|
204
|
+
} else {
|
|
205
|
+
currentData[existingItemIndex] = {
|
|
206
|
+
itemType: "reasoning",
|
|
207
|
+
reasoning: currentData[existingItemIndex].reasoning + delta,
|
|
208
|
+
index
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
...prev,
|
|
213
|
+
[executionId]: currentData
|
|
214
|
+
};
|
|
215
|
+
});
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
case "agent_handoff": {
|
|
219
|
+
setDebugData((prev) => ({
|
|
220
|
+
...prev,
|
|
221
|
+
[executionId]: [
|
|
222
|
+
...prev[executionId] || [],
|
|
223
|
+
{
|
|
224
|
+
itemType: "handoff",
|
|
225
|
+
agentName: event.data.agentName
|
|
226
|
+
}
|
|
227
|
+
]
|
|
228
|
+
}));
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
case "run_error": {
|
|
232
|
+
setDebugData((prev) => ({
|
|
233
|
+
...prev,
|
|
234
|
+
[executionId]: [
|
|
235
|
+
...prev[executionId] || [],
|
|
236
|
+
{
|
|
237
|
+
itemType: "run_error",
|
|
238
|
+
message: event.data.message,
|
|
239
|
+
code: event.data.code
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
}));
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
return {
|
|
248
|
+
handleStreamEvent
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// src/react/client-tools.ts
|
|
253
|
+
var import_react2 = require("react");
|
|
254
|
+
var import_zod = require("zod");
|
|
255
|
+
|
|
256
|
+
// src/react/utils/schema.ts
|
|
257
|
+
function cleanSchema(obj) {
|
|
258
|
+
if (Array.isArray(obj)) {
|
|
259
|
+
return obj.map(cleanSchema);
|
|
260
|
+
} else if (obj !== null && typeof obj === "object") {
|
|
261
|
+
const newObj = {};
|
|
262
|
+
const currentObj = obj;
|
|
263
|
+
for (const key in currentObj) {
|
|
264
|
+
if (key === "propertyNames" || key === "$schema") {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
newObj[key] = cleanSchema(currentObj[key]);
|
|
268
|
+
}
|
|
269
|
+
if (newObj.type === "object") {
|
|
270
|
+
if (newObj.additionalProperties === void 0 || newObj.additionalProperties && typeof newObj.additionalProperties === "object" && Object.keys(newObj.additionalProperties).length === 0) {
|
|
271
|
+
newObj.additionalProperties = false;
|
|
272
|
+
}
|
|
273
|
+
if (newObj.additionalProperties === false && newObj.properties) {
|
|
274
|
+
newObj.required = Object.keys(newObj.properties);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return newObj;
|
|
278
|
+
}
|
|
279
|
+
return obj;
|
|
280
|
+
}
|
|
281
|
+
function tryParseJSON(value) {
|
|
282
|
+
if (typeof value === "string") {
|
|
283
|
+
try {
|
|
284
|
+
return JSON.parse(value);
|
|
285
|
+
} catch {
|
|
286
|
+
return value;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return value;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// src/react/client-tools.ts
|
|
293
|
+
function resolveParameters(params) {
|
|
294
|
+
let schema;
|
|
295
|
+
if (params && typeof params === "object" && "_def" in params) {
|
|
296
|
+
schema = (0, import_zod.toJSONSchema)(params);
|
|
297
|
+
delete schema.$schema;
|
|
298
|
+
} else {
|
|
299
|
+
schema = cleanSchema(params);
|
|
300
|
+
}
|
|
301
|
+
if (schema.type === "object") {
|
|
302
|
+
schema.additionalProperties = false;
|
|
303
|
+
if (schema.properties) {
|
|
304
|
+
schema.required = Object.keys(schema.properties);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return schema;
|
|
308
|
+
}
|
|
309
|
+
function useClientToolHelpers(agentId, toolContext) {
|
|
310
|
+
const getClientToolsMap = (0, import_react2.useCallback)(() => {
|
|
311
|
+
if (!toolContext) return /* @__PURE__ */ new Map();
|
|
312
|
+
const tools = toolContext.getToolsForAgent(agentId);
|
|
313
|
+
const map = /* @__PURE__ */ new Map();
|
|
314
|
+
for (const tool of tools) {
|
|
315
|
+
if (tool.handler) {
|
|
316
|
+
map.set(tool.name, {
|
|
317
|
+
name: tool.name,
|
|
318
|
+
description: tool.description,
|
|
319
|
+
parameters: tool.parameters,
|
|
320
|
+
await: tool.await,
|
|
321
|
+
handler: tool.handler
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return map;
|
|
326
|
+
}, [agentId, toolContext]);
|
|
327
|
+
const getClientToolDefs = (0, import_react2.useCallback)(() => {
|
|
328
|
+
if (!toolContext) return [];
|
|
329
|
+
const tools = toolContext.getToolsForAgent(agentId);
|
|
330
|
+
return tools.map((tool) => ({
|
|
331
|
+
name: tool.name,
|
|
332
|
+
description: tool.description,
|
|
333
|
+
parameters: resolveParameters(tool.parameters),
|
|
334
|
+
await: tool.await
|
|
335
|
+
}));
|
|
336
|
+
}, [agentId, toolContext]);
|
|
337
|
+
return { getClientToolsMap, getClientToolDefs };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// src/core/stream.ts
|
|
341
|
+
function tryParseJSON2(value) {
|
|
342
|
+
if (typeof value === "string") {
|
|
343
|
+
try {
|
|
344
|
+
return JSON.parse(value);
|
|
345
|
+
} catch {
|
|
346
|
+
return value;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return value;
|
|
350
|
+
}
|
|
351
|
+
var FatalStreamError = class extends Error {
|
|
352
|
+
constructor(message, statusCode) {
|
|
353
|
+
super(message);
|
|
354
|
+
this.statusCode = statusCode;
|
|
355
|
+
this.name = "FatalStreamError";
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
async function executeStream(options) {
|
|
359
|
+
const {
|
|
360
|
+
url,
|
|
361
|
+
body,
|
|
362
|
+
headers,
|
|
363
|
+
callbacks,
|
|
364
|
+
clientTools,
|
|
365
|
+
signal,
|
|
366
|
+
onSessionId,
|
|
367
|
+
onPaused,
|
|
368
|
+
onAutoResume,
|
|
369
|
+
onResponse
|
|
370
|
+
} = options;
|
|
371
|
+
let fullText = "";
|
|
372
|
+
const pendingAutoResumes = [];
|
|
373
|
+
const response = await fetch(url, {
|
|
374
|
+
method: "POST",
|
|
375
|
+
headers: {
|
|
376
|
+
"Content-Type": "application/json",
|
|
377
|
+
Accept: "text/event-stream",
|
|
378
|
+
...headers
|
|
379
|
+
},
|
|
380
|
+
body: JSON.stringify(body),
|
|
381
|
+
signal
|
|
382
|
+
});
|
|
383
|
+
const sessionId = response.headers.get("X-BuildShip-Agent-Session-ID");
|
|
384
|
+
if (sessionId && onSessionId) {
|
|
385
|
+
const sessionName = response.headers.get("X-BuildShip-Agent-Session-Name") || void 0;
|
|
386
|
+
onSessionId(sessionId, sessionName);
|
|
387
|
+
}
|
|
388
|
+
onResponse?.(response);
|
|
389
|
+
if (!response.ok) {
|
|
390
|
+
let errorBody = "";
|
|
391
|
+
try {
|
|
392
|
+
errorBody = await response.text();
|
|
393
|
+
} catch {
|
|
394
|
+
}
|
|
395
|
+
const error = new FatalStreamError(
|
|
396
|
+
`HTTP ${response.status}: ${errorBody || response.statusText}`,
|
|
397
|
+
response.status
|
|
398
|
+
);
|
|
399
|
+
callbacks.onError?.(error);
|
|
400
|
+
throw error;
|
|
401
|
+
}
|
|
402
|
+
if (!response.body) {
|
|
403
|
+
callbacks.onComplete?.(fullText);
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
const reader = response.body.getReader();
|
|
407
|
+
const decoder = new TextDecoder();
|
|
408
|
+
let buffer = "";
|
|
409
|
+
try {
|
|
410
|
+
while (true) {
|
|
411
|
+
const { done, value } = await reader.read();
|
|
412
|
+
if (done) break;
|
|
413
|
+
buffer += decoder.decode(value, { stream: true });
|
|
414
|
+
const parts = buffer.split("\n\n");
|
|
415
|
+
buffer = parts.pop() || "";
|
|
416
|
+
for (const part of parts) {
|
|
417
|
+
const trimmedPart = part.trim();
|
|
418
|
+
if (!trimmedPart) continue;
|
|
419
|
+
let jsonStr = "";
|
|
420
|
+
for (const line of trimmedPart.split("\n")) {
|
|
421
|
+
if (line.startsWith("data: ")) {
|
|
422
|
+
jsonStr += line.slice(6);
|
|
423
|
+
} else if (line.startsWith("data:")) {
|
|
424
|
+
jsonStr += line.slice(5);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
jsonStr = jsonStr.trim();
|
|
428
|
+
if (!jsonStr) continue;
|
|
429
|
+
let raw;
|
|
430
|
+
try {
|
|
431
|
+
raw = JSON.parse(jsonStr);
|
|
432
|
+
} catch {
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
const streamEvent = normalizeEvent(raw);
|
|
436
|
+
handleEvent(
|
|
437
|
+
streamEvent,
|
|
438
|
+
fullText,
|
|
439
|
+
callbacks,
|
|
440
|
+
clientTools,
|
|
441
|
+
onPaused,
|
|
442
|
+
onAutoResume,
|
|
443
|
+
pendingAutoResumes
|
|
444
|
+
);
|
|
445
|
+
if (streamEvent.type === "text_delta") {
|
|
446
|
+
fullText += streamEvent.data;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if (buffer.trim()) {
|
|
451
|
+
let jsonStr = "";
|
|
452
|
+
for (const line of buffer.trim().split("\n")) {
|
|
453
|
+
if (line.startsWith("data: ")) {
|
|
454
|
+
jsonStr += line.slice(6);
|
|
455
|
+
} else if (line.startsWith("data:")) {
|
|
456
|
+
jsonStr += line.slice(5);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
jsonStr = jsonStr.trim();
|
|
460
|
+
if (jsonStr) {
|
|
461
|
+
try {
|
|
462
|
+
const raw = JSON.parse(jsonStr);
|
|
463
|
+
const streamEvent = normalizeEvent(raw);
|
|
464
|
+
handleEvent(
|
|
465
|
+
streamEvent,
|
|
466
|
+
fullText,
|
|
467
|
+
callbacks,
|
|
468
|
+
clientTools,
|
|
469
|
+
onPaused,
|
|
470
|
+
onAutoResume,
|
|
471
|
+
pendingAutoResumes
|
|
472
|
+
);
|
|
473
|
+
if (streamEvent.type === "text_delta") {
|
|
474
|
+
fullText += streamEvent.data;
|
|
475
|
+
}
|
|
476
|
+
} catch {
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
} catch (err) {
|
|
481
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
482
|
+
throw err;
|
|
483
|
+
}
|
|
484
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
485
|
+
callbacks.onError?.(error);
|
|
486
|
+
throw error;
|
|
487
|
+
}
|
|
488
|
+
if (pendingAutoResumes.length > 0) {
|
|
489
|
+
await Promise.allSettled(pendingAutoResumes);
|
|
490
|
+
}
|
|
491
|
+
callbacks.onComplete?.(fullText);
|
|
492
|
+
}
|
|
493
|
+
function normalizeEvent(raw) {
|
|
494
|
+
const typeMap = {
|
|
495
|
+
llm_text_delta: "text_delta",
|
|
496
|
+
llm_reasoning_delta: "reasoning_delta"
|
|
497
|
+
};
|
|
498
|
+
if (raw && typeof raw.type === "string" && typeMap[raw.type]) {
|
|
499
|
+
raw.type = typeMap[raw.type];
|
|
500
|
+
}
|
|
501
|
+
return raw;
|
|
502
|
+
}
|
|
503
|
+
function handleEvent(event, _fullText, callbacks, clientTools, onPaused, onAutoResume, pendingAutoResumes) {
|
|
504
|
+
callbacks.onEvent?.(event);
|
|
505
|
+
switch (event.type) {
|
|
506
|
+
case "text_delta": {
|
|
507
|
+
callbacks.onText?.(event.data);
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
case "reasoning_delta": {
|
|
511
|
+
callbacks.onReasoning?.(event.data.delta, event.data.index);
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
514
|
+
case "agent_handoff": {
|
|
515
|
+
callbacks.onAgentHandoff?.(event.data.agentName);
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
518
|
+
case "tool_call_start": {
|
|
519
|
+
const { callId, toolName, toolType, inputs: rawInputs, paused } = event.data;
|
|
520
|
+
const inputs = tryParseJSON2(rawInputs);
|
|
521
|
+
callbacks.onToolStart?.(toolName, toolType);
|
|
522
|
+
if (toolType === "client") {
|
|
523
|
+
const tool = clientTools.get(toolName);
|
|
524
|
+
if (tool) {
|
|
525
|
+
if (paused && tool.handler) {
|
|
526
|
+
const handlerPromise = (async () => {
|
|
527
|
+
try {
|
|
528
|
+
const result = await tool.handler(inputs);
|
|
529
|
+
await onAutoResume?.(callId, result);
|
|
530
|
+
} catch {
|
|
531
|
+
callbacks.onPaused?.(toolName, inputs);
|
|
532
|
+
onPaused?.({ callId, toolName, args: inputs });
|
|
533
|
+
}
|
|
534
|
+
})();
|
|
535
|
+
pendingAutoResumes.push(handlerPromise);
|
|
536
|
+
} else if (paused) {
|
|
537
|
+
callbacks.onPaused?.(toolName, inputs);
|
|
538
|
+
onPaused?.({ callId, toolName, args: inputs });
|
|
539
|
+
} else if (tool.handler) {
|
|
540
|
+
try {
|
|
541
|
+
tool.handler(inputs);
|
|
542
|
+
} catch {
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
} else if (paused) {
|
|
546
|
+
callbacks.onPaused?.(toolName, inputs);
|
|
547
|
+
onPaused?.({ callId, toolName, args: inputs });
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
case "tool_call_end": {
|
|
553
|
+
const { toolName, result, error } = event.data;
|
|
554
|
+
callbacks.onToolEnd?.(toolName, result, error);
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
557
|
+
case "run_error": {
|
|
558
|
+
const { message, code } = event.data;
|
|
559
|
+
const error = new Error(message);
|
|
560
|
+
if (code != null) error.code = code;
|
|
561
|
+
callbacks.onError?.(error);
|
|
562
|
+
break;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// src/react/utils/message.ts
|
|
568
|
+
function updateAgentMessageParts(parts, newPart) {
|
|
569
|
+
const last = parts[parts.length - 1];
|
|
570
|
+
const canMerge = last?.type === "text" && newPart.type === "text" && newPart.firstSequence === last.lastSequence + 1;
|
|
571
|
+
if (canMerge) {
|
|
572
|
+
return [
|
|
573
|
+
...parts.slice(0, -1),
|
|
574
|
+
{
|
|
575
|
+
...last,
|
|
576
|
+
text: last.text + newPart.text,
|
|
577
|
+
lastSequence: newPart.lastSequence
|
|
578
|
+
}
|
|
579
|
+
];
|
|
580
|
+
}
|
|
581
|
+
return [...parts, newPart];
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// src/react/stream-callbacks.ts
|
|
585
|
+
function buildStreamCallbacks(deps, debugKey) {
|
|
586
|
+
const { setMessages, setInProgress, syncSessionRef, messagesRef, toolContext, agentId } = deps;
|
|
587
|
+
return {
|
|
588
|
+
onComplete: () => {
|
|
589
|
+
console.log("Agent closed");
|
|
590
|
+
setInProgress(false);
|
|
591
|
+
if (syncSessionRef.current) {
|
|
592
|
+
syncSessionRef.current(messagesRef.current);
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
onError: (error) => {
|
|
596
|
+
console.log("Agent error", error);
|
|
597
|
+
setInProgress(false);
|
|
598
|
+
if (syncSessionRef.current) {
|
|
599
|
+
syncSessionRef.current(messagesRef.current);
|
|
600
|
+
}
|
|
601
|
+
},
|
|
602
|
+
onEvent: (event) => {
|
|
603
|
+
if (event.type === "text_delta") {
|
|
604
|
+
handleTextDelta(event, setMessages, syncSessionRef);
|
|
605
|
+
} else if (event.type === "tool_call_start" && event.data.toolType === "client") {
|
|
606
|
+
handleClientToolCall(event, setMessages, syncSessionRef, toolContext, agentId);
|
|
607
|
+
if (debugKey) {
|
|
608
|
+
const debugMeta = { ...event.meta, executionId: debugKey };
|
|
609
|
+
deps.debugHandlers.handleStreamEvent({
|
|
610
|
+
...event,
|
|
611
|
+
meta: debugMeta
|
|
612
|
+
});
|
|
613
|
+
if (!event.data.paused) {
|
|
614
|
+
deps.debugHandlers.handleStreamEvent({
|
|
615
|
+
type: "tool_call_end",
|
|
616
|
+
data: {
|
|
617
|
+
callId: event.data.callId,
|
|
618
|
+
toolName: event.data.toolName,
|
|
619
|
+
toolType: event.data.toolType
|
|
620
|
+
},
|
|
621
|
+
meta: debugMeta
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
} else if (debugKey) {
|
|
626
|
+
deps.debugHandlers.handleStreamEvent({
|
|
627
|
+
...event,
|
|
628
|
+
meta: { ...event.meta, executionId: debugKey }
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
var TEMPORARY_SESSION_ID2 = "sess_temp";
|
|
635
|
+
var DEFAULT_SESSION_NAME2 = "New Chat";
|
|
636
|
+
async function executeAgentStream(deps, body, headers, callbacks, debugKey) {
|
|
637
|
+
const {
|
|
638
|
+
agentUrl,
|
|
639
|
+
signal,
|
|
640
|
+
currentSessionId,
|
|
641
|
+
lastRequestContextRef,
|
|
642
|
+
messagesRef,
|
|
643
|
+
createSessionFromResponse,
|
|
644
|
+
setMessages,
|
|
645
|
+
syncSessionRef,
|
|
646
|
+
runAgentRef
|
|
647
|
+
} = deps;
|
|
648
|
+
await executeStream({
|
|
649
|
+
url: agentUrl,
|
|
650
|
+
body,
|
|
651
|
+
headers,
|
|
652
|
+
callbacks,
|
|
653
|
+
clientTools: deps.getClientToolsMap(),
|
|
654
|
+
signal,
|
|
655
|
+
onSessionId: (sessionId, sessionName) => {
|
|
656
|
+
lastRequestContextRef.current.sessionId = sessionId;
|
|
657
|
+
if (!currentSessionId || currentSessionId === TEMPORARY_SESSION_ID2) {
|
|
658
|
+
createSessionFromResponse(
|
|
659
|
+
sessionId,
|
|
660
|
+
sessionName || DEFAULT_SESSION_NAME2,
|
|
661
|
+
messagesRef.current
|
|
662
|
+
);
|
|
663
|
+
deps.setCurrentSessionId(sessionId);
|
|
664
|
+
}
|
|
665
|
+
},
|
|
666
|
+
// Session creation is handled by onSessionId above.
|
|
667
|
+
onAutoResume: async (callId, result) => {
|
|
668
|
+
setMessages((prev) => {
|
|
669
|
+
const updatedMessages = prev.map((msg) => {
|
|
670
|
+
if (msg.parts) {
|
|
671
|
+
const updatedParts = msg.parts.map((part) => {
|
|
672
|
+
if (part.type === "widget" && part.callId === callId) {
|
|
673
|
+
return { ...part, status: "submitted", result };
|
|
674
|
+
}
|
|
675
|
+
return part;
|
|
676
|
+
});
|
|
677
|
+
return { ...msg, parts: updatedParts };
|
|
678
|
+
}
|
|
679
|
+
return msg;
|
|
680
|
+
});
|
|
681
|
+
if (syncSessionRef.current) {
|
|
682
|
+
syncSessionRef.current(updatedMessages);
|
|
683
|
+
}
|
|
684
|
+
return updatedMessages;
|
|
685
|
+
});
|
|
686
|
+
if (debugKey) {
|
|
687
|
+
deps.debugHandlers.handleStreamEvent({
|
|
688
|
+
type: "tool_call_end",
|
|
689
|
+
data: { callId, toolName: "", toolType: "client", result },
|
|
690
|
+
meta: { executionId: debugKey, sequence: 0 }
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
try {
|
|
694
|
+
await runAgentRef.current?.(void 0, {
|
|
695
|
+
resumeToolCallId: callId,
|
|
696
|
+
resumeToolResult: result
|
|
697
|
+
});
|
|
698
|
+
} catch (error) {
|
|
699
|
+
console.error("Auto-resume failed", error);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
function handleTextDelta(event, setMessages, syncSessionRef) {
|
|
705
|
+
const sequence = event.meta.sequence;
|
|
706
|
+
const text = event.data;
|
|
707
|
+
const eventExecutionId = event.meta.executionId;
|
|
708
|
+
setMessages((prev) => {
|
|
709
|
+
const lastMessage = prev[prev.length - 1];
|
|
710
|
+
const newPart = {
|
|
711
|
+
type: "text",
|
|
712
|
+
text,
|
|
713
|
+
firstSequence: sequence,
|
|
714
|
+
lastSequence: sequence
|
|
715
|
+
};
|
|
716
|
+
let updatedMessages;
|
|
717
|
+
if (lastMessage?.role === "agent") {
|
|
718
|
+
const updatedMessage = {
|
|
719
|
+
...lastMessage,
|
|
720
|
+
content: lastMessage.content + text,
|
|
721
|
+
parts: updateAgentMessageParts(lastMessage.parts || [], newPart)
|
|
722
|
+
};
|
|
723
|
+
updatedMessages = [...prev.slice(0, -1), updatedMessage];
|
|
724
|
+
} else {
|
|
725
|
+
const updatedMessage = {
|
|
726
|
+
role: "agent",
|
|
727
|
+
content: text,
|
|
728
|
+
parts: [newPart],
|
|
729
|
+
executionId: eventExecutionId
|
|
730
|
+
};
|
|
731
|
+
updatedMessages = [...prev, updatedMessage];
|
|
732
|
+
}
|
|
733
|
+
if (syncSessionRef.current) {
|
|
734
|
+
syncSessionRef.current(updatedMessages);
|
|
735
|
+
}
|
|
736
|
+
return updatedMessages;
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
function handleClientToolCall(event, setMessages, syncSessionRef, toolContext, agentId) {
|
|
740
|
+
const tool = toolContext?.getTool(agentId, event.data.toolName);
|
|
741
|
+
if (!tool?.render) return;
|
|
742
|
+
setMessages((prev) => {
|
|
743
|
+
const lastMessage = prev[prev.length - 1];
|
|
744
|
+
const newPart = {
|
|
745
|
+
type: "widget",
|
|
746
|
+
toolName: event.data.toolName,
|
|
747
|
+
callId: event.data.callId,
|
|
748
|
+
inputs: tryParseJSON(event.data.inputs),
|
|
749
|
+
sequence: event.meta.sequence,
|
|
750
|
+
paused: event.data.paused,
|
|
751
|
+
status: "pending"
|
|
752
|
+
};
|
|
753
|
+
let updatedMessages;
|
|
754
|
+
if (lastMessage?.role === "agent") {
|
|
755
|
+
const updatedMessage = {
|
|
756
|
+
...lastMessage,
|
|
757
|
+
parts: updateAgentMessageParts(lastMessage.parts || [], newPart)
|
|
758
|
+
};
|
|
759
|
+
updatedMessages = [...prev.slice(0, -1), updatedMessage];
|
|
760
|
+
} else {
|
|
761
|
+
const updatedMessage = {
|
|
762
|
+
role: "agent",
|
|
763
|
+
content: "",
|
|
764
|
+
parts: [newPart],
|
|
765
|
+
executionId: event.meta.executionId
|
|
766
|
+
};
|
|
767
|
+
updatedMessages = [...prev, updatedMessage];
|
|
768
|
+
}
|
|
769
|
+
if (syncSessionRef.current) {
|
|
770
|
+
syncSessionRef.current(updatedMessages);
|
|
771
|
+
}
|
|
772
|
+
return updatedMessages;
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// src/react/use-agent.ts
|
|
777
|
+
function useAgent(agentId, agentUrl, accessKey) {
|
|
778
|
+
const { allSessions, setAllSessions, debugData, setDebugData } = useAgentGlobalState();
|
|
779
|
+
const toolContext = (0, import_react3.useContext)(AgentToolContext);
|
|
780
|
+
const [inProgress, setInProgress] = (0, import_react3.useState)(false);
|
|
781
|
+
const [messages, setMessages] = (0, import_react3.useState)([]);
|
|
782
|
+
const messagesRef = (0, import_react3.useRef)([]);
|
|
783
|
+
const [currentSessionId, setCurrentSessionId] = (0, import_react3.useState)(TEMPORARY_SESSION_ID);
|
|
784
|
+
const sessionUtils = useSessionUtils(
|
|
785
|
+
agentId,
|
|
786
|
+
allSessions,
|
|
787
|
+
setAllSessions,
|
|
788
|
+
currentSessionId,
|
|
789
|
+
setCurrentSessionId,
|
|
790
|
+
messagesRef
|
|
791
|
+
);
|
|
792
|
+
(0, import_react3.useEffect)(() => {
|
|
793
|
+
const initialSessionId = sessionUtils.getInitialSessionId();
|
|
794
|
+
setCurrentSessionId(initialSessionId);
|
|
795
|
+
}, []);
|
|
796
|
+
const debugHandlers = (0, import_react3.useMemo)(() => createDebugHandlers(setDebugData), [setDebugData]);
|
|
797
|
+
(0, import_react3.useEffect)(() => {
|
|
798
|
+
messagesRef.current = messages;
|
|
799
|
+
}, [messages]);
|
|
800
|
+
(0, import_react3.useEffect)(() => {
|
|
801
|
+
if (inProgress) return;
|
|
802
|
+
const session = sessionUtils.agentSessions[currentSessionId];
|
|
803
|
+
if (session) {
|
|
804
|
+
setMessages(session.messages);
|
|
805
|
+
} else {
|
|
806
|
+
setMessages([]);
|
|
807
|
+
}
|
|
808
|
+
}, [currentSessionId, agentId]);
|
|
809
|
+
(0, import_react3.useEffect)(() => {
|
|
810
|
+
return () => {
|
|
811
|
+
if (messagesRef.current.length > 0 && sessionUtils.syncSessionRef.current) {
|
|
812
|
+
sessionUtils.syncSessionRef.current();
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
}, []);
|
|
816
|
+
const controller = (0, import_react3.useRef)();
|
|
817
|
+
const lastRequestContextRef = (0, import_react3.useRef)({});
|
|
818
|
+
const { getClientToolsMap, getClientToolDefs } = useClientToolHelpers(agentId, toolContext);
|
|
819
|
+
const runAgentRef = (0, import_react3.useRef)();
|
|
820
|
+
const runAgent = (0, import_react3.useCallback)(
|
|
821
|
+
async (input, runOptions) => {
|
|
822
|
+
const realSessionId = currentSessionId && currentSessionId !== TEMPORARY_SESSION_ID ? currentSessionId : lastRequestContextRef.current.sessionId;
|
|
823
|
+
lastRequestContextRef.current = {
|
|
824
|
+
additionalHeaders: runOptions?.additionalHeaders ?? lastRequestContextRef.current.additionalHeaders,
|
|
825
|
+
additionalBody: runOptions?.additionalBody ?? lastRequestContextRef.current.additionalBody,
|
|
826
|
+
sessionId: realSessionId
|
|
827
|
+
};
|
|
828
|
+
setInProgress(true);
|
|
829
|
+
if (!runOptions?.resumeToolCallId && controller.current) {
|
|
830
|
+
controller.current.abort();
|
|
831
|
+
}
|
|
832
|
+
controller.current = new AbortController();
|
|
833
|
+
const headers = {
|
|
834
|
+
...lastRequestContextRef.current.additionalHeaders || {},
|
|
835
|
+
...accessKey ? { Authorization: `Bearer ${accessKey}` } : {}
|
|
836
|
+
};
|
|
837
|
+
if (realSessionId) {
|
|
838
|
+
headers["x-buildship-agent-session-id"] = realSessionId;
|
|
839
|
+
}
|
|
840
|
+
const body = {
|
|
841
|
+
stream: true,
|
|
842
|
+
...lastRequestContextRef.current.additionalBody || {}
|
|
843
|
+
};
|
|
844
|
+
if (input !== void 0) {
|
|
845
|
+
body.input = input;
|
|
846
|
+
}
|
|
847
|
+
if (runOptions?.context) {
|
|
848
|
+
Object.assign(body, { context: runOptions.context });
|
|
849
|
+
}
|
|
850
|
+
if (runOptions?.resumeToolCallId) {
|
|
851
|
+
body.toolCallResult = {
|
|
852
|
+
callId: runOptions.resumeToolCallId,
|
|
853
|
+
result: runOptions.resumeToolResult
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
const registeredToolDefs = getClientToolDefs() || [];
|
|
857
|
+
const legacyToolDefs = runOptions?.clientTools || [];
|
|
858
|
+
const allToolDefs = [...registeredToolDefs, ...legacyToolDefs];
|
|
859
|
+
if (allToolDefs.length > 0) {
|
|
860
|
+
body.clientTools = allToolDefs;
|
|
861
|
+
}
|
|
862
|
+
const debugKey = runOptions?.optimisticExecutionId || messagesRef.current.findLast((m) => m.role === "user")?.executionId;
|
|
863
|
+
const deps = {
|
|
864
|
+
agentUrl,
|
|
865
|
+
agentId,
|
|
866
|
+
accessKey,
|
|
867
|
+
currentSessionId,
|
|
868
|
+
messagesRef,
|
|
869
|
+
setMessages,
|
|
870
|
+
setInProgress,
|
|
871
|
+
setCurrentSessionId,
|
|
872
|
+
syncSessionRef: sessionUtils.syncSessionRef,
|
|
873
|
+
createSessionFromResponse: sessionUtils.createSessionFromResponse,
|
|
874
|
+
debugHandlers,
|
|
875
|
+
toolContext,
|
|
876
|
+
lastRequestContextRef,
|
|
877
|
+
getClientToolsMap,
|
|
878
|
+
signal: controller.current.signal,
|
|
879
|
+
runAgentRef
|
|
880
|
+
};
|
|
881
|
+
const callbacks = buildStreamCallbacks(deps, debugKey);
|
|
882
|
+
try {
|
|
883
|
+
await executeAgentStream(deps, body, headers, callbacks, debugKey);
|
|
884
|
+
} catch (error) {
|
|
885
|
+
console.log("Agent execution failed", error);
|
|
886
|
+
setInProgress(false);
|
|
887
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
888
|
+
sessionUtils.syncSessionRef.current(messagesRef.current);
|
|
889
|
+
}
|
|
890
|
+
throw error;
|
|
891
|
+
}
|
|
892
|
+
},
|
|
893
|
+
[
|
|
894
|
+
agentUrl,
|
|
895
|
+
accessKey,
|
|
896
|
+
currentSessionId,
|
|
897
|
+
sessionUtils.syncSessionRef,
|
|
898
|
+
sessionUtils.createSessionFromResponse,
|
|
899
|
+
debugHandlers,
|
|
900
|
+
getClientToolsMap,
|
|
901
|
+
getClientToolDefs,
|
|
902
|
+
agentId,
|
|
903
|
+
toolContext
|
|
904
|
+
]
|
|
905
|
+
);
|
|
906
|
+
(0, import_react3.useEffect)(() => {
|
|
907
|
+
runAgentRef.current = runAgent;
|
|
908
|
+
}, [runAgent]);
|
|
909
|
+
const handleSend = (0, import_react3.useCallback)(
|
|
910
|
+
async (input, options) => {
|
|
911
|
+
const userMessage = {
|
|
912
|
+
role: "user",
|
|
913
|
+
content: input,
|
|
914
|
+
executionId: Date.now().toString()
|
|
915
|
+
};
|
|
916
|
+
if (!options?.skipUserMessage) {
|
|
917
|
+
setMessages((prev) => {
|
|
918
|
+
const updatedMessages = [...prev, userMessage];
|
|
919
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
920
|
+
sessionUtils.syncSessionRef.current(updatedMessages);
|
|
921
|
+
}
|
|
922
|
+
return updatedMessages;
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
const effectiveExecutionId = options?.skipUserMessage ? messagesRef.current.findLast((m) => m.role === "user")?.executionId ?? userMessage.executionId : userMessage.executionId;
|
|
926
|
+
try {
|
|
927
|
+
await runAgent(input, {
|
|
928
|
+
...options,
|
|
929
|
+
optimisticExecutionId: effectiveExecutionId
|
|
930
|
+
});
|
|
931
|
+
} catch (error) {
|
|
932
|
+
if (!options?.skipUserMessage) {
|
|
933
|
+
setMessages((prev) => {
|
|
934
|
+
const updatedMessages = prev.some((m) => m === userMessage) ? prev : [...prev, userMessage];
|
|
935
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
936
|
+
sessionUtils.syncSessionRef.current(updatedMessages);
|
|
937
|
+
}
|
|
938
|
+
return updatedMessages;
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
throw error;
|
|
942
|
+
}
|
|
943
|
+
},
|
|
944
|
+
[runAgent, sessionUtils.syncSessionRef]
|
|
945
|
+
);
|
|
946
|
+
const resumeTool = (0, import_react3.useCallback)(
|
|
947
|
+
async (callId, result) => {
|
|
948
|
+
setMessages((prev) => {
|
|
949
|
+
const updatedMessages = prev.map((msg) => {
|
|
950
|
+
if (msg.parts) {
|
|
951
|
+
const updatedParts = msg.parts.map((part) => {
|
|
952
|
+
if (part.type === "widget" && part.callId === callId) {
|
|
953
|
+
return { ...part, status: "submitted", result };
|
|
954
|
+
}
|
|
955
|
+
return part;
|
|
956
|
+
});
|
|
957
|
+
return { ...msg, parts: updatedParts };
|
|
958
|
+
}
|
|
959
|
+
return msg;
|
|
960
|
+
});
|
|
961
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
962
|
+
sessionUtils.syncSessionRef.current(updatedMessages);
|
|
963
|
+
}
|
|
964
|
+
return updatedMessages;
|
|
965
|
+
});
|
|
966
|
+
const debugKey = messagesRef.current.findLast((m) => m.role === "user")?.executionId;
|
|
967
|
+
if (debugKey) {
|
|
968
|
+
debugHandlers.handleStreamEvent({
|
|
969
|
+
type: "tool_call_end",
|
|
970
|
+
data: { callId, toolName: "", toolType: "client", result },
|
|
971
|
+
meta: { executionId: debugKey, sequence: 0 }
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
await runAgent(void 0, {
|
|
975
|
+
resumeToolCallId: callId,
|
|
976
|
+
resumeToolResult: result
|
|
977
|
+
});
|
|
978
|
+
},
|
|
979
|
+
[runAgent, sessionUtils.syncSessionRef, debugHandlers]
|
|
980
|
+
);
|
|
981
|
+
const addOptimisticMessage = (0, import_react3.useCallback)(
|
|
982
|
+
(input) => {
|
|
983
|
+
const userMessage = {
|
|
984
|
+
role: "user",
|
|
985
|
+
content: input,
|
|
986
|
+
executionId: Date.now().toString()
|
|
987
|
+
};
|
|
988
|
+
setMessages((prev) => {
|
|
989
|
+
const updatedMessages = [...prev, userMessage];
|
|
990
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
991
|
+
sessionUtils.syncSessionRef.current(updatedMessages);
|
|
992
|
+
}
|
|
993
|
+
return updatedMessages;
|
|
994
|
+
});
|
|
995
|
+
},
|
|
996
|
+
[sessionUtils.syncSessionRef]
|
|
997
|
+
);
|
|
998
|
+
const abort = (0, import_react3.useCallback)(() => {
|
|
999
|
+
if (controller.current) {
|
|
1000
|
+
controller.current.abort();
|
|
1001
|
+
}
|
|
1002
|
+
setInProgress(false);
|
|
1003
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
1004
|
+
sessionUtils.syncSessionRef.current(messagesRef.current);
|
|
1005
|
+
}
|
|
1006
|
+
}, [sessionUtils.syncSessionRef]);
|
|
1007
|
+
return {
|
|
1008
|
+
inProgress,
|
|
1009
|
+
messages,
|
|
1010
|
+
handleSend,
|
|
1011
|
+
resumeTool,
|
|
1012
|
+
addOptimisticMessage,
|
|
1013
|
+
abort,
|
|
1014
|
+
sessionId: currentSessionId,
|
|
1015
|
+
switchSession: sessionUtils.switchSession,
|
|
1016
|
+
deleteSession: sessionUtils.deleteSession,
|
|
1017
|
+
sessions: sessionUtils.sessionsList,
|
|
1018
|
+
debugData
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
// src/react/utils/use-synced-local-storage.ts
|
|
1023
|
+
var import_react4 = require("react");
|
|
1024
|
+
function parseJSON(value, defaultValue) {
|
|
1025
|
+
if (value === null) return defaultValue;
|
|
1026
|
+
try {
|
|
1027
|
+
return JSON.parse(value);
|
|
1028
|
+
} catch {
|
|
1029
|
+
return defaultValue;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
function useSyncedLocalStorage(key, initialValue) {
|
|
1033
|
+
const [storedValue, setStoredValue] = (0, import_react4.useState)(() => {
|
|
1034
|
+
if (typeof window === "undefined") {
|
|
1035
|
+
return initialValue;
|
|
1036
|
+
}
|
|
1037
|
+
try {
|
|
1038
|
+
const item = window.localStorage.getItem(key);
|
|
1039
|
+
return parseJSON(item, initialValue);
|
|
1040
|
+
} catch (error) {
|
|
1041
|
+
console.warn(`Error reading localStorage key "${key}":`, error);
|
|
1042
|
+
return initialValue;
|
|
1043
|
+
}
|
|
1044
|
+
});
|
|
1045
|
+
const setValue = (0, import_react4.useCallback)(
|
|
1046
|
+
(value) => {
|
|
1047
|
+
try {
|
|
1048
|
+
setStoredValue((prev) => {
|
|
1049
|
+
const valueToStore = value instanceof Function ? value(prev) : value;
|
|
1050
|
+
if (typeof window !== "undefined") {
|
|
1051
|
+
window.localStorage.setItem(key, JSON.stringify(valueToStore));
|
|
1052
|
+
window.dispatchEvent(
|
|
1053
|
+
new StorageEvent("storage", { key, newValue: JSON.stringify(valueToStore) })
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
1056
|
+
return valueToStore;
|
|
1057
|
+
});
|
|
1058
|
+
} catch (error) {
|
|
1059
|
+
console.warn(`Error setting localStorage key "${key}":`, error);
|
|
1060
|
+
}
|
|
1061
|
+
},
|
|
1062
|
+
[key]
|
|
1063
|
+
);
|
|
1064
|
+
(0, import_react4.useEffect)(() => {
|
|
1065
|
+
const handleStorageChange = (event) => {
|
|
1066
|
+
if (event.key === key) {
|
|
1067
|
+
setStoredValue(parseJSON(event.newValue, initialValue));
|
|
1068
|
+
}
|
|
1069
|
+
};
|
|
1070
|
+
window.addEventListener("storage", handleStorageChange);
|
|
1071
|
+
return () => window.removeEventListener("storage", handleStorageChange);
|
|
1072
|
+
}, [key, initialValue]);
|
|
1073
|
+
return [storedValue, setValue];
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
// src/react/agent-context.tsx
|
|
1077
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1078
|
+
var AgentToolContext = (0, import_react5.createContext)(null);
|
|
1079
|
+
var AgentContext = (0, import_react5.createContext)(null);
|
|
1080
|
+
function AgentContextProvider({ children }) {
|
|
1081
|
+
const activeAgentsRef = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
|
|
1082
|
+
const runnersRef = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
|
|
1083
|
+
const listenersRef = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
|
|
1084
|
+
const toolRegistryRef = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
|
|
1085
|
+
const [, forceUpdate] = (0, import_react5.useState)({});
|
|
1086
|
+
const [allSessions, setAllSessions] = useSyncedLocalStorage(AGENT_SESSIONS_KEY, {});
|
|
1087
|
+
const [debugData, setDebugData] = useSyncedLocalStorage(
|
|
1088
|
+
AGENT_DEBUG_DATA_KEY,
|
|
1089
|
+
{}
|
|
1090
|
+
);
|
|
1091
|
+
const initializeAgent = (0, import_react5.useCallback)((agentId, agentUrl, accessKey) => {
|
|
1092
|
+
const existing = activeAgentsRef.current.get(agentId);
|
|
1093
|
+
if (!existing) {
|
|
1094
|
+
activeAgentsRef.current.set(agentId, { agentUrl, accessKey });
|
|
1095
|
+
forceUpdate({});
|
|
1096
|
+
} else if (existing.agentUrl !== agentUrl || existing.accessKey !== accessKey) {
|
|
1097
|
+
activeAgentsRef.current.set(agentId, { agentUrl, accessKey });
|
|
1098
|
+
forceUpdate({});
|
|
1099
|
+
}
|
|
1100
|
+
}, []);
|
|
1101
|
+
const registerRunner = (0, import_react5.useCallback)((agentId, runner) => {
|
|
1102
|
+
runnersRef.current.set(agentId, runner);
|
|
1103
|
+
const listeners = listenersRef.current.get(agentId);
|
|
1104
|
+
if (listeners) {
|
|
1105
|
+
listeners.forEach((callback) => callback());
|
|
1106
|
+
}
|
|
1107
|
+
}, []);
|
|
1108
|
+
const getRunner = (0, import_react5.useCallback)((agentId) => {
|
|
1109
|
+
return runnersRef.current.get(agentId) || null;
|
|
1110
|
+
}, []);
|
|
1111
|
+
const registerTool = (0, import_react5.useCallback)((agentId, config) => {
|
|
1112
|
+
if (!toolRegistryRef.current.has(agentId)) {
|
|
1113
|
+
toolRegistryRef.current.set(agentId, /* @__PURE__ */ new Map());
|
|
1114
|
+
}
|
|
1115
|
+
toolRegistryRef.current.get(agentId).set(config.name, config);
|
|
1116
|
+
}, []);
|
|
1117
|
+
const unregisterTool = (0, import_react5.useCallback)((agentId, toolName) => {
|
|
1118
|
+
toolRegistryRef.current.get(agentId)?.delete(toolName);
|
|
1119
|
+
}, []);
|
|
1120
|
+
const getTool = (0, import_react5.useCallback)((agentId, toolName) => {
|
|
1121
|
+
return toolRegistryRef.current.get(agentId)?.get(toolName);
|
|
1122
|
+
}, []);
|
|
1123
|
+
const getToolsForAgent = (0, import_react5.useCallback)((agentId) => {
|
|
1124
|
+
const toolMap = toolRegistryRef.current.get(agentId);
|
|
1125
|
+
return toolMap ? Array.from(toolMap.values()) : [];
|
|
1126
|
+
}, []);
|
|
1127
|
+
const resumeToolFromContext = (0, import_react5.useCallback)((agentId, callId, result) => {
|
|
1128
|
+
const runner = runnersRef.current.get(agentId);
|
|
1129
|
+
if (runner) {
|
|
1130
|
+
runner.resumeTool(callId, result);
|
|
1131
|
+
} else {
|
|
1132
|
+
console.warn(`Cannot resume tool: no runner found for agent "${agentId}"`);
|
|
1133
|
+
}
|
|
1134
|
+
}, []);
|
|
1135
|
+
const agentContextValue = (0, import_react5.useMemo)(
|
|
1136
|
+
() => ({
|
|
1137
|
+
initializeAgent,
|
|
1138
|
+
registerRunner,
|
|
1139
|
+
getRunner,
|
|
1140
|
+
allSessions,
|
|
1141
|
+
setAllSessions,
|
|
1142
|
+
debugData,
|
|
1143
|
+
setDebugData,
|
|
1144
|
+
runnersRef,
|
|
1145
|
+
listenersRef
|
|
1146
|
+
}),
|
|
1147
|
+
[
|
|
1148
|
+
initializeAgent,
|
|
1149
|
+
registerRunner,
|
|
1150
|
+
getRunner,
|
|
1151
|
+
allSessions,
|
|
1152
|
+
setAllSessions,
|
|
1153
|
+
debugData,
|
|
1154
|
+
setDebugData
|
|
1155
|
+
]
|
|
1156
|
+
);
|
|
1157
|
+
const toolContextValue = (0, import_react5.useMemo)(
|
|
1158
|
+
() => ({
|
|
1159
|
+
registerTool,
|
|
1160
|
+
unregisterTool,
|
|
1161
|
+
getTool,
|
|
1162
|
+
getToolsForAgent,
|
|
1163
|
+
resumeTool: resumeToolFromContext
|
|
1164
|
+
}),
|
|
1165
|
+
[registerTool, unregisterTool, getTool, getToolsForAgent, resumeToolFromContext]
|
|
1166
|
+
);
|
|
1167
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentContext.Provider, { value: agentContextValue, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AgentToolContext.Provider, { value: toolContextValue, children: [
|
|
1168
|
+
children,
|
|
1169
|
+
Array.from(activeAgentsRef.current.entries()).map(([agentId, { agentUrl, accessKey }]) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1170
|
+
AgentRunnerInstance,
|
|
1171
|
+
{
|
|
1172
|
+
agentId,
|
|
1173
|
+
agentUrl,
|
|
1174
|
+
accessKey
|
|
1175
|
+
},
|
|
1176
|
+
agentId
|
|
1177
|
+
))
|
|
1178
|
+
] }) });
|
|
1179
|
+
}
|
|
1180
|
+
function AgentRunnerInstance({
|
|
1181
|
+
agentId,
|
|
1182
|
+
agentUrl,
|
|
1183
|
+
accessKey
|
|
1184
|
+
}) {
|
|
1185
|
+
const agent = useAgent(agentId, agentUrl, accessKey);
|
|
1186
|
+
const context = (0, import_react5.useContext)(AgentContext);
|
|
1187
|
+
(0, import_react5.useEffect)(() => {
|
|
1188
|
+
if (context) {
|
|
1189
|
+
context.registerRunner(agentId, agent);
|
|
1190
|
+
}
|
|
1191
|
+
}, [agentId, agent, context]);
|
|
1192
|
+
if (!context) return null;
|
|
1193
|
+
return null;
|
|
1194
|
+
}
|
|
1195
|
+
function useAgentContext(agentId, agentUrl, accessKey) {
|
|
1196
|
+
const context = (0, import_react5.useContext)(AgentContext);
|
|
1197
|
+
if (!context) {
|
|
1198
|
+
throw new Error("useAgentContext must be used within AgentContextProvider");
|
|
1199
|
+
}
|
|
1200
|
+
const { initializeAgent, getRunner, listenersRef } = context;
|
|
1201
|
+
(0, import_react5.useEffect)(() => {
|
|
1202
|
+
initializeAgent(agentId, agentUrl, accessKey);
|
|
1203
|
+
}, [agentId, agentUrl, initializeAgent]);
|
|
1204
|
+
const [runner, setRunner] = (0, import_react5.useState)(() => getRunner(agentId));
|
|
1205
|
+
(0, import_react5.useEffect)(() => {
|
|
1206
|
+
const currentRunner = getRunner(agentId);
|
|
1207
|
+
if (currentRunner !== runner) {
|
|
1208
|
+
setRunner(currentRunner);
|
|
1209
|
+
}
|
|
1210
|
+
const callback = () => {
|
|
1211
|
+
setRunner(getRunner(agentId));
|
|
1212
|
+
};
|
|
1213
|
+
if (!listenersRef.current.has(agentId)) {
|
|
1214
|
+
listenersRef.current.set(agentId, /* @__PURE__ */ new Set());
|
|
1215
|
+
}
|
|
1216
|
+
listenersRef.current.get(agentId)?.add(callback);
|
|
1217
|
+
return () => {
|
|
1218
|
+
listenersRef.current.get(agentId)?.delete(callback);
|
|
1219
|
+
};
|
|
1220
|
+
}, [agentId, getRunner]);
|
|
1221
|
+
const placeholder = (0, import_react5.useMemo)(
|
|
1222
|
+
() => ({
|
|
1223
|
+
messages: [],
|
|
1224
|
+
inProgress: false,
|
|
1225
|
+
sessionId: "",
|
|
1226
|
+
sessions: [],
|
|
1227
|
+
debugData: {},
|
|
1228
|
+
handleSend: async () => {
|
|
1229
|
+
},
|
|
1230
|
+
resumeTool: async () => {
|
|
1231
|
+
},
|
|
1232
|
+
switchSession: () => {
|
|
1233
|
+
},
|
|
1234
|
+
deleteSession: () => {
|
|
1235
|
+
},
|
|
1236
|
+
addOptimisticMessage: () => {
|
|
1237
|
+
},
|
|
1238
|
+
abort: () => {
|
|
1239
|
+
}
|
|
1240
|
+
}),
|
|
1241
|
+
[]
|
|
1242
|
+
);
|
|
1243
|
+
return runner || placeholder;
|
|
1244
|
+
}
|
|
1245
|
+
function useAgentGlobalState() {
|
|
1246
|
+
const context = (0, import_react5.useContext)(AgentContext);
|
|
1247
|
+
if (!context) {
|
|
1248
|
+
throw new Error("useAgentGlobalState must be used within AgentContextProvider");
|
|
1249
|
+
}
|
|
1250
|
+
return {
|
|
1251
|
+
allSessions: context.allSessions,
|
|
1252
|
+
setAllSessions: context.setAllSessions,
|
|
1253
|
+
debugData: context.debugData,
|
|
1254
|
+
setDebugData: context.setDebugData
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// src/react/use-client-tool.ts
|
|
1259
|
+
var import_react6 = require("react");
|
|
1260
|
+
function useClientTool(agentId, config) {
|
|
1261
|
+
const context = (0, import_react6.useContext)(AgentToolContext);
|
|
1262
|
+
if (!context) {
|
|
1263
|
+
throw new Error("useClientTool must be used within <AgentContextProvider>");
|
|
1264
|
+
}
|
|
1265
|
+
(0, import_react6.useEffect)(() => {
|
|
1266
|
+
context.registerTool(agentId, config);
|
|
1267
|
+
return () => {
|
|
1268
|
+
context.unregisterTool(agentId, config.name);
|
|
1269
|
+
};
|
|
1270
|
+
}, [agentId, config.name, config.handler, config.render, config.parameters]);
|
|
1271
|
+
}
|
|
1272
|
+
function ToolRenderer({ agentId, part }) {
|
|
1273
|
+
const context = (0, import_react6.useContext)(AgentToolContext);
|
|
1274
|
+
const [localStatus, setLocalStatus] = (0, import_react6.useState)(part.status || "pending");
|
|
1275
|
+
(0, import_react6.useEffect)(() => {
|
|
1276
|
+
setLocalStatus(part.status || "pending");
|
|
1277
|
+
}, [part.status]);
|
|
1278
|
+
if (!context) {
|
|
1279
|
+
throw new Error("ToolRenderer must be used within <AgentContextProvider>");
|
|
1280
|
+
}
|
|
1281
|
+
const tool = context.getTool(agentId, part.toolName);
|
|
1282
|
+
const handleSubmit = (0, import_react6.useCallback)(
|
|
1283
|
+
(result) => {
|
|
1284
|
+
if (localStatus === "submitted") return;
|
|
1285
|
+
setLocalStatus("submitted");
|
|
1286
|
+
context.resumeTool(agentId, part.callId, result);
|
|
1287
|
+
},
|
|
1288
|
+
[agentId, part.callId, localStatus, context]
|
|
1289
|
+
);
|
|
1290
|
+
if (!tool?.render) {
|
|
1291
|
+
return null;
|
|
1292
|
+
}
|
|
1293
|
+
return tool.render({
|
|
1294
|
+
inputs: part.inputs,
|
|
1295
|
+
submit: part.paused ? handleSubmit : () => {
|
|
1296
|
+
},
|
|
1297
|
+
status: localStatus,
|
|
1298
|
+
result: part.result
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1302
|
+
0 && (module.exports = {
|
|
1303
|
+
AGENT_DEBUG_DATA_KEY,
|
|
1304
|
+
AGENT_SESSIONS_KEY,
|
|
1305
|
+
AgentContextProvider,
|
|
1306
|
+
AgentToolContext,
|
|
1307
|
+
DEFAULT_SESSION_NAME,
|
|
1308
|
+
TEMPORARY_SESSION_ID,
|
|
1309
|
+
ToolRenderer,
|
|
1310
|
+
cleanSchema,
|
|
1311
|
+
tryParseJSON,
|
|
1312
|
+
updateAgentMessageParts,
|
|
1313
|
+
useAgent,
|
|
1314
|
+
useAgentContext,
|
|
1315
|
+
useAgentGlobalState,
|
|
1316
|
+
useClientTool
|
|
1317
|
+
});
|
|
1318
|
+
//# sourceMappingURL=index.cjs.map
|