The durable runtime for AI agents.

Define tasks in plain TypeScript. Each run gets its own microVM, pauses whole for session input, then resumes with every input and output attached.

helmr — deploy → run bash

$ helmr deploy

Deployment dpl_4e21 queued

v12

$ helmr run review-pr --payload prNumber=482

run_8f3a

$ helmr ps

RUN ID TASK STATUS INPUT CHANNEL

run_8f3a review-pr waiting approval

$

run_8f3a waiting on approval input

One TypeScript file. Image, sandbox, task, secrets, session input.

Codex here — or Claude Code, the AI SDK, any agent SDK that runs on Node. Follow-up input stays attached to the session, not the start payload.

review-pr.ts typescript
 1import { cache, image, queue, sandbox, source, streams, task } from "@helmr/sdk" 2import { Codex } from "@openai/codex-sdk" 3import { z } from "zod" 4  5const base = image("repo-agent") 6  .from("node:24-bookworm-slim") 7  .workdir("/workspace") 8  .run(["sh", "-ceu", "apt-get update && apt-get install -y git ripgrep"]) 9  .copy("/workspace/package.json", source.file("package.json"))10  .run(["bun", "install"], {11    cache: [{ mountPath: "/root/.bun/install/cache", cache: cache("bun") }]12  })13 14const sbx = sandbox("repo-agent")15  .image(base)16  .resources({ cpu: 2, memory: "4Gi" })17 18const reviews = queue({ id: "review/pr", concurrencyLimit: 5 })19 20export const reviewPr = task({21  id: "review-pr",22  sandbox: sbx,23  queue: reviews,24  secrets: [{ name: "OPENAI_API_KEY", env: "OPENAI_API_KEY" }],25  run: async (event, ctx) => {26    const codex = new Codex()27    const thread = codex.startThread({ workingDirectory: process.cwd() })28    const turn = await thread.run("Review this PR and propose a patch.")29 30    const approval = streams.input("approval", {31      schema: z.object({ approved: z.boolean() })32    })33    await sendSlackApproval({34      sessionId: ctx.session.id,35      stream: approval.id36    })37    // The microVM freezes here — filesystem, memory, process.38    const decision = await approval.wait({39      timeout: "30m",40      tags: ["approval", "github-review"],41      metadata: { subject: "Post this review to GitHub?" },42      correlationId: `pr:${event.prNumber}`43    }).unwrap()44    if (decision.approved) await postReview(event.prNumber, turn.finalResponse)45  }46})

Pause. Schedule. Start. Reuse. Each one is a few lines.

Pause for session input

streams.input typescript
const approval = streams.input("approval", {
  schema: z.object({ approved: z.boolean() })
})

const decision = await approval.wait({
  timeout: "30m",
  tags: ["approval", "github-review"]
}).unwrap()

The microVM freezes whole while it waits. A webhook, UI, or operator appends input and resumes it mid-instruction.

Run on a schedule

schedules.task typescript
export const nightly = schedules.task({
  id: "nightly-audit",
  cron: {
    pattern: "0 9 * * 1-5",
    timezone: "Asia/Tokyo"
  },
  run: async (event, ctx) => {
    return runAudit(ctx)
  }
})

Cron lives in the task definition and deploys with it. One file owns the code and the schedule.

Start from anywhere

sessions.start typescript
const started = await sessions.start(
  reviewPr,
  { prNumber: 482 },
  { idempotencyKey: pr.head.sha }
)

Your product, a webhook, CI, or another task — deduplicated with idempotency keys.

Reuse the workspace

workspace.exec typescript
const workspace = workspaces.open(started.session.workspaceId)
const exec = await workspace.exec(["bash", "-lc", "bun test"], {
  cwd: "/workspace"
})
await exec.wait()

The session records the workflow. The workspace stays available for follow-up execs and terminals.

Run on our cloud, or in your AWS.

Early, open, Apache 2.0. Read the source before you trust it with your repos.

Helmr Cloud coming soon

A managed fleet. Projects, environments, immutable deployments — no infrastructure to run.

Read the docs

Your AWS account

The identical control plane and workers in your VPC, Apache 2.0. Credentials never leave your boundary.

Self-hosting guide