Skip to content

Add toolsmesh package for language model tools#19

Open
SferaDev wants to merge 1 commit into
mainfrom
claude/add-toolsmesh-package-wqZQM
Open

Add toolsmesh package for language model tools#19
SferaDev wants to merge 1 commit into
mainfrom
claude/add-toolsmesh-package-wqZQM

Conversation

@SferaDev

Copy link
Copy Markdown
Owner

Add a new private package that provides an AI SDK v6 compatible language model wrapper. The package converts tools into a virtual bash-like filesystem that models can explore using standard unix commands (ls, cat, grep, find) and execute TypeScript code against.

Key features:

  • Virtual filesystem that represents tools as TypeScript files
  • mesh_bash tool for exploring tool definitions using bash commands
  • mesh_exec tool for executing TypeScript code that calls tools
  • Type-safe parameter validation using Zod schemas
  • System prompt generation for RAG-optimized tool discovery
  • Middleware integration with Vercel AI SDK v6

This approach reduces context window usage by allowing models to discover tools on-demand rather than loading all schemas upfront.

@changeset-bot

changeset-bot Bot commented Dec 28, 2025

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e1a3f95

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel

vercel Bot commented Dec 28, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openapi-clients Ready Ready Preview, Comment Feb 17, 2026 8:40pm
photocall Ready Ready Preview, Comment Feb 17, 2026 8:40pm
seating Ready Ready Preview, Comment Feb 17, 2026 8:40pm
website Ready Ready Preview, Comment Feb 17, 2026 8:40pm

Request Review

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new private package toolsmesh that provides an AI SDK v6 compatible middleware for converting tools into a virtual bash-like filesystem. This approach reduces context window usage by allowing language models to discover tools on-demand using familiar Unix commands rather than loading all tool schemas upfront.

Key changes:

  • Virtual filesystem implementation that represents tools as TypeScript files with full type information
  • Two mesh tools (mesh_bash and mesh_exec) that replace original tools for exploration and execution
  • System prompt generation with RAG-optimized tool discovery instructions

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
pnpm-workspace.yaml Adds zod (3.25.76) and zod-to-json-schema (3.24.6) to the catalog
pnpm-lock.yaml Updates lockfile with new dependencies for toolsmesh package and related transitive dependencies
packages/toolsmesh/package.json New private package configuration with AI SDK and Zod dependencies
packages/toolsmesh/tsconfig.json TypeScript configuration extending react-library with ESNext module settings
packages/toolsmesh/vitest.config.ts Vitest configuration for Node.js test environment
packages/toolsmesh/src/types.ts Core type definitions for tool registry, virtual filesystem, and mesh tools
packages/toolsmesh/src/filesystem.ts Virtual filesystem implementation with tool-to-TypeScript conversion and file operations
packages/toolsmesh/src/sandbox-tools.ts Bash command emulation (ls, cat, grep, etc.) and TypeScript code execution sandbox
packages/toolsmesh/src/prompt.ts System prompt generation for guiding AI models in tool discovery
packages/toolsmesh/src/middleware.ts AI SDK v6 middleware integration and tool extraction utilities
packages/toolsmesh/src/index.ts Public API exports for the package
packages/toolsmesh/src/toolsmesh.test.ts Comprehensive test suite covering filesystem, bash commands, and tool execution
packages/toolsmesh/README.md Package overview with quick start guide and feature highlights
content/docs/packages/toolsmesh.mdx Detailed documentation including API reference, configuration, and security considerations
content/docs/index.mdx Adds toolsmesh card to documentation homepage
content/docs.json Adds toolsmesh to packages navigation
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/toolsmesh/src/types.ts Outdated
Comment thread packages/toolsmesh/src/sandbox-tools.ts Outdated
Comment thread packages/toolsmesh/src/sandbox-tools.ts Outdated
Comment on lines +279 to +280
const nonFlags = args.filter((a) => !a.startsWith("-") || (a.startsWith("-") && a.length > 2));

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The argument parsing logic !a.startsWith("-") || (a.startsWith("-") && a.length > 2) is confusing and potentially buggy. This condition would treat -ab as a non-flag argument but -a as a flag. This logic seems intended to allow patterns that start with -, but the condition a.length > 2 is arbitrary. Consider using a more explicit approach, such as checking for a -- separator or requiring patterns to be quoted.

