Skip to content

render-examples/deepagents-on-render-ts

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Deep Agents on Render (TypeScript)

A production scaffold for running LangChain Deep Agents on Render. Deep Agents gives you the agent harness — planning, subagents, a task tool, human-in-the-loop. This repo adds the two pieces you need to run it for real, both backed by Render:

  • Durable state on Render Postgres — every step of a run is checkpointed with LangGraph's PostgresSaver. Runs survive restarts, work across replicas, and can pause for human approval and resume later.
  • Distributed execution on Render Workflows — when the agent spawns a subagent via the built-in task tool, it runs as its own Render Workflow task: isolated compute, retries, and timeouts, instead of running in-process.

The example agent is a small research-report generator (an orchestrator that delegates to a researcher and an editor subagent), but the point is the wiring — swap in your own agents in src/agents/.

How it works

                 ┌──────────────────────────────────────────────┐
   POST /run ───▶│  Web service (src/server.ts)                 │
                 │  • Orchestrator Deep Agent (the planner)     │
                 │  • Postgres checkpointer (durable state)     │
                 │  • Human-in-the-loop interrupt + resume      │
                 └───────────────┬──────────────────────────────┘
                                 │ task() tool intercepted by
                                 │ WorkflowDispatchMiddleware
                                 ▼
                 ┌──────────────────────────────────────────────┐
                 │  Workflow service (src/workflow.ts)          │
                 │  • one Render task per subagent              │
                 │  • researcher        (isolated instance)     │
                 │  • editor            (isolated instance)     │
                 └──────────────────────────────────────────────┘

Two Render services participate:

Process Source Role
Web service src/server.ts Runs the orchestrator, owns the durable checkpoint/interrupt state, exposes the HTTP API.
Workflow service src/workflow.ts Registers one Render task per subagent. Each subagent runs on its own instance with retries + timeout.

The two integrations

Render Workflows as the subagent backend. The orchestrator's built-in task tool (how Deep Agents delegate to subagents) is intercepted by WorkflowDispatchMiddleware. In production it dispatches the subagent to its Render Workflow task; locally (RENDER_USE_LOCAL_DEV=true) it runs the subagent in-process so you get a fast dev loop with zero infrastructure.

Render Postgres as the checkpointer. src/checkpointer.ts builds a PostgresSaver over an explicit pg.Pool sized to your Postgres plan. The orchestrator runs in the web service (not as a Workflow) so it can pause on an interruptOn tool, persist its state to Postgres, and resume on a later request — even after a restart or deploy. If DATABASE_URL is unset it falls back to an in-memory saver so a fresh clone still runs (state is not durable until you set it).

Project layout

src/
  server.ts                     web service: /run, /resume, /threads, /healthz
  workflow.ts                   Render Workflow service: registers a task per subagent
  config.ts                     env-driven runtime config (dispatch mode, pool size, ...)
  model.ts                      model resolution from the MODEL env var
  checkpointer.ts               Postgres checkpointer + pg pool (sized to your plan)
  agents/
    index.ts                    registry of top-level orchestrators (/run/:agent)
    orchestrator.ts             the Deep Agent: subagents + HITL + checkpointer + middleware
    subagents.ts                ← THE customization surface: define your subagents here
    run-subagent.ts             how a subagent actually runs (shared by local + Workflow)
    tools.ts                    example tools (replace these)
  workflows/
    dispatch-middleware.ts      intercepts the `task` tool → Render Workflow dispatch
    render-client.ts            Render SDK wrapper: start a task run and await its result

Run locally

Requires Node >=22.12.

git clone <your-fork-url>
cd deepagents-on-render-ts
npm install
cp .env.example .env
# edit .env: set ANTHROPIC_API_KEY (or OPENAI_API_KEY + MODEL=openai:gpt-5)

With RENDER_USE_LOCAL_DEV=true (the default in .env.example), subagents run in-process — no Render account needed:

npm run dev          # http://localhost:3000

Trigger a run:

curl -s -X POST http://localhost:3000/run/research \
  -H 'content-type: application/json' \
  -d '{"input":"Research the history of espresso and write a short report."}'

The orchestrator delegates to the subagents, then tries to call publish_report — a sensitive action gated by human-in-the-loop. The response will have status: "interrupted" and a threadId. Approve it to finish:

curl -s -X POST http://localhost:3000/resume/<threadId> \
  -H 'content-type: application/json' \
  -d '{"decisions":[{"type":"approve"}]}'

Postgres locally (optional but recommended): set DATABASE_URL in .env to any Postgres instance to get durable checkpointing. Without it, the app uses an in-memory saver and state is lost on restart. To prove durability: start a run, restart the web service, then GET /threads/<threadId>/state — the pending interrupts are still there, loaded from Postgres — and /resume continues from the checkpoint.

Full-fidelity local Workflows (optional)

To run each subagent as a real Render Workflow task locally (isolated instances, retries), use the Render CLI in a second terminal:

render workflows dev -- npm run dev:workflow

See the Render Workflows docs for CLI setup.

Deploy to Render

The web service and the Workflows service deploy through two complementary mechanisms.

  1. Push your fork to GitHub.

  2. Create the Blueprint. In the Render Dashboard, create a new Blueprint from your repo. render.yaml provisions:

    • the web service (deepagents-web)
    • a managed Postgres database (deepagents-db), auto-wired to DATABASE_URL

    During sync, set the secret env vars (marked sync: false): ANTHROPIC_API_KEY (or OPENAI_API_KEY) and RENDER_API_KEY.

  3. Create the Workflow service. Workflows are not yet provisionable from render.yaml, so create one in the Dashboard:

    • New → Workflow, pointed at the same repo
    • Build command: npm ci
    • Start command: npm run start:workflow
    • Add the same model key env vars (ANTHROPIC_API_KEY / OPENAI_API_KEY, MODEL)
    • Note its slug (e.g. deepagents-workflows)

    See Triggering Task Runs for details.

  4. Point the web service at the Workflow service. On deepagents-web, set:

    • RENDER_WORKFLOW_SLUG = the Workflow service slug from step 3
    • RENDER_API_KEY = a Render API key

    Redeploy the web service. In production (NODE_ENV=production), subagents now dispatch to Workflow task runs you can watch in the Dashboard.

  5. Verify. POST /run/research on your web service URL and watch each subagent appear as a separate task run in the Workflow service's dashboard.

Add your own agent

Replace the example agents without touching the infrastructure:

  1. Define your subagents in src/agents/subagents.ts. Each entry is a { name, description, systemPrompt, tools? }. The name becomes both the subagent_type the orchestrator delegates to and the Render Workflow task name — no infra changes needed.

  2. Swap the tools in src/agents/tools.ts for your real ones (search APIs, internal services, databases).

  3. Rewrite the orchestrator prompt in src/agents/orchestrator.ts to describe your workflow, and adjust interruptOn to gate whichever tools are sensitive in your domain.

  4. (Optional) Add another orchestrator by adding it to src/agents/index.ts; it becomes reachable at POST /run/<name>.

You should not need to touch checkpointer.ts, dispatch-middleware.ts, render-client.ts, or server.ts.

HTTP API

Method & path Description
POST /run/:agent Start a run. Body: { "input": string, "threadId"?: string }. Returns a completed result or status: "interrupted" with the interrupts to approve.
POST /resume/:threadId Resume a paused run. Body: { "agent"?: string, "decisions"?: [...] }. Decisions are passed to the HITL middleware (default: a single approve).
GET /threads/:threadId/state Inspect the persisted state for a thread (proves durability).
GET /healthz Liveness check.
GET / Service info + available agents.

Environment variables

See .env.example for the full, documented list. Summary:

Variable Required Description
MODEL no Model id, "<provider>:<model>". Default anthropic:claude-sonnet-4-5.
ANTHROPIC_API_KEY / OPENAI_API_KEY yes (one) Provider key matching MODEL.
DATABASE_URL prod Postgres connection string for the checkpointer. Falls back to in-memory if unset.
DATABASE_POOL_MAX no Max pg pool connections. Size to your Postgres plan (default 8).
RENDER_USE_LOCAL_DEV no true runs subagents in-process (local dev default).
RENDER_API_KEY prod Render API key for dispatching Workflow task runs.
RENDER_WORKFLOW_SLUG prod Slug of the Workflow service hosting the subagent tasks.
PORT no Web service port (default 3000).
NODE_ENV no production switches the default dispatch mode to Render Workflows.

License

MIT

About

Deploy LangChain Deep Agents on Render Workflows and Postgres

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors