Conversation
|
🧪 Testing To try out this version of the SDK: Expires at: Fri, 08 May 2026 03:31:30 GMT |
|
Canary encountered an internal error while analyzing this PR. |
6972295 to
d19a5b9
Compare
Confidence Score: 5/5 - Safe to Merge
|
d19a5b9 to
76a7033
Compare
Confidence Score: 5/5 - Safe to Merge
|
76a7033 to
abd5f3a
Compare
Confidence Score: 5/5 - Safe to Merge
|
abd5f3a to
651d17f
Compare
Confidence Score: 5/5 - Safe to Merge
|
651d17f to
fc292db
Compare
Confidence Score: 5/5 - Safe to Merge
|
fc292db to
08cc52a
Compare
Confidence Score: 5/5 - Safe to Merge
|
Release version edited manuallyThe Pull Request version has been manually set to If you instead want to use the version number |
08cc52a to
15abf4d
Compare
Confidence Score: 5/5 - Safe to Merge
|
15abf4d to
2dbef30
Compare
Confidence Score: 5/5 - Safe to Merge
|
2dbef30 to
b86942f
Compare
Confidence Score: 5/5 - Safe to Merge
|
b86942f to
dd246c5
Compare
Confidence Score: 5/5 - Safe to Merge
|
0b83ccb to
d4e4451
Compare
Confidence Score: 5/5 - Safe to Merge
|
Confidence Score: 5/5 - Safe to Merge
|
d4e4451 to
a7b2b11
Compare
e3e797a to
e90547c
Compare
| import { Hyperspell, ClientOptions } from 'hyperspell'; | ||
|
|
||
| async function tseval(code: string) { |
There was a problem hiding this comment.
Correctness: Buffer is not a global in Deno environments; the file uses node:path and node:util prefixed imports suggesting Deno execution, so Buffer.from(...) will throw ReferenceError: Buffer is not defined unless explicitly imported from node:buffer.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In packages/mcp-server/src/code-tool-worker.ts, lines 8-10, the `tseval` function uses `Buffer.from()` but `Buffer` is not a global in Deno environments. Add `import { Buffer } from 'node:buffer';` at the top of the file alongside the other `node:` prefixed imports (`node:path`, `node:util`) to ensure `Buffer` is explicitly available.
| const log_lines: string[] = []; | ||
| const err_lines: string[] = []; | ||
| const console = { | ||
| const originalConsole = globalThis.console; | ||
| globalThis.console = { | ||
| ...originalConsole, | ||
| log: (...args: unknown[]) => { | ||
| log_lines.push(util.format(...args)); | ||
| }, |
There was a problem hiding this comment.
Correctness: Mutating globalThis.console is not safe if two requests execute concurrently: the second request overwrites the first's interception, causing log lines to route to the wrong request's arrays, and the finally restore in one request undoes the other's setup mid-execution.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In file `packages/mcp-server/src/code-tool-worker.ts`, lines 271-278 (and the corresponding finally block), the code mutates `globalThis.console` which is shared across concurrent request handlers. Two simultaneous fetch calls will stomp on each other's console override, routing log output to the wrong request's `log_lines`/`err_lines` arrays, and the `finally` restore from one request will undo the other's setup. Fix this by avoiding global mutation: instead of replacing `globalThis.console`, pass custom log/error functions as part of the execution context (e.g., inject them via a context object or closure into the tseval'd code), or ensure the worker is strictly single-threaded and document that assumption, or use an AsyncLocalStorage-based approach to track per-request console state without global mutation.
Confidence Score: 1/5 - Blocking IssuesNot safe to merge — Key Findings:
Files requiring special attention
|
| const log_lines: string[] = []; | ||
| const err_lines: string[] = []; | ||
| const console = { | ||
| const originalConsole = globalThis.console; | ||
| globalThis.console = { | ||
| ...originalConsole, | ||
| log: (...args: unknown[]) => { | ||
| log_lines.push(util.format(...args)); | ||
| }, |
There was a problem hiding this comment.
Correctness: 🐛 Mutating globalThis.console is shared state across all concurrent requests: if two fetch calls overlap, request B will overwrite request A's interceptor, and when A's finally block runs globalThis.console = originalConsole it will silently discard B's interceptor mid-flight, causing B's console.log/console.error calls to go uncaptured (or to the real console).
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In packages/mcp-server/src/code-tool-worker.ts at lines 271-278, the diff replaces a local `const console` with a mutation of `globalThis.console`. Because `fetch` is an async handler that can be invoked concurrently, this creates a race condition: concurrent requests share the same `globalThis.console` reference, so one request can stomp another's interceptor, and the `finally` restore can remove a still-active interceptor from a concurrent request. Fix this by passing a custom console object directly to the user code instead of monkey-patching the global. For example, thread a `{ log, error }` console shim as a second argument to `run_`, or inject it via the proxy, so each request operates on its own isolated capture object with no global mutation.
Confidence Score: 1/5 - Blocking IssuesNot safe to merge — this PR introduces multiple unresolved correctness bugs in Key Findings:
Files requiring special attention
|
| import { Hyperspell, ClientOptions } from 'hyperspell'; | ||
|
|
||
| async function tseval(code: string) { |
There was a problem hiding this comment.
Correctness: Buffer is a Node.js global; the rest of the file (e.g. parseError's stack comment "Deno uses V8") and the data:application/typescript import syntax strongly indicate this runs on Deno, where Buffer is not a guaranteed global and will throw ReferenceError: Buffer is not defined at runtime.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In packages/mcp-server/src/code-tool-worker.ts, lines 8-10, the tseval function uses `Buffer.from(code).toString('base64')` to base64-encode the TypeScript source string. This file runs in a Deno environment (evidenced by the 'Deno uses V8' comment in parseError and the use of data:application/typescript imports which are Deno-specific). `Buffer` is a Node.js global and is not reliably available in Deno without explicit import, causing a ReferenceError at runtime. Replace `Buffer.from(code).toString('base64')` with a Deno/browser-compatible alternative such as `btoa(unescape(encodeURIComponent(code)))` to handle arbitrary Unicode input correctly.
| const log_lines: string[] = []; | ||
| const err_lines: string[] = []; | ||
| const console = { | ||
| const originalConsole = globalThis.console; | ||
| globalThis.console = { | ||
| ...originalConsole, | ||
| log: (...args: unknown[]) => { | ||
| log_lines.push(util.format(...args)); | ||
| }, |
There was a problem hiding this comment.
Correctness: Mutating globalThis.console in an async handler creates a race condition: if two requests overlap, one request's finally block will restore the console to the second request's overridden version (not the true original), causing log/error lines to leak between requests or be lost entirely.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In packages/mcp-server/src/code-tool-worker.ts, lines 271-278, the diff introduces a race condition by saving and restoring `globalThis.console` in an async request handler. When two requests overlap, the second request's `finally` block may restore `globalThis.console` to the first request's overridden console rather than the true original. Fix this by avoiding mutation of `globalThis.console` entirely — instead, pass a custom console object (with overridden `log` and `error`) directly to the evaluated code, or use AsyncLocalStorage to isolate per-request console state.
Confidence Score: 1/5 - Blocking IssuesNot safe to merge — Key Findings:
Files requiring special attention
|
e90547c to
edc9a15
Compare
| const log_lines: string[] = []; | ||
| const err_lines: string[] = []; | ||
| const console = { | ||
| const originalConsole = globalThis.console; | ||
| globalThis.console = { | ||
| ...originalConsole, | ||
| log: (...args: unknown[]) => { | ||
| log_lines.push(util.format(...args)); | ||
| }, |
There was a problem hiding this comment.
Correctness: Mutating globalThis.console is not safe under concurrent requests: if two requests are in-flight simultaneously (e.g., both reach await run_(...)), the second request overwrites the console set by the first, causing log lines to be captured into the wrong request's log_lines/err_lines arrays and potentially leaving globalThis.console in a corrupted state after finally restores only the version saved by the first request.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In packages/mcp-server/src/code-tool-worker.ts around lines 271-278, the diff replaces a local `const console` shadow with a mutation of `globalThis.console`. This introduces a race condition: under concurrent async requests, two handlers can interleave at await points, causing each to stomp the other's console override and mix up captured log/error lines. Fix by avoiding global mutation entirely — for example, pass a custom console object directly into the evaluated code's scope rather than monkey-patching `globalThis.console`, or use AsyncLocalStorage to isolate per-request console state.
| import { Hyperspell, ClientOptions } from 'hyperspell'; | ||
|
|
||
| async function tseval(code: string) { | ||
| return import('data:application/typescript;charset=utf-8;base64,' + Buffer.from(code).toString('base64')); |
There was a problem hiding this comment.
Correctness: Buffer is a Node.js global and is not natively available in Deno unless Node.js compatibility mode is enabled; if this worker runs in a standard Deno environment, Buffer.from(code).toString('base64') will throw ReferenceError: Buffer is not defined. Use the Web-standard btoa(encodeURIComponent(code).replace(/%([0-9A-F]{2})/g, (_, p1) => String.fromCharCode(parseInt(p1, 16)))) or explicitly import Buffer from node:buffer.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In `packages/mcp-server/src/code-tool-worker.ts`, line 11, the `tseval` function uses the global `Buffer` which is Node.js-specific and unavailable in Deno without Node.js compat mode. Replace the global `Buffer` usage with an explicit import from `node:buffer`: add `const { Buffer: NodeBuffer } = await import('node:buffer');` before the return statement and replace `Buffer.from(code).toString('base64')` with `NodeBuffer.from(code).toString('base64')`. Alternatively, if the environment is guaranteed to have Node.js compat, add a top-level import `import { Buffer } from 'node:buffer';` at the top of the file.
Confidence Score: 1/5 - Blocking IssuesNot safe to merge — Key Findings:
Files requiring special attention
|
edc9a15 to
f29ed6a
Compare
| import { Hyperspell, ClientOptions } from 'hyperspell'; | ||
|
|
||
| async function tseval(code: string) { |
There was a problem hiding this comment.
Correctness: The code parameter originates directly from user-supplied request body (req.json()) and is executed without any sandboxing — an attacker who can reach this endpoint can run arbitrary code in the worker process with full access to the environment, credentials, and network.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In packages/mcp-server/src/code-tool-worker.ts, lines 8-10, the `tseval` function dynamically imports user-supplied code via a data URL. The `code` argument originates from the raw request body with no sandboxing. Evaluate whether the endpoint that calls this worker is protected by authentication/authorization, and consider whether the execution environment (Deno/Cloudflare Worker) provides any meaningful isolation. At minimum, document the trust boundary explicitly and ensure no unauthenticated path can reach this handler.
| const log_lines: string[] = []; | ||
| const err_lines: string[] = []; |
There was a problem hiding this comment.
Correctness: Mutating globalThis.console is shared state — concurrent async requests will interleave, causing one request's patched console to be overwritten by another's mid-execution, so log_lines/err_lines will capture output from the wrong request.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In packages/mcp-server/src/code-tool-worker.ts, lines 271-278 (the diff adding `globalThis.console = {...}`), there is a race condition: `globalThis.console` is a single shared global. When two async `fetch` requests run concurrently and both reach the `await run_(...)` call, each request will overwrite the other's patched console, so logs will be captured in the wrong request's `log_lines`/`err_lines` arrays. Fix this by not mutating `globalThis.console` at all — instead, pass a custom console object directly into `run_` (e.g. as a second argument), or use AsyncLocalStorage to scope the console interception per-request, ensuring concurrent requests do not interfere with each other.
| @@ -1,5 +1,6 @@ | |||
| // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. | |||
|
|
|||
| import fs from 'fs/promises'; | |||
There was a problem hiding this comment.
Correctness: A static top-level import fs from 'fs/promises' will cause module initialization to fail in non-Node.js runtimes (Deno, Cloudflare Workers, edge environments), whereas code-tool.ts deliberately uses a dynamic await import('node:fs') to avoid this exact problem. This will silently break any environment that doesn't support fs/promises at module load time.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In packages/mcp-server/src/instructions.ts, line 3, remove the static top-level `import fs from 'fs/promises';` and instead use a dynamic import inside `fetchLatestInstructionsFromFile` (around line 54): replace `return await fs.readFile(path, 'utf-8');` with `const fs = await import('fs/promises'); return await fs.readFile(path, 'utf-8');`. This matches the pattern in code-tool.ts which uses `await import('node:fs')` to avoid breaking non-Node.js environments at module load time.
Confidence Score: 1/5 - Blocking IssuesNot safe to merge — Key Findings:
Files requiring special attention
|
f29ed6a to
fce5750
Compare
| import { Hyperspell, ClientOptions } from 'hyperspell'; | ||
|
|
||
| async function tseval(code: string) { |
There was a problem hiding this comment.
Correctness: The code argument passed to tseval originates directly from the request body (req.json()) with no sandboxing beyond the proxy — a malicious caller can execute arbitrary code in the worker process by crafting the code field of the request payload.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In packages/mcp-server/src/code-tool-worker.ts at lines 8-10, the tseval function dynamically imports and executes arbitrary TypeScript code that comes directly from an untrusted HTTP request body. Ensure this worker runs in a fully isolated sandbox (e.g., a separate Deno/Worker process with no access to secrets or the filesystem), enforce strict origin/auth checks before the fetch handler is invoked, or document the trust boundary explicitly if the endpoint is internal-only.
| const log_lines: string[] = []; | ||
| const err_lines: string[] = []; | ||
| const console = { | ||
| const originalConsole = globalThis.console; | ||
| globalThis.console = { | ||
| ...originalConsole, | ||
| log: (...args: unknown[]) => { | ||
| log_lines.push(util.format(...args)); | ||
| }, |
There was a problem hiding this comment.
Correctness: 🐛 Mutating globalThis.console is not safe under concurrent requests — if two requests are in-flight simultaneously, Request B will overwrite globalThis.console with its own capture object, so Request A's console.log calls will be recorded into B's log_lines, and the finally restores will race each other. The previous local const console shadowing was request-scoped; this new approach makes console capture a shared mutable global that breaks under concurrency.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In packages/mcp-server/src/code-tool-worker.ts, lines 271-278, the diff replaces a local `const console = {...}` with a mutation of `globalThis.console`. This creates a race condition when multiple requests are handled concurrently: whichever request last sets `globalThis.console` wins, causing cross-request log capture pollution and lost logs. Fix this by using an AsyncLocalStorage or similar per-request context to pass the capture console to the evaluated code, rather than mutating the shared global. Alternatively, if evaluated code must use globalThis.console, serialize request handling or find another isolation mechanism.
|
|
||
| export interface MemoryUploadParams { | ||
| /** | ||
| * The file to ingest. |
There was a problem hiding this comment.
Correctness: Changing file from Uploadable to string breaks callers passing File, Blob, Buffer, or ReadableStream objects, which are the expected input types for a multipart file upload using multipartFormRequestOptions. A plain string value passed to multipartFormRequestOptions will be sent as-is (e.g. a filename string), not as actual binary file content, silently producing malformed uploads.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In `src/resources/memories.ts`, line 844, the `file` field in `MemoryUploadParams` was changed from `Uploadable` to `string`. This breaks the ability to pass actual file objects (File, Blob, Buffer, ReadableStream) to the upload method, which relies on `multipartFormRequestOptions` for multipart form data. Revert this field back to `Uploadable` to preserve correct upload behavior and maintain API compatibility.
Confidence Score: 1/5 - Blocking IssuesNot safe to merge — this PR introduces critical security and correctness issues that must be resolved before merging. The Key Findings:
Files requiring special attention
|
fce5750 to
7d81670
Compare
| import { Hyperspell, ClientOptions } from 'hyperspell'; | ||
|
|
||
| async function tseval(code: string) { | ||
| return import('data:application/typescript;charset=utf-8;base64,' + Buffer.from(code).toString('base64')); |
There was a problem hiding this comment.
Correctness: Buffer is a Node.js global not available in Deno or edge runtimes; the parseError function explicitly mentions Deno and the module uses export default { fetch } (edge worker pattern), so this will throw ReferenceError: Buffer is not defined at runtime in those environments.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In packages/mcp-server/src/code-tool-worker.ts at line 11, the `tseval` function uses `Buffer.from(code).toString('base64')` to base64-encode the TypeScript code. `Buffer` is a Node.js-only global and is not available in Deno or edge runtimes (the file uses `export default { fetch }` pattern and `parseError` mentions Deno explicitly). Replace `Buffer.from(code).toString('base64')` with a runtime-agnostic base64 encoding such as `btoa(unescape(encodeURIComponent(code)))` to handle Unicode safely across all environments.
|
|
||
| export interface MemoryUploadParams { | ||
| /** | ||
| * The file to ingest. |
There was a problem hiding this comment.
Correctness: Changing file from Uploadable to string is a breaking change — callers passing File, Blob, Buffer, or ReadableStream objects will now get TypeScript type errors. The upload method still calls multipartFormRequestOptions, which is designed for actual binary file uploads, so restricting to string may silently break real file upload use cases at runtime.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In file `src/resources/memories.ts`, line 844, the type of `file` in `MemoryUploadParams` was changed from `Uploadable` to `string`. This is a breaking change for any caller passing `File`, `Blob`, `Buffer`, or `ReadableStream` objects, and may silently break binary file uploads since the `upload` method still uses `multipartFormRequestOptions`. If the intent is to support both file paths (strings) and actual file objects, restore the type to `Uploadable` (or use a union like `Uploadable | string`). If only string file paths are now supported, verify that `multipartFormRequestOptions` handles plain strings correctly and update the method documentation accordingly.
| const log_lines: string[] = []; | ||
| const err_lines: string[] = []; |
There was a problem hiding this comment.
Correctness: 🔴 Mutating globalThis.console is not concurrency-safe in a server handling multiple simultaneous requests — request B will capture request A's patched console as its originalConsole, and log lines from different requests will bleed into each other's log_lines/err_lines arrays until the finally block restores the wrong reference.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In packages/mcp-server/src/code-tool-worker.ts, lines 271-278, the diff replaces a local `const console` shadow with direct mutation of `globalThis.console`. This is unsafe under concurrent requests: each request saves and restores `globalThis.console`, but overlapping requests can interleave such that (1) one request's `log` override overwrites another's, and (2) the 'original' captured by request B is already request A's patched console. Fix by keeping the original approach of passing a custom console-like object directly to user code (e.g., inject it as a parameter or use a module-scoped override only inside `tseval`) instead of mutating the shared global.
Confidence Score: 1/5 - Blocking IssuesNot safe to merge — this PR has multiple critical, unresolved correctness bugs that have been flagged repeatedly across prior reviews without being addressed. In Key Findings:
Files requiring special attention
|
7d81670 to
2b4d27c
Compare
| const log_lines: string[] = []; | ||
| const err_lines: string[] = []; |
There was a problem hiding this comment.
Correctness: 🐛 Mutating globalThis.console is not safe under concurrent requests. If two requests overlap, Request B captures Request A's overriding console as its originalConsole, and when B's finally block restores it, A's capturing function becomes the permanent global console — leaking captured log lines across requests indefinitely.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In packages/mcp-server/src/code-tool-worker.ts, lines 271-272 introduce a race condition: `globalThis.console` is mutated for the duration of an async request, but concurrent requests can interleave their save/restore operations, causing one request's capturing console to leak as the global after another request's `finally` block runs. Fix this by avoiding mutation of the shared global — instead, pass a custom console object directly into the evaluated code's scope (e.g. inject it as a variable alongside `client`), or use a per-invocation wrapper that does not mutate `globalThis`.
Confidence Score: 1/5 - Blocking IssuesNot safe to merge — this PR introduces multiple critical correctness issues that remain unresolved across 18 prior review comments and are confirmed again in the current review. Specifically, the mutation of Key Findings:
Files requiring special attention
|
Automated Release PR
0.35.1 (2026-04-08)
Full Changelog: v0.35.0...v0.35.1
Features
Bug Fixes
oidcdir (a305f3d)Chores
Refactors
This pull request is managed by Stainless's GitHub App.
The semver version number is based on included commit messages. Alternatively, you can manually set the version number in the title of this pull request.
For a better experience, it is recommended to use either rebase-merge or squash-merge when merging this pull request.
🔗 Stainless website
📚 Read the docs
🙋 Reach out for help or questions