Suggested change
const nonFlags = args.filter((a) => !a.startsWith("-") || (a.startsWith("-") && a.length > 2));
// Parse non-flag arguments (pattern and paths). Support `--` to terminate options,
// and treat leading `-` arguments as options while in options mode.
const nonFlags: string[] = [];
let inOptions = true;
for (const a of args) {
if (inOptions) {
if (a === "--") {
inOptions = false;
continue;
}
if (a.startsWith("-") && a.length > 1) {
// Option argument, skip adding to nonFlags.
continue;
}
// First non-option ends options mode.
inOptions = false;
}
nonFlags.push(a);
}

Copilot uses AI. Check for mistakes.
`;

// Create function with tools and console in scope
const fn = new Function("console", ...Object.keys(toolFunctions), wrappedCode);

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using new Function() to execute arbitrary code poses significant security risks. This allows execution of any JavaScript code, including access to the global scope, closures, and potentially Node.js APIs. The comment on line 502 mentions using Vercel Sandbox in production, but this implementation is still shipped in the package.

Consider either:

  1. Removing this implementation entirely and requiring users to provide their own secure execution environment
  2. Adding prominent security warnings in the documentation and function description
  3. Implementing stricter sandboxing such as using vm2, isolated-vm, or requiring Vercel Sandbox as a peer dependency

The current implementation creates a false sense of security with limited isolation.

Copilot uses AI. Check for mistakes.
Comment thread packages/toolsmesh/src/filesystem.ts Outdated
target: "jsonSchema7",
}) as Record<string, unknown>;

const pascalName = name.charAt(0).toUpperCase() + name.slice(1);

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pascal case conversion is too simplistic and doesn't handle camelCase or snake_case tool names properly. For example:

  • "createUser" would become "CreateUserParams" (correct)
  • "create_user" would become "Create_userParams" (incorrect, should be "CreateUserParams")
  • "API_fetchData" would become "API_fetchDataParams" (incorrect)

Consider using a more robust naming conversion function that handles different naming conventions consistently.

Copilot uses AI. Check for mistakes.
Comment thread packages/toolsmesh/src/filesystem.ts Outdated
Comment on lines +320 to +323
for (let i = 0; i < lines.length; i++) {
if (regex.test(lines[i])) {
matches.push(`${i + 1}: ${lines[i]}`);
regex.lastIndex = 0; // Reset regex state

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a global regex with test() in a loop can cause issues due to the stateful lastIndex property. Although line 323 resets lastIndex, creating a new regex instance for each line would be more robust and prevent potential bugs if the regex flags are changed. Consider creating the regex without the 'g' flag and using match() or includes() instead, or create a new regex instance for each line.

Copilot uses AI. Check for mistakes.
Comment thread packages/toolsmesh/src/sandbox-tools.ts Outdated
Comment thread packages/toolsmesh/src/sandbox-tools.ts Outdated
Comment on lines +50 to +78
function parseCommand(command: string): string[] {
const parts: string[] = [];
let current = "";
let inQuote = false;
let quoteChar = "";

for (const char of command) {
if ((char === '"' || char === "'") && !inQuote) {
inQuote = true;
quoteChar = char;
} else if (char === quoteChar && inQuote) {
inQuote = false;
quoteChar = "";
} else if (char === " " && !inQuote) {
if (current) {
parts.push(current);
current = "";
}
} else {
current += char;
}
}

if (current) {
parts.push(current);
}

return parts;
}

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The quote parser doesn't handle escaped quotes within strings. For example, the command echo "He said \"hello\"" would be incorrectly parsed because the escaped quote would still toggle the inQuote state. Consider handling escape sequences with backslashes.

Copilot uses AI. Check for mistakes.
Comment thread packages/toolsmesh/src/sandbox-tools.ts Outdated
Comment thread content/docs/packages/toolsmesh.mdx Outdated
// // ... parameters
// });
```

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The security considerations section states that mesh_exec has "No access to Node.js APIs or the real filesystem", but the current implementation actually evaluates the code string via new Function in the host runtime, which still has access to global objects (e.g., globalThis, dynamic import, and Node.js APIs in a Node environment). This mismatch can lead developers to run untrusted AI-generated code under the false assumption that it is safely sandboxed, exposing them to arbitrary code execution, secret exfiltration, and filesystem access. Update this documentation to clearly state that mesh_exec is not a secure sandbox on its own and that untrusted code must be executed only in a separate, hardened sandboxed runtime (or after a proper sandbox implementation is provided).

Suggested change
## Security considerations
The `mesh_exec` functionality is **not** a secure sandbox. The current implementation
evaluates the provided `code` string using `new Function` in the host JavaScript
runtime, which means it can access `globalThis`, dynamic `import`, and (in a Node.js
environment) Node.js APIs and the real filesystem, subject to the permissions of the
hosting process.
You **must not** treat `mesh_exec` as providing a security boundary. Do **not** run
untrusted or partially trusted AI-generated code directly with `mesh_exec`. If you need
to execute untrusted code, run it only inside a separate, hardened sandboxed runtime
(for example, a dedicated process, container, or other restricted environment), or
after integrating a proper sandbox implementation that enforces the desired isolation.

Copilot uses AI. Check for mistakes.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 17 changed files in this pull request and generated 10 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/toolsmesh/src/sandbox-tools.ts Outdated
Comment on lines +86 to +114
function parseCommand(command: string): string[] {
const parts: string[] = [];
let current = "";
let inQuote = false;
let quoteChar = "";

for (const char of command) {
if ((char === '"' || char === "'") && !inQuote) {
inQuote = true;
quoteChar = char;
} else if (char === quoteChar && inQuote) {
inQuote = false;
quoteChar = "";
} else if (char === " " && !inQuote) {
if (current) {
parts.push(current);
current = "";
}
} else {
current += char;
}
}

if (current) {
parts.push(current);
}

return parts;
}

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parseCommand function doesn't handle escaped quotes within quoted strings. For example, cat "file with \"quotes\".ts" would not be parsed correctly. Consider handling escape sequences with backslashes to allow quotes within quoted strings.

Copilot uses AI. Check for mistakes.
Comment thread packages/toolsmesh/src/sandbox-tools.ts Outdated
const searchPath = paths.length > 0 ? resolvePath(paths[0], cwd) : cwd;

