buildship-agent 1.0.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/LICENSE +21 -0
- package/README.md +901 -0
- package/dist/index.cjs +1145 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +221 -0
- package/dist/index.d.ts +221 -0
- package/dist/index.js +1116 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1145 @@
|
|
|
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/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AGENT_DEBUG_DATA_KEY: () => AGENT_DEBUG_DATA_KEY,
|
|
24
|
+
AGENT_SESSIONS_KEY: () => AGENT_SESSIONS_KEY,
|
|
25
|
+
AgentContextProvider: () => AgentContextProvider,
|
|
26
|
+
DEFAULT_SESSION_NAME: () => DEFAULT_SESSION_NAME,
|
|
27
|
+
TEMPORARY_SESSION_ID: () => TEMPORARY_SESSION_ID,
|
|
28
|
+
cleanSchema: () => cleanSchema,
|
|
29
|
+
tryParseJSON: () => tryParseJSON,
|
|
30
|
+
updateAgentMessageParts: () => updateAgentMessageParts,
|
|
31
|
+
useAgent: () => useAgent,
|
|
32
|
+
useAgentContext: () => useAgentContext,
|
|
33
|
+
useAgentGlobalState: () => useAgentGlobalState
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/agent-context.tsx
|
|
38
|
+
var import_react4 = require("react");
|
|
39
|
+
|
|
40
|
+
// src/use-agent.ts
|
|
41
|
+
var import_react2 = require("react");
|
|
42
|
+
var import_fetch_event_source = require("@microsoft/fetch-event-source");
|
|
43
|
+
|
|
44
|
+
// src/constants.ts
|
|
45
|
+
var AGENT_SESSIONS_KEY = "buildship:agent:conversations";
|
|
46
|
+
var AGENT_DEBUG_DATA_KEY = "buildship:agent:debug";
|
|
47
|
+
var DEFAULT_SESSION_NAME = "New Chat";
|
|
48
|
+
var TEMPORARY_SESSION_ID = "sess_temp";
|
|
49
|
+
|
|
50
|
+
// src/session-utils.ts
|
|
51
|
+
var import_react = require("react");
|
|
52
|
+
var useSessionUtils = (agentId, allSessions, setAllSessions, currentSessionId, setCurrentSessionId, messagesRef) => {
|
|
53
|
+
const agentSessions = (0, import_react.useMemo)(() => allSessions[agentId] || {}, [agentId, allSessions]);
|
|
54
|
+
const syncSessionRef = (0, import_react.useRef)();
|
|
55
|
+
syncSessionRef.current = (updatedMessages) => {
|
|
56
|
+
if (!currentSessionId || currentSessionId === TEMPORARY_SESSION_ID) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
setAllSessions((prev) => ({
|
|
60
|
+
...prev,
|
|
61
|
+
[agentId]: {
|
|
62
|
+
...prev[agentId],
|
|
63
|
+
[currentSessionId]: {
|
|
64
|
+
...prev[agentId]?.[currentSessionId],
|
|
65
|
+
messages: updatedMessages ?? messagesRef.current,
|
|
66
|
+
updatedAt: Date.now()
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}));
|
|
70
|
+
};
|
|
71
|
+
const getInitialSessionId = () => {
|
|
72
|
+
const sessions = Object.values(agentSessions);
|
|
73
|
+
if (sessions.length > 0) {
|
|
74
|
+
return sessions.sort((a, b) => b.updatedAt - a.updatedAt)[0].id;
|
|
75
|
+
}
|
|
76
|
+
return TEMPORARY_SESSION_ID;
|
|
77
|
+
};
|
|
78
|
+
const switchSession = (0, import_react.useCallback)(
|
|
79
|
+
(sessionId = TEMPORARY_SESSION_ID) => {
|
|
80
|
+
setCurrentSessionId(sessionId);
|
|
81
|
+
},
|
|
82
|
+
[setCurrentSessionId]
|
|
83
|
+
);
|
|
84
|
+
const deleteSession = (0, import_react.useCallback)(
|
|
85
|
+
(sessionId) => {
|
|
86
|
+
if (!sessionId || sessionId === TEMPORARY_SESSION_ID) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
setAllSessions((prev) => {
|
|
90
|
+
const updatedAgentSessions = { ...prev[agentId] };
|
|
91
|
+
delete updatedAgentSessions[sessionId];
|
|
92
|
+
return {
|
|
93
|
+
...prev,
|
|
94
|
+
[agentId]: updatedAgentSessions
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
if (sessionId === currentSessionId) {
|
|
98
|
+
const remainingSessions = Object.values(agentSessions).filter((s) => s.id !== sessionId);
|
|
99
|
+
if (remainingSessions.length > 0) {
|
|
100
|
+
const mostRecent = remainingSessions.sort((a, b) => b.updatedAt - a.updatedAt)[0];
|
|
101
|
+
setCurrentSessionId(mostRecent.id);
|
|
102
|
+
} else {
|
|
103
|
+
setCurrentSessionId(TEMPORARY_SESSION_ID);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
[agentId, currentSessionId, agentSessions, setAllSessions, setCurrentSessionId]
|
|
108
|
+
);
|
|
109
|
+
const sessionsList = (0, import_react.useMemo)(
|
|
110
|
+
() => Object.values(agentSessions).sort((a, b) => b.updatedAt - a.updatedAt),
|
|
111
|
+
[agentSessions]
|
|
112
|
+
);
|
|
113
|
+
const createSessionFromResponse = (sessionId, sessionName, currentMessages) => {
|
|
114
|
+
setAllSessions((prev) => ({
|
|
115
|
+
...prev,
|
|
116
|
+
[agentId]: {
|
|
117
|
+
...prev[agentId],
|
|
118
|
+
[sessionId]: {
|
|
119
|
+
id: sessionId,
|
|
120
|
+
createdAt: Date.now(),
|
|
121
|
+
updatedAt: Date.now(),
|
|
122
|
+
messages: currentMessages,
|
|
123
|
+
name: sessionName
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}));
|
|
127
|
+
};
|
|
128
|
+
return {
|
|
129
|
+
agentSessions,
|
|
130
|
+
syncSessionRef,
|
|
131
|
+
getInitialSessionId,
|
|
132
|
+
switchSession,
|
|
133
|
+
deleteSession,
|
|
134
|
+
sessionsList,
|
|
135
|
+
createSessionFromResponse
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// src/debug-handlers.ts
|
|
140
|
+
var createDebugHandlers = (setDebugData) => {
|
|
141
|
+
const handleDebugToolExecutionStarted = (parsed) => {
|
|
142
|
+
const executionId = parsed.meta.executionId;
|
|
143
|
+
setDebugData((prev) => ({
|
|
144
|
+
...prev,
|
|
145
|
+
[executionId]: [
|
|
146
|
+
...prev[executionId] || [],
|
|
147
|
+
{
|
|
148
|
+
itemType: "tool_call",
|
|
149
|
+
toolId: parsed.data.toolId || "unknown",
|
|
150
|
+
toolCallId: parsed.data.toolCallId,
|
|
151
|
+
status: "progress"
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
}));
|
|
155
|
+
};
|
|
156
|
+
const handleDebugToolExecutionInputs = (parsed) => {
|
|
157
|
+
const executionId = parsed.meta.executionId;
|
|
158
|
+
const toolCallId = parsed.data.toolCallId;
|
|
159
|
+
setDebugData((prev) => {
|
|
160
|
+
const currentData = [...prev[executionId] || []];
|
|
161
|
+
for (let i = currentData.length - 1; i >= 0; i--) {
|
|
162
|
+
if (currentData[i].itemType === "tool_call") {
|
|
163
|
+
const toolItem = currentData[i];
|
|
164
|
+
if (toolItem.toolCallId === toolCallId) {
|
|
165
|
+
currentData[i] = {
|
|
166
|
+
...toolItem,
|
|
167
|
+
inputs: parsed.data.value
|
|
168
|
+
};
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
...prev,
|
|
175
|
+
[executionId]: currentData
|
|
176
|
+
};
|
|
177
|
+
});
|
|
178
|
+
};
|
|
179
|
+
const handleDebugToolExecutionFinished = (parsed) => {
|
|
180
|
+
const executionId = parsed.meta.executionId;
|
|
181
|
+
const toolCallId = parsed.data.toolCallId;
|
|
182
|
+
setDebugData((prev) => {
|
|
183
|
+
const currentData = [...prev[executionId] || []];
|
|
184
|
+
for (let i = currentData.length - 1; i >= 0; i--) {
|
|
185
|
+
if (currentData[i].itemType === "tool_call") {
|
|
186
|
+
const toolItem = currentData[i];
|
|
187
|
+
if (toolItem.toolCallId === toolCallId) {
|
|
188
|
+
currentData[i] = {
|
|
189
|
+
...toolItem,
|
|
190
|
+
status: "complete",
|
|
191
|
+
output: parsed.data.value
|
|
192
|
+
};
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
...prev,
|
|
199
|
+
[executionId]: currentData
|
|
200
|
+
};
|
|
201
|
+
});
|
|
202
|
+
};
|
|
203
|
+
const handleDebugToolExecutionError = (parsed) => {
|
|
204
|
+
const executionId = parsed.meta.executionId;
|
|
205
|
+
const toolCallId = parsed.data.toolCallId;
|
|
206
|
+
setDebugData((prev) => {
|
|
207
|
+
const currentData = [...prev[executionId] || []];
|
|
208
|
+
for (let i = currentData.length - 1; i >= 0; i--) {
|
|
209
|
+
if (currentData[i].itemType === "tool_call") {
|
|
210
|
+
const toolItem = currentData[i];
|
|
211
|
+
if (toolItem.toolCallId === toolCallId) {
|
|
212
|
+
currentData[i] = {
|
|
213
|
+
...toolItem,
|
|
214
|
+
status: "error",
|
|
215
|
+
output: parsed.data.value
|
|
216
|
+
};
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
...prev,
|
|
223
|
+
[executionId]: currentData
|
|
224
|
+
};
|
|
225
|
+
});
|
|
226
|
+
};
|
|
227
|
+
const handleDebugToolLog = (parsed) => {
|
|
228
|
+
const executionId = parsed.meta.executionId;
|
|
229
|
+
const toolCallId = parsed.data.toolCallId;
|
|
230
|
+
setDebugData((prev) => {
|
|
231
|
+
const currentData = [...prev[executionId] || []];
|
|
232
|
+
for (let i = currentData.length - 1; i >= 0; i--) {
|
|
233
|
+
if (currentData[i].itemType === "tool_call") {
|
|
234
|
+
const toolItem = currentData[i];
|
|
235
|
+
if (toolItem.toolCallId === toolCallId) {
|
|
236
|
+
const currentLogs = toolItem.logs || [];
|
|
237
|
+
currentData[i] = {
|
|
238
|
+
...toolItem,
|
|
239
|
+
logs: [...currentLogs, parsed.data.value[0]]
|
|
240
|
+
};
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
...prev,
|
|
247
|
+
[executionId]: currentData
|
|
248
|
+
};
|
|
249
|
+
});
|
|
250
|
+
};
|
|
251
|
+
let lastReasoningIndex = -1;
|
|
252
|
+
const handleDebugReasoningStep = (parsed) => {
|
|
253
|
+
const executionId = parsed.meta.executionId;
|
|
254
|
+
const { delta, index } = parsed.data;
|
|
255
|
+
setDebugData((prev) => {
|
|
256
|
+
const currentData = [...prev[executionId] || []];
|
|
257
|
+
if (lastReasoningIndex < index) {
|
|
258
|
+
currentData.push({ itemType: "reasoning", reasoning: "" });
|
|
259
|
+
lastReasoningIndex = index;
|
|
260
|
+
}
|
|
261
|
+
for (let i = currentData.length - 1; i >= 0; i--) {
|
|
262
|
+
if (currentData[i].itemType === "reasoning") {
|
|
263
|
+
currentData[i] = {
|
|
264
|
+
itemType: "reasoning",
|
|
265
|
+
reasoning: currentData[i].reasoning + delta
|
|
266
|
+
};
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
...prev,
|
|
272
|
+
[executionId]: currentData
|
|
273
|
+
};
|
|
274
|
+
});
|
|
275
|
+
};
|
|
276
|
+
const handleAgentHandoff = (parsed) => {
|
|
277
|
+
const executionId = parsed.meta.executionId;
|
|
278
|
+
setDebugData((prev) => ({
|
|
279
|
+
...prev,
|
|
280
|
+
[executionId]: [
|
|
281
|
+
...prev[executionId] || [],
|
|
282
|
+
{
|
|
283
|
+
itemType: "handoff",
|
|
284
|
+
agentName: parsed.data.agentName
|
|
285
|
+
}
|
|
286
|
+
]
|
|
287
|
+
}));
|
|
288
|
+
};
|
|
289
|
+
const handleMCPToolCallStart = (parsed) => {
|
|
290
|
+
const executionId = parsed.meta.executionId;
|
|
291
|
+
setDebugData((prev) => ({
|
|
292
|
+
...prev,
|
|
293
|
+
[executionId]: [
|
|
294
|
+
...prev[executionId] || [],
|
|
295
|
+
{
|
|
296
|
+
itemType: "mcp_tool_call",
|
|
297
|
+
toolName: parsed.data.toolName,
|
|
298
|
+
serverName: parsed.data.serverName,
|
|
299
|
+
callId: parsed.data.callId,
|
|
300
|
+
status: "progress",
|
|
301
|
+
inputs: parsed.data.inputs
|
|
302
|
+
}
|
|
303
|
+
]
|
|
304
|
+
}));
|
|
305
|
+
};
|
|
306
|
+
const handleMCPToolCallEnd = (parsed) => {
|
|
307
|
+
const executionId = parsed.meta.executionId;
|
|
308
|
+
const callId = parsed.data.callId;
|
|
309
|
+
setDebugData((prev) => {
|
|
310
|
+
const currentData = [...prev[executionId] || []];
|
|
311
|
+
for (let i = currentData.length - 1; i >= 0; i--) {
|
|
312
|
+
if (currentData[i].itemType === "mcp_tool_call") {
|
|
313
|
+
const mcpToolItem = currentData[i];
|
|
314
|
+
if (mcpToolItem.callId === callId) {
|
|
315
|
+
currentData[i] = {
|
|
316
|
+
...mcpToolItem,
|
|
317
|
+
status: parsed.data.error ? "error" : "complete",
|
|
318
|
+
output: parsed.data.result,
|
|
319
|
+
error: parsed.data.error
|
|
320
|
+
};
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return {
|
|
326
|
+
...prev,
|
|
327
|
+
[executionId]: currentData
|
|
328
|
+
};
|
|
329
|
+
});
|
|
330
|
+
};
|
|
331
|
+
const handleDebugEvent = (parsed) => {
|
|
332
|
+
switch (parsed.type) {
|
|
333
|
+
case "debug_tool_execution_started":
|
|
334
|
+
handleDebugToolExecutionStarted(parsed);
|
|
335
|
+
break;
|
|
336
|
+
case "debug_tool_execution_inputs":
|
|
337
|
+
handleDebugToolExecutionInputs(parsed);
|
|
338
|
+
break;
|
|
339
|
+
case "debug_tool_execution_finished":
|
|
340
|
+
handleDebugToolExecutionFinished(parsed);
|
|
341
|
+
break;
|
|
342
|
+
case "debug_tool_execution_error":
|
|
343
|
+
handleDebugToolExecutionError(parsed);
|
|
344
|
+
break;
|
|
345
|
+
case "debug_tool_log":
|
|
346
|
+
handleDebugToolLog(parsed);
|
|
347
|
+
break;
|
|
348
|
+
case "llm_reasoning_delta":
|
|
349
|
+
handleDebugReasoningStep(parsed);
|
|
350
|
+
break;
|
|
351
|
+
case "agent_handoff":
|
|
352
|
+
handleAgentHandoff(parsed);
|
|
353
|
+
break;
|
|
354
|
+
case "mcp_tool_call_start":
|
|
355
|
+
handleMCPToolCallStart(parsed);
|
|
356
|
+
break;
|
|
357
|
+
case "mcp_tool_call_end":
|
|
358
|
+
handleMCPToolCallEnd(parsed);
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
return {
|
|
363
|
+
handleDebugEvent
|
|
364
|
+
};
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
// src/utils/schema.ts
|
|
368
|
+
function cleanSchema(obj) {
|
|
369
|
+
if (Array.isArray(obj)) {
|
|
370
|
+
return obj.map(cleanSchema);
|
|
371
|
+
} else if (obj !== null && typeof obj === "object") {
|
|
372
|
+
const newObj = {};
|
|
373
|
+
const currentObj = obj;
|
|
374
|
+
for (const key in currentObj) {
|
|
375
|
+
if (key === "propertyNames" || key === "$schema") {
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
newObj[key] = cleanSchema(currentObj[key]);
|
|
379
|
+
}
|
|
380
|
+
if (newObj.type === "object") {
|
|
381
|
+
if (newObj.additionalProperties === void 0 || newObj.additionalProperties && typeof newObj.additionalProperties === "object" && Object.keys(newObj.additionalProperties).length === 0) {
|
|
382
|
+
newObj.additionalProperties = false;
|
|
383
|
+
}
|
|
384
|
+
if (newObj.additionalProperties === false && newObj.properties) {
|
|
385
|
+
newObj.required = Object.keys(newObj.properties);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return newObj;
|
|
389
|
+
}
|
|
390
|
+
return obj;
|
|
391
|
+
}
|
|
392
|
+
function tryParseJSON(value) {
|
|
393
|
+
if (typeof value === "string") {
|
|
394
|
+
try {
|
|
395
|
+
return JSON.parse(value);
|
|
396
|
+
} catch {
|
|
397
|
+
return value;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return value;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// src/utils/message.ts
|
|
404
|
+
function updateAgentMessageParts(parts, newPart) {
|
|
405
|
+
const sorted = [...parts, newPart].sort((a, b) => {
|
|
406
|
+
const aStart = a.type === "text" ? a.firstSequence : a.sequence;
|
|
407
|
+
const bStart = b.type === "text" ? b.firstSequence : b.sequence;
|
|
408
|
+
return aStart - bStart;
|
|
409
|
+
});
|
|
410
|
+
return sorted.reduce((acc, current) => {
|
|
411
|
+
const last = acc[acc.length - 1];
|
|
412
|
+
const canMerge = last?.type === "text" && current.type === "text" && current.firstSequence === last.lastSequence + 1;
|
|
413
|
+
if (canMerge) {
|
|
414
|
+
acc[acc.length - 1] = {
|
|
415
|
+
...last,
|
|
416
|
+
text: last.text + current.text,
|
|
417
|
+
lastSequence: current.lastSequence
|
|
418
|
+
};
|
|
419
|
+
return acc;
|
|
420
|
+
}
|
|
421
|
+
acc.push(current);
|
|
422
|
+
return acc;
|
|
423
|
+
}, []);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// src/use-agent.ts
|
|
427
|
+
function useAgent(agentId, agentUrl, accessKey) {
|
|
428
|
+
const { allSessions, setAllSessions, debugData, setDebugData } = useAgentGlobalState();
|
|
429
|
+
const [inProgress, setInProgress] = (0, import_react2.useState)(false);
|
|
430
|
+
const [messages, setMessages] = (0, import_react2.useState)([]);
|
|
431
|
+
const [pendingToolCalls, setPendingToolCalls] = (0, import_react2.useState)(/* @__PURE__ */ new Map());
|
|
432
|
+
const [isPaused, setIsPaused] = (0, import_react2.useState)(false);
|
|
433
|
+
const messagesRef = (0, import_react2.useRef)([]);
|
|
434
|
+
const clientToolsRef = (0, import_react2.useRef)([]);
|
|
435
|
+
const [currentSessionId, setCurrentSessionId] = (0, import_react2.useState)(TEMPORARY_SESSION_ID);
|
|
436
|
+
const sessionUtils = useSessionUtils(
|
|
437
|
+
agentId,
|
|
438
|
+
allSessions,
|
|
439
|
+
setAllSessions,
|
|
440
|
+
currentSessionId,
|
|
441
|
+
setCurrentSessionId,
|
|
442
|
+
messagesRef
|
|
443
|
+
);
|
|
444
|
+
(0, import_react2.useEffect)(() => {
|
|
445
|
+
const initialSessionId = sessionUtils.getInitialSessionId();
|
|
446
|
+
setCurrentSessionId(initialSessionId);
|
|
447
|
+
}, []);
|
|
448
|
+
const debugHandlers = createDebugHandlers(setDebugData);
|
|
449
|
+
(0, import_react2.useEffect)(() => {
|
|
450
|
+
messagesRef.current = messages;
|
|
451
|
+
}, [messages]);
|
|
452
|
+
(0, import_react2.useEffect)(() => {
|
|
453
|
+
if (inProgress) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
const session = sessionUtils.agentSessions[currentSessionId];
|
|
457
|
+
if (session) {
|
|
458
|
+
setMessages(session.messages);
|
|
459
|
+
} else {
|
|
460
|
+
setMessages([]);
|
|
461
|
+
}
|
|
462
|
+
}, [currentSessionId, sessionUtils.agentSessions, agentId, inProgress]);
|
|
463
|
+
(0, import_react2.useEffect)(() => {
|
|
464
|
+
return () => {
|
|
465
|
+
if (messagesRef.current.length > 0 && sessionUtils.syncSessionRef.current) {
|
|
466
|
+
sessionUtils.syncSessionRef.current();
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
}, []);
|
|
470
|
+
const controller = (0, import_react2.useRef)();
|
|
471
|
+
const runAgent = (0, import_react2.useCallback)(
|
|
472
|
+
async (input, options) => {
|
|
473
|
+
setInProgress(true);
|
|
474
|
+
if (controller.current) {
|
|
475
|
+
controller.current.abort();
|
|
476
|
+
}
|
|
477
|
+
controller.current = new AbortController();
|
|
478
|
+
const headers = {
|
|
479
|
+
"Content-Type": "application/json",
|
|
480
|
+
...options?.additionalHeaders || {},
|
|
481
|
+
...accessKey ? { Authorization: `Bearer ${accessKey}` } : {}
|
|
482
|
+
};
|
|
483
|
+
if (currentSessionId) {
|
|
484
|
+
headers["x-buildship-agent-session-id"] = currentSessionId;
|
|
485
|
+
}
|
|
486
|
+
const body = {
|
|
487
|
+
stream: true,
|
|
488
|
+
input,
|
|
489
|
+
context: options?.context || {},
|
|
490
|
+
testBuildId: options?.testBuildId,
|
|
491
|
+
tools: options?.tools?.map((t) => ({
|
|
492
|
+
...t,
|
|
493
|
+
parameters: cleanSchema(t.parameters)
|
|
494
|
+
})),
|
|
495
|
+
clientTools: options?.clientTools?.map((t) => ({
|
|
496
|
+
...t,
|
|
497
|
+
parameters: cleanSchema(t.parameters)
|
|
498
|
+
}))
|
|
499
|
+
};
|
|
500
|
+
try {
|
|
501
|
+
await (0, import_fetch_event_source.fetchEventSource)(agentUrl, {
|
|
502
|
+
method: "POST",
|
|
503
|
+
headers,
|
|
504
|
+
signal: controller.current.signal,
|
|
505
|
+
openWhenHidden: true,
|
|
506
|
+
body: JSON.stringify(body),
|
|
507
|
+
onopen: async (resp) => {
|
|
508
|
+
if (resp.status >= 400) {
|
|
509
|
+
console.log(`AI onopen error with status: ${resp.status}`);
|
|
510
|
+
if (resp.status === 404) {
|
|
511
|
+
throw new Error(`Not Found (${resp.statusText})`);
|
|
512
|
+
}
|
|
513
|
+
throw new Error(`Error status ${resp.status}`);
|
|
514
|
+
}
|
|
515
|
+
if (!currentSessionId || currentSessionId === TEMPORARY_SESSION_ID) {
|
|
516
|
+
const sessionId = resp.headers.get("x-buildship-agent-session-id");
|
|
517
|
+
if (sessionId) {
|
|
518
|
+
const currentMessages = messagesRef.current;
|
|
519
|
+
const sessionName = resp.headers.get("x-buildship-agent-session-name") || DEFAULT_SESSION_NAME;
|
|
520
|
+
sessionUtils.createSessionFromResponse(sessionId, sessionName, currentMessages);
|
|
521
|
+
setCurrentSessionId(sessionId);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
const executionId = resp.headers.get("x-buildship-agent-execution-id");
|
|
525
|
+
if (executionId) {
|
|
526
|
+
setMessages((prev) => {
|
|
527
|
+
const lastMessage = prev[prev.length - 1];
|
|
528
|
+
if (lastMessage?.role === "user") {
|
|
529
|
+
const updatedMessage = {
|
|
530
|
+
...lastMessage,
|
|
531
|
+
executionId
|
|
532
|
+
};
|
|
533
|
+
const updatedMessages = [...prev.slice(0, -1), updatedMessage];
|
|
534
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
535
|
+
sessionUtils.syncSessionRef.current(updatedMessages);
|
|
536
|
+
}
|
|
537
|
+
return updatedMessages;
|
|
538
|
+
}
|
|
539
|
+
return prev;
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
onmessage: (event) => {
|
|
544
|
+
try {
|
|
545
|
+
const parsed = JSON.parse(event.data);
|
|
546
|
+
if (parsed.type === "llm_text_delta") {
|
|
547
|
+
setMessages((prev) => {
|
|
548
|
+
const lastMessage = prev[prev.length - 1];
|
|
549
|
+
const sequence = parsed.meta.sequence;
|
|
550
|
+
const newPart = {
|
|
551
|
+
type: "text",
|
|
552
|
+
text: parsed.data,
|
|
553
|
+
firstSequence: sequence,
|
|
554
|
+
lastSequence: sequence
|
|
555
|
+
};
|
|
556
|
+
let updatedMessages;
|
|
557
|
+
if (lastMessage?.role === "agent") {
|
|
558
|
+
const updatedMessage = {
|
|
559
|
+
...lastMessage,
|
|
560
|
+
content: lastMessage.content + parsed.data,
|
|
561
|
+
// content is still useful for simple displays
|
|
562
|
+
parts: updateAgentMessageParts(lastMessage.parts || [], newPart)
|
|
563
|
+
};
|
|
564
|
+
updatedMessages = [...prev.slice(0, -1), updatedMessage];
|
|
565
|
+
} else {
|
|
566
|
+
const updatedMessage = {
|
|
567
|
+
role: "agent",
|
|
568
|
+
content: parsed.data,
|
|
569
|
+
parts: [newPart],
|
|
570
|
+
executionId: parsed.meta.executionId
|
|
571
|
+
};
|
|
572
|
+
updatedMessages = [...prev, updatedMessage];
|
|
573
|
+
}
|
|
574
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
575
|
+
sessionUtils.syncSessionRef.current(updatedMessages);
|
|
576
|
+
}
|
|
577
|
+
return updatedMessages;
|
|
578
|
+
});
|
|
579
|
+
} else if (parsed.type === "client_tool_call") {
|
|
580
|
+
const { toolName, callId, inputs } = parsed.data;
|
|
581
|
+
const toolDefinition = clientToolsRef.current.find((t) => t.name === toolName);
|
|
582
|
+
const requiresResult = toolDefinition?.requiresResult ?? false;
|
|
583
|
+
if (requiresResult) {
|
|
584
|
+
setPendingToolCalls((prev) => {
|
|
585
|
+
const updated = new Map(prev);
|
|
586
|
+
updated.set(callId, {
|
|
587
|
+
toolName,
|
|
588
|
+
callId,
|
|
589
|
+
inputs: tryParseJSON(inputs),
|
|
590
|
+
requiresResult
|
|
591
|
+
});
|
|
592
|
+
return updated;
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
setMessages((prev) => {
|
|
596
|
+
const lastMessage = prev[prev.length - 1];
|
|
597
|
+
const newPart = {
|
|
598
|
+
type: "widget",
|
|
599
|
+
toolName,
|
|
600
|
+
callId,
|
|
601
|
+
inputs: tryParseJSON(inputs),
|
|
602
|
+
sequence: parsed.meta.sequence
|
|
603
|
+
};
|
|
604
|
+
let updatedMessages;
|
|
605
|
+
if (lastMessage?.role === "agent") {
|
|
606
|
+
const updatedMessage = {
|
|
607
|
+
...lastMessage,
|
|
608
|
+
parts: updateAgentMessageParts(lastMessage.parts || [], newPart)
|
|
609
|
+
};
|
|
610
|
+
updatedMessages = [...prev.slice(0, -1), updatedMessage];
|
|
611
|
+
} else {
|
|
612
|
+
const updatedMessage = {
|
|
613
|
+
role: "agent",
|
|
614
|
+
content: "",
|
|
615
|
+
parts: [newPart],
|
|
616
|
+
executionId: parsed.meta.executionId
|
|
617
|
+
};
|
|
618
|
+
updatedMessages = [...prev, updatedMessage];
|
|
619
|
+
}
|
|
620
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
621
|
+
sessionUtils.syncSessionRef.current(updatedMessages);
|
|
622
|
+
}
|
|
623
|
+
return updatedMessages;
|
|
624
|
+
});
|
|
625
|
+
} else if (parsed.type.startsWith("debug_") || parsed.type === "llm_reasoning_delta" || parsed.type === "agent_handoff" || parsed.type === "mcp_tool_call_start" || parsed.type === "mcp_tool_call_end") {
|
|
626
|
+
debugHandlers.handleDebugEvent(parsed);
|
|
627
|
+
}
|
|
628
|
+
} catch (e) {
|
|
629
|
+
console.log("Failed to parse agent message", e);
|
|
630
|
+
}
|
|
631
|
+
},
|
|
632
|
+
onclose: () => {
|
|
633
|
+
console.log("Agent closed");
|
|
634
|
+
setInProgress(false);
|
|
635
|
+
setPendingToolCalls((currentPending) => {
|
|
636
|
+
if (currentPending.size > 0) {
|
|
637
|
+
setIsPaused(true);
|
|
638
|
+
}
|
|
639
|
+
return currentPending;
|
|
640
|
+
});
|
|
641
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
642
|
+
sessionUtils.syncSessionRef.current();
|
|
643
|
+
}
|
|
644
|
+
},
|
|
645
|
+
onerror: (e) => {
|
|
646
|
+
console.log("Agent error", e);
|
|
647
|
+
setInProgress(false);
|
|
648
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
649
|
+
sessionUtils.syncSessionRef.current(messagesRef.current);
|
|
650
|
+
}
|
|
651
|
+
throw new Error("Failed to execute agent");
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
} catch (error) {
|
|
655
|
+
console.log("Agent execution failed", error);
|
|
656
|
+
setInProgress(false);
|
|
657
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
658
|
+
sessionUtils.syncSessionRef.current(messagesRef.current);
|
|
659
|
+
}
|
|
660
|
+
throw error;
|
|
661
|
+
}
|
|
662
|
+
},
|
|
663
|
+
[agentUrl, accessKey, currentSessionId, sessionUtils, debugHandlers]
|
|
664
|
+
);
|
|
665
|
+
const resumeAgent = (0, import_react2.useCallback)(
|
|
666
|
+
async (toolResults, options) => {
|
|
667
|
+
if (!currentSessionId || currentSessionId === TEMPORARY_SESSION_ID) {
|
|
668
|
+
throw new Error("Cannot resume: no active session");
|
|
669
|
+
}
|
|
670
|
+
setInProgress(true);
|
|
671
|
+
setIsPaused(false);
|
|
672
|
+
if (controller.current) {
|
|
673
|
+
controller.current.abort();
|
|
674
|
+
}
|
|
675
|
+
controller.current = new AbortController();
|
|
676
|
+
const headers = {
|
|
677
|
+
"Content-Type": "application/json",
|
|
678
|
+
...options?.additionalHeaders || {},
|
|
679
|
+
...accessKey ? { Authorization: `Bearer ${accessKey}` } : {}
|
|
680
|
+
};
|
|
681
|
+
const baseUrl = agentUrl.replace(/\/executeAgent\/.*$/, "");
|
|
682
|
+
const resumeUrl = `${baseUrl}/resumeAgent/${currentSessionId}`;
|
|
683
|
+
const body = {
|
|
684
|
+
stream: true,
|
|
685
|
+
input: options?.input || "",
|
|
686
|
+
context: options?.context || {},
|
|
687
|
+
clientToolResults: toolResults
|
|
688
|
+
};
|
|
689
|
+
try {
|
|
690
|
+
await (0, import_fetch_event_source.fetchEventSource)(resumeUrl, {
|
|
691
|
+
method: "POST",
|
|
692
|
+
headers,
|
|
693
|
+
signal: controller.current.signal,
|
|
694
|
+
openWhenHidden: true,
|
|
695
|
+
body: JSON.stringify(body),
|
|
696
|
+
onopen: async (resp) => {
|
|
697
|
+
if (resp.status >= 400) {
|
|
698
|
+
console.log(`AI onopen error with status: ${resp.status}`);
|
|
699
|
+
if (resp.status === 404) {
|
|
700
|
+
throw new Error(`Session not found (${resp.statusText})`);
|
|
701
|
+
}
|
|
702
|
+
throw new Error(`Error status ${resp.status}`);
|
|
703
|
+
}
|
|
704
|
+
const executionId = resp.headers.get("x-buildship-agent-execution-id");
|
|
705
|
+
if (executionId && options?.input) {
|
|
706
|
+
setMessages((prev) => {
|
|
707
|
+
const userMessage = {
|
|
708
|
+
role: "user",
|
|
709
|
+
content: options.input || "",
|
|
710
|
+
executionId
|
|
711
|
+
};
|
|
712
|
+
const updatedMessages = [...prev, userMessage];
|
|
713
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
714
|
+
sessionUtils.syncSessionRef.current(updatedMessages);
|
|
715
|
+
}
|
|
716
|
+
return updatedMessages;
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
},
|
|
720
|
+
onmessage: (event) => {
|
|
721
|
+
try {
|
|
722
|
+
const parsed = JSON.parse(event.data);
|
|
723
|
+
if (parsed.type === "llm_text_delta") {
|
|
724
|
+
setMessages((prev) => {
|
|
725
|
+
const lastMessage = prev[prev.length - 1];
|
|
726
|
+
const sequence = parsed.meta.sequence;
|
|
727
|
+
const newPart = {
|
|
728
|
+
type: "text",
|
|
729
|
+
text: parsed.data,
|
|
730
|
+
firstSequence: sequence,
|
|
731
|
+
lastSequence: sequence
|
|
732
|
+
};
|
|
733
|
+
let updatedMessages;
|
|
734
|
+
if (lastMessage?.role === "agent") {
|
|
735
|
+
const updatedMessage = {
|
|
736
|
+
...lastMessage,
|
|
737
|
+
content: lastMessage.content + parsed.data,
|
|
738
|
+
parts: updateAgentMessageParts(lastMessage.parts || [], newPart)
|
|
739
|
+
};
|
|
740
|
+
updatedMessages = [...prev.slice(0, -1), updatedMessage];
|
|
741
|
+
} else {
|
|
742
|
+
const updatedMessage = {
|
|
743
|
+
role: "agent",
|
|
744
|
+
content: parsed.data,
|
|
745
|
+
parts: [newPart],
|
|
746
|
+
executionId: parsed.meta.executionId
|
|
747
|
+
};
|
|
748
|
+
updatedMessages = [...prev, updatedMessage];
|
|
749
|
+
}
|
|
750
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
751
|
+
sessionUtils.syncSessionRef.current(updatedMessages);
|
|
752
|
+
}
|
|
753
|
+
return updatedMessages;
|
|
754
|
+
});
|
|
755
|
+
} else if (parsed.type === "client_tool_call") {
|
|
756
|
+
const { toolName, callId, inputs } = parsed.data;
|
|
757
|
+
const toolDefinition = clientToolsRef.current.find((t) => t.name === toolName);
|
|
758
|
+
const requiresResult = toolDefinition?.requiresResult ?? false;
|
|
759
|
+
if (requiresResult) {
|
|
760
|
+
setPendingToolCalls((prev) => {
|
|
761
|
+
const updated = new Map(prev);
|
|
762
|
+
updated.set(callId, {
|
|
763
|
+
toolName,
|
|
764
|
+
callId,
|
|
765
|
+
inputs: tryParseJSON(inputs),
|
|
766
|
+
requiresResult
|
|
767
|
+
});
|
|
768
|
+
return updated;
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
setMessages((prev) => {
|
|
772
|
+
const lastMessage = prev[prev.length - 1];
|
|
773
|
+
const newPart = {
|
|
774
|
+
type: "widget",
|
|
775
|
+
toolName,
|
|
776
|
+
callId,
|
|
777
|
+
inputs: tryParseJSON(inputs),
|
|
778
|
+
sequence: parsed.meta.sequence
|
|
779
|
+
};
|
|
780
|
+
let updatedMessages;
|
|
781
|
+
if (lastMessage?.role === "agent") {
|
|
782
|
+
const updatedMessage = {
|
|
783
|
+
...lastMessage,
|
|
784
|
+
parts: updateAgentMessageParts(lastMessage.parts || [], newPart)
|
|
785
|
+
};
|
|
786
|
+
updatedMessages = [...prev.slice(0, -1), updatedMessage];
|
|
787
|
+
} else {
|
|
788
|
+
const updatedMessage = {
|
|
789
|
+
role: "agent",
|
|
790
|
+
content: "",
|
|
791
|
+
parts: [newPart],
|
|
792
|
+
executionId: parsed.meta.executionId
|
|
793
|
+
};
|
|
794
|
+
updatedMessages = [...prev, updatedMessage];
|
|
795
|
+
}
|
|
796
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
797
|
+
sessionUtils.syncSessionRef.current(updatedMessages);
|
|
798
|
+
}
|
|
799
|
+
return updatedMessages;
|
|
800
|
+
});
|
|
801
|
+
} else if (parsed.type.startsWith("debug_") || parsed.type === "llm_reasoning_delta" || parsed.type === "agent_handoff" || parsed.type === "mcp_tool_call_start" || parsed.type === "mcp_tool_call_end") {
|
|
802
|
+
debugHandlers.handleDebugEvent(parsed);
|
|
803
|
+
}
|
|
804
|
+
} catch (e) {
|
|
805
|
+
console.log("Failed to parse agent message", e);
|
|
806
|
+
}
|
|
807
|
+
},
|
|
808
|
+
onclose: () => {
|
|
809
|
+
console.log("Agent resume closed");
|
|
810
|
+
setInProgress(false);
|
|
811
|
+
setPendingToolCalls((currentPending) => {
|
|
812
|
+
if (currentPending.size > 0) {
|
|
813
|
+
setIsPaused(true);
|
|
814
|
+
}
|
|
815
|
+
return currentPending;
|
|
816
|
+
});
|
|
817
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
818
|
+
sessionUtils.syncSessionRef.current();
|
|
819
|
+
}
|
|
820
|
+
},
|
|
821
|
+
onerror: (e) => {
|
|
822
|
+
console.log("Agent resume error", e);
|
|
823
|
+
setInProgress(false);
|
|
824
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
825
|
+
sessionUtils.syncSessionRef.current(messagesRef.current);
|
|
826
|
+
}
|
|
827
|
+
throw new Error("Failed to resume agent");
|
|
828
|
+
}
|
|
829
|
+
});
|
|
830
|
+
} catch (error) {
|
|
831
|
+
console.log("Agent resume execution failed", error);
|
|
832
|
+
setInProgress(false);
|
|
833
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
834
|
+
sessionUtils.syncSessionRef.current(messagesRef.current);
|
|
835
|
+
}
|
|
836
|
+
throw error;
|
|
837
|
+
}
|
|
838
|
+
},
|
|
839
|
+
[agentUrl, accessKey, currentSessionId, sessionUtils, debugHandlers]
|
|
840
|
+
);
|
|
841
|
+
const handleSend = (0, import_react2.useCallback)(
|
|
842
|
+
async (input, options) => {
|
|
843
|
+
if (options?.clientTools) {
|
|
844
|
+
clientToolsRef.current = options.clientTools;
|
|
845
|
+
}
|
|
846
|
+
setPendingToolCalls(/* @__PURE__ */ new Map());
|
|
847
|
+
setIsPaused(false);
|
|
848
|
+
const userMessage = {
|
|
849
|
+
role: "user",
|
|
850
|
+
content: input,
|
|
851
|
+
executionId: Date.now().toString()
|
|
852
|
+
};
|
|
853
|
+
if (!options?.skipUserMessage) {
|
|
854
|
+
setMessages((prev) => {
|
|
855
|
+
const updatedMessages = [...prev, userMessage];
|
|
856
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
857
|
+
sessionUtils.syncSessionRef.current(updatedMessages);
|
|
858
|
+
}
|
|
859
|
+
return updatedMessages;
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
try {
|
|
863
|
+
await runAgent(input, options);
|
|
864
|
+
} catch (error) {
|
|
865
|
+
if (!options?.skipUserMessage) {
|
|
866
|
+
setMessages((prev) => {
|
|
867
|
+
const updatedMessages = prev.some((m) => m === userMessage) ? prev : [...prev, userMessage];
|
|
868
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
869
|
+
sessionUtils.syncSessionRef.current(updatedMessages);
|
|
870
|
+
}
|
|
871
|
+
return updatedMessages;
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
throw error;
|
|
875
|
+
}
|
|
876
|
+
},
|
|
877
|
+
[runAgent, sessionUtils.syncSessionRef]
|
|
878
|
+
);
|
|
879
|
+
const addOptimisticMessage = (0, import_react2.useCallback)(
|
|
880
|
+
(input) => {
|
|
881
|
+
const userMessage = {
|
|
882
|
+
role: "user",
|
|
883
|
+
content: input,
|
|
884
|
+
executionId: Date.now().toString()
|
|
885
|
+
};
|
|
886
|
+
setMessages((prev) => {
|
|
887
|
+
const updatedMessages = [...prev, userMessage];
|
|
888
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
889
|
+
sessionUtils.syncSessionRef.current(updatedMessages);
|
|
890
|
+
}
|
|
891
|
+
return updatedMessages;
|
|
892
|
+
});
|
|
893
|
+
},
|
|
894
|
+
[sessionUtils.syncSessionRef]
|
|
895
|
+
);
|
|
896
|
+
const submitToolResults = (0, import_react2.useCallback)(
|
|
897
|
+
async (toolResults, options) => {
|
|
898
|
+
setPendingToolCalls((prev) => {
|
|
899
|
+
const updated = new Map(prev);
|
|
900
|
+
toolResults.forEach((result) => {
|
|
901
|
+
updated.delete(result.callId);
|
|
902
|
+
});
|
|
903
|
+
return updated;
|
|
904
|
+
});
|
|
905
|
+
try {
|
|
906
|
+
await resumeAgent(toolResults, options);
|
|
907
|
+
} catch (error) {
|
|
908
|
+
console.error("Failed to submit tool results:", error);
|
|
909
|
+
throw error;
|
|
910
|
+
}
|
|
911
|
+
},
|
|
912
|
+
[resumeAgent]
|
|
913
|
+
);
|
|
914
|
+
const abort = (0, import_react2.useCallback)(() => {
|
|
915
|
+
if (controller.current) {
|
|
916
|
+
controller.current.abort();
|
|
917
|
+
}
|
|
918
|
+
setInProgress(false);
|
|
919
|
+
if (sessionUtils.syncSessionRef.current) {
|
|
920
|
+
sessionUtils.syncSessionRef.current(messagesRef.current);
|
|
921
|
+
}
|
|
922
|
+
}, [sessionUtils.syncSessionRef]);
|
|
923
|
+
return {
|
|
924
|
+
inProgress,
|
|
925
|
+
messages,
|
|
926
|
+
handleSend,
|
|
927
|
+
addOptimisticMessage,
|
|
928
|
+
submitToolResults,
|
|
929
|
+
abort,
|
|
930
|
+
sessionId: currentSessionId,
|
|
931
|
+
switchSession: sessionUtils.switchSession,
|
|
932
|
+
deleteSession: sessionUtils.deleteSession,
|
|
933
|
+
sessions: sessionUtils.sessionsList,
|
|
934
|
+
debugData,
|
|
935
|
+
pendingToolCalls: Array.from(pendingToolCalls.values()),
|
|
936
|
+
isPaused
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
// src/utils/use-synced-local-storage.ts
|
|
941
|
+
var import_react3 = require("react");
|
|
942
|
+
function parseJSON(value, defaultValue) {
|
|
943
|
+
if (value === null) return defaultValue;
|
|
944
|
+
try {
|
|
945
|
+
return JSON.parse(value);
|
|
946
|
+
} catch {
|
|
947
|
+
return defaultValue;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
function useSyncedLocalStorage(key, initialValue) {
|
|
951
|
+
const [storedValue, setStoredValue] = (0, import_react3.useState)(() => {
|
|
952
|
+
if (typeof window === "undefined") {
|
|
953
|
+
return initialValue;
|
|
954
|
+
}
|
|
955
|
+
try {
|
|
956
|
+
const item = window.localStorage.getItem(key);
|
|
957
|
+
return parseJSON(item, initialValue);
|
|
958
|
+
} catch (error) {
|
|
959
|
+
console.warn(`Error reading localStorage key "${key}":`, error);
|
|
960
|
+
return initialValue;
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
const setValue = (0, import_react3.useCallback)(
|
|
964
|
+
(value) => {
|
|
965
|
+
try {
|
|
966
|
+
setStoredValue((prev) => {
|
|
967
|
+
const valueToStore = value instanceof Function ? value(prev) : value;
|
|
968
|
+
if (typeof window !== "undefined") {
|
|
969
|
+
window.localStorage.setItem(key, JSON.stringify(valueToStore));
|
|
970
|
+
window.dispatchEvent(
|
|
971
|
+
new StorageEvent("storage", { key, newValue: JSON.stringify(valueToStore) })
|
|
972
|
+
);
|
|
973
|
+
}
|
|
974
|
+
return valueToStore;
|
|
975
|
+
});
|
|
976
|
+
} catch (error) {
|
|
977
|
+
console.warn(`Error setting localStorage key "${key}":`, error);
|
|
978
|
+
}
|
|
979
|
+
},
|
|
980
|
+
[key]
|
|
981
|
+
);
|
|
982
|
+
(0, import_react3.useEffect)(() => {
|
|
983
|
+
const handleStorageChange = (event) => {
|
|
984
|
+
if (event.key === key) {
|
|
985
|
+
setStoredValue(parseJSON(event.newValue, initialValue));
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
window.addEventListener("storage", handleStorageChange);
|
|
989
|
+
return () => window.removeEventListener("storage", handleStorageChange);
|
|
990
|
+
}, [key, initialValue]);
|
|
991
|
+
return [storedValue, setValue];
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
// src/agent-context.tsx
|
|
995
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
996
|
+
var AgentContext = (0, import_react4.createContext)(null);
|
|
997
|
+
function AgentContextProvider({ children }) {
|
|
998
|
+
const activeAgentsRef = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
|
|
999
|
+
const runnersRef = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
|
|
1000
|
+
const listenersRef = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
|
|
1001
|
+
const [, forceUpdate] = (0, import_react4.useState)({});
|
|
1002
|
+
const [allSessions, setAllSessions] = useSyncedLocalStorage(AGENT_SESSIONS_KEY, {});
|
|
1003
|
+
const [debugData, setDebugData] = useSyncedLocalStorage(
|
|
1004
|
+
AGENT_DEBUG_DATA_KEY,
|
|
1005
|
+
{}
|
|
1006
|
+
);
|
|
1007
|
+
const initializeAgent = (0, import_react4.useCallback)((agentId, agentUrl, accessKey) => {
|
|
1008
|
+
const existing = activeAgentsRef.current.get(agentId);
|
|
1009
|
+
if (!existing) {
|
|
1010
|
+
activeAgentsRef.current.set(agentId, { agentUrl, accessKey });
|
|
1011
|
+
forceUpdate({});
|
|
1012
|
+
} else if (existing.agentUrl !== agentUrl || existing.accessKey !== accessKey) {
|
|
1013
|
+
activeAgentsRef.current.set(agentId, { agentUrl, accessKey });
|
|
1014
|
+
forceUpdate({});
|
|
1015
|
+
}
|
|
1016
|
+
}, []);
|
|
1017
|
+
const registerRunner = (0, import_react4.useCallback)((agentId, runner) => {
|
|
1018
|
+
runnersRef.current.set(agentId, runner);
|
|
1019
|
+
const listeners = listenersRef.current.get(agentId);
|
|
1020
|
+
if (listeners) {
|
|
1021
|
+
listeners.forEach((callback) => callback());
|
|
1022
|
+
}
|
|
1023
|
+
}, []);
|
|
1024
|
+
const getRunner = (0, import_react4.useCallback)((agentId) => {
|
|
1025
|
+
return runnersRef.current.get(agentId) || null;
|
|
1026
|
+
}, []);
|
|
1027
|
+
const contextValue = (0, import_react4.useMemo)(
|
|
1028
|
+
() => ({
|
|
1029
|
+
initializeAgent,
|
|
1030
|
+
registerRunner,
|
|
1031
|
+
getRunner,
|
|
1032
|
+
allSessions,
|
|
1033
|
+
setAllSessions,
|
|
1034
|
+
debugData,
|
|
1035
|
+
setDebugData,
|
|
1036
|
+
runnersRef,
|
|
1037
|
+
listenersRef
|
|
1038
|
+
}),
|
|
1039
|
+
[
|
|
1040
|
+
initializeAgent,
|
|
1041
|
+
registerRunner,
|
|
1042
|
+
getRunner,
|
|
1043
|
+
allSessions,
|
|
1044
|
+
setAllSessions,
|
|
1045
|
+
debugData,
|
|
1046
|
+
setDebugData
|
|
1047
|
+
]
|
|
1048
|
+
);
|
|
1049
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AgentContext.Provider, { value: contextValue, children: [
|
|
1050
|
+
children,
|
|
1051
|
+
Array.from(activeAgentsRef.current.entries()).map(([agentId, { agentUrl, accessKey }]) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentRunnerInstance, { agentId, agentUrl, accessKey }, agentId))
|
|
1052
|
+
] });
|
|
1053
|
+
}
|
|
1054
|
+
function AgentRunnerInstance({
|
|
1055
|
+
agentId,
|
|
1056
|
+
agentUrl,
|
|
1057
|
+
accessKey
|
|
1058
|
+
}) {
|
|
1059
|
+
const agent = useAgent(agentId, agentUrl, accessKey);
|
|
1060
|
+
const context = (0, import_react4.useContext)(AgentContext);
|
|
1061
|
+
if (!context) return null;
|
|
1062
|
+
(0, import_react4.useEffect)(() => {
|
|
1063
|
+
context.registerRunner(agentId, agent);
|
|
1064
|
+
}, [agentId, agent, context]);
|
|
1065
|
+
return null;
|
|
1066
|
+
}
|
|
1067
|
+
function useAgentContext(agentId, agentUrl, accessKey) {
|
|
1068
|
+
const context = (0, import_react4.useContext)(AgentContext);
|
|
1069
|
+
if (!context) {
|
|
1070
|
+
throw new Error("useAgentContext must be used within AgentContextProvider");
|
|
1071
|
+
}
|
|
1072
|
+
const { initializeAgent, getRunner, listenersRef } = context;
|
|
1073
|
+
(0, import_react4.useEffect)(() => {
|
|
1074
|
+
initializeAgent(agentId, agentUrl, accessKey);
|
|
1075
|
+
}, [agentId, agentUrl, initializeAgent]);
|
|
1076
|
+
const [runner, setRunner] = (0, import_react4.useState)(() => getRunner(agentId));
|
|
1077
|
+
(0, import_react4.useEffect)(() => {
|
|
1078
|
+
const currentRunner = getRunner(agentId);
|
|
1079
|
+
if (currentRunner !== runner) {
|
|
1080
|
+
setRunner(currentRunner);
|
|
1081
|
+
}
|
|
1082
|
+
const callback = () => {
|
|
1083
|
+
setRunner(getRunner(agentId));
|
|
1084
|
+
};
|
|
1085
|
+
if (!listenersRef.current.has(agentId)) {
|
|
1086
|
+
listenersRef.current.set(agentId, /* @__PURE__ */ new Set());
|
|
1087
|
+
}
|
|
1088
|
+
listenersRef.current.get(agentId)?.add(callback);
|
|
1089
|
+
return () => {
|
|
1090
|
+
listenersRef.current.get(agentId)?.delete(callback);
|
|
1091
|
+
};
|
|
1092
|
+
}, [agentId, getRunner]);
|
|
1093
|
+
const placeholder = (0, import_react4.useMemo)(
|
|
1094
|
+
() => ({
|
|
1095
|
+
messages: [],
|
|
1096
|
+
inProgress: false,
|
|
1097
|
+
sessionId: "",
|
|
1098
|
+
sessions: [],
|
|
1099
|
+
debugData: {},
|
|
1100
|
+
pendingToolCalls: [],
|
|
1101
|
+
isPaused: false,
|
|
1102
|
+
handleSend: async () => {
|
|
1103
|
+
},
|
|
1104
|
+
submitToolResults: async () => {
|
|
1105
|
+
},
|
|
1106
|
+
switchSession: () => {
|
|
1107
|
+
},
|
|
1108
|
+
deleteSession: () => {
|
|
1109
|
+
},
|
|
1110
|
+
addOptimisticMessage: () => {
|
|
1111
|
+
},
|
|
1112
|
+
abort: () => {
|
|
1113
|
+
}
|
|
1114
|
+
}),
|
|
1115
|
+
[]
|
|
1116
|
+
);
|
|
1117
|
+
return runner || placeholder;
|
|
1118
|
+
}
|
|
1119
|
+
function useAgentGlobalState() {
|
|
1120
|
+
const context = (0, import_react4.useContext)(AgentContext);
|
|
1121
|
+
if (!context) {
|
|
1122
|
+
throw new Error("useAgentGlobalState must be used within AgentContextProvider");
|
|
1123
|
+
}
|
|
1124
|
+
return {
|
|
1125
|
+
allSessions: context.allSessions,
|
|
1126
|
+
setAllSessions: context.setAllSessions,
|
|
1127
|
+
debugData: context.debugData,
|
|
1128
|
+
setDebugData: context.setDebugData
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1132
|
+
0 && (module.exports = {
|
|
1133
|
+
AGENT_DEBUG_DATA_KEY,
|
|
1134
|
+
AGENT_SESSIONS_KEY,
|
|
1135
|
+
AgentContextProvider,
|
|
1136
|
+
DEFAULT_SESSION_NAME,
|
|
1137
|
+
TEMPORARY_SESSION_ID,
|
|
1138
|
+
cleanSchema,
|
|
1139
|
+
tryParseJSON,
|
|
1140
|
+
updateAgentMessageParts,
|
|
1141
|
+
useAgent,
|
|
1142
|
+
useAgentContext,
|
|
1143
|
+
useAgentGlobalState
|
|
1144
|
+
});
|
|
1145
|
+
//# sourceMappingURL=index.cjs.map
|