@inkeep/agents-api 0.0.0-dev-20260122003353 → 0.0.0-dev-20260122004923
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.well-known/workflow/v1/flow.cjs +44 -44
- package/dist/.well-known/workflow/v1/flow.cjs.debug.json +2 -2
- package/dist/.well-known/workflow/v1/step.cjs +130 -130
- package/dist/.well-known/workflow/v1/step.cjs.debug.json +2 -2
- package/dist/createApp.d.ts +2 -2
- package/dist/domains/evals/routes/datasetTriggers.d.ts +2 -2
- package/dist/domains/manage/routes/conversations.d.ts +2 -2
- package/dist/domains/manage/routes/index.d.ts +2 -2
- package/dist/domains/manage/routes/mcp.d.ts +2 -2
- package/dist/domains/manage/routes/signoz.d.ts +2 -2
- package/dist/domains/run/agents/Agent.js +95 -29
- package/dist/domains/run/routes/chat.js +46 -0
- package/dist/domains/run/routes/chatDataStream.js +105 -12
- package/dist/domains/run/services/ToolApprovalUiBus.d.ts +28 -0
- package/dist/domains/run/services/ToolApprovalUiBus.js +44 -0
- package/dist/domains/run/utils/stream-helpers.d.ts +134 -0
- package/dist/domains/run/utils/stream-helpers.js +182 -0
- package/dist/env.d.ts +2 -2
- package/dist/factory.d.ts +262 -262
- package/dist/index.d.ts +261 -261
- package/dist/middleware/evalsAuth.d.ts +2 -2
- package/dist/middleware/manageAuth.d.ts +2 -2
- package/dist/middleware/projectAccess.d.ts +2 -2
- package/dist/middleware/projectConfig.d.ts +3 -3
- package/dist/middleware/requirePermission.d.ts +2 -2
- package/dist/middleware/tenantAccess.d.ts +2 -2
- package/dist/middleware/tracing.d.ts +3 -3
- package/package.json +3 -3
|
@@ -15,6 +15,7 @@ import { IncrementalStreamParser } from "../services/IncrementalStreamParser.js"
|
|
|
15
15
|
import { MidGenerationCompressor } from "../services/MidGenerationCompressor.js";
|
|
16
16
|
import { pendingToolApprovalManager } from "../services/PendingToolApprovalManager.js";
|
|
17
17
|
import { ResponseFormatter } from "../services/ResponseFormatter.js";
|
|
18
|
+
import { toolApprovalUiBus } from "../services/ToolApprovalUiBus.js";
|
|
18
19
|
import { generateToolId } from "../utils/agent-operations.js";
|
|
19
20
|
import { jsonSchemaToZod } from "../utils/data-component-schema.js";
|
|
20
21
|
import { ArtifactCreateSchema, ArtifactReferenceSchema } from "../utils/artifact-component-schema.js";
|
|
@@ -223,6 +224,12 @@ var Agent = class {
|
|
|
223
224
|
execute: async (args, context$1) => {
|
|
224
225
|
const startTime = Date.now();
|
|
225
226
|
const toolCallId = context$1?.toolCallId || generateToolId();
|
|
227
|
+
const streamHelper = this.getStreamingHelper();
|
|
228
|
+
const chunkString = (s, size = 16) => {
|
|
229
|
+
const out = [];
|
|
230
|
+
for (let i = 0; i < s.length; i += size) out.push(s.slice(i, i + size));
|
|
231
|
+
return out;
|
|
232
|
+
};
|
|
226
233
|
const activeSpan = trace.getActiveSpan();
|
|
227
234
|
if (activeSpan) {
|
|
228
235
|
const attributes = {
|
|
@@ -237,9 +244,26 @@ var Agent = class {
|
|
|
237
244
|
if (options?.mcpServerName) attributes["ai.toolCall.mcpServerName"] = options.mcpServerName;
|
|
238
245
|
activeSpan.setAttributes(attributes);
|
|
239
246
|
}
|
|
240
|
-
const
|
|
247
|
+
const isInternalToolForUi = toolName.includes("save_tool_result") || toolName.includes("thinking_complete") || toolName.startsWith("transfer_to_") || toolName.startsWith("delegate_to_");
|
|
241
248
|
const needsApproval = options?.needsApproval || false;
|
|
242
|
-
if (streamRequestId && !
|
|
249
|
+
if (streamRequestId && streamHelper && !isInternalToolForUi) {
|
|
250
|
+
const inputText = JSON.stringify(args ?? {});
|
|
251
|
+
await streamHelper.writeToolInputStart({
|
|
252
|
+
toolCallId,
|
|
253
|
+
toolName
|
|
254
|
+
});
|
|
255
|
+
for (const part of chunkString(inputText, 16)) await streamHelper.writeToolInputDelta({
|
|
256
|
+
toolCallId,
|
|
257
|
+
inputTextDelta: part
|
|
258
|
+
});
|
|
259
|
+
await streamHelper.writeToolInputAvailable({
|
|
260
|
+
toolCallId,
|
|
261
|
+
toolName,
|
|
262
|
+
input: args ?? {},
|
|
263
|
+
providerMetadata: context$1?.providerMetadata
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
if (streamRequestId && !isInternalToolForUi) {
|
|
243
267
|
const toolCallData = {
|
|
244
268
|
toolName,
|
|
245
269
|
input: args,
|
|
@@ -256,7 +280,7 @@ var Agent = class {
|
|
|
256
280
|
const result = await originalExecute(args, context$1);
|
|
257
281
|
const duration = Date.now() - startTime;
|
|
258
282
|
const toolResultConversationId = this.getToolResultConversationId();
|
|
259
|
-
if (streamRequestId && !
|
|
283
|
+
if (streamRequestId && !isInternalToolForUi && toolResultConversationId) try {
|
|
260
284
|
const messagePayload = {
|
|
261
285
|
id: generateId(),
|
|
262
286
|
tenantId: this.config.tenantId,
|
|
@@ -284,7 +308,7 @@ var Agent = class {
|
|
|
284
308
|
conversationId: toolResultConversationId
|
|
285
309
|
}, "Failed to store tool result in conversation history");
|
|
286
310
|
}
|
|
287
|
-
if (streamRequestId && !
|
|
311
|
+
if (streamRequestId && !isInternalToolForUi) agentSessionManager.recordEvent(streamRequestId, "tool_result", this.config.id, {
|
|
288
312
|
toolName,
|
|
289
313
|
output: result,
|
|
290
314
|
toolCallId,
|
|
@@ -292,11 +316,17 @@ var Agent = class {
|
|
|
292
316
|
relationshipId,
|
|
293
317
|
needsApproval
|
|
294
318
|
});
|
|
319
|
+
const isDeniedResult = !!result && typeof result === "object" && "__inkeepToolDenied" in result && result.__inkeepToolDenied === true;
|
|
320
|
+
if (streamRequestId && streamHelper && !isInternalToolForUi) if (isDeniedResult) await streamHelper.writeToolOutputDenied({ toolCallId });
|
|
321
|
+
else await streamHelper.writeToolOutputAvailable({
|
|
322
|
+
toolCallId,
|
|
323
|
+
output: result
|
|
324
|
+
});
|
|
295
325
|
return result;
|
|
296
326
|
} catch (error) {
|
|
297
327
|
const duration = Date.now() - startTime;
|
|
298
328
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
299
|
-
if (streamRequestId && !
|
|
329
|
+
if (streamRequestId && !isInternalToolForUi) agentSessionManager.recordEvent(streamRequestId, "tool_result", this.config.id, {
|
|
300
330
|
toolName,
|
|
301
331
|
output: null,
|
|
302
332
|
toolCallId,
|
|
@@ -305,6 +335,10 @@ var Agent = class {
|
|
|
305
335
|
relationshipId,
|
|
306
336
|
needsApproval
|
|
307
337
|
});
|
|
338
|
+
if (streamRequestId && streamHelper && !isInternalToolForUi) await streamHelper.writeToolOutputError({
|
|
339
|
+
toolCallId,
|
|
340
|
+
error: errorMessage
|
|
341
|
+
});
|
|
308
342
|
throw error;
|
|
309
343
|
}
|
|
310
344
|
}
|
|
@@ -374,7 +408,7 @@ var Agent = class {
|
|
|
374
408
|
const sessionWrappedTool = tool({
|
|
375
409
|
description: originalTool.description,
|
|
376
410
|
inputSchema: originalTool.inputSchema,
|
|
377
|
-
execute: async (args, { toolCallId }) => {
|
|
411
|
+
execute: async (args, { toolCallId, providerMetadata }) => {
|
|
378
412
|
let processedArgs;
|
|
379
413
|
try {
|
|
380
414
|
processedArgs = parseEmbeddedJson(args);
|
|
@@ -412,22 +446,52 @@ var Agent = class {
|
|
|
412
446
|
requestSpan.setStatus({ code: SpanStatusCode.OK });
|
|
413
447
|
requestSpan.end();
|
|
414
448
|
});
|
|
415
|
-
const
|
|
416
|
-
if (
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
"subAgent.id": this.config.id,
|
|
420
|
-
"subAgent.name": this.config.name
|
|
421
|
-
} }, (denialSpan) => {
|
|
422
|
-
logger.info({
|
|
423
|
-
toolName,
|
|
424
|
-
toolCallId,
|
|
425
|
-
reason: approvalResult.reason
|
|
426
|
-
}, "Tool execution denied by user");
|
|
427
|
-
denialSpan.setStatus({ code: SpanStatusCode.OK });
|
|
428
|
-
denialSpan.end();
|
|
429
|
-
return `User denied approval to run this tool: ${approvalResult.reason}`;
|
|
449
|
+
const streamHelper = this.getStreamingHelper();
|
|
450
|
+
if (streamHelper) await streamHelper.writeToolApprovalRequest({
|
|
451
|
+
approvalId: `aitxt-${toolCallId}`,
|
|
452
|
+
toolCallId
|
|
430
453
|
});
|
|
454
|
+
else if (this.isDelegatedAgent) {
|
|
455
|
+
const streamRequestId$1 = this.getStreamRequestId();
|
|
456
|
+
if (streamRequestId$1) await toolApprovalUiBus.publish(streamRequestId$1, {
|
|
457
|
+
type: "approval-needed",
|
|
458
|
+
toolCallId,
|
|
459
|
+
toolName,
|
|
460
|
+
input: finalArgs,
|
|
461
|
+
providerMetadata,
|
|
462
|
+
approvalId: `aitxt-${toolCallId}`
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
const approvalResult = await pendingToolApprovalManager.waitForApproval(toolCallId, toolName, args, this.conversationId || "unknown", this.config.id);
|
|
466
|
+
if (!approvalResult.approved) {
|
|
467
|
+
if (!streamHelper && this.isDelegatedAgent) {
|
|
468
|
+
const streamRequestId$1 = this.getStreamRequestId();
|
|
469
|
+
if (streamRequestId$1) await toolApprovalUiBus.publish(streamRequestId$1, {
|
|
470
|
+
type: "approval-resolved",
|
|
471
|
+
toolCallId,
|
|
472
|
+
approved: false
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
return tracer.startActiveSpan("tool.approval_denied", { attributes: {
|
|
476
|
+
"tool.name": toolName,
|
|
477
|
+
"tool.callId": toolCallId,
|
|
478
|
+
"subAgent.id": this.config.id,
|
|
479
|
+
"subAgent.name": this.config.name
|
|
480
|
+
} }, (denialSpan) => {
|
|
481
|
+
logger.info({
|
|
482
|
+
toolName,
|
|
483
|
+
toolCallId,
|
|
484
|
+
reason: approvalResult.reason
|
|
485
|
+
}, "Tool execution denied by user");
|
|
486
|
+
denialSpan.setStatus({ code: SpanStatusCode.OK });
|
|
487
|
+
denialSpan.end();
|
|
488
|
+
return {
|
|
489
|
+
__inkeepToolDenied: true,
|
|
490
|
+
toolCallId,
|
|
491
|
+
reason: approvalResult.reason
|
|
492
|
+
};
|
|
493
|
+
});
|
|
494
|
+
}
|
|
431
495
|
tracer.startActiveSpan("tool.approval_approved", { attributes: {
|
|
432
496
|
"tool.name": toolName,
|
|
433
497
|
"tool.callId": toolCallId,
|
|
@@ -441,6 +505,14 @@ var Agent = class {
|
|
|
441
505
|
approvedSpan.setStatus({ code: SpanStatusCode.OK });
|
|
442
506
|
approvedSpan.end();
|
|
443
507
|
});
|
|
508
|
+
if (!streamHelper && this.isDelegatedAgent) {
|
|
509
|
+
const streamRequestId$1 = this.getStreamRequestId();
|
|
510
|
+
if (streamRequestId$1) await toolApprovalUiBus.publish(streamRequestId$1, {
|
|
511
|
+
type: "approval-resolved",
|
|
512
|
+
toolCallId,
|
|
513
|
+
approved: true
|
|
514
|
+
});
|
|
515
|
+
}
|
|
444
516
|
}
|
|
445
517
|
logger.debug({
|
|
446
518
|
toolName,
|
|
@@ -500,10 +572,7 @@ var Agent = class {
|
|
|
500
572
|
result: enhancedResult,
|
|
501
573
|
timestamp: Date.now()
|
|
502
574
|
});
|
|
503
|
-
return
|
|
504
|
-
result: enhancedResult,
|
|
505
|
-
toolCallId
|
|
506
|
-
};
|
|
575
|
+
return enhancedResult;
|
|
507
576
|
} catch (error) {
|
|
508
577
|
logger.error({
|
|
509
578
|
toolName,
|
|
@@ -795,10 +864,7 @@ var Agent = class {
|
|
|
795
864
|
result,
|
|
796
865
|
timestamp: Date.now()
|
|
797
866
|
});
|
|
798
|
-
return
|
|
799
|
-
result,
|
|
800
|
-
toolCallId
|
|
801
|
-
};
|
|
867
|
+
return result;
|
|
802
868
|
} catch (error) {
|
|
803
869
|
logger.error({
|
|
804
870
|
toolName: functionToolDef.name,
|
|
@@ -3,6 +3,7 @@ import runDbClient_default from "../../../data/db/runDbClient.js";
|
|
|
3
3
|
import { contextValidationMiddleware } from "../context/validation.js";
|
|
4
4
|
import { handleContextResolution } from "../context/context.js";
|
|
5
5
|
import "../context/index.js";
|
|
6
|
+
import { toolApprovalUiBus } from "../services/ToolApprovalUiBus.js";
|
|
6
7
|
import { errorOp } from "../utils/agent-operations.js";
|
|
7
8
|
import { createSSEStreamHelper } from "../utils/stream-helpers.js";
|
|
8
9
|
import { ExecutionHandler } from "../handlers/executionHandler.js";
|
|
@@ -222,9 +223,50 @@ app.openapi(chatCompletionsRoute, async (c) => {
|
|
|
222
223
|
"database.operation": "insert"
|
|
223
224
|
});
|
|
224
225
|
return streamSSE(c, async (stream$1) => {
|
|
226
|
+
const chunkString = (s, size = 16) => {
|
|
227
|
+
const out = [];
|
|
228
|
+
for (let i = 0; i < s.length; i += size) out.push(s.slice(i, i + size));
|
|
229
|
+
return out;
|
|
230
|
+
};
|
|
231
|
+
let unsubscribe;
|
|
225
232
|
try {
|
|
226
233
|
const sseHelper = createSSEStreamHelper(stream$1, requestId, timestamp);
|
|
227
234
|
await sseHelper.writeRole();
|
|
235
|
+
const seenToolCalls = /* @__PURE__ */ new Set();
|
|
236
|
+
const seenOutputs = /* @__PURE__ */ new Set();
|
|
237
|
+
unsubscribe = toolApprovalUiBus.subscribe(requestId, async (event) => {
|
|
238
|
+
if (event.type === "approval-needed") {
|
|
239
|
+
if (seenToolCalls.has(event.toolCallId)) return;
|
|
240
|
+
seenToolCalls.add(event.toolCallId);
|
|
241
|
+
await sseHelper.writeToolInputStart({
|
|
242
|
+
toolCallId: event.toolCallId,
|
|
243
|
+
toolName: event.toolName
|
|
244
|
+
});
|
|
245
|
+
const inputText = JSON.stringify(event.input ?? {});
|
|
246
|
+
for (const part of chunkString(inputText, 16)) await sseHelper.writeToolInputDelta({
|
|
247
|
+
toolCallId: event.toolCallId,
|
|
248
|
+
inputTextDelta: part
|
|
249
|
+
});
|
|
250
|
+
await sseHelper.writeToolInputAvailable({
|
|
251
|
+
toolCallId: event.toolCallId,
|
|
252
|
+
toolName: event.toolName,
|
|
253
|
+
input: event.input ?? {},
|
|
254
|
+
providerMetadata: event.providerMetadata
|
|
255
|
+
});
|
|
256
|
+
await sseHelper.writeToolApprovalRequest({
|
|
257
|
+
approvalId: event.approvalId,
|
|
258
|
+
toolCallId: event.toolCallId
|
|
259
|
+
});
|
|
260
|
+
} else if (event.type === "approval-resolved") {
|
|
261
|
+
if (seenOutputs.has(event.toolCallId)) return;
|
|
262
|
+
seenOutputs.add(event.toolCallId);
|
|
263
|
+
if (event.approved) await sseHelper.writeToolOutputAvailable({
|
|
264
|
+
toolCallId: event.toolCallId,
|
|
265
|
+
output: { status: "approved" }
|
|
266
|
+
});
|
|
267
|
+
else await sseHelper.writeToolOutputDenied({ toolCallId: event.toolCallId });
|
|
268
|
+
}
|
|
269
|
+
});
|
|
228
270
|
logger.info({ subAgentId }, "Starting execution");
|
|
229
271
|
const emitOperations = c.req.header("x-emit-operations") === "true";
|
|
230
272
|
const forwardedHeaders = {};
|
|
@@ -275,6 +317,10 @@ app.openapi(chatCompletionsRoute, async (c) => {
|
|
|
275
317
|
} catch (streamError) {
|
|
276
318
|
logger.error({ streamError }, "Failed to write error to stream");
|
|
277
319
|
}
|
|
320
|
+
} finally {
|
|
321
|
+
try {
|
|
322
|
+
unsubscribe?.();
|
|
323
|
+
} catch (_e) {}
|
|
278
324
|
}
|
|
279
325
|
});
|
|
280
326
|
});
|
|
@@ -4,6 +4,7 @@ import { contextValidationMiddleware } from "../context/validation.js";
|
|
|
4
4
|
import { handleContextResolution } from "../context/context.js";
|
|
5
5
|
import "../context/index.js";
|
|
6
6
|
import { pendingToolApprovalManager } from "../services/PendingToolApprovalManager.js";
|
|
7
|
+
import { toolApprovalUiBus } from "../services/ToolApprovalUiBus.js";
|
|
7
8
|
import { errorOp } from "../utils/agent-operations.js";
|
|
8
9
|
import { createBufferingStreamHelper, createVercelStreamHelper } from "../utils/stream-helpers.js";
|
|
9
10
|
import { ExecutionHandler } from "../handlers/executionHandler.js";
|
|
@@ -34,16 +35,31 @@ const chatDataStreamRoute = createRoute({
|
|
|
34
35
|
"tool"
|
|
35
36
|
]),
|
|
36
37
|
content: z.any(),
|
|
37
|
-
parts: z.array(z.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
parts: z.array(z.union([
|
|
39
|
+
z.object({
|
|
40
|
+
type: z.union([z.enum([
|
|
41
|
+
"text",
|
|
42
|
+
"image",
|
|
43
|
+
"audio",
|
|
44
|
+
"video",
|
|
45
|
+
"file"
|
|
46
|
+
]), z.string().regex(/^data-/, "Type must start with \"data-\"")]),
|
|
47
|
+
text: z.string().optional()
|
|
48
|
+
}),
|
|
49
|
+
z.object({
|
|
50
|
+
type: z.string().regex(/^tool-/, "Type must start with \"tool-\""),
|
|
51
|
+
toolCallId: z.string(),
|
|
52
|
+
state: z.literal("approval-responded"),
|
|
53
|
+
approval: z.object({
|
|
54
|
+
id: z.string(),
|
|
55
|
+
approved: z.boolean(),
|
|
56
|
+
reason: z.string().optional()
|
|
57
|
+
}),
|
|
58
|
+
input: z.any().optional(),
|
|
59
|
+
callProviderMetadata: z.any().optional()
|
|
60
|
+
}),
|
|
61
|
+
z.object({ type: z.literal("step-start") })
|
|
62
|
+
])).optional()
|
|
47
63
|
})),
|
|
48
64
|
id: z.string().optional(),
|
|
49
65
|
conversationId: z.string().optional(),
|
|
@@ -74,7 +90,39 @@ app.openapi(chatDataStreamRoute, async (c) => {
|
|
|
74
90
|
agentId
|
|
75
91
|
}, "Extracted chatDataStream parameters");
|
|
76
92
|
const body = c.get("requestBody") || {};
|
|
77
|
-
const
|
|
93
|
+
const approvalPart = (body.messages || []).flatMap((m) => m?.parts || []).find((p) => p?.state === "approval-responded" && typeof p?.toolCallId === "string");
|
|
94
|
+
const isApprovalResponse = !!approvalPart;
|
|
95
|
+
const conversationId = isApprovalResponse ? body.conversationId : body.conversationId || getConversationId();
|
|
96
|
+
if (isApprovalResponse) {
|
|
97
|
+
if (!conversationId) return c.json({
|
|
98
|
+
success: false,
|
|
99
|
+
error: "conversationId is required for approval response"
|
|
100
|
+
}, 400);
|
|
101
|
+
const toolCallId = approvalPart.toolCallId;
|
|
102
|
+
const approved = !!approvalPart.approval?.approved;
|
|
103
|
+
const reason = approvalPart.approval?.reason;
|
|
104
|
+
if (!await getConversation(runDbClient_default)({
|
|
105
|
+
scopes: {
|
|
106
|
+
tenantId,
|
|
107
|
+
projectId
|
|
108
|
+
},
|
|
109
|
+
conversationId
|
|
110
|
+
})) return c.json({
|
|
111
|
+
success: false,
|
|
112
|
+
error: "Conversation not found"
|
|
113
|
+
}, 404);
|
|
114
|
+
if (!(approved ? pendingToolApprovalManager.approveToolCall(toolCallId) : pendingToolApprovalManager.denyToolCall(toolCallId, reason))) return c.json({
|
|
115
|
+
success: true,
|
|
116
|
+
toolCallId,
|
|
117
|
+
approved,
|
|
118
|
+
alreadyProcessed: true
|
|
119
|
+
});
|
|
120
|
+
return c.json({
|
|
121
|
+
success: true,
|
|
122
|
+
toolCallId,
|
|
123
|
+
approved
|
|
124
|
+
});
|
|
125
|
+
}
|
|
78
126
|
const targetTenantId = c.req.header("x-target-tenant-id");
|
|
79
127
|
const targetProjectId = c.req.header("x-target-project-id");
|
|
80
128
|
const targetAgentId = c.req.header("x-target-agent-id");
|
|
@@ -227,16 +275,58 @@ app.openapi(chatDataStreamRoute, async (c) => {
|
|
|
227
275
|
}
|
|
228
276
|
const dataStream = createUIMessageStream({ execute: async ({ writer }) => {
|
|
229
277
|
const streamHelper = createVercelStreamHelper(writer);
|
|
278
|
+
let unsubscribe;
|
|
230
279
|
try {
|
|
231
280
|
const emitOperations = c.req.header("x-emit-operations") === "true";
|
|
232
281
|
const executionHandler = new ExecutionHandler();
|
|
233
282
|
const datasetRunId = c.req.header("x-inkeep-dataset-run-id");
|
|
283
|
+
const requestId = `chatds-${Date.now()}`;
|
|
284
|
+
const chunkString = (s, size = 16) => {
|
|
285
|
+
const out = [];
|
|
286
|
+
for (let i = 0; i < s.length; i += size) out.push(s.slice(i, i + size));
|
|
287
|
+
return out;
|
|
288
|
+
};
|
|
289
|
+
const seenToolCalls = /* @__PURE__ */ new Set();
|
|
290
|
+
const seenOutputs = /* @__PURE__ */ new Set();
|
|
291
|
+
unsubscribe = toolApprovalUiBus.subscribe(requestId, async (event) => {
|
|
292
|
+
if (event.type === "approval-needed") {
|
|
293
|
+
if (seenToolCalls.has(event.toolCallId)) return;
|
|
294
|
+
seenToolCalls.add(event.toolCallId);
|
|
295
|
+
await streamHelper.writeToolInputStart({
|
|
296
|
+
toolCallId: event.toolCallId,
|
|
297
|
+
toolName: event.toolName
|
|
298
|
+
});
|
|
299
|
+
const inputText = JSON.stringify(event.input ?? {});
|
|
300
|
+
for (const part of chunkString(inputText, 16)) await streamHelper.writeToolInputDelta({
|
|
301
|
+
toolCallId: event.toolCallId,
|
|
302
|
+
inputTextDelta: part
|
|
303
|
+
});
|
|
304
|
+
await streamHelper.writeToolInputAvailable({
|
|
305
|
+
toolCallId: event.toolCallId,
|
|
306
|
+
toolName: event.toolName,
|
|
307
|
+
input: event.input ?? {},
|
|
308
|
+
providerMetadata: event.providerMetadata
|
|
309
|
+
});
|
|
310
|
+
await streamHelper.writeToolApprovalRequest({
|
|
311
|
+
approvalId: event.approvalId,
|
|
312
|
+
toolCallId: event.toolCallId
|
|
313
|
+
});
|
|
314
|
+
} else if (event.type === "approval-resolved") {
|
|
315
|
+
if (seenOutputs.has(event.toolCallId)) return;
|
|
316
|
+
seenOutputs.add(event.toolCallId);
|
|
317
|
+
if (event.approved) await streamHelper.writeToolOutputAvailable({
|
|
318
|
+
toolCallId: event.toolCallId,
|
|
319
|
+
output: { status: "approved" }
|
|
320
|
+
});
|
|
321
|
+
else await streamHelper.writeToolOutputDenied({ toolCallId: event.toolCallId });
|
|
322
|
+
}
|
|
323
|
+
});
|
|
234
324
|
if (!(await executionHandler.execute({
|
|
235
325
|
executionContext,
|
|
236
326
|
conversationId,
|
|
237
327
|
userMessage: userText,
|
|
238
328
|
initialAgentId: subAgentId,
|
|
239
|
-
requestId
|
|
329
|
+
requestId,
|
|
240
330
|
sseHelper: streamHelper,
|
|
241
331
|
emitOperations,
|
|
242
332
|
datasetRunId: datasetRunId || void 0,
|
|
@@ -246,6 +336,9 @@ app.openapi(chatDataStreamRoute, async (c) => {
|
|
|
246
336
|
logger.error({ err }, "Streaming error");
|
|
247
337
|
await streamHelper.writeOperation(errorOp("Internal server error", "system"));
|
|
248
338
|
} finally {
|
|
339
|
+
try {
|
|
340
|
+
unsubscribe?.();
|
|
341
|
+
} catch (_e) {}
|
|
249
342
|
if ("cleanup" in streamHelper && typeof streamHelper.cleanup === "function") streamHelper.cleanup();
|
|
250
343
|
}
|
|
251
344
|
} });
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/domains/run/services/ToolApprovalUiBus.d.ts
|
|
2
|
+
type ToolApprovalUiEvent = {
|
|
3
|
+
type: 'approval-needed';
|
|
4
|
+
toolCallId: string;
|
|
5
|
+
toolName: string;
|
|
6
|
+
input: any;
|
|
7
|
+
providerMetadata?: any;
|
|
8
|
+
approvalId: string;
|
|
9
|
+
} | {
|
|
10
|
+
type: 'approval-resolved';
|
|
11
|
+
toolCallId: string;
|
|
12
|
+
approved: boolean;
|
|
13
|
+
};
|
|
14
|
+
type Listener = (event: ToolApprovalUiEvent) => void | Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* In-process event bus keyed by streamRequestId.
|
|
17
|
+
*
|
|
18
|
+
* Used to propagate approval UI events from delegated agents (who must not write to stream)
|
|
19
|
+
* up to the user-facing request handler, which can stream tool UI parts to the client.
|
|
20
|
+
*/
|
|
21
|
+
declare class ToolApprovalUiBus {
|
|
22
|
+
private listeners;
|
|
23
|
+
subscribe(streamRequestId: string, listener: Listener): () => void;
|
|
24
|
+
publish(streamRequestId: string, event: ToolApprovalUiEvent): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
declare const toolApprovalUiBus: ToolApprovalUiBus;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { ToolApprovalUiBus, ToolApprovalUiEvent, toolApprovalUiBus };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { getLogger } from "../../../logger.js";
|
|
2
|
+
|
|
3
|
+
//#region src/domains/run/services/ToolApprovalUiBus.ts
|
|
4
|
+
const logger = getLogger("ToolApprovalUiBus");
|
|
5
|
+
/**
|
|
6
|
+
* In-process event bus keyed by streamRequestId.
|
|
7
|
+
*
|
|
8
|
+
* Used to propagate approval UI events from delegated agents (who must not write to stream)
|
|
9
|
+
* up to the user-facing request handler, which can stream tool UI parts to the client.
|
|
10
|
+
*/
|
|
11
|
+
var ToolApprovalUiBus = class {
|
|
12
|
+
listeners = /* @__PURE__ */ new Map();
|
|
13
|
+
subscribe(streamRequestId, listener) {
|
|
14
|
+
if (!streamRequestId) return () => {};
|
|
15
|
+
const set = this.listeners.get(streamRequestId) ?? /* @__PURE__ */ new Set();
|
|
16
|
+
set.add(listener);
|
|
17
|
+
this.listeners.set(streamRequestId, set);
|
|
18
|
+
return () => {
|
|
19
|
+
const existing = this.listeners.get(streamRequestId);
|
|
20
|
+
if (!existing) return;
|
|
21
|
+
existing.delete(listener);
|
|
22
|
+
if (existing.size === 0) this.listeners.delete(streamRequestId);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
async publish(streamRequestId, event) {
|
|
26
|
+
if (!streamRequestId) return;
|
|
27
|
+
const set = this.listeners.get(streamRequestId);
|
|
28
|
+
if (!set || set.size === 0) return;
|
|
29
|
+
for (const listener of set) try {
|
|
30
|
+
await listener(event);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
logger.warn({
|
|
33
|
+
streamRequestId,
|
|
34
|
+
eventType: event.type,
|
|
35
|
+
toolCallId: event.toolCallId,
|
|
36
|
+
error: error instanceof Error ? error.message : String(error)
|
|
37
|
+
}, "ToolApprovalUiBus listener failed");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const toolApprovalUiBus = new ToolApprovalUiBus();
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
export { ToolApprovalUiBus, toolApprovalUiBus };
|