Skip to content

feat(deepagents): PTC via Worker repl#296

Open
Christian Bromann (christian-bromann) wants to merge 3 commits intocb/ptcfrom
cb/ptc-repl
Open

feat(deepagents): PTC via Worker repl#296
Christian Bromann (christian-bromann) wants to merge 3 commits intocb/ptcfrom
cb/ptc-repl

Conversation

@christian-bromann
Copy link
Member

@christian-bromann Christian Bromann (christian-bromann) commented Mar 11, 2026

Summary

Extends createSandboxPtcMiddleware to work without any sandbox backend. When no backend is provided (or the backend doesn't support spawnInteractive()), the middleware adds a js_eval tool backed by an isolated Worker thread with toolCall() and spawnAgent() as async globals. The agent writes JavaScript with Promise.all() for parallel tool calls and subagent spawning — no Deno, Modal, Docker, or VFS needed.

// Before: required a sandbox backend
createSandboxPtcMiddleware({ backend: sandbox, ptc: true })

// After: backend is optional — Worker REPL is used when omitted
createSandboxPtcMiddleware({ ptc: true })

Motivation

The sandbox PTC from #287 requires a sandbox provider with spawnInteractive(). Many users just want programmatic tool calling and subagent spawning without setting up sandbox infrastructure. This PR adds a lightweight alternative: an in-process JavaScript REPL running in a Worker thread, with the same toolCall()/spawnAgent() API.

How it works

The middleware now detects the backend type and picks the right mode:

Backend Mode Tool Parallelism
Sandbox with spawnInteractive() Sandbox PTC execute (bash/python/node) & + wait
Non-sandbox backend or no backend Worker REPL js_eval (JavaScript) Promise.all()

In Worker REPL mode:

  1. The middleware registers a js_eval tool and hides all PTC tools (including task) from the model — the agent must use toolCall()/spawnAgent() inside js_eval
  2. js_eval creates a Node.js Worker Thread (or Web Worker in browsers) that evaluates the code with toolCall() and spawnAgent() injected as async globals
  3. Tool calls from the Worker are dispatched via postMessage to the main thread, which invokes the actual tool with the LangGraph config (so task/subagents work correctly), then sends the result back
  4. Promise.all() works naturally since toolCall() returns Promises and the Worker's event loop isn't blocked

Environment detection

// Prefers Web Worker if available (browser), falls back to Node.js worker_threads
if (typeof globalThis.Worker === "function") return "web";
const mod = globalThis.process?.getBuiltinModule?.("node:worker_threads");
if (mod?.Worker) return "node";

Usage

import { createDeepAgent, createSandboxPtcMiddleware, StateBackend } from "deepagents";

const agent = createDeepAgent({
  model: "claude-sonnet-4-5-20250929",
  backend: (config) => new StateBackend(config),
  subagents: [{ name: "analyst", description: "...", systemPrompt: "..." }],
  middleware: [
    createSandboxPtcMiddleware({ ptc: true }),
  ],
});

The agent gets filesystem tools from the StateBackend and js_eval from the PTC middleware. Inside js_eval, the agent writes:

const classifications = await Promise.all(
  employees.map(emp => toolCall("classify_record", emp))
);
const analyses = await Promise.all(
  classifications.map(c => spawnAgent(`Analyse: ${c}`, "analyst"))
);

Example trace: https://smith.langchain.com/public/71192fe6-3a0b-4068-b057-816ae384f053/r

@changeset-bot
Copy link

changeset-bot bot commented Mar 11, 2026

⚠️ No Changeset found

Latest commit: 8e9ceaf

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

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.

1 participant