History

decent ai sdk

Rhys Sullivan committed 8 months ago

Commit 46a39bf1 parent 1aa43db17 files · +1090 −0Browse files

Unified · Split

17 files
  1. AGENTS.md +0 −0
  2. bun.lock +54 −0
  3. package.json +15 −0
  4. packages/ai-sdk-adapter/package.json +23 −0
  5. packages/ai-sdk-adapter/src/ai.ts +12 −0
  6. packages/ai-sdk-adapter/src/example-dynamic.ts +54 −0
  7. packages/ai-sdk-adapter/src/example-static.ts +42 −0
  8. packages/ai-sdk-adapter/src/example.ts +2 −0
  9. packages/ai-sdk-adapter/src/index.ts +435 −0
  10. packages/ai-sdk-adapter/tsconfig.json +15 −0
  11. packages/engine/package.json +18 −0
  12. packages/engine/src/index.ts +13 −0
  13. packages/engine/src/runtime-adapters.ts +149 −0
  14. packages/engine/tsconfig.json +13 −0
  15. packages/runtime-local-inproc/package.json +19 −0
  16. packages/runtime-local-inproc/src/index.ts +213 −0
  17. packages/runtime-local-inproc/tsconfig.json +13 −0
AGENTS.mdrenamed from legacy2/AGENTS.md+0−0

File renamed without changes.