try {
const results = grepFiles(fs, pattern, recursive ? searchPath : undefined);

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The grep command does not handle non-recursive search properly. When the recursive flag is not set, grepFiles is called with undefined as the search path, which means it will search all files. For non-recursive grep, it should search only files specified in paths, or if no specific file is given, only the current directory. Consider passing the search path or implementing file-specific grep logic.

Suggested change
const results = grepFiles(fs, pattern, recursive ? searchPath : undefined);
const results = grepFiles(fs, pattern, searchPath);

Copilot uses AI. Check for mistakes.
Comment thread packages/toolsmesh/src/sandbox-tools.ts Outdated
Comment on lines +440 to +449
for (let i = 0; i < files.length; i++) {
const file = files[i];
const relativePath = file.path.substring(basePath.length);
if (!relativePath) continue;

const parts = relativePath.split("/").filter(Boolean);
const indent = "│ ".repeat(parts.length - 1);
const isLast =
i === files.length - 1 ||
!files[i + 1]?.path.startsWith(file.path.substring(0, file.path.lastIndexOf("/")));

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tree command's logic for determining if a file is the last one in its directory is incorrect. The condition checks if the next file's path starts with the current file's parent directory, but this doesn't properly identify the last item in a directory when there are nested subdirectories. This could result in incorrect tree visualization with wrong branch characters (├── vs └──). Consider tracking the last item per directory level instead.

Suggested change
for (let i = 0; i < files.length; i++) {
const file = files[i];
const relativePath = file.path.substring(basePath.length);
if (!relativePath) continue;
const parts = relativePath.split("/").filter(Boolean);
const indent = "│ ".repeat(parts.length - 1);
const isLast =
i === files.length - 1 ||
!files[i + 1]?.path.startsWith(file.path.substring(0, file.path.lastIndexOf("/")));
// Build a map from parent directory path to its direct children (full paths)
const childrenMap = new Map<string, string[]>();
for (const file of files) {
const relativePath = file.path.substring(basePath.length);
if (!relativePath) continue;
const parts = relativePath.split("/").filter(Boolean);
if (parts.length === 0) continue;
const parentParts = parts.slice(0, -1);
const parentPath =
parentParts.length === 0
? basePath
: basePath.replace(/\/?$/, "/") + parentParts.join("/") + "/";
const siblings = childrenMap.get(parentPath);
if (siblings) {
siblings.push(file.path);
} else {
childrenMap.set(parentPath, [file.path]);
}
}
for (const file of files) {
const relativePath = file.path.substring(basePath.length);
if (!relativePath) continue;
const parts = relativePath.split("/").filter(Boolean);
if (parts.length === 0) continue;
// Compute indentation based on whether each ancestor is the last in its directory
let indent = "";
for (let depth = 0; depth < parts.length - 1; depth++) {
const ancestorName = parts[depth];
const ancestorParentParts = parts.slice(0, depth);
const ancestorParentPath =
ancestorParentParts.length === 0
? basePath
: basePath.replace(/\/?$/, "/") + ancestorParentParts.join("/") + "/";
const ancestorFullPath = ancestorParentPath + ancestorName + "/";
const ancestorSiblings = childrenMap.get(ancestorParentPath) || [];
const isAncestorLast =
ancestorSiblings.length > 0 &&
ancestorSiblings[ancestorSiblings.length - 1] === ancestorFullPath;
indent += isAncestorLast ? " " : "│ ";
}
// Determine if this file is the last among its siblings
const parentParts = parts.slice(0, -1);
const parentPath =
parentParts.length === 0
? basePath
: basePath.replace(/\/?$/, "/") + parentParts.join("/") + "/";
const siblings = childrenMap.get(parentPath) || [];
const isLast =
siblings.length > 0 && siblings[siblings.length - 1] === file.path;

Copilot uses AI. Check for mistakes.
Comment thread packages/toolsmesh/src/filesystem.ts Outdated
Comment on lines +13 to +68
function jsonSchemaToTs(schema: Record<string, unknown>, indent = 0): string {
const pad = " ".repeat(indent);

if (schema.type === "string") {
if (schema.enum) {
return (schema.enum as string[]).map((v) => `"${v}"`).join(" | ");
}
return "string";
}
if (schema.type === "number" || schema.type === "integer") {
return "number";
}
if (schema.type === "boolean") {
return "boolean";
}
if (schema.type === "null") {
return "null";
}
if (schema.type === "array") {
const items = schema.items as Record<string, unknown> | undefined;
if (items) {
return `Array<${jsonSchemaToTs(items, indent)}>`;
}
return "unknown[]";
}
if (schema.type === "object" || schema.properties) {
const properties = schema.properties as Record<string, Record<string, unknown>> | undefined;
if (!properties || Object.keys(properties).length === 0) {
return "Record<string, unknown>";
}
const required = (schema.required as string[]) ?? [];
const lines = Object.entries(properties).map(([key, prop]) => {
const isOptional = !required.includes(key);
const desc = prop.description ? ` // ${prop.description}` : "";
return `${pad} ${key}${isOptional ? "?" : ""}: ${jsonSchemaToTs(prop, indent + 1)};${desc}`;
});
return `{\n${lines.join("\n")}\n${pad}}`;
}
if (schema.anyOf) {
return (schema.anyOf as Record<string, unknown>[])
.map((s) => jsonSchemaToTs(s, indent))
.join(" | ");
}
if (schema.oneOf) {
return (schema.oneOf as Record<string, unknown>[])
.map((s) => jsonSchemaToTs(s, indent))
.join(" | ");
}
if (schema.allOf) {
return (schema.allOf as Record<string, unknown>[])
.map((s) => jsonSchemaToTs(s, indent))
.join(" & ");
}

return "unknown";
}

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The jsonSchemaToTs function doesn't handle several JSON Schema constructs that could be present in Zod schemas: const, default, format, pattern, minLength, maxLength, minimum, maximum, nullable types, and tuples. This could lead to incomplete or inaccurate TypeScript type generation. Consider adding support for these common schema features or documenting the limitations.

Copilot uses AI. Check for mistakes.
Comment thread packages/toolsmesh/src/middleware.ts Outdated
Comment on lines +60 to +64
// Store tool executors for handling calls
const meshToolExecutors = new Map<string, (params: unknown) => Promise<string>>();
meshToolExecutors.set(meshBash.name, (params) => meshBash.execute(params));
meshToolExecutors.set(meshExec.name, (params) => meshExec.execute(params));

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The meshToolExecutors map is created but never used in the middleware. Lines 61-63 store the tool executors, but the wrapGenerate and wrapStream methods don't use them. The comments on lines 107-108 and 114 suggest tool execution is handled by the application layer, but then this variable serves no purpose and should be removed to avoid confusion.

Suggested change
// Store tool executors for handling calls
const meshToolExecutors = new Map<string, (params: unknown) => Promise<string>>();
meshToolExecutors.set(meshBash.name, (params) => meshBash.execute(params));
meshToolExecutors.set(meshExec.name, (params) => meshExec.execute(params));

Copilot uses AI. Check for mistakes.
Comment thread packages/toolsmesh/src/compaction.ts Outdated
Comment on lines +240 to +249
/**
* Write a tool result to the virtual filesystem.
*/
function writeResultToFilesystem(
filesystem: VirtualFilesystem,
sessionId: string,
toolName: string,
toolCallId: string,
result: unknown,
): string {

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The writeResultToFilesystem function mutates the filesystem parameter directly by adding files to it. This side effect is not clearly documented in the function's JSDoc comment. Since the filesystem is passed in from outside and modified, this could lead to unexpected behavior if the caller is not aware of this mutation. Consider documenting this side effect explicitly in the function's documentation.

Copilot uses AI. Check for mistakes.
Comment thread packages/toolsmesh/src/sandbox-tools.ts Outdated
Comment on lines +31 to +41
let vercelSandbox: VercelSandbox | null = null;

/**
* Try to load @vercel/sandbox if available.
*/
async function getVercelSandbox(): Promise<VercelSandbox | null> {
if (vercelSandbox !== null) return vercelSandbox;
try {
const module = await import("@vercel/sandbox");
vercelSandbox = module as unknown as VercelSandbox;
return vercelSandbox;

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The module-level vercelSandbox cache persists across different tool instances and could lead to issues if different configurations are needed or if the module needs to be reloaded. Additionally, in serverless environments, this cache might persist across requests unexpectedly. Consider making the cache instance-specific or documenting this behavior.

Suggested change
let vercelSandbox: VercelSandbox | null = null;
/**
* Try to load @vercel/sandbox if available.
*/
async function getVercelSandbox(): Promise<VercelSandbox | null> {
if (vercelSandbox !== null) return vercelSandbox;
try {
const module = await import("@vercel/sandbox");
vercelSandbox = module as unknown as VercelSandbox;
return vercelSandbox;
/**
* Try to load @vercel/sandbox if available.
*/
async function getVercelSandbox(): Promise<VercelSandbox | null> {
try {
const module = await import("@vercel/sandbox");
return module as unknown as VercelSandbox;

Copilot uses AI. Check for mistakes.
Comment thread packages/toolsmesh/src/sandbox-tools.ts Outdated
Comment on lines +602 to +611
const toolDefinitions = Object.entries(tools)
.map(([name]) => {
return `
const ${name} = async (params) => {
// Tool: ${name}
// This would call the actual tool in production
return { __toolCall: "${name}", params };
};`;
})
.join("\n");

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Vercel sandbox implementation doesn't actually execute the tools - it returns mock objects with __toolCall instead. This means the tool execution in the sandbox is not functional. The comment on line 607 says "This would call the actual tool in production", but this suggests the implementation is incomplete. Either the actual tool execution should be implemented, or this limitation should be clearly documented in the function's JSDoc comment and the main documentation.

Copilot uses AI. Check for mistakes.
Comment thread packages/toolsmesh/src/middleware.ts Outdated
Comment on lines +174 to +178
registry[name] = {
description: tool.description ?? `Tool: ${name}`,
parameters: tool.parameters as ToolRegistry[string]["parameters"],
execute: tool.execute as ToolRegistry[string]["execute"],
};

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The extractTools function performs unsafe type assertions on lines 176-177 without validating that parameters is actually a Zod schema or that execute matches the expected signature. If the input doesn't conform to the expected format, this could lead to runtime errors when the tools are used. Consider adding runtime validation using Zod or at least documenting the strict input requirements.

Copilot uses AI. Check for mistakes.
Comment thread packages/toolsmesh/src/compaction.ts Outdated
}

result.compactedCount++;
result.bytesSaved += resultStr.length;

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bytesSaved calculation on line 185 only accounts for the size of the original result but doesn't subtract the size of the replacement text (file reference or placeholder). The actual bytes saved would be resultStr.length - replacementLength. This makes the reported savings metric inaccurate and potentially misleading.

Copilot uses AI. Check for mistakes.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/toolsmesh/src/types.ts Outdated
Comment on lines +62 to +68
export type SandboxConfig = {
/**
* Use @vercel/sandbox for true isolation.
* Requires @vercel/sandbox to be installed.
* @default true when @vercel/sandbox is available
*/
useVercelSandbox?: boolean;

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SandboxConfig type includes a useVercelSandbox option that defaults to true when @vercel/sandbox is available. However, the actual implementation in sandbox-tools.ts never checks this option or attempts to use @vercel/sandbox. This configuration option is misleading and should either be removed or the implementation should be updated to actually use it.

Copilot uses AI. Check for mistakes.
Comment on lines +50 to +58
"peerDependencies": {
"@vercel/sandbox": ">=0.1.0",
"ai": ">=6.0.0"
},
"peerDependenciesMeta": {
"@vercel/sandbox": {
"optional": true
}
}

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The package.json declares @vercel/sandbox as an optional peer dependency, and the documentation extensively describes how to use it. However, the actual implementation never imports or uses @vercel/sandbox. This peer dependency declaration is misleading.

Either:

  1. Implement actual @vercel/sandbox integration in the code
  2. Remove this peer dependency declaration and update the documentation

Given that the PR description mentions "Middleware integration with Vercel AI SDK v6" and discusses sandbox execution, it appears the implementation is incomplete.

Copilot uses AI. Check for mistakes.
Comment on lines +108 to +130
async function executeInSandbox(
code: string,
tools: ToolRegistry,
_fs: VirtualFilesystem,
config?: SandboxConfig,
): Promise<string> {
const allowLocal = config?.dangerouslyAllowLocalExecution === true;

// Use local execution when explicitly allowed
if (allowLocal) {
return executeLocally(code, tools);
}

// Return helpful error message
return `[error] Code execution requires sandbox configuration.

To enable execution, use one of:
1. Install @vercel/sandbox for production use
2. Enable unsafe local execution for development:
sandbox: { dangerouslyAllowLocalExecution: true }

WARNING: Local execution has access to globalThis and is not secure for untrusted code.`;
}

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation claims that @vercel/sandbox is used for secure code execution, but this function never attempts to use it. The function only checks for dangerouslyAllowLocalExecution and returns an error message otherwise. This means that even when @vercel/sandbox is installed, it won't be used.

To fix this issue, the function should:

  1. Try to dynamically import @vercel/sandbox when useVercelSandbox is true or when @vercel/sandbox is available
  2. Fall back to the error message only if @vercel/sandbox is not available AND dangerouslyAllowLocalExecution is false

This is a critical discrepancy between the documented behavior and the actual implementation.

Copilot uses AI. Check for mistakes.
Comment thread content/docs/packages/toolsmesh.mdx Outdated
Comment on lines +441 to +488
Toolsmesh uses [@vercel/sandbox](https://vercel.com/docs/vercel-sandbox) for secure code execution in isolated Linux VMs. When `@vercel/sandbox` is installed, all AI-generated code runs in true isolation.

### Production Setup (Recommended)

```bash
npm install @vercel/sandbox
```

```typescript
const middleware = createToolsmeshMiddleware({
tools: myTools,
// Vercel sandbox is used automatically when available
});
```

### Local Development

For local development without Vercel sandbox, you can enable unsafe local execution:

```typescript
const middleware = createToolsmeshMiddleware({
tools: myTools,
sandbox: {
// WARNING: Only use for local development with trusted models
dangerouslyAllowLocalExecution: true,
},
});
```

### Sandbox Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `useVercelSandbox` | `boolean` | `true` | Use @vercel/sandbox when available |
| `dangerouslyAllowLocalExecution` | `boolean` | `false` | Allow local execution without sandbox |
| `timeout` | `number` | `30000` | Execution timeout in milliseconds |

### Security Model

When using Vercel sandbox:
- Code runs in isolated Firecracker MicroVMs
- No access to host filesystem or network (unless configured)
- Automatic cleanup after execution

When using local execution (`dangerouslyAllowLocalExecution: true`):
- **Not secure** - code has access to `globalThis`
- Only use with trusted AI models in development
- Tool parameters are still validated against Zod schemas

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation in the mdx file states that "Toolsmesh uses @vercel/sandbox for secure code execution in isolated Linux VMs. When @vercel/sandbox is installed, all AI-generated code runs in true isolation." However, the actual implementation in sandbox-tools.ts does not use @vercel/sandbox at all. The code only supports dangerouslyAllowLocalExecution.

This creates a false expectation for users who install @vercel/sandbox expecting their code to run in isolation. The documentation should be updated to reflect the actual implementation, or the implementation should be updated to actually use @vercel/sandbox when available.

Copilot uses AI. Check for mistakes.
Converts tools into a discoverable virtual filesystem that AI models
explore using bash commands (via just-bash) and execute via TypeScript.
Reduces context usage by letting models discover tools on-demand instead
of loading all schemas upfront.

- Virtual filesystem with per-tool .ts files, index, and README
- mesh_bash tool powered by just-bash for ls/cat/grep/find/etc
- mesh_exec tool for TypeScript execution with dangerouslyAllowLocalExecution
- AI SDK v6 middleware integration via wrapLanguageModel
- Compaction utilities for long-running conversations
- Zod v4 native JSON Schema conversion (z.toJSONSchema)
- 50 tests covering all functionality
@pkg-pr-new

pkg-pr-new Bot commented Feb 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

ai-gateway-proxy

npm i https://pkg.pr.new/ai-gateway-proxy@19

cloudflare-api-js

npm i https://pkg.pr.new/cloudflare-api-js@19

keycloak-api

npm i https://pkg.pr.new/keycloak-api@19

litellm-api

npm i https://pkg.pr.new/litellm-api@19

netlify-api

npm i https://pkg.pr.new/netlify-api@19

nuki-api-js

npm i https://pkg.pr.new/nuki-api-js@19

rollup-plugin-import-cdn

npm i https://pkg.pr.new/rollup-plugin-import-cdn@19

v0-api

npm i https://pkg.pr.new/v0-api@19

vercel-api-js

npm i https://pkg.pr.new/vercel-api-js@19

zoom-api-js

npm i https://pkg.pr.new/zoom-api-js@19

commit: e1a3f95

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants