veryfront 0.1.152 → 0.1.155
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/esm/deno.js +1 -1
- package/esm/src/agent/ag-ui-handler.d.ts +6 -0
- package/esm/src/agent/ag-ui-handler.d.ts.map +1 -1
- package/esm/src/agent/ag-ui-handler.js +115 -19
- package/esm/src/agent/ag-ui-run-control.d.ts +33 -0
- package/esm/src/agent/ag-ui-run-control.d.ts.map +1 -0
- package/esm/src/agent/ag-ui-run-control.js +102 -0
- package/esm/src/agent/index.d.ts +1 -0
- package/esm/src/agent/index.d.ts.map +1 -1
- package/esm/src/agent/index.js +1 -0
- package/esm/src/agent/runtime/chat-stream-handler.d.ts.map +1 -1
- package/esm/src/agent/runtime/chat-stream-handler.js +1 -25
- package/esm/src/agent/runtime/error-utils.d.ts +4 -0
- package/esm/src/agent/runtime/error-utils.d.ts.map +1 -0
- package/esm/src/agent/runtime/error-utils.js +25 -0
- package/esm/src/agent/runtime/index.d.ts.map +1 -1
- package/esm/src/agent/runtime/index.js +1 -25
- package/esm/src/observability/error-collector.d.ts +1 -0
- package/esm/src/observability/error-collector.d.ts.map +1 -1
- package/esm/src/observability/error-collector.js +6 -3
- package/esm/src/routing/api/module-loader/loader.js +26 -28
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
- package/src/deno.js +1 -1
- package/src/src/agent/ag-ui-handler.ts +187 -27
- package/src/src/agent/ag-ui-run-control.ts +161 -0
- package/src/src/agent/index.ts +8 -0
- package/src/src/agent/runtime/chat-stream-handler.ts +1 -33
- package/src/src/agent/runtime/error-utils.ts +32 -0
- package/src/src/agent/runtime/index.ts +1 -33
- package/src/src/observability/error-collector.ts +13 -3
- package/src/src/routing/api/module-loader/loader.ts +37 -38
- package/src/src/utils/version-constant.ts +1 -1
|
@@ -293,24 +293,32 @@ function createImportMapPlugin(projectDir, adapter, config) {
|
|
|
293
293
|
logger.debug(`Import map resolved: ${args.path} -> ${absolutePath}`);
|
|
294
294
|
return { path: absolutePath, namespace: "import-map" };
|
|
295
295
|
});
|
|
296
|
-
build.onLoad({ filter: /.*/, namespace: "import-map" },
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
contents,
|
|
301
|
-
loader: getLoaderForFile(filePath),
|
|
302
|
-
resolveDir: pathHelper.dirname(filePath),
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
catch (error) {
|
|
306
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
307
|
-
logger.error(`Failed to load file via import map: ${args.path}`, error);
|
|
308
|
-
return { errors: [{ text: `Failed to load: ${msg}` }] };
|
|
309
|
-
}
|
|
296
|
+
build.onLoad({ filter: /.*/, namespace: "import-map" }, createNamespaceOnLoadHandler({
|
|
297
|
+
adapter,
|
|
298
|
+
projectDir,
|
|
299
|
+
errorLabel: "file via import map",
|
|
310
300
|
}));
|
|
311
301
|
},
|
|
312
302
|
};
|
|
313
303
|
}
|
|
304
|
+
function createNamespaceOnLoadHandler(options) {
|
|
305
|
+
const { adapter, projectDir, errorLabel } = options;
|
|
306
|
+
return wrapWithCurrentContext(async (args) => {
|
|
307
|
+
try {
|
|
308
|
+
const { filePath, contents } = await readFileWithExtensions(adapter, args.path, FILE_EXTENSIONS, projectDir);
|
|
309
|
+
return {
|
|
310
|
+
contents,
|
|
311
|
+
loader: getLoaderForFile(filePath),
|
|
312
|
+
resolveDir: pathHelper.dirname(filePath),
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
317
|
+
logger.error(`Failed to load ${errorLabel}: ${args.path}`, error);
|
|
318
|
+
return { errors: [{ text: `Failed to load: ${msg}` }] };
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
}
|
|
314
322
|
/** Resolves relative imports through the adapter's virtual FS for remote projects. */
|
|
315
323
|
function createAdapterResolvePlugin(adapter, projectDir) {
|
|
316
324
|
return {
|
|
@@ -337,20 +345,10 @@ function createAdapterResolvePlugin(adapter, projectDir) {
|
|
|
337
345
|
// callbacks fire from the child process message handler, losing the
|
|
338
346
|
// AsyncLocalStorage store. Without this, MultiProjectFSAdapter.getAdapter()
|
|
339
347
|
// cannot resolve the per-project adapter and all file reads fail silently.
|
|
340
|
-
build.onLoad({ filter: /.*/, namespace: "vf-adapter" },
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
contents,
|
|
345
|
-
loader: getLoaderForFile(filePath),
|
|
346
|
-
resolveDir: pathHelper.dirname(filePath),
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
catch (error) {
|
|
350
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
351
|
-
logger.error(`Failed to load via adapter: ${args.path}`, error);
|
|
352
|
-
return { errors: [{ text: `Failed to load: ${msg}` }] };
|
|
353
|
-
}
|
|
348
|
+
build.onLoad({ filter: /.*/, namespace: "vf-adapter" }, createNamespaceOnLoadHandler({
|
|
349
|
+
adapter,
|
|
350
|
+
projectDir,
|
|
351
|
+
errorLabel: "via adapter",
|
|
354
352
|
}));
|
|
355
353
|
},
|
|
356
354
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.155";
|
|
2
2
|
//# sourceMappingURL=version-constant.d.ts.map
|
package/package.json
CHANGED
package/src/deno.js
CHANGED
|
@@ -2,7 +2,14 @@ import * as dntShim from "../../_dnt.shims.js";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { getAgent } from "./composition/index.js";
|
|
4
4
|
import type { Agent, Message } from "./types.js";
|
|
5
|
+
import {
|
|
6
|
+
AgentRuntime,
|
|
7
|
+
RunAlreadyExistsError,
|
|
8
|
+
type RunResumeSessionManager,
|
|
9
|
+
} from "./runtime/index.js";
|
|
5
10
|
import { INVALID_ARGUMENT } from "../errors/index.js";
|
|
11
|
+
import { SKILL_TOOL_IDS } from "../skill/types.js";
|
|
12
|
+
import { type Tool, toolRegistry } from "../tool/index.js";
|
|
6
13
|
import {
|
|
7
14
|
createStreamTransformState,
|
|
8
15
|
finalizeRunEvents,
|
|
@@ -101,6 +108,7 @@ export type AgUiContextItem = z.infer<typeof AgUiContextItemSchema>;
|
|
|
101
108
|
export type AgUiRequest = z.infer<typeof AgUiRequestSchema>;
|
|
102
109
|
|
|
103
110
|
type AgUiRuntimePart = Record<string, unknown> & { type: string };
|
|
111
|
+
type AgUiResumeValue = { result: unknown; isError: boolean };
|
|
104
112
|
|
|
105
113
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
106
114
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -259,24 +267,29 @@ function enqueueEvent(
|
|
|
259
267
|
}
|
|
260
268
|
|
|
261
269
|
async function createAgUiStreamResponse(
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
270
|
+
options: {
|
|
271
|
+
agentId: string;
|
|
272
|
+
request: AgUiRequest;
|
|
273
|
+
runId: string;
|
|
274
|
+
threadId: string;
|
|
275
|
+
upstreamBody: ReadableStream<Uint8Array> | null;
|
|
276
|
+
upstreamStatus: number;
|
|
277
|
+
upstreamStatusText?: string;
|
|
278
|
+
onFinish?: () => void;
|
|
279
|
+
onError?: (error: unknown) => void;
|
|
280
|
+
},
|
|
265
281
|
): Promise<dntShim.Response> {
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const upstream = result.toDataStreamResponse();
|
|
279
|
-
const upstreamBody = upstream.body;
|
|
282
|
+
const {
|
|
283
|
+
agentId,
|
|
284
|
+
request,
|
|
285
|
+
runId,
|
|
286
|
+
threadId,
|
|
287
|
+
upstreamBody,
|
|
288
|
+
upstreamStatus,
|
|
289
|
+
upstreamStatusText,
|
|
290
|
+
onFinish,
|
|
291
|
+
onError,
|
|
292
|
+
} = options;
|
|
280
293
|
|
|
281
294
|
const stream = new ReadableStream<Uint8Array>({
|
|
282
295
|
start: async (controller) => {
|
|
@@ -285,7 +298,7 @@ async function createAgUiStreamResponse(
|
|
|
285
298
|
let remainder = "";
|
|
286
299
|
const decoder = new TextDecoder();
|
|
287
300
|
|
|
288
|
-
if (!enqueueEvent(controller, "RunStarted", { runId, threadId, agentId
|
|
301
|
+
if (!enqueueEvent(controller, "RunStarted", { runId, threadId, agentId })) {
|
|
289
302
|
return;
|
|
290
303
|
}
|
|
291
304
|
if (!enqueueEvent(controller, "StateSnapshot", { snapshot: {} })) {
|
|
@@ -302,6 +315,7 @@ async function createAgUiStreamResponse(
|
|
|
302
315
|
return;
|
|
303
316
|
}
|
|
304
317
|
}
|
|
318
|
+
onFinish?.();
|
|
305
319
|
closeController(controller);
|
|
306
320
|
return;
|
|
307
321
|
}
|
|
@@ -340,7 +354,9 @@ async function createAgUiStreamResponse(
|
|
|
340
354
|
return;
|
|
341
355
|
}
|
|
342
356
|
}
|
|
357
|
+
onFinish?.();
|
|
343
358
|
} catch (error) {
|
|
359
|
+
onError?.(error);
|
|
344
360
|
enqueueEvent(controller, "RunError", {
|
|
345
361
|
message: error instanceof Error ? error.message : "Agent run failed",
|
|
346
362
|
});
|
|
@@ -352,16 +368,147 @@ async function createAgUiStreamResponse(
|
|
|
352
368
|
});
|
|
353
369
|
|
|
354
370
|
return new dntShim.Response(stream, {
|
|
355
|
-
status:
|
|
356
|
-
statusText:
|
|
371
|
+
status: upstreamStatus,
|
|
372
|
+
statusText: upstreamStatusText,
|
|
357
373
|
headers: { ...AG_UI_HEADERS },
|
|
358
374
|
});
|
|
359
375
|
}
|
|
360
376
|
|
|
377
|
+
async function createAgUiDirectStreamResponse(
|
|
378
|
+
agent: Agent,
|
|
379
|
+
request: AgUiRequest,
|
|
380
|
+
baseContext: Record<string, unknown>,
|
|
381
|
+
): Promise<dntShim.Response> {
|
|
382
|
+
const threadId = request.threadId ?? dntShim.crypto.randomUUID();
|
|
383
|
+
const runId = request.runId ?? generateRunId();
|
|
384
|
+
|
|
385
|
+
await agent.clearMemory();
|
|
386
|
+
|
|
387
|
+
const result = await agent.stream({
|
|
388
|
+
messages: normalizeMessages(request.messages),
|
|
389
|
+
context: buildStreamContext(request, baseContext, threadId, runId),
|
|
390
|
+
...(request.model ? { model: request.model } : {}),
|
|
391
|
+
...(request.maxOutputTokens ? { maxOutputTokens: request.maxOutputTokens } : {}),
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
const upstream = result.toDataStreamResponse();
|
|
395
|
+
return await createAgUiStreamResponse({
|
|
396
|
+
agentId: agent.id,
|
|
397
|
+
request,
|
|
398
|
+
runId,
|
|
399
|
+
threadId,
|
|
400
|
+
upstreamBody: upstream.body,
|
|
401
|
+
upstreamStatus: upstream.status,
|
|
402
|
+
upstreamStatusText: upstream.statusText,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function createInjectedAgUiTool(
|
|
407
|
+
runId: string,
|
|
408
|
+
tool: AgUiInjectedTool,
|
|
409
|
+
sessionManager: RunResumeSessionManager<AgUiResumeValue>,
|
|
410
|
+
): Tool {
|
|
411
|
+
return {
|
|
412
|
+
id: tool.name,
|
|
413
|
+
type: "function",
|
|
414
|
+
description: tool.description ?? tool.name,
|
|
415
|
+
inputSchema: z.record(z.string(), z.unknown()),
|
|
416
|
+
inputSchemaJson: (tool.parameters ??
|
|
417
|
+
{ type: "object", properties: {}, additionalProperties: true }) as Tool["inputSchemaJson"],
|
|
418
|
+
execute: async (_input, context) => {
|
|
419
|
+
const toolCallId = typeof context?.toolCallId === "string" ? context.toolCallId : null;
|
|
420
|
+
if (!toolCallId) {
|
|
421
|
+
throw new Error(`Missing toolCallId for injected tool "${tool.name}"`);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const submitted = await sessionManager.waitForSignal(runId, toolCallId);
|
|
425
|
+
if (submitted.isError) {
|
|
426
|
+
throw new Error(
|
|
427
|
+
typeof submitted.result === "string"
|
|
428
|
+
? submitted.result
|
|
429
|
+
: JSON.stringify(submitted.result),
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
return submitted.result;
|
|
433
|
+
},
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
async function createAgUiInjectedToolsStreamResponse(
|
|
438
|
+
agent: Agent,
|
|
439
|
+
request: AgUiRequest,
|
|
440
|
+
baseContext: Record<string, unknown>,
|
|
441
|
+
sessionManager: RunResumeSessionManager<AgUiResumeValue>,
|
|
442
|
+
): Promise<dntShim.Response> {
|
|
443
|
+
const threadId = request.threadId ?? dntShim.crypto.randomUUID();
|
|
444
|
+
const runId = request.runId ?? generateRunId();
|
|
445
|
+
|
|
446
|
+
try {
|
|
447
|
+
sessionManager.startRun({ runId, threadId });
|
|
448
|
+
} catch (error) {
|
|
449
|
+
if (error instanceof RunAlreadyExistsError) {
|
|
450
|
+
return dntShim.Response.json({ error: "Run already active" }, { status: 409 });
|
|
451
|
+
}
|
|
452
|
+
throw error;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const injectedTools = Object.fromEntries(
|
|
456
|
+
request.tools.map((tool) => [tool.name, createInjectedAgUiTool(runId, tool, sessionManager)]),
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
const mergedTools: Agent["config"]["tools"] = !agent.config.tools
|
|
460
|
+
? injectedTools
|
|
461
|
+
: agent.config.tools === true
|
|
462
|
+
? {
|
|
463
|
+
...Object.fromEntries(
|
|
464
|
+
[...toolRegistry.getAll()]
|
|
465
|
+
.filter(([toolId]) => agent.config.skills || !SKILL_TOOL_IDS.has(toolId))
|
|
466
|
+
.map(([toolId]) => [toolId, true]),
|
|
467
|
+
),
|
|
468
|
+
...injectedTools,
|
|
469
|
+
}
|
|
470
|
+
: { ...agent.config.tools, ...injectedTools };
|
|
471
|
+
|
|
472
|
+
const runtime = new AgentRuntime(agent.id, {
|
|
473
|
+
...agent.config,
|
|
474
|
+
tools: mergedTools,
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
let upstreamBody: ReadableStream<Uint8Array>;
|
|
478
|
+
try {
|
|
479
|
+
upstreamBody = await runtime.stream(
|
|
480
|
+
normalizeMessages(request.messages),
|
|
481
|
+
buildStreamContext(request, baseContext, threadId, runId),
|
|
482
|
+
undefined,
|
|
483
|
+
request.model,
|
|
484
|
+
request.maxOutputTokens,
|
|
485
|
+
);
|
|
486
|
+
} catch (error) {
|
|
487
|
+
sessionManager.failRun(runId);
|
|
488
|
+
throw error;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return await createAgUiStreamResponse({
|
|
492
|
+
agentId: agent.id,
|
|
493
|
+
request,
|
|
494
|
+
runId,
|
|
495
|
+
threadId,
|
|
496
|
+
upstreamBody,
|
|
497
|
+
upstreamStatus: 200,
|
|
498
|
+
onFinish: () => {
|
|
499
|
+
sessionManager.completeRun(runId);
|
|
500
|
+
},
|
|
501
|
+
onError: () => {
|
|
502
|
+
sessionManager.failRun(runId);
|
|
503
|
+
},
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
|
|
361
507
|
export interface AgUiHandlerOptions {
|
|
362
508
|
context?:
|
|
363
509
|
| Record<string, unknown>
|
|
364
510
|
| ((request: dntShim.Request) => Record<string, unknown> | Promise<Record<string, unknown>>);
|
|
511
|
+
sessionManager?: RunResumeSessionManager<AgUiResumeValue>;
|
|
365
512
|
}
|
|
366
513
|
|
|
367
514
|
export interface AgUiHandlerConfigWithAgent extends AgUiHandlerOptions {
|
|
@@ -418,12 +565,25 @@ export function createAgUiHandler(
|
|
|
418
565
|
const parsed = AgUiRequestSchema.parse(await request.json());
|
|
419
566
|
|
|
420
567
|
if (parsed.tools.length > 0) {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
568
|
+
if (!options?.sessionManager) {
|
|
569
|
+
return dntShim.Response.json(
|
|
570
|
+
{
|
|
571
|
+
error:
|
|
572
|
+
"Injected AG-UI tools require a public RunResumeSessionManager on createAgUiHandler().",
|
|
573
|
+
},
|
|
574
|
+
{ status: 501 },
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const context = typeof options?.context === "function"
|
|
579
|
+
? await options.context(request)
|
|
580
|
+
: options?.context ?? {};
|
|
581
|
+
|
|
582
|
+
return await createAgUiInjectedToolsStreamResponse(
|
|
583
|
+
agent,
|
|
584
|
+
parsed,
|
|
585
|
+
context,
|
|
586
|
+
options.sessionManager,
|
|
427
587
|
);
|
|
428
588
|
}
|
|
429
589
|
|
|
@@ -431,7 +591,7 @@ export function createAgUiHandler(
|
|
|
431
591
|
? await options.context(request)
|
|
432
592
|
: options?.context ?? {};
|
|
433
593
|
|
|
434
|
-
return await
|
|
594
|
+
return await createAgUiDirectStreamResponse(agent, parsed, context);
|
|
435
595
|
} catch (error) {
|
|
436
596
|
if (error instanceof z.ZodError) {
|
|
437
597
|
return dntShim.Response.json(
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { INVALID_ARGUMENT } from "../errors/index.js";
|
|
4
|
+
import {
|
|
5
|
+
RunNotActiveError,
|
|
6
|
+
RunResumeSessionManager,
|
|
7
|
+
WaitConflictError,
|
|
8
|
+
WaitNotPendingError,
|
|
9
|
+
} from "./runtime/resume-session.js";
|
|
10
|
+
|
|
11
|
+
const RESUME_PATH_REGEX = /^\/api\/ag-ui\/runs\/([^/]+)\/resume$/;
|
|
12
|
+
const CANCEL_PATH_REGEX = /^\/api\/ag-ui\/runs\/([^/]+)$/;
|
|
13
|
+
|
|
14
|
+
export const AgUiResumeSignalSchema = z.discriminatedUnion("type", [
|
|
15
|
+
z.object({
|
|
16
|
+
type: z.literal("tool_result"),
|
|
17
|
+
toolCallId: z.string().min(1).max(128),
|
|
18
|
+
result: z.unknown(),
|
|
19
|
+
isError: z.boolean().optional().default(false),
|
|
20
|
+
}),
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
export type AgUiResumeSignal = z.infer<typeof AgUiResumeSignalSchema>;
|
|
24
|
+
|
|
25
|
+
type ResumeValue = {
|
|
26
|
+
result: unknown;
|
|
27
|
+
isError: boolean;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function isRequest(value: unknown): value is dntShim.Request {
|
|
31
|
+
return (
|
|
32
|
+
typeof value === "object" &&
|
|
33
|
+
value !== null &&
|
|
34
|
+
"json" in value &&
|
|
35
|
+
typeof value.json === "function" &&
|
|
36
|
+
"url" in value &&
|
|
37
|
+
typeof value.url === "string" &&
|
|
38
|
+
"method" in value &&
|
|
39
|
+
typeof value.method === "string"
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function extractRequest(requestOrCtx: unknown): dntShim.Request {
|
|
44
|
+
if (isRequest(requestOrCtx)) return requestOrCtx;
|
|
45
|
+
|
|
46
|
+
if (typeof requestOrCtx === "object" && requestOrCtx !== null && "request" in requestOrCtx) {
|
|
47
|
+
const candidate = (requestOrCtx as Record<string, unknown>).request;
|
|
48
|
+
if (isRequest(candidate)) return candidate;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
throw INVALID_ARGUMENT.create({
|
|
52
|
+
detail: "Invalid handler argument: expected Request or APIContext",
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getRunId(pathname: string, regex: RegExp): string | null {
|
|
57
|
+
return regex.exec(pathname)?.[1] ?? null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface AgUiRunControlHandlerOptions {
|
|
61
|
+
resolveRunId?:
|
|
62
|
+
| ((input: { request: dntShim.Request; requestOrCtx: unknown }) => string | null)
|
|
63
|
+
| ((input: { request: dntShim.Request; requestOrCtx: unknown }) => Promise<string | null>);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface AgUiResumeHandlerOptions extends AgUiRunControlHandlerOptions {
|
|
67
|
+
sessionManager: RunResumeSessionManager<ResumeValue>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface AgUiCancelHandlerOptions<T = unknown> extends AgUiRunControlHandlerOptions {
|
|
71
|
+
sessionManager: RunResumeSessionManager<T>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function resolveRunId(
|
|
75
|
+
requestOrCtx: unknown,
|
|
76
|
+
request: dntShim.Request,
|
|
77
|
+
options: AgUiRunControlHandlerOptions | undefined,
|
|
78
|
+
regex: RegExp,
|
|
79
|
+
): Promise<string | null> {
|
|
80
|
+
const explicit = await options?.resolveRunId?.({ request, requestOrCtx });
|
|
81
|
+
if (explicit) return explicit;
|
|
82
|
+
return getRunId(new URL(request.url).pathname, regex);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function createAgUiResumeHandler(
|
|
86
|
+
options: AgUiResumeHandlerOptions,
|
|
87
|
+
): (requestOrCtx: unknown) => Promise<dntShim.Response> {
|
|
88
|
+
return async function POST(requestOrCtx: unknown): Promise<dntShim.Response> {
|
|
89
|
+
const request = extractRequest(requestOrCtx);
|
|
90
|
+
const runId = await resolveRunId(requestOrCtx, request, options, RESUME_PATH_REGEX);
|
|
91
|
+
|
|
92
|
+
if (!runId) {
|
|
93
|
+
return dntShim.Response.json({ error: "Run not found" }, { status: 404 });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const parsed = AgUiResumeSignalSchema.parse(await request.json());
|
|
98
|
+
const outcome = options.sessionManager.submitSignal(runId, {
|
|
99
|
+
waitKey: parsed.toolCallId,
|
|
100
|
+
value: {
|
|
101
|
+
result: parsed.result,
|
|
102
|
+
isError: parsed.isError,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return dntShim.Response.json(outcome, { status: 200 });
|
|
107
|
+
} catch (error) {
|
|
108
|
+
if (error instanceof z.ZodError) {
|
|
109
|
+
return dntShim.Response.json(
|
|
110
|
+
{
|
|
111
|
+
error: "Invalid AG-UI resume request",
|
|
112
|
+
details: error.issues.map((issue) => ({
|
|
113
|
+
path: issue.path,
|
|
114
|
+
message: issue.message,
|
|
115
|
+
})),
|
|
116
|
+
},
|
|
117
|
+
{ status: 400 },
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (error instanceof WaitConflictError) {
|
|
122
|
+
return dntShim.Response.json({ error: "TOOL_RESULT_CONFLICT" }, { status: 409 });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (error instanceof WaitNotPendingError) {
|
|
126
|
+
return dntShim.Response.json({ error: "TOOL_RESULT_NOT_WAITING" }, { status: 409 });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (error instanceof RunNotActiveError) {
|
|
130
|
+
return dntShim.Response.json({ error: "RUN_NOT_ACTIVE" }, { status: 410 });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return dntShim.Response.json(
|
|
134
|
+
{
|
|
135
|
+
error: error instanceof Error ? error.message : "Internal resume failed",
|
|
136
|
+
},
|
|
137
|
+
{ status: 500 },
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function createAgUiCancelHandler<T = unknown>(
|
|
144
|
+
options: AgUiCancelHandlerOptions<T>,
|
|
145
|
+
): (requestOrCtx: unknown) => Promise<dntShim.Response> {
|
|
146
|
+
return async function DELETE(requestOrCtx: unknown): Promise<dntShim.Response> {
|
|
147
|
+
const request = extractRequest(requestOrCtx);
|
|
148
|
+
const runId = await resolveRunId(requestOrCtx, request, options, CANCEL_PATH_REGEX);
|
|
149
|
+
|
|
150
|
+
if (!runId) {
|
|
151
|
+
return dntShim.Response.json({ error: "Run not found" }, { status: 404 });
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const accepted = options.sessionManager.cancelRun(runId);
|
|
155
|
+
if (accepted) {
|
|
156
|
+
return dntShim.Response.json({ accepted: true }, { status: 202 });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return new dntShim.Response(null, { status: 204 });
|
|
160
|
+
};
|
|
161
|
+
}
|
package/src/src/agent/index.ts
CHANGED
|
@@ -145,6 +145,14 @@ export {
|
|
|
145
145
|
type AgUiRuntimeRequest,
|
|
146
146
|
AgUiRuntimeRequestSchema,
|
|
147
147
|
} from "./runtime-ag-ui-contract.js";
|
|
148
|
+
export {
|
|
149
|
+
type AgUiCancelHandlerOptions,
|
|
150
|
+
type AgUiResumeHandlerOptions,
|
|
151
|
+
type AgUiResumeSignal,
|
|
152
|
+
AgUiResumeSignalSchema,
|
|
153
|
+
createAgUiCancelHandler,
|
|
154
|
+
createAgUiResumeHandler,
|
|
155
|
+
} from "./ag-ui-run-control.js";
|
|
148
156
|
export {
|
|
149
157
|
type AgUiContextItem,
|
|
150
158
|
type AgUiHandlerConfigWithAgent,
|
|
@@ -15,6 +15,7 @@ import { serverLogger } from "../../utils/index.js";
|
|
|
15
15
|
import { isAnyDebugEnabled } from "../../utils/constants/env.js";
|
|
16
16
|
import { setActiveSpanAttributes, withSpan } from "../../observability/tracing/otlp-setup.js";
|
|
17
17
|
import { getHostEnv } from "../../platform/compat/process.js";
|
|
18
|
+
import { stringifyToolError, throwIfAborted } from "./error-utils.js";
|
|
18
19
|
|
|
19
20
|
const logger = serverLogger.component("agent");
|
|
20
21
|
|
|
@@ -84,39 +85,6 @@ function normalizeToolInputObject(input: unknown): Record<string, unknown> {
|
|
|
84
85
|
return {};
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
function createAbortError(reason?: unknown): Error {
|
|
88
|
-
if (reason instanceof Error) {
|
|
89
|
-
return reason;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return new DOMException(
|
|
93
|
-
typeof reason === "string" && reason.length > 0 ? reason : "The operation was aborted",
|
|
94
|
-
"AbortError",
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function throwIfAborted(abortSignal?: AbortSignal): void {
|
|
99
|
-
if (abortSignal?.aborted) {
|
|
100
|
-
throw createAbortError(abortSignal.reason);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function stringifyToolError(output: unknown): string {
|
|
105
|
-
if (typeof output === "string" && output.length > 0) {
|
|
106
|
-
return output;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (output instanceof Error && typeof output.message === "string" && output.message.length > 0) {
|
|
110
|
-
return output.message;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
return JSON.stringify(output);
|
|
115
|
-
} catch {
|
|
116
|
-
return String(output);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
88
|
function summarizeDebugValue(value: unknown): unknown {
|
|
121
89
|
if (value instanceof Error) {
|
|
122
90
|
return {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function createAbortError(reason?: unknown): Error {
|
|
2
|
+
if (reason instanceof Error) {
|
|
3
|
+
return reason;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
return new DOMException(
|
|
7
|
+
typeof reason === "string" && reason.length > 0 ? reason : "The operation was aborted",
|
|
8
|
+
"AbortError",
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function throwIfAborted(abortSignal?: AbortSignal): void {
|
|
13
|
+
if (abortSignal?.aborted) {
|
|
14
|
+
throw createAbortError(abortSignal.reason);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function stringifyToolError(error: unknown): string {
|
|
19
|
+
if (typeof error === "string" && error.length > 0) {
|
|
20
|
+
return error;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (error instanceof Error && typeof error.message === "string" && error.message.length > 0) {
|
|
24
|
+
return error.message;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
return JSON.stringify(error);
|
|
29
|
+
} catch {
|
|
30
|
+
return String(error);
|
|
31
|
+
}
|
|
32
|
+
}
|