Agents
Typed, durable agents in six SDKs. Streaming methods, read-only methods, reflection, and state that survives anything.
Typed, durable agents in six SDKs. Streaming methods, read-only methods, reflection, and state that survives anything.
Typed tools with host-enforced middleware, a built-in toolkit — shell, files, git, Node, npm, TypeScript, web fetch — and MCP in both directions.
The apps, pages, and files agents ship to people — served by Golem, backed by the agent, with login and resumable streams built in.
The harness chooses what an agent does. The runtime decides what it can do.
Typed agents in TypeScript, Effect, Rust, Go, Scala, or MoonBit. State survives crashes and redeploys. Tool calls never fire twice. Your code — and the host — decide what's allowed.
export const Orders = defineAgent({
name: 'Orders',
id: { customerId: z.string() },
http: http.mount('/orders/{customerId}'),
config: { systemPrompt: z.string() },
methods: {
handle: method({
input: { request: z.string(), orderId: z.string() },
returns: z.object({ resolved: z.boolean() }),
}),
},
})
export const OrdersImpl = Orders.implement({
init: () => ({ history: [] as Message[] }),
methods: {
async handle({ request, orderId }) {
// No DB writes — this push survives crashes, deploys, host migrations
this.history.push({ role: 'user', content: request })
// LLM sees full conversation; system prompt comes from typed config
const outcome = await llm.run({
prompt: this.config.systemPrompt, history: this.history,
tools: [cancelOrder, changeAddress], context: { orderId },
})
this.history.push({ role: 'assistant', content: outcome.message })
// Refunds aren't in the LLM's toolset — agent code gates them via HITL
if (outcome.needsRefund) {
// This await can sit for days at zero cost — no queue, no cron, no state table
const webhook = createWebhook()
await notifyApprover(webhook.getUrl(), outcome)
const { approved } = (await webhook).json()
if (approved) {
// Crash, retry, restart — still one charge. No silent double-charges.
const result = await refundOrder({ orderId, amount: outcome.refundAmount })
this.history.push({ role: 'tool', content: JSON.stringify(result) })
}
}
return { resolved: true }
},
},
}) class OrdersConfig extends defineConfig("Orders.Config", {
systemPrompt: Schema.String,
}) {}
export const Orders = defineAgent({
name: "Orders",
id: { customerId: Schema.String },
http: Http.mount("/orders/{customerId}"),
config: OrdersConfig,
methods: {
handle: method({
input: { request: Schema.String, orderId: Schema.String },
success: Schema.Struct({ resolved: Schema.Boolean }),
}),
},
}).implement<Ref.Ref<Message[]>>({
init: () => Ref.make<Message[]>([]),
methods: (history) => ({
handle: ({ request, orderId }) =>
Effect.gen(function* () {
// No DB writes — this update survives crashes, deploys, host migrations
yield* Ref.update(history, (h) => [...h, { role: "user", content: request }])
// LLM sees full conversation; system prompt comes from typed config
const config = yield* OrdersConfig
const outcome = yield* llm.run({
prompt: yield* config.systemPrompt, history: yield* Ref.get(history),
tools: [cancelOrder, changeAddress], context: { orderId },
})
yield* Ref.update(history, (h) => [...h, { role: "assistant", content: outcome.message }])
// Refunds aren't in the LLM's toolset — agent code gates them via HITL
if (outcome.needsRefund) {
// This await can sit for days at zero cost — no queue, no cron, no state table
const webhook = yield* Webhook.create
yield* notifyApprover(webhook.url, outcome)
const { approved } = yield* (yield* webhook.await).decode(Approval)
if (approved) {
// Crash, retry, restart — still one charge. No silent double-charges.
const result = yield* refundOrder({ orderId, amount: outcome.refundAmount })
yield* Ref.update(history, (h) => [...h, { role: "tool", content: JSON.stringify(result) }])
}
}
return { resolved: true }
}),
}),
}) #[derive(ConfigSchema)]
pub struct OrdersConfig { pub system_prompt: String }
#[agent_definition(mount = "/orders/{customer_id}")]
pub trait Orders {
fn new(customer_id: String, #[agent_config] config: Config<OrdersConfig>) -> Self;
async fn handle(&mut self, request: String, order_id: String) -> bool;
}
struct OrdersImpl { config: Config<OrdersConfig>, history: Vec<Message> }
#[agent_implementation]
impl Orders for OrdersImpl {
fn new(_customer_id: String, #[agent_config] config: Config<OrdersConfig>) -> Self {
Self { config, history: Vec::new() }
}
async fn handle(&mut self, request: String, order_id: String) -> bool {
// No DB writes — this push survives crashes, deploys, host migrations
self.history.push(Message::user(request));
// LLM sees full conversation; system prompt comes from typed config
let system_prompt = self.config.get().expect("config").system_prompt;
let outcome = llm::run(
&system_prompt, &self.history,
vec![cancel_order(), change_address()], order_id.clone(),
).await;
self.history.push(Message::assistant(outcome.message.clone()));
// Refunds aren't in the LLM's toolset — agent code gates them via HITL
if outcome.needs_refund {
// This await can sit for days at zero cost — no queue, no cron, no state table
let webhook = create_webhook().expect("webhook");
notify_approver(webhook.url(), &outcome).await;
let approval: Approval = webhook.await.json().expect("approval");
if approval.approved {
// Crash, retry, restart — still one charge. No silent double-charges.
let result = refund_order(order_id, outcome.refund_amount).await;
self.history.push(Message::tool(result.to_string()));
}
}
true
}
} type ID struct{ Customer string }
type Config struct{ SystemPrompt string }
type HandleIn struct{ Request, OrderID string }
var Orders = golem.DefineConfiguredAgent[ID, Config](golem.Spec{
Name: "Orders",
HTTP: &golem.Mount{Path: "/orders/{customer}"},
})
var Handle = Orders.Method[HandleIn, bool]("handle")
type state struct{ history []Message }
func (s *state) add(role, content string) {
s.history = append(s.history, Message{role, content})
}
var agent = Orders.Implement(func(ID) *state { return &state{} })
func init() {
agent.Handle(Handle, func(ctx *golem.Context[state], in HandleIn) bool {
// No DB writes — this append survives crashes, deploys, host migrations
ctx.State.add("user", in.Request)
// LLM sees full conversation; system prompt comes from typed config
outcome := llm.Run(ctx.Config(Orders).SystemPrompt, ctx.State.history,
[]Tool{cancelOrder, changeAddress}, in.OrderID)
ctx.State.add("assistant", outcome.Message)
// Refunds aren't in the LLM's toolset — agent code gates them via HITL
if outcome.NeedsRefund {
// This await can sit for days at zero cost — no queue, no cron, no state table
webhook := golem.NewWebhook[Approval]()
notifyApprover(webhook.URL(), outcome)
if webhook.MustAwait().Approved {
// Crash, retry, restart — still one charge. No silent double-charges.
result := refundOrder(in.OrderID, outcome.RefundAmount)
ctx.State.add("tool", result.String())
}
}
return true
})
} final case class OrdersConfig(systemPrompt: String) derives Schema
@agentDefinition(mount = "/orders/{customerId}")
trait Orders extends BaseAgent with AgentConfig[OrdersConfig]:
class Id(val customerId: String)
def handle(request: String, orderId: String): Future[Boolean]
@agentImplementation()
final class OrdersImpl(customerId: String, config: Config[OrdersConfig]) extends Orders:
private var history: Vector[Message] = Vector.empty
override def handle(request: String, orderId: String): Future[Boolean] =
// No DB writes — this append survives crashes, deploys, host migrations
history = history :+ Message("user", request)
for
// LLM sees full conversation; system prompt comes from typed config
outcome <- llm.run(
prompt = config.value.systemPrompt, history = history,
tools = Seq(cancelOrder, changeAddress), context = Map("orderId" -> orderId))
_ = history = history :+ Message("assistant", outcome.message)
// Refunds aren't in the LLM's toolset — agent code gates them via HITL.
// This await can sit for days at zero cost — no queue, no cron, no state table
approved <-
if outcome.needsRefund then awaitApproval(HostApi.createWebhook(), outcome)
else Future.successful(false)
// Crash, retry, restart — still one charge. No silent double-charges.
_ <-
if approved then refundOrder(orderId, outcome.refundAmount)
.map(result => history = history :+ Message("tool", result.toString))
else Future.unit
yield true #derive.config
pub(all) struct OrdersConfig { system_prompt : String }
#derive.agent
#derive.mount("/orders/{customer_id}")
struct Orders {
config : @config.Config[OrdersConfig]
mut history : Array[Message]
}
fn Orders::new(customer_id : String, config : @config.Config[OrdersConfig]) -> Orders {
let _ = customer_id
{ config, history: [] }
}
pub async fn Orders::handle(self : Self, request : String, order_id : String) -> Bool {
// No DB writes — this push survives crashes, deploys, host migrations
self.history.push({ role: "user", content: request })
// LLM sees full conversation; system prompt comes from typed config
let outcome = @llm.run(
prompt = self.config.value.system_prompt, history = self.history,
tools = [cancel_order(), change_address()], context = { "order_id": order_id },
)
self.history.push({ role: "assistant", content: outcome.message })
// Refunds aren't in the LLM's toolset — agent code gates them via HITL
if outcome.needs_refund {
// This await can sit for days at zero cost — no queue, no cron, no state table
let webhook = @webhook.create()
notify_approver(webhook.url(), outcome)
if webhook.wait().text() == "approved" {
// Crash, retry, restart — still one charge. No silent double-charges.
let result = refund_order(order_id, outcome.refund_amount)
self.history.push({ role: "tool", content: result.to_json().stringify() })
}
}
true
} The coding agents that matter most are the ones business users never see: the support bot that writes a script to reconcile an account, the assistant that builds the report from three systems.
On Golem, the shell, files, git, Node, npm, and TypeScript run inside the sandbox as isolated tool calls — under a path policy the agent can't bypass and a permission card that names the only hosts it may reach. No Linux VM to boot, sync, or pay for. Kill the server mid-task and the agent picks up where it stopped.
Harness inside or outside the sandbox — your choice. The guarantees come from the host, not from where the loop runs.
Read the 1.6 announcement →State changes and effects are captured automatically, without serialization, state machines, or annotations. Agents suspend for days or weeks at zero compute and zero memory cost, resuming with the same memory, locals, and call stack.
Treat memory as durable.
Code keeps running exactly once through any interruption, as if nothing happened. A tool call that crashed mid-execution completes; a workflow paused for days picks up where it stopped. This is transactional code execution.
Ship code that runs exactly once.
Every agent and every tool call runs in its own WebAssembly instance — its own memory, no system calls. Authority comes from permission cards the runtime mints: they narrow, never widen, and revocation cascades. Tool middleware runs in the host on every call, secrets are opaque handles, and every decision is journaled.
Turn policies into guarantees.
Front-ends, reports, live output — served by the same runtime that runs the agent, from the same deployment, under the same authority, with the same audit trail.
Mount any Fetch-compatible framework, and publish OpenAPI.
Versioned with the deployment, served without waking an agent.
Agents publish what they write — reports, builds, workspaces.
OAuth2 with PKCE for single-page apps, tokens issued by Golem.
Stream to the browser; a reload picks up where it left off.
A real URL from the first golem deploy, locally and in the cloud.
export const AppRouter =
defineHttpRouter('AppRouter')
.mount('/app')
.implement((req) => app.fetch(req)) Tool calls firing twice. State lost mid-node. SQL checkpointers under load. These aren't problems LangChain solves — they're runtime problems. Golem solves them as runtime guarantees.
Runtimes deliver what frameworks can't even promise.
Bring your favorite LLM SDKs, your tool libraries, your utilities — anything that's just code. They run on Golem, and your agent logic and tool primitives inherit the runtime's guarantees, without modification.
Use Golem's lightweight SDKs only when you want runtime-specific features: durability hooks, forking, rollbacks, agent and tool discovery. MCP servers you import inherit the same durability and middleware. Frameworks that bring their own runtime aren't officially supported today.
The runtime source is auditable, the WASM components are inspectable, and the license transitions to Apache‑2.0 — staying out of your way today, fully permissive tomorrow.
Run Golem where you run everything else — on a laptop, in Docker, in Kubernetes, on any cloud, or on-prem.
Same runtime, same capabilities, same guarantees, same operational behavior across supported languages.
Built by the wizards behind ZIO — the open-source effect system running in production at companies across fintech, ad tech, and AI infrastructure for the better part of a decade.
Scaffold a durable agent, run it locally, kill the process at any line, and watch it resume exactly where it stopped.
# Install: download from github.com/golemcloud/golem/releases
# Templates: ts | effect | rust | go | scala | moonbit
golem new --template ts --component-name example:counter --yes my-agent
cd my-agent && golem build
golem repl