bun.lock+54−0
@@ -0,0 +1,54 @@
1+{
2+ "lockfileVersion": 1,
3+ "configVersion": 1,
4+ "workspaces": {
5+ "": {
6+ "name": "executor-v3",
7+ "devDependencies": {
8+ "typescript": "^5.9.3",
9+ },
10+ },
11+ "packages/ai-sdk-adapter": {
12+ "name": "@executor-v3/ai-sdk-adapter",
13+ "dependencies": {
14+ "ai": "^6.0.0",
15+ "zod": "^4.3.6",
16+ },
17+ "devDependencies": {
18+ "@types/node": "^24.3.1",
19+ "bun-types": "^1.2.22",
20+ },
21+ },
22+ },
23+ "packages": {
24+ "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.66", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.19", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-SIQ0YY0iMuv+07HLsZ+bB990zUJ6S4ujORAh+Jv1V2KGNn73qQKnGO0JBk+w+Res8YqOFSycwDoWcFlQrVxS4A=="],
25+
26+ "@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
27+
28+ "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.19", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-3eG55CrSWCu2SXlqq2QCsFjo3+E7+Gmg7i/oRVoSZzIodTuDSfLb3MRje67xE9RFea73Zao7Lm4mADIfUETKGg=="],
29+
30+ "@executor-v3/ai-sdk-adapter": ["@executor-v3/ai-sdk-adapter@workspace:packages/ai-sdk-adapter"],
31+
32+ "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
33+
34+ "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
35+
36+ "@types/node": ["@types/node@24.11.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-fPxQqz4VTgPI/IQ+lj9r0h+fDR66bzoeMGHp8ASee+32OSGIkeASsoZuJixsQoVef1QJbeubcPBxKk22QVoWdw=="],
37+
38+ "@vercel/oidc": ["@vercel/oidc@3.1.0", "", {}, "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w=="],
39+
40+ "ai": ["ai@6.0.116", "", { "dependencies": { "@ai-sdk/gateway": "3.0.66", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.19", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7yM+cTmyRLeNIXwt4Vj+mrrJgVQ9RMIW5WO0ydoLoYkewIvsMcvUmqS4j2RJTUXaF1HphwmSKUMQ/HypNRGOmA=="],
41+
42+ "bun-types": ["bun-types@1.3.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg=="],
43+
44+ "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="],
45+
46+ "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="],
47+
48+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
49+
50+ "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
51+
52+ "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
53+ }
54+}
package.json+15−0
@@ -0,0 +1,15 @@
1+{
2+ "name": "executor-v3",
3+ "private": true,
4+ "type": "module",
5+ "packageManager": "bun@1.2.22",
6+ "workspaces": [
7+ "packages/*"
8+ ],
9+ "scripts": {
10+ "typecheck": "bun run --filter='*' typecheck"
11+ },
12+ "devDependencies": {
13+ "typescript": "^5.9.3"
14+ }
15+}
packages/ai-sdk-adapter/package.json+23−0
@@ -0,0 +1,23 @@
1+{
2+ "name": "@executor-v3/ai-sdk-adapter",
3+ "private": true,
4+ "type": "module",
5+ "exports": {
6+ ".": "./src/index.ts",
7+ "./ai": "./src/ai.ts",
8+ "./example": "./src/example.ts",
9+ "./example-static": "./src/example-static.ts",
10+ "./example-dynamic": "./src/example-dynamic.ts"
11+ },
12+ "scripts": {
13+ "typecheck": "bunx tsc --noEmit -p tsconfig.json"
14+ },
15+ "dependencies": {
16+ "ai": "^6.0.0",
17+ "zod": "^4.3.6"
18+ },
19+ "devDependencies": {
20+ "@types/node": "^24.3.1",
21+ "bun-types": "^1.2.22"
22+ }
23+}
packages/ai-sdk-adapter/src/ai.ts+12−0
@@ -0,0 +1,12 @@
1+export {
2+ createDynamicDiscovery,
3+ createStaticDiscoveryFromTools,
4+ createToolsFromAiSdkTools,
5+ toExecutorTool,
6+ wrapTool,
7+ type AiSdkToolMap,
8+ type ExecutorAiSdkMetadata,
9+ type ExecutorToolDefinition,
10+ type ExecutorToolInput,
11+ type ExecutorToolMap,
12+} from "./index";
packages/ai-sdk-adapter/src/example-dynamic.ts+54−0
@@ -0,0 +1,54 @@
1+import {
2+ createDynamicDiscovery,
3+ type SearchProvider,
4+ type ToolDirectory,
5+ type ToolPath,
6+} from "./index";
7+
8+const asToolPath = (value: string): ToolPath => value as ToolPath;
9+
10+const directory: ToolDirectory = {
11+ async listNamespaces() {
12+ return [
13+ { namespace: "source.src_api", toolCount: 6800 },
14+ { namespace: "source.src_mcp", toolCount: 3200 },
15+ ];
16+ },
17+ async listTools() {
18+ return [{ path: asToolPath("source.src_api.github.issues.list") }];
19+ },
20+ async getByPath({ path }: { path: ToolPath; includeSchemas: boolean }) {
21+ return {
22+ path,
23+ sourceKey: "source.src_api",
24+ description: "Hydrated metadata for selected path",
25+ inputHint: "object",
26+ outputHint: "object",
27+ };
28+ },
29+ async getByPaths({ paths }: { paths: readonly ToolPath[]; includeSchemas: boolean }) {
30+ return paths.map((path) => ({
31+ path,
32+ sourceKey: "source.src_api",
33+ description: "Hydrated from metadata store",
34+ inputHint: "object",
35+ outputHint: "object",
36+ }));
37+ },
38+};
39+
40+const search: SearchProvider = {
41+ async search({ limit }) {
42+ return [
43+ { path: asToolPath("source.src_api.github.issues.list"), score: 0.99 },
44+ { path: asToolPath("source.src_api.github.issues.create"), score: 0.92 },
45+ ].slice(0, limit);
46+ },
47+};
48+
49+export const dynamicDemo = createDynamicDiscovery({
50+ directory,
51+ search,
52+});
53+
54+console.log(dynamicDemo)
packages/ai-sdk-adapter/src/example-static.ts+42−0
@@ -0,0 +1,42 @@
1+import { convertFileListToFileUIParts, tool } from "ai";
2+import { z } from "zod";
3+
4+import { createStaticDiscoveryFromTools, toExecutorTool } from "./index";
5+
6+const listIssues = tool({
7+ description: "List repository issues",
8+ inputSchema: z.object({ owner: z.string(), repo: z.string() }),
9+ execute: async ({ owner, repo }: { owner: string; repo: string }) => ({
10+ items: [`${owner}/${repo}#1`],
11+ }),
12+});
13+
14+const createIssue = tool({
15+ description: "Create repository issue",
16+ inputSchema: z.object({
17+ owner: z.string(),
18+ repo: z.string(),
19+ title: z.string(),
20+ }),
21+ execute: async ({ owner, repo, title }: { owner: string; repo: string; title: string }) => ({
22+ id: "issue_123",
23+ owner,
24+ repo,
25+ title,
26+ }),
27+});
28+
29+const wrappedTools = {
30+ "github.issues.list": listIssues,
31+ "github.issues.create": toExecutorTool({
32+ tool: createIssue,
33+ metadata: {
34+ interaction: "required",
35+ },
36+ }),
37+};
38+
39+export const staticDemo = createStaticDiscoveryFromTools({
40+ tools: wrappedTools,
41+ sourceKey: "api.github",
42+});
packages/ai-sdk-adapter/src/example.ts+2−0
@@ -0,0 +1,2 @@
1+export { staticDemo } from "./example-static";
2+export { dynamicDemo } from "./example-dynamic";
packages/ai-sdk-adapter/src/index.ts+435−0
@@ -0,0 +1,435 @@
1+import type { ToolSet } from "ai";
2+
3+export type ToolPath = string & { readonly __toolPath: unique symbol };
4+
5+export type ToolDescriptor = {
6+ path: ToolPath;
7+ sourceKey: string;
8+ description?: string;
9+ interaction?: "auto" | "required";
10+ inputHint?: string;
11+ outputHint?: string;
12+ inputSchemaJson?: string;
13+ outputSchemaJson?: string;
14+ refHintKeys?: readonly string[];
15+};
16+
17+export type ExecutorAiSdkMetadata = {
18+ interaction?: "auto" | "required";
19+ inputHint?: string;
20+ outputHint?: string;
21+ inputSchemaJson?: string;
22+ outputSchemaJson?: string;
23+ refHintKeys?: readonly string[];
24+ sourceKey?: string;
25+};
26+
27+export type AiSdkToolMap = ToolSet;
28+
29+export type ExecutorToolDefinition = {
30+ tool: ToolSet[string];
31+ metadata?: ExecutorAiSdkMetadata;
32+};
33+
34+export type ExecutorToolInput = ToolSet[string] | ExecutorToolDefinition;
35+
36+export type ExecutorToolMap = Record<string, ExecutorToolInput>;
37+
38+type ResolvedExecutorTool = {
39+ path: ToolPath;
40+ tool: ToolSet[string];
41+ metadata?: ExecutorAiSdkMetadata;
42+};
43+
44+export type SearchHit = {
45+ path: ToolPath;
46+ score: number;
47+};
48+
49+export interface SearchProvider {
50+ search(input: {
51+ query: string;
52+ limit: number;
53+ }): Promise<readonly SearchHit[]>;
54+}
55+
56+export interface ToolDirectory {
57+ listNamespaces(input: {
58+ limit: number;
59+ }): Promise<readonly { namespace: string; toolCount: number }[]>;
60+
61+ listTools(input: {
62+ namespace?: string;
63+ query?: string;
64+ limit: number;
65+ }): Promise<readonly { path: ToolPath }[]>;
66+
67+ getByPath(input: {
68+ path: ToolPath;
69+ includeSchemas: boolean;
70+ }): Promise<ToolDescriptor | null>;
71+
72+ getByPaths(input: {
73+ paths: readonly ToolPath[];
74+ includeSchemas: boolean;
75+ }): Promise<readonly ToolDescriptor[]>;
76+}
77+
78+export type CatalogPrimitive = {
79+ namespaces(input: {
80+ limit?: number;
81+ }): Promise<{ namespaces: readonly { namespace: string; toolCount: number }[] }>;
82+ tools(input: {
83+ namespace?: string;
84+ query?: string;
85+ limit?: number;
86+ }): Promise<{ results: readonly { path: ToolPath }[] }>;
87+};
88+
89+export type DescribePrimitive = {
90+ tool(input: {
91+ path: ToolPath;
92+ includeSchemas?: boolean;
93+ }): Promise<ToolDescriptor | null>;
94+};
95+
96+export type DiscoverPrimitive = {
97+ run(input: {
98+ query: string;
99+ limit?: number;
100+ includeSchemas?: boolean;
101+ }): Promise<{
102+ bestPath: ToolPath | null;
103+ results: readonly (Record<string, unknown> & {
104+ path: ToolPath;
105+ score: number;
106+ })[];
107+ total: number;
108+ }>;
109+};
110+
111+export type DiscoveryPrimitives = {
112+ catalog?: CatalogPrimitive;
113+ describe?: DescribePrimitive;
114+ discover?: DiscoverPrimitive;
115+};
116+
117+const asToolPath = (value: string): ToolPath => value as ToolPath;
118+
119+export function wrapTool(input: {
120+ tool: ToolSet[string];
121+ metadata?: ExecutorAiSdkMetadata;
122+}): ExecutorToolDefinition {
123+ return {
124+ tool: input.tool,
125+ metadata: input.metadata,
126+ };
127+}
128+
129+export const toExecutorTool = wrapTool;
130+
131+const isExecutorToolDefinition = (
132+ value: ExecutorToolInput,
133+): value is ExecutorToolDefinition =>
134+ typeof value === "object" && value !== null && "tool" in value;
135+
136+const stringifySchema = (value: unknown): string | undefined => {
137+ if (value === undefined || value === null) {
138+ return undefined;
139+ }
140+
141+ try {
142+ return JSON.stringify(value);
143+ } catch {
144+ return undefined;
145+ }
146+};
147+
148+const inferHintFromSchemaJson = (
149+ schemaJson: string | undefined,
150+ fallback: string,
151+): string => {
152+ if (!schemaJson) {
153+ return fallback;
154+ }
155+
156+ try {
157+ const parsed = JSON.parse(schemaJson) as Record<string, unknown>;
158+ const title = typeof parsed.title === "string" ? parsed.title.trim() : "";
159+ if (title.length > 0) {
160+ return title;
161+ }
162+
163+ if (parsed.type === "object") {
164+ const properties =
165+ parsed.properties &&
166+ typeof parsed.properties === "object" &&
167+ !Array.isArray(parsed.properties)
168+ ? Object.keys(parsed.properties as Record<string, unknown>)
169+ : [];
170+ if (properties.length > 0) {
171+ const shown = properties.slice(0, 3).join(", ");
172+ return properties.length <= 3
173+ ? `object { ${shown} }`
174+ : `object { ${shown}, ... }`;
175+ }
176+ return "object";
177+ }
178+
179+ if (parsed.type === "array") {
180+ return "array";
181+ }
182+
183+ if (typeof parsed.type === "string") {
184+ return parsed.type;
185+ }
186+ } catch {
187+ // Ignore malformed schema and fall back.
188+ }
189+
190+ return fallback;
191+};
192+
193+export function createToolsFromAiSdkTools(input: {
194+ tools: AiSdkToolMap;
195+ sourceKey?: string;
196+}): ExecutorToolMap {
197+ const { tools, sourceKey = "in_memory.ai_sdk" } = input;
198+
199+ return Object.fromEntries(
200+ Object.entries(tools)
201+ .sort(([left], [right]) => left.localeCompare(right))
202+ .map(([path, tool]) => [
203+ path,
204+ wrapTool({
205+ tool,
206+ metadata: { sourceKey },
207+ }),
208+ ]),
209+ ) as ExecutorToolMap;
210+}
211+
212+const resolveToolsFromMap = (input: {
213+ tools: ExecutorToolMap;
214+ sourceKey?: string;
215+}): ResolvedExecutorTool[] => {
216+ const defaultSourceKey = input.sourceKey ?? "in_memory.ai_sdk";
217+
218+ return Object.entries(input.tools)
219+ .sort(([left], [right]) => left.localeCompare(right))
220+ .map(([path, value]) => {
221+ const entry = isExecutorToolDefinition(value)
222+ ? value
223+ : { tool: value };
224+ const metadata = entry.metadata
225+ ? {
226+ sourceKey: defaultSourceKey,
227+ ...entry.metadata,
228+ }
229+ : { sourceKey: defaultSourceKey };
230+
231+ return {
232+ path: asToolPath(path),
233+ tool: entry.tool,
234+ metadata,
235+ } satisfies ResolvedExecutorTool;
236+ });
237+};
238+
239+export function toolDescriptorsFromTools(input: {
240+ tools: ExecutorToolMap;
241+ sourceKey?: string;
242+}): ToolDescriptor[] {
243+ const resolvedTools = resolveToolsFromMap({
244+ tools: input.tools,
245+ sourceKey: input.sourceKey,
246+ });
247+
248+ return resolvedTools.map((entry) => {
249+ const metadata = entry.metadata;
250+ const definition = entry.tool;
251+ const inputSchemaJson =
252+ metadata?.inputSchemaJson ??
253+ stringifySchema((definition as { inputSchema?: unknown }).inputSchema) ??
254+ stringifySchema((definition as { parameters?: unknown }).parameters);
255+ const outputSchemaJson =
256+ metadata?.outputSchemaJson ??
257+ stringifySchema((definition as { outputSchema?: unknown }).outputSchema);
258+
259+ return {
260+ path: entry.path,
261+ sourceKey: metadata?.sourceKey ?? "in_memory.ai_sdk",
262+ description: (definition as { description?: string }).description,
263+ interaction: metadata?.interaction,
264+ inputHint:
265+ metadata?.inputHint ?? inferHintFromSchemaJson(inputSchemaJson, "input"),
266+ outputHint:
267+ metadata?.outputHint ?? inferHintFromSchemaJson(outputSchemaJson, "output"),
268+ inputSchemaJson,
269+ outputSchemaJson,
270+ refHintKeys: metadata?.refHintKeys,
271+ } satisfies ToolDescriptor;
272+ });
273+}
274+
275+export function createStaticDiscoveryFromTools(input: {
276+ tools: ExecutorToolMap;
277+ sourceKey?: string;
278+}): {
279+ preloadedTools: ToolDescriptor[];
280+ primitives: DiscoveryPrimitives;
281+ executeDescription: string;
282+} {
283+ const preloadedTools = toolDescriptorsFromTools({
284+ tools: input.tools,
285+ sourceKey: input.sourceKey,
286+ });
287+ const primitives = createDiscoveryPrimitives({});
288+
289+ return {
290+ preloadedTools,
291+ primitives,
292+ executeDescription: buildExecuteDescription({
293+ preloadedTools,
294+ primitives,
295+ }),
296+ };
297+}
298+
299+export function createDynamicDiscovery(input: {
300+ directory: ToolDirectory;
301+ search?: SearchProvider;
302+ preloadedTools?: readonly ToolDescriptor[];
303+}): {
304+ preloadedTools: readonly ToolDescriptor[];
305+ primitives: DiscoveryPrimitives;
306+ executeDescription: string;
307+} {
308+ const preloadedTools = input.preloadedTools ?? [];
309+ const primitives = createDiscoveryPrimitives({
310+ directory: input.directory,
311+ search: input.search,
312+ });
313+
314+ return {
315+ preloadedTools,
316+ primitives,
317+ executeDescription: buildExecuteDescription({
318+ preloadedTools,
319+ primitives,
320+ }),
321+ };
322+}
323+
324+export function createDiscoveryPrimitives(input: {
325+ directory?: ToolDirectory;
326+ search?: SearchProvider;
327+}): DiscoveryPrimitives {
328+ const { directory, search } = input;
329+
330+ const catalog: CatalogPrimitive | undefined = directory
331+ ? {
332+ namespaces: async ({ limit = 200 }) => ({
333+ namespaces: await directory.listNamespaces({ limit }),
334+ }),
335+ tools: async ({ namespace, query, limit = 200 }) => ({
336+ results: await directory.listTools({ namespace, query, limit }),
337+ }),
338+ }
339+ : undefined;
340+
341+ const describe: DescribePrimitive | undefined = directory
342+ ? {
343+ tool: async ({ path, includeSchemas = false }) =>
344+ directory.getByPath({ path, includeSchemas }),
345+ }
346+ : undefined;
347+
348+ const discover: DiscoverPrimitive | undefined =
349+ directory && search
350+ ? {
351+ run: async ({ query, limit = 12, includeSchemas = false }) => {
352+ const hits = await search.search({ query, limit });
353+ const descriptors = await directory.getByPaths({
354+ paths: hits.map((hit) => hit.path),
355+ includeSchemas,
356+ });
357+
358+ const byPath = new Map(
359+ descriptors.map((descriptor) => [descriptor.path, descriptor]),
360+ );
361+ const hydrated = hits
362+ .map((hit) => {
363+ const descriptor = byPath.get(hit.path);
364+ if (!descriptor) {
365+ return null;
366+ }
367+
368+ return {
369+ path: descriptor.path,
370+ score: hit.score,
371+ description: descriptor.description,
372+ interaction: descriptor.interaction ?? "auto",
373+ inputHint: descriptor.inputHint,
374+ outputHint: descriptor.outputHint,
375+ ...(includeSchemas
376+ ? {
377+ inputSchemaJson: descriptor.inputSchemaJson,
378+ outputSchemaJson: descriptor.outputSchemaJson,
379+ refHintKeys: descriptor.refHintKeys,
380+ }
381+ : {}),
382+ };
383+ })
384+ .filter(Boolean) as Array<
385+ Record<string, unknown> & { path: ToolPath; score: number }
386+ >;
387+
388+ return {
389+ bestPath: hydrated[0]?.path ?? null,
390+ results: hydrated,
391+ total: hydrated.length,
392+ };
393+ },
394+ }
395+ : undefined;
396+
397+ return { catalog, describe, discover };
398+}
399+
400+export function buildExecuteDescription(input: {
401+ preloadedTools: readonly ToolDescriptor[];
402+ primitives: DiscoveryPrimitives;
403+}): string {
404+ const { preloadedTools, primitives } = input;
405+ const hasCatalog = Boolean(primitives.catalog);
406+ const hasDescribe = Boolean(primitives.describe);
407+ const hasDiscover = Boolean(primitives.discover);
408+
409+ if (!hasCatalog && !hasDescribe && !hasDiscover) {
410+ return [
411+ "Execute TypeScript in sandbox; call tools directly.",
412+ "Available tool paths:",
413+ ...preloadedTools.map((tool) => `- ${tool.path}`),
414+ "Do not use fetch; use tools.* only.",
415+ ].join("\n");
416+ }
417+
418+ return [
419+ "Execute TypeScript in sandbox; call tools via helper workflow.",
420+ "Workflow:",
421+ hasCatalog
422+ ? "1) const namespaces = await tools.catalog.namespaces({ limit: 200 });"
423+ : "",
424+ hasDiscover
425+ ? '2) const matches = await tools.discover.run({ query: "<intent>", limit: 12 });'
426+ : '2) const toolsList = await tools.catalog.tools({ query: "<intent>", limit: 50 });',
427+ hasDescribe
428+ ? "3) const details = await tools.describe.tool({ path, includeSchemas: true });"
429+ : "",
430+ "4) Call selected tools.<path>(input).",
431+ "Do not use fetch; use tools.* only.",
432+ ]
433+ .filter(Boolean)
434+ .join("\n");
435+}
packages/ai-sdk-adapter/tsconfig.json+15−0
@@ -0,0 +1,15 @@
1+{
2+ "compilerOptions": {
3+ "target": "ES2022",
4+ "module": "ESNext",
5+ "moduleResolution": "Bundler",
6+ "strict": true,
7+ "skipLibCheck": true,
8+ "noEmit": true,
9+ "lib": ["ES2022"],
10+ "types": ["bun-types", "node"]
11+ },
12+ "include": [
13+ "src/**/*.ts"
14+ ]
15+}
packages/engine/package.json+18−0
@@ -0,0 +1,18 @@
1+{
2+ "name": "@executor-v3/engine",
3+ "private": true,
4+ "type": "module",
5+ "exports": {
6+ ".": "./src/index.ts"
7+ },
8+ "scripts": {
9+ "typecheck": "bunx tsc --noEmit -p tsconfig.json"
10+ },
11+ "dependencies": {
12+ "effect": "^3.19.8"
13+ },
14+ "devDependencies": {
15+ "@types/node": "^24.3.1",
16+ "bun-types": "^1.2.22"
17+ }
18+}
packages/engine/src/index.ts+13−0
@@ -0,0 +1,13 @@
1+export {
2+ RuntimeAdapterError,
3+ RuntimeAdapterRegistryError,
4+ RuntimeAdapterRegistryService,
5+ RuntimeAdapterRegistryLive,
6+ makeRuntimeAdapterRegistry,
7+ type RuntimeAdapter,
8+ type RuntimeAdapterKind,
9+ type RuntimeAdapterRegistry,
10+ type RuntimeExecuteError,
11+ type RuntimeExecuteInput,
12+ type RuntimeToolCallService,
13+} from "./runtime-adapters";
packages/engine/src/runtime-adapters.ts+149−0
@@ -0,0 +1,149 @@
1+import * as Context from "effect/Context";
2+import * as Data from "effect/Data";
3+import * as Effect from "effect/Effect";
4+import * as Layer from "effect/Layer";
5+
6+export type RuntimeAdapterKind = string;
7+
8+export type RuntimeToolCallService = {
9+ callTool: (input: {
10+ runId: string;
11+ callId: string;
12+ toolPath: string;
13+ input?: Record<string, unknown>;
14+ credentialContext?: unknown;
15+ }) => Effect.Effect<unknown, RuntimeAdapterError>;
16+};
17+
18+export type RuntimeExecuteInput = {
19+ runId: string;
20+ code: string;
21+ toolCallService?: RuntimeToolCallService;
22+ timeoutMs?: number;
23+};
24+
25+export class RuntimeAdapterError extends Data.TaggedError("RuntimeAdapterError")<{
26+ operation: string;
27+ runtimeKind: RuntimeAdapterKind;
28+ message: string;
29+ details: string | null;
30+}> {}
31+
32+export type RuntimeExecuteError = RuntimeAdapterError;
33+
34+export interface RuntimeAdapter {
35+ readonly kind: RuntimeAdapterKind;
36+ readonly isAvailable: () => Effect.Effect<boolean>;
37+ readonly execute: (
38+ input: RuntimeExecuteInput,
39+ ) => Effect.Effect<unknown, RuntimeExecuteError>;
40+}
41+
42+export class RuntimeAdapterRegistryError extends Data.TaggedError(
43+ "RuntimeAdapterRegistryError",
44+)<{
45+ operation: string;
46+ runtimeKind: RuntimeAdapterKind;
47+ message: string;
48+}> {}
49+
50+export interface RuntimeAdapterRegistry {
51+ readonly register: (
52+ adapter: RuntimeAdapter,
53+ ) => Effect.Effect<void, RuntimeAdapterRegistryError>;
54+
55+ readonly registerAll: (
56+ adapters: ReadonlyArray<RuntimeAdapter>,
57+ ) => Effect.Effect<void, RuntimeAdapterRegistryError>;
58+
59+ readonly get: (
60+ runtimeKind: RuntimeAdapterKind,
61+ ) => Effect.Effect<RuntimeAdapter, RuntimeAdapterRegistryError>;
62+
63+ readonly execute: (
64+ input: RuntimeExecuteInput & { runtimeKind: RuntimeAdapterKind },
65+ ) => Effect.Effect<unknown, RuntimeExecuteError | RuntimeAdapterRegistryError>;
66+}
67+
68+export class RuntimeAdapterRegistryService extends Context.Tag(
69+ "@executor-v3/engine/RuntimeAdapterRegistryService",
70+)<RuntimeAdapterRegistryService, RuntimeAdapterRegistry>() {}
71+
72+const duplicateRuntimeAdapterError = (
73+ runtimeKind: RuntimeAdapterKind,
74+): RuntimeAdapterRegistryError =>
75+ new RuntimeAdapterRegistryError({
76+ operation: "register",
77+ runtimeKind,
78+ message: `Runtime adapter already registered: ${runtimeKind}`,
79+ });
80+
81+const runtimeAdapterNotFoundError = (
82+ operation: string,
83+ runtimeKind: RuntimeAdapterKind,
84+): RuntimeAdapterRegistryError =>
85+ new RuntimeAdapterRegistryError({
86+ operation,
87+ runtimeKind,
88+ message: `No runtime adapter registered for kind: ${runtimeKind}`,
89+ });
90+
91+export const makeRuntimeAdapterRegistry = (
92+ initialAdapters: ReadonlyArray<RuntimeAdapter> = [],
93+): RuntimeAdapterRegistry => {
94+ const adaptersByKind = new Map<RuntimeAdapterKind, RuntimeAdapter>();
95+
96+ const register = (
97+ adapter: RuntimeAdapter,
98+ ): Effect.Effect<void, RuntimeAdapterRegistryError> =>
99+ Effect.gen(function* () {
100+ if (adaptersByKind.has(adapter.kind)) {
101+ return yield* duplicateRuntimeAdapterError(adapter.kind);
102+ }
103+
104+ adaptersByKind.set(adapter.kind, adapter);
105+ });
106+
107+ const registerAll = (
108+ adapters: ReadonlyArray<RuntimeAdapter>,
109+ ): Effect.Effect<void, RuntimeAdapterRegistryError> =>
110+ Effect.forEach(adapters, register, { discard: true });
111+
112+ const get = (
113+ runtimeKind: RuntimeAdapterKind,
114+ ): Effect.Effect<RuntimeAdapter, RuntimeAdapterRegistryError> =>
115+ Effect.gen(function* () {
116+ const adapter = adaptersByKind.get(runtimeKind);
117+ if (!adapter) {
118+ return yield* runtimeAdapterNotFoundError("get", runtimeKind);
119+ }
120+
121+ return adapter;
122+ });
123+
124+ const execute = (
125+ input: RuntimeExecuteInput & { runtimeKind: RuntimeAdapterKind },
126+ ): Effect.Effect<unknown, RuntimeExecuteError | RuntimeAdapterRegistryError> =>
127+ Effect.gen(function* () {
128+ const adapter = yield* get(input.runtimeKind);
129+ return yield* adapter.execute(input);
130+ });
131+
132+ for (const adapter of initialAdapters) {
133+ if (!adaptersByKind.has(adapter.kind)) {
134+ adaptersByKind.set(adapter.kind, adapter);
135+ }
136+ }
137+
138+ return {
139+ register,
140+ registerAll,
141+ get,
142+ execute,
143+ };
144+};
145+
146+export const RuntimeAdapterRegistryLive = (
147+ initialAdapters: ReadonlyArray<RuntimeAdapter> = [],
148+): Layer.Layer<RuntimeAdapterRegistryService> =>
149+ Layer.succeed(RuntimeAdapterRegistryService, makeRuntimeAdapterRegistry(initialAdapters));
packages/engine/tsconfig.json+13−0
@@ -0,0 +1,13 @@
1+{
2+ "compilerOptions": {
3+ "target": "ES2022",
4+ "module": "ESNext",
5+ "moduleResolution": "Bundler",
6+ "strict": true,
7+ "skipLibCheck": true,
8+ "noEmit": true,
9+ "lib": ["ES2022"],
10+ "types": ["bun-types", "node"]
11+ },
12+ "include": ["src/**/*.ts"]
13+}
packages/runtime-local-inproc/package.json+19−0
@@ -0,0 +1,19 @@
1+{
2+ "name": "@executor-v3/runtime-local-inproc",
3+ "private": true,
4+ "type": "module",
5+ "exports": {
6+ ".": "./src/index.ts"
7+ },
8+ "scripts": {
9+ "typecheck": "bunx tsc --noEmit -p tsconfig.json"
10+ },
11+ "dependencies": {
12+ "@executor-v3/engine": "workspace:*",
13+ "effect": "^3.19.8"
14+ },
15+ "devDependencies": {
16+ "@types/node": "^24.3.1",
17+ "bun-types": "^1.2.22"
18+ }
19+}
packages/runtime-local-inproc/src/index.ts+213−0
@@ -0,0 +1,213 @@
1+import * as Effect from "effect/Effect";
2+import * as Runtime from "effect/Runtime";
3+import * as Exit from "effect/Exit";
4+import * as Cause from "effect/Cause";
5+import * as Option from "effect/Option";
6+
7+import {
8+ RuntimeAdapterError,
9+ type RuntimeAdapter,
10+ type RuntimeToolCallService,
11+} from "@executor-v3/engine";
12+
13+const runtimeKind = "local-inproc";
14+const runCallResultCache = new Map<string, unknown>();
15+
16+export type ExecuteJavaScriptInput = {
17+ runId: string;
18+ code: string;
19+ toolCallService?: RuntimeToolCallService;
20+};
21+
22+const runtimeError = (
23+ operation: string,
24+ message: string,
25+ details: string | null,
26+): RuntimeAdapterError =>
27+ new RuntimeAdapterError({
28+ operation,
29+ runtimeKind,
30+ message,
31+ details,
32+ });
33+
34+const missingToolCallServiceError = (toolPath: string): RuntimeAdapterError =>
35+ runtimeError(
36+ "call_tool",
37+ `No tool call service configured for tool path: ${toolPath}`,
38+ null,
39+ );
40+
41+const isRuntimeAdapterErrorLike = (
42+ cause: unknown,
43+): cause is {
44+ operation: string;
45+ runtimeKind: string;
46+ details: string | null;
47+ _tag?: string;
48+} => {
49+ if (!cause || typeof cause !== "object") {
50+ return false;
51+ }
52+
53+ const record = cause as Record<string, unknown>;
54+ return (record._tag === "RuntimeAdapterError" || record.name === "RuntimeAdapterError")
55+ && typeof record.operation === "string"
56+ && typeof record.runtimeKind === "string"
57+ && (typeof record.details === "string" || record.details === null || record.details === undefined);
58+};
59+
60+const toExecutionError = (cause: unknown): RuntimeAdapterError =>
61+ cause instanceof RuntimeAdapterError
62+ ? cause
63+ : isRuntimeAdapterErrorLike(cause)
64+ ? new RuntimeAdapterError({
65+ operation: cause.operation,
66+ runtimeKind: cause.runtimeKind,
67+ message: cause instanceof Error && cause.message.length > 0
68+ ? cause.message
69+ : "Runtime adapter error",
70+ details: cause.details ?? null,
71+ })
72+ : runtimeError(
73+ "execute",
74+ "JavaScript execution failed",
75+ cause instanceof Error ? cause.stack ?? cause.message : String(cause),
76+ );
77+
78+const normalizeToolInput = (args: unknown): Record<string, unknown> | undefined => {
79+ if (!args || typeof args !== "object" || Array.isArray(args)) {
80+ return undefined;
81+ }
82+
83+ return args as Record<string, unknown>;
84+};
85+
86+const invokeTool = (
87+ runId: string,
88+ callId: string,
89+ toolPath: string,
90+ args: unknown,
91+ toolCallService: RuntimeToolCallService | undefined,
92+): Effect.Effect<unknown, RuntimeAdapterError> =>
93+ Effect.gen(function* () {
94+ if (!toolCallService) {
95+ return yield* missingToolCallServiceError(toolPath);
96+ }
97+
98+ const cacheKey = `${runId}:${callId}`;
99+ if (runCallResultCache.has(cacheKey)) {
100+ return runCallResultCache.get(cacheKey);
101+ }
102+
103+ const result = yield* toolCallService.callTool({
104+ runId,
105+ callId,
106+ toolPath,
107+ input: normalizeToolInput(args),
108+ });
109+
110+ runCallResultCache.set(cacheKey, result);
111+ return result;
112+ });
113+
114+const nextDeterministicCallId = (counter: { value: number }): string => {
115+ const callId = `call_${String(counter.value).padStart(6, "0")}`;
116+ counter.value += 1;
117+ return callId;
118+};
119+
120+const runPromiseWithTypedError = (
121+ runtime: Runtime.Runtime<never>,
122+): (<A, E>(effect: Effect.Effect<A, E>) => Promise<A>) =>
123+ async <A, E>(effect: Effect.Effect<A, E>): Promise<A> => {
124+ const exit = await Runtime.runPromiseExit(runtime)(effect);
125+ if (Exit.isSuccess(exit)) {
126+ return exit.value;
127+ }
128+
129+ const failure = Cause.failureOption(exit.cause);
130+ if (Option.isSome(failure)) {
131+ throw failure.value;
132+ }
133+
134+ throw new Error(Cause.pretty(exit.cause));
135+ };
136+
137+const createToolsProxy = (
138+ runId: string,
139+ runPromise: <A, E>(effect: Effect.Effect<A, E>) => Promise<A>,
140+ toolCallService: RuntimeToolCallService | undefined,
141+ callCounter: { value: number },
142+ path: ReadonlyArray<string> = [],
143+): unknown => {
144+ const callable = () => undefined;
145+
146+ return new Proxy(callable, {
147+ get(_target, prop) {
148+ if (prop === "then") {
149+ return undefined;
150+ }
151+
152+ if (typeof prop !== "string") {
153+ return undefined;
154+ }
155+
156+ return createToolsProxy(runId, runPromise, toolCallService, callCounter, [...path, prop]);
157+ },
158+ apply(_target, _thisArg, args) {
159+ const toolPath = path.join(".");
160+ if (!toolPath) {
161+ throw new Error("Tool path missing in invocation");
162+ }
163+
164+ const toolArgs = args.length > 0 ? args[0] : undefined;
165+ const callId = nextDeterministicCallId(callCounter);
166+ return runPromise(invokeTool(runId, callId, toolPath, toolArgs, toolCallService));
167+ },
168+ });
169+};
170+
171+const runJavaScript = (
172+ code: string,
173+ tools: unknown,
174+): Effect.Effect<unknown, RuntimeAdapterError> =>
175+ Effect.tryPromise({
176+ try: async () => {
177+ const execute = new Function(
178+ "tools",
179+ `"use strict"; return (async () => {\n${code}\n})();`,
180+ ) as (tools: unknown) => Promise<unknown>;
181+
182+ return await execute(tools);
183+ },
184+ catch: toExecutionError,
185+ });
186+
187+export const executeJavaScriptWithTools = (
188+ input: ExecuteJavaScriptInput,
189+): Effect.Effect<unknown, RuntimeAdapterError> =>
190+ Effect.gen(function* () {
191+ const runtime = yield* Effect.runtime<never>();
192+ const runPromise = runPromiseWithTypedError(runtime);
193+ const callCounter = { value: 0 };
194+ const toolsProxy = createToolsProxy(
195+ input.runId,
196+ runPromise,
197+ input.toolCallService,
198+ callCounter,
199+ );
200+
201+ return yield* runJavaScript(input.code, toolsProxy);
202+ });
203+
204+export const makeLocalInProcessRuntimeAdapter = (): RuntimeAdapter => ({
205+ kind: runtimeKind,
206+ isAvailable: () => Effect.succeed(true),
207+ execute: (input) =>
208+ executeJavaScriptWithTools({
209+ runId: input.runId,
210+ code: input.code,
211+ toolCallService: input.toolCallService,
212+ }),
213+});
packages/runtime-local-inproc/tsconfig.json+13−0
@@ -0,0 +1,13 @@
1+{
2+ "compilerOptions": {
3+ "target": "ES2022",
4+ "module": "ESNext",
5+ "moduleResolution": "Bundler",
6+ "strict": true,
7+ "skipLibCheck": true,
8+ "noEmit": true,
9+ "lib": ["ES2022"],
10+ "types": ["bun-types", "node"]
11+ },
12+ "include": ["src/**/*.ts"]
13+}