veryfront 0.1.218 → 0.1.220
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/cli/commands/init/config-generator.d.ts +13 -1
- package/esm/cli/commands/init/config-generator.d.ts.map +1 -1
- package/esm/cli/commands/init/config-generator.js +15 -1
- package/esm/cli/commands/init/init-command.d.ts.map +1 -1
- package/esm/cli/commands/init/init-command.js +9 -2
- package/esm/deno.d.ts +0 -1
- package/esm/deno.js +1 -2
- package/esm/src/agent/ag-ui-detached-start.d.ts +98 -0
- package/esm/src/agent/ag-ui-detached-start.d.ts.map +1 -0
- package/esm/src/agent/ag-ui-detached-start.js +299 -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/integrations/_data.js +1 -1
- package/esm/src/integrations/schema.d.ts +1 -0
- package/esm/src/integrations/schema.d.ts.map +1 -1
- package/esm/src/integrations/schema.js +8 -0
- package/esm/src/server/runtime-handler/request-tracker.d.ts.map +1 -1
- package/esm/src/server/runtime-handler/request-tracker.js +5 -6
- package/esm/src/server/runtime-handler/request-utils.d.ts.map +1 -1
- package/esm/src/server/runtime-handler/request-utils.js +1 -0
- 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/cli/commands/init/config-generator.ts +33 -0
- package/src/cli/commands/init/init-command.ts +9 -2
- package/src/deno.js +1 -2
- package/src/src/agent/ag-ui-detached-start.ts +453 -0
- package/src/src/agent/index.ts +8 -0
- package/src/src/integrations/_data.ts +1 -1
- package/src/src/integrations/schema.ts +8 -0
- package/src/src/server/runtime-handler/request-tracker.ts +5 -7
- package/src/src/server/runtime-handler/request-utils.ts +1 -0
- package/src/src/utils/version-constant.ts +1 -1
|
@@ -140,6 +140,14 @@ export const IntegrationConfigSchema = z.object({
|
|
|
140
140
|
description: z.string(),
|
|
141
141
|
auth: OAuthConfigSchema,
|
|
142
142
|
envVars: z.array(EnvVarSchema).optional(),
|
|
143
|
+
/**
|
|
144
|
+
* Optional map of npm packages to semver ranges. When this integration is
|
|
145
|
+
* selected during `veryfront init`, these deps are merged into the
|
|
146
|
+
* generated project's `package.json#dependencies`. Use this for templates
|
|
147
|
+
* that import packages beyond the init scaffold's defaults (react,
|
|
148
|
+
* react-dom, veryfront, zod).
|
|
149
|
+
*/
|
|
150
|
+
npmDependencies: z.record(z.string(), z.string()).optional(),
|
|
143
151
|
tools: z.array(IntegrationToolSchema),
|
|
144
152
|
prompts: z.array(IntegrationPromptSchema).optional(),
|
|
145
153
|
suggestedWith: z.array(z.string()).optional(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-tracker.d.ts","sourceRoot":"","sources":["../../../../src/src/server/runtime-handler/request-tracker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AASlD,UAAU,cAAc;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC;IAClD,aAAa,CAAC,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC;CACvD;AAiBD,cAAM,cAAc;IAClB,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,cAAc,CAAqD;IAC3E,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,aAAa,CAAK;;IAM1B,OAAO,CAAC,kBAAkB;IA4B1B,KAAK,CACH,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,GAAG,CAAC,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,GACjB,IAAI;
|
|
1
|
+
{"version":3,"file":"request-tracker.d.ts","sourceRoot":"","sources":["../../../../src/src/server/runtime-handler/request-tracker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AASlD,UAAU,cAAc;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC;IAClD,aAAa,CAAC,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC;CACvD;AAiBD,cAAM,cAAc;IAClB,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,cAAc,CAAqD;IAC3E,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,aAAa,CAAK;;IAM1B,OAAO,CAAC,kBAAkB;IA4B1B,KAAK,CACH,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,GAAG,CAAC,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,GACjB,IAAI;IAuDP,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,IAAI;IAgCvE,gBAAgB,IAAI,MAAM;IAI1B,mBAAmB,IAAI,cAAc,EAAE;IAIvC,QAAQ,IAAI;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE;IAS9E,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,SAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAsD7E,QAAQ,IAAI,IAAI;CAUjB;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import * as dntShim from "../../../_dnt.shims.js";
|
|
8
8
|
import { serverLogger } from "../../utils/index.js";
|
|
9
9
|
import { unrefTimer } from "../../platform/compat/process.js";
|
|
10
|
-
import { isWebSocketPath } from "./request-utils.js";
|
|
10
|
+
import { isLightweightPath, isWebSocketPath } from "./request-utils.js";
|
|
11
11
|
const logger = serverLogger.component("request-tracker");
|
|
12
12
|
/** Threshold in ms before logging a warning about a slow request */
|
|
13
13
|
const SLOW_REQUEST_THRESHOLD_MS = 10_000; // 10 seconds
|
|
@@ -66,8 +66,9 @@ class RequestTracker {
|
|
|
66
66
|
env,
|
|
67
67
|
releaseId,
|
|
68
68
|
};
|
|
69
|
-
// WebSocket connections are long-lived by design
|
|
70
|
-
|
|
69
|
+
// WebSocket connections are long-lived by design and lightweight internal
|
|
70
|
+
// asset/module requests can be noisy under CI jitter — don't flag them as stuck.
|
|
71
|
+
if (!isWebSocketPath(path) && !isLightweightPath(path)) {
|
|
71
72
|
tracked.slowTimer = dntShim.setTimeout(() => {
|
|
72
73
|
const elapsedMs = Math.round(performance.now() - startTime);
|
|
73
74
|
logger.warn("Slow request detected", {
|
|
@@ -118,9 +119,7 @@ class RequestTracker {
|
|
|
118
119
|
this.totalTimedOut++;
|
|
119
120
|
else
|
|
120
121
|
this.totalCompleted++;
|
|
121
|
-
|
|
122
|
-
tracked.path.startsWith("/_veryfront/");
|
|
123
|
-
if (isModuleRequest) {
|
|
122
|
+
if (isLightweightPath(tracked.path)) {
|
|
124
123
|
if (durationMs > MODULE_REQUEST_LOG_THRESHOLD_MS) {
|
|
125
124
|
logger.debug(`${tracked.method} ${tracked.path} ${statusCode} ${durationMs}ms`);
|
|
126
125
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-utils.d.ts","sourceRoot":"","sources":["../../../../src/src/server/runtime-handler/request-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAErE,qDAAqD;AACrD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAkBpD;AAED,sDAAsD;AACtD,eAAO,MAAM,gBAAgB,aAA+C,CAAC;AAK7E,yFAAyF;AACzF,wBAAgB,iBAAiB,IAAI,MAAM,CAK1C;AAED,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAEhC,sEAAsE;AACtE,eAAO,MAAM,gBAAgB,eAA4B,CAAC;AAE1D,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED,uFAAuF;AACvF,eAAO,MAAM,yBAAyB,
|
|
1
|
+
{"version":3,"file":"request-utils.d.ts","sourceRoot":"","sources":["../../../../src/src/server/runtime-handler/request-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAErE,qDAAqD;AACrD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAkBpD;AAED,sDAAsD;AACtD,eAAO,MAAM,gBAAgB,aAA+C,CAAC;AAK7E,yFAAyF;AACzF,wBAAgB,iBAAiB,IAAI,MAAM,CAK1C;AAED,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAEhC,sEAAsE;AACtE,eAAO,MAAM,gBAAgB,eAA4B,CAAC;AAE1D,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED,uFAAuF;AACvF,eAAO,MAAM,yBAAyB,UASrC,CAAC;AAEF,mFAAmF;AACnF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEnE"}
|
|
@@ -48,6 +48,7 @@ export function isMonitoringPath(pathname) {
|
|
|
48
48
|
/** Lightweight paths that should skip concurrency limiting (modules, static assets) */
|
|
49
49
|
export const LIGHTWEIGHT_PATH_PREFIXES = [
|
|
50
50
|
"/_vf_modules/",
|
|
51
|
+
"/_vf_styles/",
|
|
51
52
|
"/_veryfront/modules/",
|
|
52
53
|
"/_veryfront/hydration-runtime.js",
|
|
53
54
|
"/_veryfront/preview-hmr.js",
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.220";
|
|
2
2
|
//# sourceMappingURL=version-constant.d.ts.map
|
package/package.json
CHANGED
|
@@ -5,9 +5,23 @@ import { createFileSystem } from "../../../src/platform/index.js";
|
|
|
5
5
|
// Keep init scaffold aligned with current framework default React major/minor.
|
|
6
6
|
const DEFAULT_INIT_REACT_VERSION = "19.1.1";
|
|
7
7
|
|
|
8
|
+
export interface CreatePackageJsonOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Selected integrations whose `connector.json#npmDependencies` should be
|
|
11
|
+
* merged into the generated project's `package.json#dependencies`.
|
|
12
|
+
* First declaration wins on version collisions; framework pins
|
|
13
|
+
* (react, react-dom, veryfront, zod) always take precedence.
|
|
14
|
+
*/
|
|
15
|
+
integrations?: Array<{
|
|
16
|
+
name: string;
|
|
17
|
+
npmDependencies?: Record<string, string>;
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
|
|
8
21
|
export async function createPackageJson(
|
|
9
22
|
projectDir: string,
|
|
10
23
|
projectName?: string,
|
|
24
|
+
options: CreatePackageJsonOptions = {},
|
|
11
25
|
): Promise<void> {
|
|
12
26
|
const fs = createFileSystem();
|
|
13
27
|
|
|
@@ -19,6 +33,24 @@ export async function createPackageJson(
|
|
|
19
33
|
templateDeps = existing.dependencies ?? {};
|
|
20
34
|
}
|
|
21
35
|
|
|
36
|
+
// Merge per-integration deps. First declaration wins; collisions are logged.
|
|
37
|
+
const integrationDeps: Record<string, string> = {};
|
|
38
|
+
for (const integration of options.integrations ?? []) {
|
|
39
|
+
for (const [pkg, range] of Object.entries(integration.npmDependencies ?? {})) {
|
|
40
|
+
if (pkg in integrationDeps) {
|
|
41
|
+
if (integrationDeps[pkg] !== range) {
|
|
42
|
+
logger.warn(
|
|
43
|
+
`[init] ${integration.name} requested ${pkg}@${range} but ${pkg}@${
|
|
44
|
+
integrationDeps[pkg]
|
|
45
|
+
} is already pinned by an earlier integration - keeping the earlier pin`,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
integrationDeps[pkg] = range;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
22
54
|
const dirName = projectDir.split(/[/\\]/).pop();
|
|
23
55
|
const packageJson = {
|
|
24
56
|
name: projectName ?? dirName ?? "veryfront-project",
|
|
@@ -34,6 +66,7 @@ export async function createPackageJson(
|
|
|
34
66
|
},
|
|
35
67
|
dependencies: {
|
|
36
68
|
...templateDeps,
|
|
69
|
+
...integrationDeps,
|
|
37
70
|
react: `^${DEFAULT_INIT_REACT_VERSION}`,
|
|
38
71
|
"react-dom": `^${DEFAULT_INIT_REACT_VERSION}`,
|
|
39
72
|
veryfront: `^${VERSION}`,
|
|
@@ -269,6 +269,7 @@ export async function initCommand(options: InitOptions): Promise<void> {
|
|
|
269
269
|
|
|
270
270
|
const allEnvVars: EnvVarConfig[] = templateConfig?.envVars ? [...templateConfig.envVars] : [];
|
|
271
271
|
const featureTips: string[] = [];
|
|
272
|
+
let loadedIntegrations: ResolvedIntegration[] = [];
|
|
272
273
|
|
|
273
274
|
if (features.length) {
|
|
274
275
|
const { ordered, errors } = await resolveFeatures(features);
|
|
@@ -309,10 +310,11 @@ export async function initCommand(options: InitOptions): Promise<void> {
|
|
|
309
310
|
if (baseConfig?.envVars) allEnvVars.push(...baseConfig.envVars);
|
|
310
311
|
|
|
311
312
|
const {
|
|
312
|
-
integrations:
|
|
313
|
+
integrations: resolvedIntegrations,
|
|
313
314
|
files: integrationFiles,
|
|
314
315
|
errors: integrationErrors,
|
|
315
316
|
} = await loadIntegrations(integrations);
|
|
317
|
+
loadedIntegrations = resolvedIntegrations;
|
|
316
318
|
|
|
317
319
|
if (integrationErrors.length) {
|
|
318
320
|
for (const error of integrationErrors) logger.warn(error);
|
|
@@ -359,7 +361,12 @@ export async function initCommand(options: InitOptions): Promise<void> {
|
|
|
359
361
|
|
|
360
362
|
// Skip in quiet/TUI mode since local dev uses CDN and package.json can cause hydration issues
|
|
361
363
|
if (!options.quiet) {
|
|
362
|
-
await createPackageJson(projectDir, projectName
|
|
364
|
+
await createPackageJson(projectDir, projectName, {
|
|
365
|
+
integrations: loadedIntegrations.map((integration) => ({
|
|
366
|
+
name: integration.config.name,
|
|
367
|
+
npmDependencies: integration.config.npmDependencies,
|
|
368
|
+
})),
|
|
369
|
+
});
|
|
363
370
|
}
|
|
364
371
|
|
|
365
372
|
if (allEnvVars.length) {
|
package/src/deno.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
"name": "veryfront",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.220",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"nodeModulesDir": "auto",
|
|
6
6
|
"workspace": [
|
|
@@ -264,7 +264,6 @@ export default {
|
|
|
264
264
|
"tailwindcss/plugin": "https://esm.sh/tailwindcss@4.2.2/plugin",
|
|
265
265
|
"tailwindcss/defaultTheme": "https://esm.sh/tailwindcss@4.2.2/defaultTheme",
|
|
266
266
|
"tailwindcss/colors": "https://esm.sh/tailwindcss@4.2.2/colors",
|
|
267
|
-
"pg": "npm:pg@8.13.1",
|
|
268
267
|
"@opentelemetry/api": "npm:@opentelemetry/api@1.9.0",
|
|
269
268
|
"@opentelemetry/core": "npm:@opentelemetry/core@2.6.0",
|
|
270
269
|
"@opentelemetry/context-async-hooks": "npm:@opentelemetry/context-async-hooks@2.6.0",
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { INVALID_ARGUMENT } from "../errors/index.js";
|
|
3
|
+
import { SKILL_TOOL_IDS } from "../skill/types.js";
|
|
4
|
+
import { type Tool, toolRegistry } from "../tool/index.js";
|
|
5
|
+
import { streamDataStreamEvents } from "./data-stream.js";
|
|
6
|
+
import { type AgUiInjectedTool, type AgUiRequest, AgUiRequestSchema } from "./ag-ui-handler.js";
|
|
7
|
+
import {
|
|
8
|
+
AgentRuntime,
|
|
9
|
+
RunAlreadyExistsError,
|
|
10
|
+
type RunResumeSessionManager,
|
|
11
|
+
} from "./runtime/index.js";
|
|
12
|
+
import type { Agent, Message } from "./types.js";
|
|
13
|
+
|
|
14
|
+
const AGENT_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
15
|
+
const AG_UI_DETACHED_RUN_ID_SCHEMA = z.string().min(1).max(128).regex(AGENT_ID_PATTERN);
|
|
16
|
+
const MAX_TEXT_PART_LENGTH = 10_000;
|
|
17
|
+
|
|
18
|
+
type AgUiResumeValue = {
|
|
19
|
+
result: unknown;
|
|
20
|
+
isError: boolean;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type AgUiContextValue =
|
|
24
|
+
| Record<string, unknown>
|
|
25
|
+
| ((request: Request) => Record<string, unknown> | Promise<Record<string, unknown>>);
|
|
26
|
+
|
|
27
|
+
type AgUiRuntimePart = Record<string, unknown> & { type: string };
|
|
28
|
+
|
|
29
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
30
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function normalizeToolArgs(part: Record<string, unknown>): Record<string, unknown> {
|
|
34
|
+
if (isRecord(part.args)) return part.args;
|
|
35
|
+
if (isRecord(part.input)) return part.input;
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizeMessagePart(part: Record<string, unknown>): Message["parts"][number] | null {
|
|
40
|
+
if (
|
|
41
|
+
part.type === "text" && typeof part.text === "string" &&
|
|
42
|
+
part.text.length <= MAX_TEXT_PART_LENGTH
|
|
43
|
+
) {
|
|
44
|
+
return { type: "text", text: part.text };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (part.type === "tool_call" && typeof part.id === "string" && typeof part.name === "string") {
|
|
48
|
+
return {
|
|
49
|
+
type: "tool-call",
|
|
50
|
+
toolCallId: part.id,
|
|
51
|
+
toolName: part.name,
|
|
52
|
+
args: normalizeToolArgs(part),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (
|
|
57
|
+
part.type === "tool-call" &&
|
|
58
|
+
typeof part.toolCallId === "string" &&
|
|
59
|
+
typeof part.toolName === "string"
|
|
60
|
+
) {
|
|
61
|
+
return {
|
|
62
|
+
type: "tool-call",
|
|
63
|
+
toolCallId: part.toolCallId,
|
|
64
|
+
toolName: part.toolName,
|
|
65
|
+
args: normalizeToolArgs(part),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (
|
|
70
|
+
typeof part.type === "string" &&
|
|
71
|
+
part.type.startsWith("tool-") &&
|
|
72
|
+
part.type !== "tool-result" &&
|
|
73
|
+
typeof part.toolCallId === "string" &&
|
|
74
|
+
typeof part.toolName === "string"
|
|
75
|
+
) {
|
|
76
|
+
return {
|
|
77
|
+
type: part.type,
|
|
78
|
+
toolCallId: part.toolCallId,
|
|
79
|
+
toolName: part.toolName,
|
|
80
|
+
args: normalizeToolArgs(part),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (part.type === "tool_result" && typeof part.tool_call_id === "string") {
|
|
85
|
+
return {
|
|
86
|
+
type: "tool-result",
|
|
87
|
+
toolCallId: part.tool_call_id,
|
|
88
|
+
toolName: typeof part.tool_name === "string" ? part.tool_name : "unknown",
|
|
89
|
+
result: "output" in part ? part.output : undefined,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (part.type === "tool-result" && typeof part.toolCallId === "string") {
|
|
94
|
+
return {
|
|
95
|
+
type: "tool-result",
|
|
96
|
+
toolCallId: part.toolCallId,
|
|
97
|
+
toolName: typeof part.toolName === "string" ? part.toolName : "unknown",
|
|
98
|
+
result: "result" in part ? part.result : undefined,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function normalizeMessages(messages: AgUiRequest["messages"]): Message[] {
|
|
106
|
+
return messages.map((message) => ({
|
|
107
|
+
id: message.id,
|
|
108
|
+
role: message.role,
|
|
109
|
+
parts: message.parts
|
|
110
|
+
.map((part) => normalizeMessagePart(part))
|
|
111
|
+
.filter((part): part is Message["parts"][number] => part !== null),
|
|
112
|
+
...(message.createdAt ? { timestamp: Date.parse(message.createdAt) || undefined } : {}),
|
|
113
|
+
...(message.metadata ? { metadata: message.metadata } : {}),
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function isRequest(obj: unknown): obj is Request {
|
|
118
|
+
return (
|
|
119
|
+
typeof obj === "object" &&
|
|
120
|
+
obj !== null &&
|
|
121
|
+
"json" in obj &&
|
|
122
|
+
typeof obj.json === "function" &&
|
|
123
|
+
"url" in obj &&
|
|
124
|
+
typeof obj.url === "string" &&
|
|
125
|
+
"method" in obj &&
|
|
126
|
+
typeof obj.method === "string"
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function extractRequest(requestOrCtx: unknown): Request {
|
|
131
|
+
if (isRequest(requestOrCtx)) return requestOrCtx;
|
|
132
|
+
|
|
133
|
+
if (typeof requestOrCtx === "object" && requestOrCtx !== null && "request" in requestOrCtx) {
|
|
134
|
+
const candidate = (requestOrCtx as Record<string, unknown>).request;
|
|
135
|
+
if (isRequest(candidate)) return candidate;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
throw INVALID_ARGUMENT.create({
|
|
139
|
+
detail: "Invalid handler argument: expected Request or APIContext",
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function buildStreamContext(
|
|
144
|
+
request: AgUiDetachedStartRequest,
|
|
145
|
+
baseContext: Record<string, unknown>,
|
|
146
|
+
threadId: string,
|
|
147
|
+
runId: string,
|
|
148
|
+
): Record<string, unknown> {
|
|
149
|
+
return {
|
|
150
|
+
...baseContext,
|
|
151
|
+
threadId,
|
|
152
|
+
runId,
|
|
153
|
+
agUi: {
|
|
154
|
+
context: request.context,
|
|
155
|
+
forwardedProps: request.forwardedProps,
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function createInjectedAgUiTool(
|
|
161
|
+
runId: string,
|
|
162
|
+
tool: AgUiInjectedTool,
|
|
163
|
+
sessionManager: RunResumeSessionManager<AgUiResumeValue>,
|
|
164
|
+
): Tool {
|
|
165
|
+
return {
|
|
166
|
+
id: tool.name,
|
|
167
|
+
type: "function",
|
|
168
|
+
description: tool.description ?? tool.name,
|
|
169
|
+
inputSchema: z.record(z.string(), z.unknown()),
|
|
170
|
+
inputSchemaJson: (tool.parameters ??
|
|
171
|
+
{ type: "object", properties: {}, additionalProperties: true }) as Tool["inputSchemaJson"],
|
|
172
|
+
execute: async (_input, context) => {
|
|
173
|
+
const toolCallId = typeof context?.toolCallId === "string" ? context.toolCallId : null;
|
|
174
|
+
if (!toolCallId) {
|
|
175
|
+
throw new Error(`Missing toolCallId for injected tool "${tool.name}"`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
sessionManager.prepareForSignal(runId, toolCallId);
|
|
179
|
+
const submitted = await sessionManager.waitForSignal(runId, toolCallId);
|
|
180
|
+
if (submitted.isError) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
typeof submitted.result === "string"
|
|
183
|
+
? submitted.result
|
|
184
|
+
: JSON.stringify(submitted.result),
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
return submitted.result;
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function buildMergedTools(
|
|
193
|
+
agent: Agent,
|
|
194
|
+
request: AgUiDetachedStartRequest,
|
|
195
|
+
sessionManager: RunResumeSessionManager<AgUiResumeValue>,
|
|
196
|
+
): Agent["config"]["tools"] {
|
|
197
|
+
const injectedTools = Object.fromEntries(
|
|
198
|
+
request.tools.map((tool) => [
|
|
199
|
+
tool.name,
|
|
200
|
+
createInjectedAgUiTool(request.runId, tool, sessionManager),
|
|
201
|
+
]),
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
if (!agent.config.tools) {
|
|
205
|
+
return Object.keys(injectedTools).length > 0 ? injectedTools : undefined;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (agent.config.tools === true) {
|
|
209
|
+
const merged: Record<string, Tool | boolean> = {};
|
|
210
|
+
for (const [toolId] of toolRegistry.getAll()) {
|
|
211
|
+
if (!agent.config.skills && SKILL_TOOL_IDS.has(toolId)) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
merged[toolId] = true;
|
|
215
|
+
}
|
|
216
|
+
return { ...merged, ...injectedTools };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return { ...agent.config.tools, ...injectedTools };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function resolveContextValue(
|
|
223
|
+
value: AgUiContextValue | undefined,
|
|
224
|
+
request: Request,
|
|
225
|
+
): Promise<Record<string, unknown>> {
|
|
226
|
+
if (typeof value === "function") {
|
|
227
|
+
return await value(request);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return value ?? {};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function scheduleDetachedTask(requestOrCtx: unknown, task: Promise<void>): void {
|
|
234
|
+
if (
|
|
235
|
+
typeof requestOrCtx === "object" &&
|
|
236
|
+
requestOrCtx !== null &&
|
|
237
|
+
"waitUntil" in requestOrCtx &&
|
|
238
|
+
typeof (requestOrCtx as Record<string, unknown>).waitUntil === "function"
|
|
239
|
+
) {
|
|
240
|
+
((requestOrCtx as { waitUntil: (promise: Promise<void>) => void }).waitUntil)(task);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
void task;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function drainRuntimeStream(
|
|
248
|
+
stream: ReadableStream<Uint8Array>,
|
|
249
|
+
): Promise<void> {
|
|
250
|
+
for await (const _event of streamDataStreamEvents(stream) as AsyncIterable<AgUiRuntimePart>) {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export const AgUiDetachedStartRequestSchema = AgUiRequestSchema.extend({
|
|
256
|
+
threadId: z.string().uuid(),
|
|
257
|
+
runId: AG_UI_DETACHED_RUN_ID_SCHEMA,
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
export const AgUiDetachedStartAcceptedSchema = z.object({
|
|
261
|
+
accepted: z.literal(true),
|
|
262
|
+
duplicate: z.boolean(),
|
|
263
|
+
runId: AG_UI_DETACHED_RUN_ID_SCHEMA,
|
|
264
|
+
threadId: z.string().uuid(),
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
export type AgUiDetachedStartRequest = z.infer<typeof AgUiDetachedStartRequestSchema>;
|
|
268
|
+
export type AgUiDetachedStartAccepted = z.infer<typeof AgUiDetachedStartAcceptedSchema>;
|
|
269
|
+
|
|
270
|
+
interface AgUiDetachedStartExecutionInput {
|
|
271
|
+
request: AgUiDetachedStartRequest;
|
|
272
|
+
requestOrCtx: unknown;
|
|
273
|
+
rawRequest: Request;
|
|
274
|
+
context: Record<string, unknown>;
|
|
275
|
+
abortSignal: AbortSignal;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
type AgUiDetachedExecutionStarter = (
|
|
279
|
+
input: AgUiDetachedStartExecutionInput,
|
|
280
|
+
) => Promise<void> | void;
|
|
281
|
+
|
|
282
|
+
interface AgUiDetachedStartHandlerOptionsBase {
|
|
283
|
+
sessionManager: RunResumeSessionManager<AgUiResumeValue>;
|
|
284
|
+
context?: AgUiContextValue;
|
|
285
|
+
startDetachedExecution?: AgUiDetachedExecutionStarter;
|
|
286
|
+
onAccepted?: (input: {
|
|
287
|
+
request: AgUiDetachedStartRequest;
|
|
288
|
+
runId: string;
|
|
289
|
+
threadId: string;
|
|
290
|
+
}) => Promise<void> | void;
|
|
291
|
+
onDuplicate?: (input: {
|
|
292
|
+
request: AgUiDetachedStartRequest;
|
|
293
|
+
runId: string;
|
|
294
|
+
threadId: string;
|
|
295
|
+
}) => Promise<void> | void;
|
|
296
|
+
onFinish?: (input: { runId: string; threadId: string }) => Promise<void> | void;
|
|
297
|
+
onError?: (input: { runId: string; threadId: string; error: unknown }) => Promise<void> | void;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export type AgUiDetachedStartHandlerOptions =
|
|
301
|
+
| (AgUiDetachedStartHandlerOptionsBase & { agent: Agent })
|
|
302
|
+
| (AgUiDetachedStartHandlerOptionsBase & {
|
|
303
|
+
agent?: undefined;
|
|
304
|
+
startDetachedExecution: AgUiDetachedExecutionStarter;
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
async function startDefaultDetachedExecution(input: {
|
|
308
|
+
agent: Agent;
|
|
309
|
+
request: AgUiDetachedStartRequest;
|
|
310
|
+
context: Record<string, unknown>;
|
|
311
|
+
abortSignal: AbortSignal;
|
|
312
|
+
sessionManager: RunResumeSessionManager<AgUiResumeValue>;
|
|
313
|
+
}): Promise<void> {
|
|
314
|
+
const runtime = new AgentRuntime(input.agent.id, {
|
|
315
|
+
...input.agent.config,
|
|
316
|
+
tools: buildMergedTools(input.agent, input.request, input.sessionManager),
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const runtimeStream = await runtime.stream(
|
|
320
|
+
normalizeMessages(input.request.messages),
|
|
321
|
+
buildStreamContext(input.request, input.context, input.request.threadId, input.request.runId),
|
|
322
|
+
undefined,
|
|
323
|
+
input.request.model,
|
|
324
|
+
input.request.maxOutputTokens,
|
|
325
|
+
input.abortSignal,
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
await drainRuntimeStream(runtimeStream);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export function createAgUiDetachedStartHandler(
|
|
332
|
+
options: AgUiDetachedStartHandlerOptions,
|
|
333
|
+
): (requestOrCtx: unknown) => Promise<Response> {
|
|
334
|
+
if (!options.agent && !options.startDetachedExecution) {
|
|
335
|
+
throw new Error(
|
|
336
|
+
"Detached AG-UI start requires either an agent or startDetachedExecution handler.",
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return async function POST(requestOrCtx: unknown): Promise<Response> {
|
|
341
|
+
const request = extractRequest(requestOrCtx);
|
|
342
|
+
|
|
343
|
+
try {
|
|
344
|
+
const parsed = AgUiDetachedStartRequestSchema.parse(await request.json());
|
|
345
|
+
const context = await resolveContextValue(options.context, request);
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
const abortSignal = options.sessionManager.startRun({
|
|
349
|
+
runId: parsed.runId,
|
|
350
|
+
threadId: parsed.threadId,
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
await options.onAccepted?.({
|
|
354
|
+
request: parsed,
|
|
355
|
+
runId: parsed.runId,
|
|
356
|
+
threadId: parsed.threadId,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
const detachedTask = (async () => {
|
|
360
|
+
try {
|
|
361
|
+
if (options.startDetachedExecution) {
|
|
362
|
+
await options.startDetachedExecution({
|
|
363
|
+
request: parsed,
|
|
364
|
+
requestOrCtx,
|
|
365
|
+
rawRequest: request,
|
|
366
|
+
context,
|
|
367
|
+
abortSignal,
|
|
368
|
+
});
|
|
369
|
+
} else if (options.agent) {
|
|
370
|
+
await startDefaultDetachedExecution({
|
|
371
|
+
agent: options.agent,
|
|
372
|
+
request: parsed,
|
|
373
|
+
context,
|
|
374
|
+
abortSignal,
|
|
375
|
+
sessionManager: options.sessionManager,
|
|
376
|
+
});
|
|
377
|
+
} else {
|
|
378
|
+
throw new Error(
|
|
379
|
+
"Detached AG-UI start configuration became invalid during execution.",
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
options.sessionManager.completeRun(parsed.runId);
|
|
384
|
+
await options.onFinish?.({
|
|
385
|
+
runId: parsed.runId,
|
|
386
|
+
threadId: parsed.threadId,
|
|
387
|
+
});
|
|
388
|
+
} catch (error) {
|
|
389
|
+
options.sessionManager.failRun(parsed.runId);
|
|
390
|
+
await options.onError?.({
|
|
391
|
+
runId: parsed.runId,
|
|
392
|
+
threadId: parsed.threadId,
|
|
393
|
+
error,
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
})().catch(() => undefined);
|
|
397
|
+
|
|
398
|
+
scheduleDetachedTask(requestOrCtx, detachedTask);
|
|
399
|
+
|
|
400
|
+
return Response.json(
|
|
401
|
+
{
|
|
402
|
+
accepted: true,
|
|
403
|
+
duplicate: false,
|
|
404
|
+
runId: parsed.runId,
|
|
405
|
+
threadId: parsed.threadId,
|
|
406
|
+
} satisfies AgUiDetachedStartAccepted,
|
|
407
|
+
{ status: 202 },
|
|
408
|
+
);
|
|
409
|
+
} catch (error) {
|
|
410
|
+
if (error instanceof RunAlreadyExistsError) {
|
|
411
|
+
await options.onDuplicate?.({
|
|
412
|
+
request: parsed,
|
|
413
|
+
runId: parsed.runId,
|
|
414
|
+
threadId: parsed.threadId,
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
return Response.json(
|
|
418
|
+
{
|
|
419
|
+
accepted: true,
|
|
420
|
+
duplicate: true,
|
|
421
|
+
runId: parsed.runId,
|
|
422
|
+
threadId: parsed.threadId,
|
|
423
|
+
} satisfies AgUiDetachedStartAccepted,
|
|
424
|
+
{ status: 202 },
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
options.sessionManager.failRun(parsed.runId);
|
|
429
|
+
throw error;
|
|
430
|
+
}
|
|
431
|
+
} catch (error) {
|
|
432
|
+
if (error instanceof z.ZodError) {
|
|
433
|
+
return Response.json(
|
|
434
|
+
{
|
|
435
|
+
error: "Invalid AG-UI detached start request",
|
|
436
|
+
details: error.issues.map((issue) => ({
|
|
437
|
+
path: issue.path,
|
|
438
|
+
message: issue.message,
|
|
439
|
+
})),
|
|
440
|
+
},
|
|
441
|
+
{ status: 400 },
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return Response.json(
|
|
446
|
+
{
|
|
447
|
+
error: error instanceof Error ? error.message : "Internal detached start failed",
|
|
448
|
+
},
|
|
449
|
+
{ status: 500 },
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
}
|
package/src/src/agent/index.ts
CHANGED
|
@@ -173,6 +173,14 @@ export {
|
|
|
173
173
|
getProviderNativeToolNames,
|
|
174
174
|
type ProviderNativeToolInventoryOptions,
|
|
175
175
|
} from "./provider-native-tool-inventory.js";
|
|
176
|
+
export {
|
|
177
|
+
type AgUiDetachedStartAccepted,
|
|
178
|
+
AgUiDetachedStartAcceptedSchema,
|
|
179
|
+
type AgUiDetachedStartHandlerOptions,
|
|
180
|
+
type AgUiDetachedStartRequest,
|
|
181
|
+
AgUiDetachedStartRequestSchema,
|
|
182
|
+
createAgUiDetachedStartHandler,
|
|
183
|
+
} from "./ag-ui-detached-start.js";
|
|
176
184
|
export {
|
|
177
185
|
type AgUiCancelHandlerOptions,
|
|
178
186
|
type AgUiResumeHandlerOptions,
|