Lightweight
WebAssembly, not Linux: each VM has its own memory, file system and environment, with no guest OS. Code can be tens of kilobytes; a VM starts in under 1 ms in the best case scenario.
Golem runs AI agents, their tools and their artifacts. Each agent runs in a lightweight WebAssembly VM that resumes mid-function after node restarts, rescaling, long waits and crashes. Tool calls run exactly once, under permissions agents can't bypass.
Golem runs every agent and every tool call in its own WebAssembly VM. Every call out of a VM goes through Golem, which enforces your permissions and records external interactions so the VM can resume anywhere.
WebAssembly, not Linux: each VM has its own memory, file system and environment, with no guest OS. Code can be tens of kilobytes; a VM starts in under 1 ms in the best case scenario.
A waiting VM is suspended, using no CPU or memory, for minutes or months. After a crash or node restart it's restored where it left off, with its files, without repeating completed calls.
A VM reaches its own files, the network, secrets and other VMs only through Golem's permission checks, which no code, including AI-generated code, can bypass.
const Email = z. object({ id: z. string(), body: z. string() })
const Label = z. enum([ 'urgent', 'billing', 'other'])
export const Triage = defineAgent({
name: 'TriageAgent',
// each inbox gets its own agent, in its own WebAssembly VM:
id: { inbox: z. string() },
methods: {
triage: method({
input: { emails: z. array(Email) },
returns: z. record(z. string(), Label),
}),
},
}). implement({
init: ({ id }) => ({ inbox: id.inbox }),
methods: {
async triage({ emails }) {
const labels: Record<string, z. infer<typeof Label>> = {}
for ( const email of emails) {
const label = Label. parse( await classify(email.body))
// it resumes here after a crash, with memory and files:
await applyLabel( this.inbox, email.id, label)
labels[email.id] = label
}
return labels
},
},
}) import { DatabaseSync } from 'node:sqlite'
const Texts = z. array(z. string())
export const Notes = defineAgent({
name: 'NotesAgent',
id: { user: z. string() },
methods: {
add: method({ input: { text: z. string() }, returns: z. void() }),
find: method({ input: { term: z. string() }, returns: Texts }),
},
}). implement({
init: () => {
fs. mkdirSync( '/data', { recursive: true })
// SQLite uses this agent's durable file system, not a server:
const db = new DatabaseSync( '/data/notes.db' )
db. exec( 'CREATE TABLE IF NOT EXISTS notes (text TEXT NOT NULL)' )
return { db }
},
methods: {
add({ text }) {
this.db. prepare( 'INSERT INTO notes VALUES (?)' ). run(text)
},
find({ term }) {
const rows = this.db
. prepare( 'SELECT text FROM notes WHERE text LIKE ?')
. all( `%${term}%`)
return Texts. parse(rows. map(( row) => row.text))
},
},
}) import { generateText, Output } from 'ai'
import { anthropic } from '@ai-sdk/anthropic'
import { openai } from '@ai-sdk/openai'
const Review = z. object({ ok: z. boolean(), notes: z. string() })
const Draft = z. object({ text: z. string(), review: Review })
export const Drafter = defineAgent({
name: 'DraftAgent',
id: { topic: z. string() },
methods: {
write: method({ input: { idea: z. string() }, returns: Draft }),
},
}). implement({
init: ({ id }) => ({ topic: id.topic }),
methods: {
async write({ idea }) {
// draft with one provider, review with another:
const draft = await generateText({
model: openai( 'gpt-4.1' ),
prompt: `Product update on ${this. topic}: ${idea}`,
})
const { output } = await generateText({
model: anthropic( 'claude-sonnet-4-5'),
output: Output. object({ schema: Review }),
prompt: `Review this product update:\n${draft. text}`,
})
return { text: draft.text, review: output }
},
},
}) export const Planner = defineAgent({
name: 'PlannerAgent',
id: { topic: z. string() },
methods: { plan: method({ input: {}, returns: Plan }) },
}). implement({
init: ({ id }) => ({ topic: id.topic }),
methods: {
async plan() {
// wait for a typed result from a Rust ResearchAgent:
const facts = await ResearchAgent. get( this.topic). gather()
// send now, without waiting for a result:
SlackAgent. get( 'ops').post. trigger(facts.summary)
// discover agents, their methods, and available tools:
const agents = reflection. getAllAgentTypes(). map(( a) =>
`${a. name}: ${a. methods. map(( m) => m. name). join( ', ')}`)
const tools = reflection. getAllToolTypes(). map(( t) => t.name)
return { facts, agents, tools }
},
},
}) import { setTimeout as sleep } from 'node:timers/promises'
export const Checkout = defineAgent({
name: 'CheckoutAgent',
id: { orderId: z. string() },
// webhooks require this agent to have an HTTP mount:
http: http. mount( '/checkout/{orderId}'),
methods: {
pay: method({ input: { amount: z. int() }, returns: z. void() }),
},
}). implement({
init: ({ id }) => ({ orderId: id.orderId }),
methods: {
async pay({ amount }) {
const paid = createWebhook()
await createPaymentLink( this.orderId, amount, paid. getUrl())
// schedule a call on another agent for tomorrow:
const reminder = Mailer.client. get({ orderId: this.orderId })
.remind. schedule( inDays( 1))
// it waits here for days if needed, using no CPU or memory:
const event = PaymentEvent. parse(( await paid). json())
reminder. cancel()
if (event.status !== 'succeeded') return
// sleep for a week; its deadline survives restarts:
await sleep( 7 * 24 * 60 * 60_000)
await sendSurvey( this.orderId)
},
},
}) export const Writer = defineAgent({
name: 'WriterAgent',
id: { doc: z. string() },
http: http. mount( '/docs/{doc}'),
methods: {
draft: method({
input: { brief: z. string() },
// clients resume this Durable Stream from an offset:
returns: s. stream(z. string()),
http: http. post( '/draft'),
}),
},
}). implement({
init: () => ({}),
methods: {
draft({ brief }) {
return AgentStream. from(( async function* () {
for ( const section of ['Summary', 'Details', 'Actions']) {
// completed model calls are not repeated on recovery:
const reply = await openai.responses. create({
model: 'gpt-4.1' ,
input: `${brief}\nWrite the ${section} section.` ,
})
yield reply.output_text
}
})())
},
},
}) export const Stock = defineAgent({
name: 'StockAgent',
id: { sku: z. string() },
methods: {
restock: method({ input: { n: z. int() }, returns: z. void() }),
level: method({
input: {}, returns: z. int(),
// repeat calls come from a cache, without loading the agent:
// the cache clears when a restock succeeds:
// outgoing HTTP, agent calls and key-value writes fail:
readOnly: true,
}),
},
}). implement({
init: () => ({ units: 0 }),
methods: {
restock({ n }) { this.units += n },
level() { return this.units },
},
}) const Email = Schema. Struct({
id: Schema.String, body: Schema.String,
})
const Label = Schema. Literals([ "urgent", "billing", "other"])
export const Triage = defineAgent({
name: "TriageAgent",
// each inbox gets its own agent, in its own WebAssembly VM:
id: { inbox: Schema.String },
methods: {
triage: method({
input: { emails: Schema. Array(Email) },
success: Schema. Record(Schema.String, Label),
}),
},
}). implement({
init: ({ inbox }) => Effect. succeed({ inbox }),
methods: ({ inbox }) => ({
triage: ({ emails }) => Effect. gen( function* () {
const labels: Record<string, typeof Label.Type> = {}
for ( const email of emails) {
const label = yield* classify(email.body). pipe(
Effect. flatMap(Schema. decodeUnknownEffect(Label)))
// it resumes here after a crash, with memory and files:
yield* applyLabel(inbox, email.id, label)
labels[email.id] = label
}
return labels
}). pipe(Effect.orDie),
}),
}) import { SqliteClient } from "@golemcloud/effect-golem/sqlite"
const Texts = Schema. Array(Schema.String)
export const Notes = defineAgent({
name: "NotesAgent",
id: { user: Schema.String },
methods: {
add: method({
input: { text: Schema.String }, success: Schema.Void,
}),
find: method({
input: { term: Schema.String }, success: Texts,
}),
},
}). implement({
init: () => Effect. gen( function* () {
fs. mkdirSync( "/data", { recursive: true })
// SQLite uses this agent's durable file system, not a server:
return yield* SqliteClient. make({ filename: "/data/notes.db" })
}). pipe(Effect. tap(( db) =>
db`CREATE TABLE IF NOT EXISTS notes (text TEXT NOT NULL)` )),
methods: ( db) => ({
add: ({ text }) => db`INSERT INTO notes VALUES (${ text})`. pipe(
Effect.asVoid, Effect.orDie),
find: ({ term }) => db`SELECT text FROM notes
WHERE text LIKE ${`%${term}%`}`. pipe(
Effect. map(( rows) => rows. map(( row) => row.text)),
Effect. flatMap(Schema. decodeUnknownEffect(Texts)),
Effect.orDie),
}),
}) import { LanguageModel } from "effect/ai"
import { AnthropicLanguageModel } from "@effect/ai-anthropic"
import { OpenAiLanguageModel } from "@effect/ai-openai"
const Review = Schema. Struct({
ok: Schema.Boolean, notes: Schema.String,
})
const Draft = Schema. Struct({ text: Schema.String, review: Review })
export const Drafter = defineAgent({
name: "DraftAgent",
id: { topic: Schema.String },
methods: { write: method({
input: { idea: Schema.String }, success: Draft,
}) },
}). implement({
init: ({ topic }) => Effect. succeed({ topic }),
methods: ({ topic }) => ({
write: ({ idea }) => Effect. gen( function* () {
// draft with one provider, review with another:
const draft = yield* LanguageModel. generateText({
prompt: `Product update on ${topic}: ${idea}`,
}). pipe(Effect. provide(OpenAiLanguageModel. model( "gpt-4.1" )))
const { value } = yield* LanguageModel. generateObject({
schema: Review,
prompt: `Review this product update:\n${draft. text}`,
}). pipe(Effect. provide(
AnthropicLanguageModel. model( "claude-sonnet-4-5")))
return { text: draft.text, review: value }
}). pipe(Effect. provide(providers), Effect.orDie),
}),
}) export const Planner = defineAgent({
name: "PlannerAgent",
id: { topic: Schema.String },
methods: { plan: method({ input: {}, success: Plan }) },
}). implement({
init: ({ topic }) => Effect. succeed({ topic }),
methods: ({ topic }) => ({
plan: () => Effect. gen( function* () {
// wait for a typed result from a Rust ResearchAgent:
const research = yield* ResearchAgent. get(topic)
const facts = yield* research. gather()
// send now, without waiting for a result:
const slack = yield* SlackAgent. get( "ops")
yield* slack.post. trigger(facts.summary)
// discover agents, their methods, and available tools:
const agents = ( yield* Reflection.getAllAgentTypes). map(( a) =>
`${a. name}: ${a. methods. map(( m) => m. name). join( ", ")}`)
const tools = ( yield* Reflection. getAllToolTypes())
. map(( t) => t.name)
return { facts, agents, tools }
}). pipe(Effect.scoped, Effect.orDie),
}),
}) export const Checkout = defineAgent({
name: "CheckoutAgent",
id: { orderId: Schema.String },
// webhooks require this agent to have an HTTP mount:
http: Http. mount( "/checkout/{orderId}"),
methods: {
pay: method({
input: { amount: Schema.Int }, success: Schema.Void,
}),
},
}). implement({
init: ({ orderId }) => Effect. succeed({ orderId }),
methods: ({ orderId }) => ({
pay: ({ amount }) => Effect. gen( function* () {
const paid = yield* Webhook.create
yield* createPaymentLink(orderId, amount, paid.url)
// schedule a call on another agent for tomorrow:
const mailer = yield* Mailer.client. get({ orderId })
const reminder = yield* mailer.remind. schedule( inDays( 1), {})
// it waits here for days if needed, using no CPU or memory:
const payload = yield* paid.await
const event = yield* payload. decode(PaymentEvent)
yield* reminder. cancel()
if (event.status !== "succeeded") return
// sleep for a week; its deadline survives restarts:
yield* Effect. sleep( "7 days")
yield* sendSurvey(orderId)
}). pipe(Effect.scoped, Effect.orDie),
}),
}) export const Writer = defineAgent({
name: "WriterAgent",
id: { doc: Schema.String },
http: Http. mount( "/docs/{doc}"),
methods: {
draft: method({
input: { brief: Schema.String },
// clients resume this Durable Stream from an offset:
success: WitTypes. AgentStream(Schema.String),
http: [Http. post( "/draft")],
}),
},
}). implement({
init: () => Effect.void,
methods: () => ({
draft: ({ brief }) => Effect. succeed(
Stream. fromIterable([ "Summary", "Details", "Actions"]). pipe(
Stream. mapEffect(( section) =>
// completed model calls are not repeated on recovery:
LanguageModel. generateText({
prompt: `${brief}\nWrite the ${section} section.` ,
}),
),
Stream. map(( reply) => reply.text),
Stream. provide(OpenAiLanguageModel. model( "gpt-4.1" )),
Stream. provide(openai), Stream.orDie,
),
),
}),
}) export const Stock = defineAgent({
name: "StockAgent",
id: { sku: Schema.String },
methods: {
restock: method({
input: { n: Schema.Int }, success: Schema.Void,
}),
level: method({
input: {}, success: Schema.Int,
// repeat calls come from a cache, without loading the agent:
// the cache clears when a restock succeeds:
// outgoing HTTP, agent calls and key-value writes fail:
readOnly: true,
}),
},
}). implement({
init: () => Ref. make( 0),
methods: ( units) => ({
restock: ({ n }) => Ref. update(units, ( u) => u + n),
level: () => Ref. get(units),
}),
}) #[derive( Schema)] pub struct Email { id: String, body: String }
#[derive( Schema, Clone, Copy)]
pub enum Label { Urgent, Billing, Other }
#[agent_definition]
pub trait TriageAgent {
// each inbox gets its own agent, in its own WebAssembly VM:
fn new(inbox : String) -> Self;
async fn triage( &self, emails: Vec<Email>)
-> Result<HashMap<String, Label>, AppError>;
}
struct TriageAgentImpl { inbox: String }
#[agent_implementation]
impl TriageAgent for TriageAgentImpl {
fn new(inbox : String) -> Self { Self { inbox } }
async fn triage( &self, emails: Vec<Email>)
-> Result<HashMap<String, Label>, AppError> {
let mut labels = HashMap::new();
for email in emails {
let label: Label = classify( &email. body).await? ;
// it resumes here after a crash, with memory and files:
apply_label( &self. inbox, &email. id, label).await? ;
labels. insert(email . id, label);
}
Ok(labels)
}
} use rusqlite::Connection;
#[agent_definition]
pub trait NotesAgent {
fn new(user : String) -> Self;
fn add( &mut self, text: String) -> Result<(), AppError>;
fn find( &self, term: String) -> Result<Vec<String>, AppError>;
}
fn open() -> Result<Connection, AppError> {
fs::create_dir_all( "/data")?;
// SQLite uses this agent's durable file system, not a server:
let db = Connection::open( "/data/notes.db" )?;
db. execute_batch(
"CREATE TABLE IF NOT EXISTS notes (text TEXT NOT NULL)" )?;
Ok(db)
}
struct NotesAgentImpl;
#[agent_implementation]
impl NotesAgent for NotesAgentImpl {
fn new(_user : String) -> Self { Self }
fn add( &mut self, text: String) -> Result<(), AppError> {
open() ?. execute( "INSERT INTO notes VALUES (?)" , [text])?;
Ok(())
}
fn find( &self, term: String) -> Result<Vec<String>, AppError> {
let db = open() ?;
let mut stmt =
db. prepare( "SELECT text FROM notes WHERE text LIKE ?")?;
let rows = stmt. query_map([ format!( "%{term}%")], |r| r. get( 0))?;
Ok(rows . collect::<Result<_, _>>() ?)
}
} use golem_ai_llm::LlmProvider;
use golem_ai_llm_anthropic::{Anthropic, AnthropicConfig};
use golem_ai_llm_openai::{OpenAI, OpenAiConfig};
#[derive( Schema)] pub struct Review { ok: bool, notes: String }
#[derive( Schema)] pub struct Draft { text: String, review: Review }
#[agent_definition]
pub trait DraftAgent {
fn new(topic : String) -> Self;
async fn write( &self, idea: String) -> Result<Draft, AppError>;
}
struct DraftAgentImpl { topic: String }
#[agent_implementation]
impl DraftAgent for DraftAgentImpl {
fn new(topic : String) -> Self { Self { topic } }
async fn write( &self, idea: String) -> Result<Draft, AppError> {
// draft with one provider, review with another:
let draft = text( OpenAI::send(
OpenAiConfig::from_env() ?,
vec![user( &format!( "Product update on {}: {idea}",
self. topic))],
model( "gpt-4.1" ),
).await )?;
let review = json( Anthropic::send(
AnthropicConfig::from_env() ?,
vec![user( &format!( "Review this product update:\n{draft}"))],
json_model::<Review>( "claude-sonnet-4-5"),
).await )?;
Ok( Draft { text: draft, review })
}
} #[agent_definition]
pub trait PlannerAgent {
fn new(topic : String) -> Self;
async fn plan( &self) -> Result<Plan, AppError>;
}
struct PlannerAgentImpl { topic: String }
#[agent_implementation]
impl PlannerAgent for PlannerAgentImpl {
fn new(topic : String) -> Self { Self { topic } }
async fn plan( &self) -> Result<Plan, AppError> {
// wait for a typed result from a TypeScript ResearchAgent:
let facts = ResearchAgent::get( self. topic. clone()) ?
. gather() .await? ;
// send now, without waiting for a result:
SlackAgent::get( "ops". into()) ?
. trigger_post(facts . summary. clone()) ?;
// discover agents, their methods, and available tools:
let agents = reflection::get_all_agent_types() ?. iter()
. map( |a| format!( "{}: {}", a. name(), a . methods() . iter()
. map( |m| m. name()) . collect::<Vec<_>>() . join( ", ")))
. collect();
let tools = reflection::get_all_tool_types() ?. iter()
. map( |t| t. lookup_name() . to_string()) . collect();
Ok( Plan { facts, agents, tools })
}
} use golem_rust::wasip3::clocks::monotonic_clock::wait_for as sleep;
// webhooks require this agent to have an HTTP mount:
#[agent_definition(mount = "/checkout/{order_id}")]
pub trait CheckoutAgent {
fn new(order_id : String) -> Self;
async fn pay( &mut self, amount: u64) -> Result<(), AppError>;
}
struct CheckoutAgentImpl { order_id: String }
#[agent_implementation]
impl CheckoutAgent for CheckoutAgentImpl {
fn new(order_id : String) -> Self { Self { order_id } }
async fn pay( &mut self, amount: u64) -> Result<(), AppError> {
let paid = create_webhook() ?;
create_payment_link( &self. order_id, amount, paid. url()) .await? ;
// schedule a call on another agent for tomorrow:
let reminder = MailerAgentClient::get( self. order_id. clone())
. schedule_cancelable_remind( in_days( 1))?;
// it waits here for days if needed, using no CPU or memory:
let event: PaymentEvent = paid.await. json() ?;
reminder. cancel();
if event. status != "succeeded" { return Ok(()); }
// sleep for a week; its deadline survives restarts:
sleep( 7 * 24 * 60 * 60 * 1_000_000_000).await ;
send_survey( &self. order_id).await? ;
Ok(())
}
} #[agent_definition(mount = "/docs/{doc}")]
pub trait WriterAgent {
fn new(doc : String) -> Self;
#[endpoint(post = "/draft")]
// clients resume this Durable Stream from an offset:
fn draft( &self, brief: String) -> AgentStream<String>;
}
struct WriterAgentImpl;
#[agent_implementation]
impl WriterAgent for WriterAgentImpl {
fn new(_doc : String) -> Self { Self }
fn draft( &self, brief: String) -> AgentStream<String> {
let ( mut out, stream) = AgentStream::new();
spawn_local( async move {
for section in ["Summary", "Details", "Actions"] {
// completed model calls are not repeated on recovery:
let reply = text( OpenAI::send(
OpenAiConfig::from_env() . expect( "no OpenAI config"),
vec![user( &format!(
"{brief}\nWrite the {section} section." ))],
model( "gpt-4.1" ),
).await ). expect( "model call failed");
if out. write_one(reply) .await. is_err() { return; }
}
});
stream
}
} #[agent_definition]
pub trait StockAgent {
fn new(sku : String) -> Self;
fn restock( &mut self, n: u64);
// repeat calls come from a cache, without loading the agent:
// the cache clears when a restock succeeds:
// outgoing HTTP, agent calls and key-value writes fail:
#[read_only]
fn level( &self) -> u64;
}
struct StockAgentImpl { units: u64 }
#[agent_implementation]
impl StockAgent for StockAgentImpl {
fn new(_sku : String) -> Self { Self { units: 0 } }
fn restock( &mut self, n: u64) { self. units += n; }
fn level( &self) -> u64 { self. units }
} final case class Email( id: String, body: String) derives Schema
enum Label derives Schema:
case Urgent, Billing, Other
@agentDefinition()
trait TriageAgent extends BaseAgent:
// each inbox gets its own agent, in its own WebAssembly VM:
class Id( val inbox: String)
def triage( emails: List[Email]): Future[Map[String, Label]]
@agentImplementation()
final class TriageAgentImpl( inbox: String) extends TriageAgent:
def triage( emails: List[Email]): Future[Map[String, Label]] =
emails.foldLeft( Future.successful( Map.empty[ String, Label])):
(labels, email) =>
for
done <- labels
label <- classify(email.body).map( Label.valueOf)
// it resumes here after a crash, with memory and files:
_ <- applyLabel(inbox, email.id, label)
yield done + (email.id -> label) Scala, MoonBit and Go agents each have their own durable file system too, but no supported SQLite library.
import ai.{ generateText, Output}
import aisdk. anthropic. anthropic
import aisdk. openai. openai
final case class Review( ok: Boolean, notes: String) derives Schema
final case class Draft( text: String, review: Review) derives Schema
@agentDefinition()
trait DraftAgent extends BaseAgent:
class Id( val topic: String)
def write( idea: String): Future[Draft]
@agentImplementation()
final class DraftAgentImpl( topic: String) extends DraftAgent:
def write( idea: String): Future[Draft] =
for
// draft with one provider, review with another:
draft <- generateText(
model = openai( "gpt-4.1" ),
prompt = s"Product update on $topic: $idea",
)
reviewed <- generateText(
model = anthropic( "claude-sonnet-4-5"),
output = Output.`object`[ Review],
prompt = s"Review this product update:\n${draft.text} ",
)
yield Draft(draft.text, reviewed.output) @agentDefinition()
trait PlannerAgent extends BaseAgent:
class Id( val topic: String)
def plan() : Future[Plan]
@agentImplementation()
final class PlannerAgentImpl( topic: String) extends PlannerAgent:
def plan() : Future[Plan] =
// wait for a typed result from a Rust ResearchAgent:
ResearchAgentClient.get(topic).gather().map : facts =>
// send now, without waiting for a result:
SlackAgentClient.get( "ops").post.trigger(facts.summary)
// discover agents, their methods, and available tools:
val agents = Reflection.getAllAgentTypes().orThrow.map : a =>
s"${a.name} : ${a.methods.map(_.name).mkString( ", ")}"
val tools = Reflection.getAllToolTypes().orThrow.map(_.name)
Plan(facts, agents, tools) // webhooks require this agent to have an HTTP mount:
@agentDefinition(mount = "/checkout/{orderId}")
trait CheckoutAgent extends BaseAgent:
class Id( val orderId: String)
def pay( amount: Int): Future[Unit]
@agentImplementation()
final class CheckoutAgentImpl( orderId: String)
extends CheckoutAgent:
def pay( amount: Int): Future[Unit] =
val paid = HostApi.createWebhook()
for
_ <- createPaymentLink(orderId, amount, paid.url)
// schedule a call on another agent for tomorrow:
reminder <- MailerAgentClient.get(orderId)
.remind.scheduleCancelableAt(inDays( 1))
// it waits here for days if needed, using no CPU or memory:
event <- paid.await().map(_.json[ PaymentEvent]())
_ = reminder.cancel()
_ <- if event.status != "succeeded" then Future.unit
else
// sleep for a week; its deadline survives restarts:
sleep( 7 * 24 * 60 * 60_000)
.flatMap(_ => sendSurvey(orderId))
yield () @agentDefinition(mount = "/docs/{doc}")
trait WriterAgent extends BaseAgent:
class Id( val doc: String)
@endpoint( "POST", "/draft")
// clients resume this Durable Stream from an offset:
def draft( brief: String): AgentStream[String]
@agentImplementation()
final class WriterAgentImpl( doc: String) extends WriterAgent:
def draft( brief: String): AgentStream[String] =
val sections = Iterator( "Summary", "Details", "Actions")
AgentStream.fromPull : () =>
if !sections.hasNext then Future.successful( None)
else
val section = sections.next()
// completed model calls are not repeated on recovery:
openai.responses.create( ResponseParams(
model = "gpt-4.1" ,
input = s"$brief\nWrite the $section section." ,
)).toFuture.map(reply => Some(reply.output_text)) @agentDefinition()
trait StockAgent extends BaseAgent:
class Id( val sku: String)
def restock( n: Int): Unit
// repeat calls come from a cache, without loading the agent:
// the cache clears when a restock succeeds:
// outgoing HTTP, agent calls and key-value writes fail:
@readOnly
def level() : Int
@agentImplementation()
final class StockAgentImpl( sku: String) extends StockAgent:
private var units = 0
def restock( n: Int): Unit = units += n
def level() : Int = units #derive.golem_schema
struct Email { id : String; body : String }
#derive.golem_schema
enum Label { Urgent; Billing; Other }
#derive.agent
struct TriageAgent {
inbox : String
}
// each inbox gets its own agent, in its own WebAssembly VM:
fn TriageAgent::new(inbox : String) -> TriageAgent {
{ inbox, }
}
pub async fn TriageAgent::triage(
self : Self,
emails : Array[Email],
) -> Map[String, Label] {
let labels : Map[String, Label] = {}
for email in emails {
let label = classify(email.body)
// it resumes here after a crash, with memory and files:
apply_label(self.inbox, email.id, label)
labels[email.id] = label
}
labels
} Scala, MoonBit and Go agents each have their own durable file system too, but no supported SQLite library.
#derive.golem_schema
struct Review { ok : Bool; notes : String } derive( FromJson)
#derive.golem_schema
struct Draft { text : String; review : Review }
#derive.agent
struct DraftAgent {
topic : String
}
fn DraftAgent::new(topic : String) -> DraftAgent {
{ topic, }
}
pub async fn DraftAgent::write(
self : Self, idea : String,
) -> Draft {
// draft with one provider, review with another:
let draft = @llm. generate_text(
model=@llm. openai( "gpt-4.1" ),
prompt="Product update on \{self.topic }: \{idea}",
)
let review : Review = @llm. generate_object(
model=@llm. anthropic( "claude-sonnet-4-5"),
prompt="Review this product update:\n\{draft}",
)
{ text: draft, review }
} #derive.agent
struct PlannerAgent {
topic : String
}
fn PlannerAgent::new(topic : String) -> PlannerAgent {
{ topic, }
}
pub async fn PlannerAgent::plan(self : Self) -> Plan {
// wait for a typed result from a Rust ResearchAgent:
let facts = @research. ResearchAgentClient::get(self.topic)
. gather()
// send now, without waiting for a result:
@slack. SlackAgentClient::get( "ops"). trigger_post(facts.summary)
// discover agents, their methods, and available tools:
let agents = @reflection. get_all_agent_types(). map(a =>
"\{a. name() }: \{a. methods(). map( m => m. name()). join( ", ")}")
let tools = @reflection. get_all_tool_types()
. map(t => t. lookup_name())
{ facts, agents, tools }
} #derive.agent
// webhooks require this agent to have an HTTP mount:
#derive.mount ( "/checkout/{order_id}")
struct CheckoutAgent {
order_id : String
}
fn CheckoutAgent::new(order_id : String) -> CheckoutAgent {
{ order_id, }
}
pub async fn CheckoutAgent::pay(self : Self, amount : Int) -> Unit {
let paid = @webhook. create()
create_payment_link(self.order_id, amount, paid. url())
// schedule a call on another agent for tomorrow:
let reminder = MailerAgentClient::get(self.order_id)
. schedule_cancelable_remind( in_days( 1))
// it waits here for days if needed, using no CPU or memory:
let body = @json. parse(paid. wait(). text())
let event : PaymentEvent = @json. from_json(body)
reminder. cancel()
if event.status != "succeeded" { return }
// sleep for a week; its deadline survives restarts:
@monotonicClock. wait_for( 7UL * 24 * 60 * 60 * 1_000_000_000)
send_survey(self.order_id)
} #derive.agent
#derive.mount ( "/docs/{doc}")
struct WriterAgent {
doc : String
}
fn WriterAgent::new(doc : String) -> WriterAgent {
{ doc, }
}
#derive.endpoint ( post="/draft")
pub fn WriterAgent::draft(
self : Self,
brief : String,
// clients resume this Durable Stream from an offset:
) -> @schema. AgentStream[String] {
@schema. AgentStream::produce( async fn(out) {
for section in ["Summary", "Details", "Actions"] {
// completed model calls are not repeated on recovery:
let reply = @llm. generate_text(
model=@llm. openai( "gpt-4.1" ),
prompt="\{brief}\nWrite the \{section} section." ,
)
guard out. write_one(reply) is Accepted else { return }
}
})
} #derive.agent
struct StockAgent {
sku : String
mut units : Int
}
fn StockAgent::new(sku : String) -> StockAgent {
{ sku, units: 0 }
}
pub fn StockAgent::restock(self : Self, n : Int) -> Unit {
self.units += n
}
// repeat calls come from a cache, without loading the agent:
// the cache clears when a restock succeeds:
// outgoing HTTP, agent calls and key-value writes fail:
#derive.read_only
pub fn StockAgent::level(self : Self) -> Int {
self.units
} type Email struct{ ID, Body string }
// each inbox gets its own agent, in its own WebAssembly VM:
type TriageID struct{ Inbox string }
type TriageIn struct{ Emails []Email }
var Triager = golem. DefineAgent[TriageID](
golem. Spec{Name: "TriageAgent"})
var Triage = Triager. Method[TriageIn, map[string]Label]( "triage")
var _ = Triager. Implement( func( id TriageID) *TriageID {
return &id
}). Handle(Triage, golem. Adapt(( *TriageID).triage))
func ( t *TriageID) triage( in TriageIn) map[string]Label {
labels := map[string]Label{}
for _, email := range in.Emails {
label := parseLabel( classify(email.Body))
// it resumes here after a crash, with memory and files:
applyLabel(t.Inbox, email.ID, label)
labels[email.ID] = label
}
return labels
} Scala, MoonBit and Go agents each have their own durable file system too, but no supported SQLite library.
var g = genkit. Init(ctx,
genkit. WithPlugins( &openai. OpenAI{}, &anthropic. Anthropic{}))
type Review struct {
OK bool `json:"ok"`
Notes string `json:"notes"`
}
type Draft struct {
Text string
Review Review
}
type DraftID struct{ Topic string }
type WriteIn struct{ Idea string }
var Drafter = golem. DefineAgent[DraftID](
golem. Spec{Name: "DraftAgent"})
var Write = Drafter. Method[WriteIn, Draft]( "write")
var _ = Drafter. Implement( func( id DraftID) *DraftID {
return &id
}). Handle(Write, golem. Adapt( func( d *DraftID, in WriteIn) Draft {
// draft with one provider, review with another:
draft := golem. Must(genkit. GenerateText(ctx, g,
ai. WithModelName( "openai/gpt-4.1" ),
ai. WithPrompt( "Product update on "+d.Topic +": "+in.Idea)))
review, _, err := genkit. GenerateData[Review](ctx, g,
ai. WithModelName( "anthropic/claude-sonnet-4-5"),
ai. WithPrompt( "Review this product update:\n"+draft))
golem. Must0(err)
return Draft{Text: draft, Review: *review}
})) type PlannerID struct{ Topic string }
var Planner = golem. DefineAgent[PlannerID](
golem. Spec{Name: "PlannerAgent"})
var Plan = Planner. Method[golem. Unit, PlanResult]( "plan")
var _ = Planner. Implement( func( id PlannerID) *PlannerID {
return &id
}). Handle(Plan, golem. Adapt0( func( p *PlannerID) PlanResult {
// wait for a typed result from a Rust ResearchAgent:
facts := researcher. MustGetResearchAgent(
researcher. ResearchAgentID{Topic: p.Topic}). MustGather()
// send now, without waiting for a result:
slack. MustGetSlackAgent( slack. SlackAgentID{Channel: "ops"}).
MustTriggerPost(facts.Summary)
// discover agents, their methods, and available tools:
var agents, tools []string
for _, a := range reflection. DiscoverAgentTypes() {
var methods []string
for _, m := range a. Methods() {
methods = append(methods, m. Name())
}
agents = append(agents,
a. Name() +": "+strings. Join(methods, ", "))
}
for _, t := range reflection. DiscoverTools() {
tools = append(tools, t. Name())
}
return PlanResult{Facts: facts, Agents: agents, Tools: tools}
})) type CheckoutID struct{ OrderID string }
type PayIn struct{ Amount int32 }
var Checkout = golem. DefineAgent[CheckoutID]( golem. Spec{
Name: "CheckoutAgent",
// webhooks require this agent to have an HTTP mount:
HTTP: &golem. Mount{Path: "/checkout/{orderId}"},
})
var Pay = Checkout. Method[PayIn, golem. Unit]( "pay")
var _ = Checkout. Implement( func( id CheckoutID) *CheckoutID {
return &id
}). Handle(Pay, golem. AdaptUnit( func( c *CheckoutID, in PayIn) {
paid := golem. NewWebhook[PaymentEvent]()
createPaymentLink(c.OrderID, in.Amount, paid. URL())
// schedule a call on another agent for tomorrow:
mailer := Mailer. Get( MailerID{OrderID: c.OrderID})
reminder := Remind. MustSchedule(mailer, inDays( 1), golem. Unit{})
// it waits here for days if needed, using no CPU or memory:
event := paid. MustAwait()
reminder. Cancel()
if event.Status != "succeeded" {
return
}
// sleep for a week; its deadline survives restarts:
time. Sleep( 7 * 24 * time.Hour)
sendSurvey(c.OrderID)
})) type WriterID struct{ Doc string }
type DraftIn struct{ Brief string }
var Writer = golem. DefineAgent[WriterID]( golem. Spec{
Name: "WriterAgent", HTTP: &golem. Mount{Path: "/docs/{doc}"},
})
var Draft = Writer. Method[DraftIn, golem. AgentStream[string]](
// clients resume this Durable Stream from an offset:
"draft", golem. HTTP(golem. POST( "/draft")))
var _ = Writer. Implement( func( id WriterID) *WriterID {
return &id
}). Handle(Draft, golem. Adapt(( *WriterID).draft))
func ( *WriterID) draft( in DraftIn) golem. AgentStream[string] {
return golem. ProduceStream(in.write)
}
func ( in DraftIn) write( w *golem. AgentStreamWriter[string]) error {
sections := []string{"Summary", "Details", "Actions"}
for _, section := range sections {
// completed model calls are not repeated on recovery:
reply := golem. Must(ai.Responses. New(ctx,
responses. ResponseNewParams{Model: "gpt-4.1" ,
Input: responses. ResponseNewParamsInputUnion{
OfString: openai. String(in.Brief +
"\nWrite the " + section + " section." )}}))
if err := w. Write(reply. OutputText()); err != nil {
return err
}
}
return nil
} type StockID struct{ SKU string }
type RestockIn struct{ N int32 }
type stock struct{ units int32 }
var Stock = golem. DefineAgent[StockID](
golem. Spec{Name: "StockAgent"})
var Restock = Stock. Method[RestockIn, golem. Unit]( "restock")
var Level = Stock. Method[golem. Unit, int32]( "level",
// repeat calls come from a cache, without loading the agent:
// the cache clears when a restock succeeds:
// outgoing HTTP, agent calls and key-value writes fail:
golem. ReadOnly())
var impl = Stock. Implement( func( StockID) *stock { return &stock{} })
var _ = impl. Handle(Restock, golem. AdaptUnit(( *stock).restock))
var _ = impl. Handle(Level, golem. Adapt0(( *stock).level))
func ( s *stock) restock( in RestockIn) { s.units += in.N }
func ( s *stock) level() int32 { return s.units } export const summarize = toolDefinition( 'summarize')
. body(( b) => b. positional( 'url', z. string()). returns(z. string()))
summarize. implement({
// each call gets a fresh VM that can't read the agent's memory:
summarize: async ({ url }) => {
// recovery reuses this completed fetch's response:
const page = await ( await fetch(url)). text()
return ok( await summarizeText(page))
},
})
export const Digest = defineAgent({
name: 'DigestAgent',
id: { topic: z. string() },
methods: {
build: method({ input: { urls: Urls }, returns: Urls }),
},
}). implement({
init: () => ({}),
methods: {
async build({ urls }) {
const out: string[] = []
for ( const url of urls) {
out. push( await summarize. client(). summarize({ url }))
}
return out
},
},
}) export const outline = toolDefinition( 'outline', {
// it works on the agent's files when its binding allows it:
requiresFilesystem: true,
}). body(( b) => b. positional( 'file', z. string()). returns(Headings))
outline. implement({
outline: async ({ file }) => {
const lines = fs. readFileSync(file, 'utf8'). split( '\n')
return ok(lines. filter(( line) => line. startsWith( '#')))
},
})
export const Builder = defineAgent({
name: 'BuildAgent',
id: { project: z. string() },
methods: { build: method({ input: {}, returns: Headings }) },
}). implement({
init: () => ({}),
methods: {
async build() {
// bash calls the bound git, npm, tsc and node tools:
const run = await BashClient. newClient(). run(
'git pull && npm install && tsc && node dist/build.js' ,
'/workspace/site', 600)
if (run.exitCode !== 0) throw new Error(run.stderr)
const file = '/workspace/site/dist/report.md'
return outline. client(). outline({ file })
},
},
}) export async function stripeRefund(
revealedKey: string, charge: string, amount: number,
): Promise<Response> {
// Golem adds an idempotency key unless you set one:
// Stripe honors that key, so a retry refunds once:
return fetch( 'https://api.stripe.com/v1/refunds' , {
method: 'POST',
headers: { Authorization: 'Bearer ' + revealedKey },
body: new URLSearchParams({ charge, amount: String(amount) }),
})
} // define a grant for connections to the orders API only:
const ordersApi: permissions. PatternGrant = {
class: 'network', owner: '', recipient: '*',
verb: 'connect', resourceId: 'orders.example.com:443' ,
}
export function shareOrders( target: AgentId) {
const bounds = { lowerPositive: [ordersApi] }
const parent = permissions. deriveFromWallet(bounds). unwrap()
// install a one-hour child; keep its parent for revocation:
const child = parent. derive(
bounds, permissions.Lifespan. iso8601( 'PT1H'),
). unwrap()
child. install(permissions.Holder. agent(target)). unwrap()
return parent
}
export function revokeOrders( parent: Card) {
// revoking it revokes the child and every card derived from it:
parent. revoke(). unwrap()
} const refuse = ( val: string) =>
new ToolInvokeError({ tag: 'constraint-violation', val })
// once configured, this wraps every refund call:
// agent code cannot list, remove or bypass it:
export const refundPolicy = refund. middleware({
name: 'refund-policy',
implementation: {
refund: async ( args, { underlying, principal }) => {
// reject calls by their arguments and enforce a rate limit:
if (args.amount > 100_000) throw refuse( 'over the limit')
await checkRateLimit(principal)
if (args.amount > 10_000) {
// the durable caller can wait for a person across restarts:
const approval = createPromise()
await askFinance(approval, args.charge, args.amount)
const verdict = await awaitPromise(approval)
if ( !approved(verdict)) throw refuse( 'declined')
}
// forwarding cannot widen the agent's permissions:
const receipt = await underlying. refund(args)
// record the refund and hide the customer's email:
await audit(args.charge, args.amount, receipt.id)
return { ... receipt, customerEmail: '[redacted]' }
},
},
}) export const Support = defineAgent({
name: 'SupportAgent',
id: { ticket: z. string() },
// the agent receives an opaque handle, not the key:
config: { stripeKey: s. secret(z. string()) },
methods: {
issueRefund: method({
input: { charge: z. string(), amount: z. int() },
returns: Refund,
}),
},
}). implement({
init: () => ({}),
methods: {
issueRefund({ charge, amount }) {
// refund needs a grant to reveal it; each reveal is recorded:
// a binding can let a tool forward handles but reveal none:
return refund. client(). refund({
charge, amount, apiKey: this.config.stripeKey,
})
},
},
}) import { CrmFindCustomerClient }
from 'crm-find-customer-tool-guest-client'
export const Account = defineAgent({
name: 'AccountAgent',
id: { team: z. string() },
methods: {
describe: method({
// over MCP, this method becomes a tool with this description:
description: 'Summarize a customer account by email',
input: { email: z. string() }, returns: z. string(),
}),
},
}). implement({
init: () => ({}),
methods: {
async describe({ email }) {
// a client generated from the MCP server's schemas:
// Golem checks, records and replays it like any tool call:
// a retry resends the same idempotency key to the server:
const call = CrmFindCustomerClient. newClient()
. crm_find_customer(email)
const { result } = await call. collect()
if (result.status === 'rejected') throw result.reason
// the MCP server's output schema types this result:
const { name, plan } = result.value.structured
return `${name} is on the ${plan} plan`
},
},
}) export const summarize = Tool. toolDefinition( "summarize"). body(
( b) => b. positional( "url", Schema.String). returns(Schema.String))
summarize. implement({
// each call gets a fresh VM that can't read the agent's memory:
summarize: ({ url }) => Effect. gen( function* () {
// recovery reuses this completed fetch's response:
const page = yield* HttpClient. get(url). pipe(
Effect. flatMap(( res) => res.text))
return Tool. ok( yield* summarizeText(page))
}). pipe(Effect.orDie),
}, FetchHttpClient.layer)
export const Digest = defineAgent({
name: "DigestAgent",
id: { topic: Schema.String },
methods: {
build: method({ input: { urls: Urls }, success: Urls }),
},
}). implement({
init: () => Effect.void,
methods: () => ({
build: ({ urls }) => Effect. gen( function* () {
const out: string[] = []
for ( const url of urls) {
out. push( yield* summarize. client()({ url }))
}
return out
}). pipe(Effect.orDie),
}),
}) export const outline = Tool. toolDefinition( "outline", {
// it works on the agent's files when its binding allows it:
requiresFilesystem: true,
}). body(( b) => b. positional( "file", Schema.String)
. returns(Headings))
outline. implement({
outline: ({ file }) => Effect. sync(() => {
const lines = fs. readFileSync(file, "utf8"). split( "\n")
return Tool. ok(lines. filter(( line) => line. startsWith( "#")))
}),
})
export const Builder = defineAgent({
name: "BuildAgent",
id: { project: Schema.String },
methods: { build: method({ input: {}, success: Headings }) },
}). implement({
init: () => Effect.void,
methods: () => ({
build: () => Effect. gen( function* () {
// bash calls the bound git, npm, tsc and node tools:
const run = yield* BashClient. newClient(). run(
"git pull && npm install && tsc && node dist/build.js" ,
"/workspace/site", 600)
if (run.exitCode !== 0) return yield* Effect. die(run.stderr)
const file = "/workspace/site/dist/report.md"
const headings: unknown = yield* outline. client()({ file })
return yield* Schema. decodeUnknownEffect(Headings)(headings)
}). pipe(Effect.orDie),
}),
}) export const stripeRefund = (
revealedKey: string, charge: string, amount: number,
) =>
// Golem adds an idempotency key unless you set one:
// Stripe honors that key, so a retry refunds once:
HttpClientRequest. post( "https://api.stripe.com/v1/refunds" ). pipe(
HttpClientRequest. bearerToken(revealedKey),
HttpClientRequest. bodyUrlParams({ charge, amount }),
HttpClient.execute,
) // define a grant for connections to the orders API only:
const ordersApi: Permissions. PatternGrant = {
class: "network", owner: "", recipient: "*",
verb: "connect", resourceId: "orders.example.com:443" ,
}
export const shareOrders = Effect. fn( function* ( target: AgentId) {
const bounds = { lowerPositive: [ordersApi] }
const parent = yield* Permissions. deriveFromWallet(bounds)
// install a one-hour child; keep its parent for revocation:
const child = yield* Permissions. derive(
parent, bounds, Permissions.Lifespan. fromIso8601( "PT1H"),
)
yield* Permissions. install(
child, Permissions.Holder. agent(target))
return parent
})
export const revokeOrders = ( parent: Card) =>
// revoking it revokes the child and every card derived from it:
Permissions. revoke(parent) const refuse = ( val: string) => Effect. fail(
new Middleware. MiddlewareError(
{ tag: "constraint-violation", val }))
// once configured, this wraps every refund call:
// agent code cannot list, remove or bypass it:
export const refundPolicy = Middleware. typed({
name: "refund-policy",
presented: refund, parameters: Middleware.NoParameters,
handler: {
refund: ( args, ctx) => Effect. gen( function* () {
// reject calls by their arguments and enforce a rate limit:
if (args.amount > 100_000) yield* refuse( "over the limit")
yield* checkRateLimit(ctx.principal)
if (args.amount > 10_000) {
// the durable caller can wait for a person across restarts:
const approval = yield* Agents.Promises.create
yield* askFinance(approval, args.charge, args.amount)
const verdict = yield* Agents.Promises. await(approval)
if ( !approved(verdict)) yield* refuse( "declined")
}
// forwarding cannot widen the agent's permissions:
const receipt = yield* ctx. underlying(args)
// record the refund and hide the customer's email:
yield* audit(args.charge, args.amount, receipt.id)
return { ... receipt, customerEmail: "[redacted]" }
}). pipe(Effect. catchTag( "AgentsHostError", Effect.die)),
},
}) // the agent receives an opaque handle, not the key:
class SupportConfig extends defineConfig( "SupportAgent.Config" , {
stripeKey: Schema. Redacted(Schema.String),
}) {}
export const Support = defineAgent({
name: "SupportAgent",
id: { ticket: Schema.String },
config: SupportConfig,
methods: {
issueRefund: method({
input: { charge: Schema.String, amount: Schema.Int },
success: Refund,
}),
},
}). implement({
init: () => Effect.void,
methods: () => ({
issueRefund: ({ charge, amount }) => Effect. gen( function* () {
const config = yield* SupportConfig
// refund needs a grant to reveal it; each reveal is recorded:
// a binding can let a tool forward handles but reveal none:
return yield* refund. client()({
charge, amount, apiKey: yield* config.stripeKey.borrow,
})
}). pipe(Effect.orDie),
}),
}) import { CrmFindCustomerClient }
from "crm-find-customer-tool-guest-client"
export const Account = defineAgent({
name: "AccountAgent",
id: { team: Schema.String },
methods: {
describe: method({
// over MCP, this method becomes a tool with this description:
description: "Summarize a customer account by email",
input: { email: Schema.String }, success: Schema.String,
}),
},
}). implement({
init: () => Effect.void,
methods: () => ({
describe: ({ email }) => Effect. gen( function* () {
// a client generated from the MCP server's schemas:
// Golem checks, records and replays it like any tool call:
// a retry resends the same idempotency key to the server:
const call = yield* CrmFindCustomerClient. newClient()
. crm_find_customer(email)
const { result } = yield* call.collect
const value = yield* Effect. fromResult(result)
// the MCP server's output schema types this result:
const { name, plan } = value.structured
return `${name} is on the ${plan} plan`
}). pipe(Effect.scoped, Effect.orDie),
}),
}) #[tool_definition]
pub trait Summarize {
async fn summarize( &self, url: Url) -> Result<String, AppError>;
}
#[tool_implementation]
impl Summarize for SummarizeImpl {
// each call gets a fresh VM that can't read the agent's memory:
async fn summarize( &self, url: Url) -> Result<String, AppError> {
let req = Request::get(url) . body( vec![])?;
// recovery reuses this completed fetch's response:
let page = http::response_text( http::send(req) .await? ).await? ;
Ok( summarize_text( &page).await? )
}
}
#[agent_definition]
pub trait DigestAgent {
fn new(topic : String) -> Self;
async fn build( &self, urls: Urls) -> Result<Urls, AppError>;
}
#[agent_implementation]
impl DigestAgent for DigestAgentImpl {
fn new(_topic : String) -> Self { Self }
async fn build( &self, urls: Urls) -> Result<Urls, AppError> {
let mut out = vec![];
for url in urls {
out. push( SummarizeClient::new() . summarize(url) .await? );
}
Ok(out)
}
} // it works on the agent's files when its binding allows it:
#[tool_definition(requires_filesystem = true)]
pub trait Outline {
fn outline( &self, file: String) -> Result<Headings, AppError>;
}
#[tool_implementation]
impl Outline for OutlineImpl {
fn outline( &self, file: String) -> Result<Headings, AppError> {
Ok( fs::read_to_string(file) ?. lines()
. filter( |l| l. starts_with( '#')). map( Into::into). collect())
}
}
#[agent_definition]
pub trait BuildAgent {
fn new(project : String) -> Self;
async fn build( &self) -> Result<Headings, AppError>;
}
#[agent_implementation]
impl BuildAgent for BuildAgentImpl {
fn new(_project : String) -> Self { Self }
async fn build( &self) -> Result<Headings, AppError> {
// bash calls the bound git, npm, tsc and node tools:
let run = BashClient::new() . run(
"git pull && npm install && tsc && node dist/build.js" . into(),
"/workspace/site". into(), 600).await? ;
if run. exit_code != 0 { return Err(run . stderr. into()); }
let file = "/workspace/site/dist/report.md" . to_string();
Ok( OutlineClient::default() . outline(file) .await? )
}
} pub async fn stripe_refund(
revealed_key: &str, charge: &str, amount: u64,
) -> Result<http::Response, AppError> {
// Golem adds an idempotency key unless you set one:
// Stripe honors that key, so a retry refunds once:
let form = [( "charge", charge), ( "amount", &amount. to_string())];
let req = Request::post( "https://api.stripe.com/v1/refunds" )
. header( AUTHORIZATION, format!( "Bearer {revealed_key}"))
. header( CONTENT_TYPE, "application/x-www-form-urlencoded")
. body( serde_urlencoded::to_string(form) ?. into_bytes()) ?;
Ok( http::send(req) .await? )
} // define a grant for connections to the orders API only:
fn orders_api() -> PatternGrant {
PatternGrant::new(
"network", "", "*", "connect", "orders.example.com:443" ,
)
}
pub fn share_orders(target : AgentId)
-> Result<PersistentCard, AppError> {
let bounds = Bounds {
lower_positive: vec![orders_api()], .. Bounds::default()
};
let parent = permissions::derive_from_wallet( &bounds, None)?;
// install a one-hour child; keep its parent for revocation:
let hour = Lifespan::iso8601( "PT1H")?;
let child = parent. derive( &bounds, Some( &hour))?;
child. install( &Holder::Agent(target)) ?;
Ok(parent)
}
pub fn revoke_orders(parent : PersistentCard)
-> Result<(), AppError> {
// revoking it revokes the child and every card derived from it:
parent. revoke() ?;
Ok(())
} fn refuse(why : &str) -> ToolInvokeError<AppError> {
ToolInvokeError::ConstraintViolation(why . into())
}
// once configured, this wraps every refund call:
// agent code cannot list, remove or bypass it:
#[tool_middleware(name = "refund-policy",
constructor = RefundPolicy::new)]
impl RefundMiddleware for RefundPolicy {
async fn refund( &self, underlying: &RefundUnderlying,
charge: String, amount: u64, api_key: GuestSecretHandle,
caller: Principal,
) -> Result<Receipt, ToolInvokeError<AppError>> {
// reject calls by their arguments and enforce a rate limit:
if amount > 100_000 { return Err( refuse( "over the limit")); }
check_rate_limit( &caller).await? ;
if amount > 10_000 {
// the durable caller can wait for a person across restarts:
let approval = create_promise();
ask_finance( &approval, &charge, amount).await? ;
let verdict = await_promise( &approval).await ;
if !approved( &verdict) { return Err( refuse( "declined")); }
}
// forwarding cannot widen the agent's permissions:
let receipt = underlying. refund(charge . clone(), amount, api_key)
.await? ;
// record the refund and hide the customer's email:
audit( &charge, amount, &receipt. id).await? ;
Ok( Receipt { customer_email: "[redacted]". into(), .. receipt })
}
} #[derive( ConfigSchema)]
pub struct SupportConfig {
// the agent receives an opaque handle, not the key:
#[config_schema(secret)] stripe_key : Secret<String>,
}
#[agent_definition]
pub trait SupportAgent {
fn new(ticket : String,
#[agent_config] config: Config<SupportConfig>) -> Self;
async fn issue_refund( &self, charge: String, amount: u64)
-> Result<Receipt, AppError>;
}
struct SupportAgentImpl { config: Config<SupportConfig> }
#[agent_implementation]
impl SupportAgent for SupportAgentImpl {
fn new(_ticket : String,
#[agent_config] config: Config<SupportConfig>) -> Self {
Self { config }
}
async fn issue_refund( &self, charge: String, amount: u64)
-> Result<Receipt, AppError> {
// refund needs a grant to reveal it; each reveal is recorded:
// a binding can let a tool forward handles but reveal none:
let api_key = self. config. get() ?. stripe_key. handle() ?;
Ok( RefundClient::new() . refund(charge, amount, api_key) .await? )
}
} use crm_find_customer_tool_guest_client::CrmFindCustomerClient;
#[agent_definition]
pub trait AccountAgent {
fn new(team : String) -> Self;
// over MCP, this method becomes a tool with this description:
#[description( "Summarize a customer account by email")]
async fn describe( &self, email: String)
-> Result<String, AppError>;
}
struct AccountAgentImpl;
#[agent_implementation]
impl AccountAgent for AccountAgentImpl {
fn new(_team : String) -> Self { Self }
async fn describe( &self, email: String)
-> Result<String, AppError> {
// a client generated from the MCP server's schemas:
// Golem checks, records and replays it like any tool call:
// a retry resends the same idempotency key to the server:
let call = CrmFindCustomerClient::new()
. crm_find_customer(email) .await? ;
let result = call. collect() .await. result?;
// the MCP server's output schema types this result:
let customer = result. structured;
let (name, plan) = (customer . name, customer. plan);
Ok( format!( "{name} is on the {plan} plan"))
}
} @toolDefinition()
trait Summarize:
def summarize( url: String): Future[String]
@toolImplementation()
final class SummarizeImpl extends Summarize:
// each call gets a fresh VM that can't read the agent's memory:
def summarize( url: String): Future[String] =
// recovery reuses this completed fetch's response:
fetch(url).toFuture.flatMap(_.text().toFuture)
.flatMap(summarizeText)
@agentDefinition()
trait DigestAgent extends BaseAgent:
class Id( val topic: String)
def build( urls: Urls): Future[Urls]
@agentImplementation()
final class DigestAgentImpl( topic: String) extends DigestAgent:
def build( urls: Urls): Future[Urls] =
urls.foldLeft( Future.successful( List.empty[ String])):
(out, url) =>
for
done <- out
summary <- SummarizeClient().summarize(url).map(_.orThrow)
yield done :+ summary @toolDefinition(
// it works on the agent's files when its binding allows it:
requiresFilesystem = true,
)
trait Outline:
def outline( file: String): Headings
@toolImplementation()
final class OutlineImpl extends Outline:
def outline( file: String): Headings =
fs.readFileSync(file, "utf8").split( '\n').toList
.filter(_.startsWith( "#"))
@agentDefinition()
trait BuildAgent extends BaseAgent:
class Id( val project: String)
def build() : Future[Headings]
@agentImplementation()
final class BuildAgentImpl( project: String) extends BuildAgent:
def build() : Future[Headings] =
for
// bash calls the bound git, npm, tsc and node tools:
run <- BashClient.newClient().run(
"git pull && npm install && tsc && node dist/build.js" ,
"/workspace/site", UInt( 600)).map(_.orThrow)
_ = if run.exitCode.value != 0 then sys.error(run.stderr)
file = "/workspace/site/dist/report.md"
headings <- OutlineClient().outline(file).map(_.orThrow)
yield headings def stripeRefund(
revealedKey: String, charge: String, amount: Int,
): Future[Response] =
// Golem adds an idempotency key unless you set one:
// Stripe honors that key, so a retry refunds once:
fetch( "https://api.stripe.com/v1/refunds" , new RequestInit {
method = HttpMethod. POST
headers = js. Dictionary(
"Authorization" -> ( "Bearer " + revealedKey))
body = URLSearchParams(js. Dictionary(
"charge" -> charge, "amount" -> amount.toString))
}).toFuture // define a grant for connections to the orders API only:
val ordersApi = PermissionsApi. PatternGrant(
className = "network", owner = "", recipient = "*",
verb = "connect", resourceId = "orders.example.com:443" ,
)
def shareOrders( target: AgentId): PermissionsApi. PersistentCard =
val bounds =
PermissionsApi. Bounds(lowerPositive = List(ordersApi))
val parent = PermissionsApi.deriveFromWallet(bounds).orThrow
// install a one-hour child; keep its parent for revocation:
val child = parent.derive(
bounds, Some( PermissionsApi. Lifespan.iso8601( "PT1H")),
).orThrow
child.install( PermissionsApi. Holder. Agent(target)).orThrow
parent
def revokeOrders( parent: PermissionsApi. PersistentCard): Unit =
// revoking it revokes the child and every card derived from it:
parent.revoke().orThrow def refuse( why: String) =
Future.failed( Refused( ConstraintViolation(why)))
// once configured, this wraps every refund call:
// agent code cannot list, remove or bypass it:
@toolMiddleware(name = "refund-policy")
final class RefundPolicy extends RefundToolMiddleware:
def refund(
underlying: RefundToolUnderlying, charge: String, amount: Int,
apiKey: GuestSecretHandle, caller: Principal,
): Future[Either[ToolInvokeError[RefundError], Refund]] =
val done = for
// reject calls by their arguments and enforce a rate limit:
_ <- if amount > 100_000 then refuse( "over the limit")
else checkRateLimit(caller)
_ <- if amount <= 10_000 then Future.unit else
// the durable caller can wait for a person across restarts:
val approval = HostApi.createPromise()
for
_ <- askFinance(approval, charge, amount)
verdict <- HostApi.awaitPromise(approval)
_ <- if approved(verdict) then Future.unit
else refuse( "declined")
yield ()
// forwarding cannot widen the agent's permissions:
forwarded <- underlying.refund(charge, amount, apiKey)
.toMiddlewareResult
// record the refund and hide the customer's email:
_ <- forwarded.fold(_ => Future.unit,
r => audit(charge, amount, r.id))
yield forwarded.map(_.copy(customerEmail = "[redacted]"))
done.recover { case Refused(error) => Left(error) } // the agent receives an opaque handle, not the key:
final case class SupportConfig( stripeKey: Secret[String])
derives Schema
@agentDefinition()
trait SupportAgent extends BaseAgent
with AgentConfig[SupportConfig]:
class Id( val ticket: String)
def issueRefund( charge: String, amount: Int): Future[Refund]
@agentImplementation()
final class SupportAgentImpl(
ticket: String, config: Config[SupportConfig],
) extends SupportAgent:
def issueRefund( charge: String, amount: Int): Future[Refund] =
// refund needs a grant to reveal it; each reveal is recorded:
// a binding can let a tool forward handles but reveal none:
RefundToolClient().refund(
charge, amount, config.value.stripeKey.handle(),
).map(_.orThrow) import golem. bridge. client. crm_find_customer. CrmFindCustomerClient
@agentDefinition()
trait AccountAgent extends BaseAgent:
class Id( val team: String)
// over MCP, this method becomes a tool with this description:
@description( "Summarize a customer account by email")
def describe( email: String): Future[String]
@agentImplementation()
final class AccountAgentImpl( team: String) extends AccountAgent:
def describe( email: String): Future[String] =
// a client generated from the MCP server's schemas:
// Golem checks, records and replays it like any tool call:
// a retry resends the same idempotency key to the server:
val call = CrmFindCustomerClient.newClient()
.crmFindCustomer(email).orThrow
call.collect().map : collected =>
// the MCP server's output schema types this result:
val found = collected.result.orThrow.structured
s"${found.name} is on the ${found.plan} plan" #derive.tool ( "summarize")
struct Summarize {}
// each call gets a fresh VM that can't read the agent's memory:
pub async fn Summarize::summarize(url : String) -> String {
// recovery reuses this completed fetch's response:
let page = read_text( @http. send( get(url)). unwrap())
summarize_text(page)
}
#derive.agent
struct DigestAgent {
topic : String
}
fn DigestAgent::new(topic : String) -> DigestAgent {
{ topic, }
}
pub async fn DigestAgent::build(
self : Self, urls : Array[String],
) -> Array[String] {
let out = []
for url in urls {
out. push( SummarizeClient::new(). summarize(url). unwrap())
}
out
} // it works on the agent's files when its binding allows it:
#derive.tool ( "outline", requires_filesystem=true)
struct Outline {}
pub async fn Outline::outline(file : String) -> Array[String] {
let root = @fs. get_root_dir(). unwrap()
let text = @fs. read_string(root, file). unwrap()
let lines = text. split( "\n"). map(line => line. to_owned())
lines. filter(line => line. has_prefix( "#")). collect()
}
#derive.agent
struct BuildAgent {
project : String
}
fn BuildAgent::new(project : String) -> BuildAgent {
{ project, }
}
pub async fn BuildAgent::build(self : Self) -> Array[String] {
// bash calls the bound git, npm, tsc and node tools:
let run = @bash. BashClient::new(). run(
"git pull && npm install && tsc && node dist/build.js" ,
"/workspace/site", 600,
). unwrap()
if run.exit_code != 0 { fail(run.stderr) }
let file = "/workspace/site/dist/report.md"
OutlineClient::new(). outline(file). unwrap()
} pub async fn stripe_refund(
revealed_key : String, charge : String, amount : Int,
) -> @http. Response {
// Golem adds an idempotency key unless you set one:
// Stripe honors that key, so a retry refunds once:
@http. send( form_post(
"https://api.stripe.com/v1/refunds" ,
[( "charge", charge), ( "amount", amount. to_string())],
headers=[( "Authorization", "Bearer " + revealed_key)],
)). unwrap()
} // define a grant for connections to the orders API only:
let orders_api : @permissions. PatternGrant = {
class: "network", owner: "", recipient: "*",
verb: "connect", resource_id: "orders.example.com:443" ,
}
pub fn share_orders(
target : @types. AgentId,
) -> @permissions. PersistentCard raise {
let bounds = @permissions. Bounds::new(lower_positive =[orders_api])
let parent = @permissions. derive_from_wallet(bounds)
// install a one-hour child; keep its parent for revocation:
let child = parent. derive_child(
bounds, lifespan=@permissions. Lifespan::iso8601( "PT1H"),
)
child. install( Agent(target))
parent
}
pub fn revoke_orders(
parent : @permissions. PersistentCard,
) -> Unit raise {
// revoking it revokes the child and every card derived from it:
ignore(parent. revoke())
} fn[E] refuse(val : String) -> @toolMiddleware. ToolInvokeError[E] {
ConstraintViolation(val)
}
// once configured, this wraps every refund call:
// agent code cannot list, remove or bypass it:
#derive.tool_middleware ( "refund-policy", presented="Refund")
struct RefundPolicy {}
pub async fn RefundPolicy::refund(
underlying : RefundUnderlying, charge : String, amount : Int,
api_key : @secrets. SecretHandle[String],
principal : @tool. Principal,
) -> Result[RefundResult,
@toolMiddleware. ToolInvokeError[RefundError]] {
// reject calls by their arguments and enforce a rate limit:
if amount > 100_000 { return Err( refuse( "over the limit")) }
if check_rate_limit(principal) is Err(e) { return Err(e) }
if amount > 10_000 {
// the durable caller can wait for a person across restarts:
let approval = @api. create_promise()
ask_finance(approval, charge, amount)
let verdict = @api. await_promise(approval)
if !approved(verdict) { return Err( refuse( "declined")) }
}
// forwarding cannot widen the agent's permissions:
let forwarded = underlying. refund(charge, amount, api_key)
guard forwarded is Ok(receipt) else { return forwarded }
// record the refund and hide the customer's email:
audit(charge, amount, receipt.id)
Ok({ ..receipt, customer_email: "[redacted]" })
} #derive.config
struct SupportConfig {
// the agent receives an opaque handle, not the key:
stripe_key : @config. Secret[String]
}
#derive.agent
struct SupportAgent {
ticket : String
config : SupportConfig
}
fn SupportAgent::new(
ticket : String, config : @config. Config[SupportConfig],
) -> SupportAgent {
{ ticket, config: config.value }
}
pub async fn SupportAgent::issue_refund(
self : Self, charge : String, amount : Int,
) -> RefundResult {
// refund needs a grant to reveal it; each reveal is recorded:
// a binding can let a tool forward handles but reveal none:
RefundClient::new(). refund(
charge, amount,
@secrets. SecretHandle::from_config(self.config.stripe_key),
). unwrap()
} #derive.agent
struct AccountAgent {
team : String
}
fn AccountAgent::new(team : String) -> AccountAgent {
{ team, }
}
// over MCP, this method becomes a tool with this description:
/// Summarize a customer account by email
pub async fn AccountAgent::describe(
self : Self, email : String,
) -> String {
// a client generated from the MCP server's schemas:
// Golem checks, records and replays it like any tool call:
// a retry resends the same idempotency key to the server:
let call = @crm. CrmFindCustomerClient::new()
. crm_find_customer(email). unwrap()
let { result, .. } = call. collect()
guard result is Ok(found) else { fail( "CRM lookup failed") }
// the MCP server's output schema types this result:
let { name, plan } = found.structured
"\{name} is on the \{plan} plan"
} var Tool = tool. DefineTool[Summarizer]( "summarize", tool. Spec{})
var Summarize = Tool. Body[Args, string](
func( a *Args, s *tool. CommandSpec) { s. Positional( &a.URL) })
var _ = Summarize. Handle(summarize)
// each call gets a fresh VM that can't read the agent's memory:
func summarize( _ *tool. Context, a Args) ( string, error) {
// recovery reuses this completed fetch's response:
res := golem. Must(http. Get(a.URL))
defer res.Body. Close()
page := golem. Must(io. ReadAll(res.Body))
return summarizeText( string(page)), nil
}
var Digest = golem. DefineAgent[DigestID](
golem. Spec{Name: "DigestAgent"})
var Build = Digest. Method[BuildIn, []string]( "build")
var _ = Digest. Implement( func( DigestID) *digest {
return &digest{}
}). Handle(Build, golem. Adapt( func( _ *digest, in BuildIn) []string {
var out []string
for _, url := range in.URLs {
out = append(out, golem. Must(Summarize. Call( func( a *Args) {
a.URL = url
})))
}
return out
})) var Outline = tool. DefineTool[Outliner]( "outline", tool. Spec{
// it works on the agent's files when its binding allows it:
RequiresFilesystem: true,
}). Body[Args, Headings]( func( a *Args, s *tool. CommandSpec) {
s. Positional( &a.File)
})
var _ = Outline. Handle(
func( _ *tool. Context, a Args) ( Headings, error) {
text := string(golem. Must(os. ReadFile(a.File)))
headings := regexp. MustCompile( "(?m)^#.*" )
return headings. FindAllString(text, -1), nil
})
var Builder = golem. DefineAgent[BuildID](
golem. Spec{Name: "BuildAgent"})
var Build = Builder. Method[golem. Unit, Headings]( "build")
var _ = Builder. Implement( func( id BuildID) *BuildID {
return &id
}). Handle(Build, golem. Adapt0( func( *BuildID) Headings {
// bash calls the bound git, npm, tsc and node tools:
script := "git pull && npm install && tsc && node dist/build.js"
run := golem. Must(bash.Run. Call( func( a *bash. RunArgs) {
a.Script, a.Cwd, a.Timeout = script, "/workspace/site", 600
}))
if run.ExitCode != 0 {
panic(run.Stderr)
}
file := "/workspace/site/dist/report.md"
return golem. Must(Outline. Call( func( a *Args) { a.File = file }))
})) func StripeRefund(
revealedKey, charge string, amount int32,
) ( *http. Response, error) {
// Golem adds an idempotency key unless you set one:
// Stripe honors that key, so a retry refunds once:
form := url. Values{"charge": {charge},
"amount": {fmt. Sprint(amount)}}
req := golem. Must(http. NewRequest( "POST",
"https://api.stripe.com/v1/refunds" ,
strings. NewReader(form. Encode())))
req.Header. Set( "Authorization", "Bearer "+revealedKey)
req.Header. Set( "Content-Type",
"application/x-www-form-urlencoded")
return http.DefaultClient. Do(req)
} // define a grant for connections to the orders API only:
var ordersAPI = golem. PatternGrant{
Class: "network", Owner: "", Recipient: "*",
Verb: "connect", ResourceID: "orders.example.com:443" ,
}
func ShareOrders( target golem. AgentID) golem. PersistentCard {
bounds := golem. Bounds{
LowerPositive: []golem. PatternGrant{ordersAPI}}
parent := golem. MustDeriveFromWallet(
bounds, golem. None[golem. Lifespan]())
// install a one-hour child; keep its parent for revocation:
child := parent. MustDerive(
bounds, golem. Some(golem. LifespanOf(time.Hour)))
golem. Must0(child. Install(golem. AgentHolder(target)))
return parent
}
func RevokeOrders( parent golem. PersistentCard) {
// revoking it revokes the child and every card derived from it:
golem. Must(parent. Revoke())
} // once configured, this wraps every refund call:
// agent code cannot list, remove or bypass it:
var Policy = refund.Tool. Middleware[golem. Unit]( "refund-policy",
tool. MiddlewareSpec{})
var _ = Policy. Handle(refund.Root, func(
ctx *tool. MiddlewareContext[golem. Unit], a refund. RootArgs,
) ( r refund. Refund, err error) {
// reject calls by their arguments and enforce a rate limit:
if a.Amount > 100_000 {
return r, tool. ConstraintViolation( "over the limit")
}
if err = checkRateLimit(ctx. Principal()); err != nil {
return r, tool. ConstraintViolation( "%v", err)
}
if a.Amount > 10_000 {
// the durable caller can wait for a person across restarts:
approval := golem. NewPromise[Verdict]()
askFinance(approval. ID(), a.Charge, a.Amount)
if !approval. MustAwait().Approved {
return r, tool. ConstraintViolation( "declined")
}
}
// forwarding cannot widen the agent's permissions:
r, err = Policy. Underlying(ctx, refund.Root). Forward(a)
if err == nil {
// record the refund and hide the customer's email:
audit(a.Charge, a.Amount, r.ID)
r.CustomerEmail = "[redacted]"
}
return r, err
}) type SupportID struct{ Ticket string }
type SupportConfig struct {
// the agent receives an opaque handle, not the key:
StripeKey golem. Secret[string]
}
type RefundIn struct {
Charge string
Amount int32
}
type support struct{}
var Support = golem. DefineConfiguredAgent[SupportID, SupportConfig](
golem. Spec{Name: "SupportAgent"})
var IssueRefund = Support. Method[RefundIn, refund. Refund](
"issueRefund")
var _ = Support. Implement( func( SupportID) *support {
return &support{}
}). Handle(IssueRefund, func(
ctx *golem. Context[support], in RefundIn,
) refund. Refund {
// refund needs a grant to reveal it; each reveal is recorded:
// a binding can let a tool forward handles but reveal none:
return golem. Must(refund.Root. Call( func( a *refund. RootArgs) {
a.Charge, a.Amount = in.Charge, in.Amount
a.APIKey = ctx. Config(Support).StripeKey
}))
}) type AccountID struct{ Team string }
type DescribeIn struct{ Email string }
var Account = golem. DefineAgent[AccountID](
golem. Spec{Name: "AccountAgent"})
var Describe = Account. Method[DescribeIn, string]( "describe",
// over MCP, this method becomes a tool with this description:
golem. Description( "Summarize a customer account by email"))
var _ = Account. Implement( func( id AccountID) *AccountID {
return &id
}). Handle(Describe, golem. Adapt(( *AccountID).describe))
func ( *AccountID) describe( in DescribeIn) string {
// a client generated from the MCP server's schemas:
// Golem checks, records and replays it like any tool call:
// a retry resends the same idempotency key to the server:
call := golem. Must(crm.Root. Call( func( a *crm. RootArgs) {
a.Email = in.Email
}))
out := call. Collect()
golem. Must0(out.Err)
// the MCP server's output schema types this result:
c := out.Result.Structured
return c.Name + " is on the " + c.Plan + " plan"
} export const Analyst = defineAgent({
name: 'AnalystAgent',
id: { chat: z. string() },
http: http. mount( '/chats/{chat}', {
// Golem serves files this agent writes, never half-written:
exposeFiles: [{ route: '/*', path: '/out/$1' }],
}),
methods: { ask: method({ input: { question: z. string() },
returns: z. string(), http: http. post( '/ask') }) },
}). implement({
init: ({ id }) => ({ chat: id.chat }),
methods: {
async ask({ question }) {
// the model calls this tool when a user asks for a graph:
const chart = tool({
description: 'Chart sales for a quarter and link to it',
inputSchema: Quarter,
execute: async ({ quarter: q }) => {
const name = crypto. randomUUID()
fs. mkdirSync( '/out', { recursive: true })
fs. writeFileSync( `/out/${name}.png` , await chartPng(q))
fs. writeFileSync( `/out/${name}.html` , pageFor(q, name))
return `/chats/${this. chat}/${name}.html`
},
})
return ( await generateText({ model, prompt: question,
tools: { chart }, stopWhen: stepCountIs( 5) })).text
},
},
}) // AppAgent's model calls this tool to save an app it just wrote:
export const present = toolDefinition( 'present', {
requiresFilesystem: true,
}). body(( b) => b. positional( 'name', Name)
. positional( 'html', z. string()). returns(z. string()))
present. implement({
present: async ({ name, html }) => {
// the app is saved in the calling agent's own files:
fs. mkdirSync( '/out', { recursive: true })
fs. writeFileSync( `/out/${name}.html` , html)
return ok( `${name}.html` )
},
})
export const Apps = defineAgent({
name: 'AppAgent',
id: { chat: z. string() },
http: http. mount( '/apps/{chat}', {
exposeFiles: [{ route: '/*', path: '/out/$1' }],
}),
methods: {
sales: method({
input: { region: z. string() }, returns: z. array(Row),
// the app's buttons fetch('sales?region=...') from here:
http: http. get( '/sales?region={region}'),
}),
},
}). implement({
init: () => ({}),
// each click runs this as durable code in the agent's own VM:
methods: { sales: ({ region }) => querySales(region) },
}) export const Dashboards = defineAgent({
name: 'DashboardAgent',
id: { user: z. string() },
// the path names the agent: /dashboards/ana is Ana's own agent:
http: http. mount( '/dashboards/{user}', {
auth: true,
exposeFiles: [{ route: '/*', path: '/out/$1' }],
}),
methods: {
ask: method({ input: { request: z. string() },
returns: z. string(), http: http. post( '/ask') }),
},
}). implement({
// the first request to /dashboards/ana creates her agent:
init: ({ id }) => ({ user: id.user }),
methods: {
async ask({ request }) {
const name = crypto. randomUUID()
fs. mkdirSync( '/out', { recursive: true })
// the model builds this dashboard for her one request:
fs. writeFileSync( `/out/${name}.html` ,
await dashboardFor( this.user, request))
// what it made for her is served under her path:
return `/dashboards/${this. user}/${name}.html`
},
},
}) const Png = s. unstructuredBinary({ mimeTypes: [ 'image/png'] })
export const Charts = defineAgent({
name: 'ChartAgent',
id: { team: z. string() },
http: http. mount( '/charts/{team}'),
methods: {
chart: method({
// over MCP, a chat assistant calls this when a user asks:
description: 'Draw a sales chart for one quarter',
input: { quarter: z. string() },
// the reply is the PNG itself, over HTTP or MCP:
returns: Png,
http: http. get( '/chart.png?quarter={quarter}' ),
}),
},
}). implement({
init: ({ id }) => ({ team: id.team }),
methods: {
async chart({ quarter }) {
// the chart is drawn for this call, and no file is written:
const val = drawChart( await querySales( this.team, quarter))
return { tag: 'inline', val, mimeType: 'image/png' }
},
},
}) // asked to export results as CSV, the agent replies with a link:
// the link is /exports/<chat>/<random id>.csv, for that user only:
export const Exports = defineHttpRouter( 'ExportsRouter')
. mount( '/exports')
. implementRaw( async ( request) => {
const path = /^\/exports\/( [\w-]+)\/( [\w-]{36})\. csv$/
const m = request.path. match(path)
if (request.method !== 'GET' || !m) {
return { status: 404, headers: [], body: empty() }
}
// that user's agent builds the CSV now and streams it:
const csv = await Analyst.client. get({ chat: m[ 1] })
. exportCsv({ id: m[ 2] })
return {
status: 200,
headers: [
header( 'content-type', 'text/csv'),
// the browser saves it as a download:
header( 'content-disposition',
'attachment; filename="results.csv"' ),
],
body: csv,
}
}) export const Billing = defineAgent({
name: 'BillingAgent',
id: { org: z. string() },
// Golem checks the user's sign-in before any code here runs:
http: http. mount( '/billing/{org}', { auth: true }),
methods: {
ask: method({
// Golem passes in the user who signed in:
input: { question: z. string(), caller: s. principal() },
returns: z. string(), http: http. post( '/ask'),
}),
},
}). implement({
init: ({ id }) => ({ org: id.org }),
methods: {
async ask({ question, caller }) {
if (caller.tag !== 'oidc') throw new Error( 'sign-in required')
// the issuer can be Google, GitLab or any OIDC provider:
const { issuer, sub } = caller
// the model makes a page from that user's own invoices:
const invoices = await invoicesOf( this.org, issuer, sub)
return pageFor(question, invoices)
},
},
}) const Png = s. unstructuredBinary({ mimeTypes: [ 'image/png'] })
export const PrivateCharts = defineAgent({
name: 'PrivateChartsAgent',
id: { team: z. string() },
http: http. mount( '/private/{team}', { auth: true }),
methods: {
ask: method({
input: { question: z. string(), caller: s. principal() },
returns: z. number(), http: http. post( '/ask'),
}),
open: method({ input: { n: z. number(), caller: s. principal() },
returns: s. option(Png), http: http. get( '/charts/{n}') }),
},
}). implement({
init: () : { charts: Chart[] } => ({ charts: [] }),
methods: {
async ask({ question, caller }) {
// the agent charts a signed-in user's question for that user:
const png = await answerWithChart(question)
this.charts. push({ askedBy: userKey(caller), png })
return this.charts. length
},
open({ n, caller }) {
const made = this.charts[n - 1]
// anyone else, even signed in, gets a 404:
if (made?.askedBy !== userKey(caller)) return undefined
return { tag: 'inline', val: made.png, mimeType: 'image/png' }
},
},
}) const Charts = Toolkit. make(Tool. make( "chart", {
description: "Chart sales for a quarter and link to it",
parameters: Quarter, success: Schema.String }))
export const Analyst = defineAgent({
name: "AnalystAgent",
id: { chat: Schema.String },
http: Http. mount( "/chats/{chat}", {
// Golem serves files this agent writes, never half-written:
exposeFiles: [{ route: "/*", path: "/out/$1" }],
}),
methods: { ask: method({ input: { question: Schema.String },
success: Schema.String, http: [Http. post( "/ask")] }) },
}). implement({
init: ({ chat }) => Effect. succeed({ chat }),
methods: ({ chat }) => ({
ask: ({ question }) => Effect. gen( function* () {
// the model calls this tool when a user asks for a graph:
const tools = Charts. toLayer({ chart: ({ quarter: q }) =>
Effect. promise( async () => {
const name = crypto. randomUUID()
fs. mkdirSync( "/out", { recursive: true })
fs. writeFileSync( `/out/${name}.png` , await chartPng(q))
fs. writeFileSync( `/out/${name}.html` , pageFor(q, name))
return `/chats/${chat}/${name}.html`
}) })
return yield* answer(question, Charts)
. pipe(Effect. provide(tools))
}),
}),
}) // AppAgent's model calls this tool to save an app it just wrote:
export const present = Tool. toolDefinition( "present", {
requiresFilesystem: true,
}). body(( b) => b. positional( "name", Name)
. positional( "html", Schema.String). returns(Schema.String))
present. implement({
present: ({ name, html }) => Effect. sync(() => {
// the app is saved in the calling agent's own files:
fs. mkdirSync( "/out", { recursive: true })
fs. writeFileSync( `/out/${name}.html` , html)
return Tool. ok( `${name}.html` )
}),
})
export const Apps = defineAgent({
name: "AppAgent",
id: { chat: Schema.String },
http: Http. mount( "/apps/{chat}", {
exposeFiles: [{ route: "/*", path: "/out/$1" }],
}),
methods: {
sales: method({
input: { region: Schema.String }, success: Schema. Array(Row),
// the app's buttons fetch('sales?region=...') from here:
http: [Http. get( "/sales?region={region}")],
}),
},
}). implement({
init: () => Effect.void,
// each click runs this as durable code in the agent's own VM:
methods: () => ({ sales: ({ region }) => querySales(region) }),
}) export const Dashboards = defineAgent({
name: "DashboardAgent",
id: { user: Schema.String },
// the path names the agent: /dashboards/ana is Ana's own agent:
http: Http. mount( "/dashboards/{user}", {
auth: true,
exposeFiles: [{ route: "/*", path: "/out/$1" }],
}),
methods: {
ask: method({ input: { request: Schema.String },
success: Schema.String, http: [Http. post( "/ask")] }),
},
}). implement({
// the first request to /dashboards/ana creates her agent:
init: ({ user }) => Effect. succeed({ user }),
methods: ( self) => ({
ask: ({ request }) => Effect. gen( function* () {
const name = crypto. randomUUID()
fs. mkdirSync( "/out", { recursive: true })
// the model builds this dashboard for her one request:
fs. writeFileSync( `/out/${name}.html` ,
yield* dashboardFor(self.user, request))
// what it made for her is served under her path:
return `/dashboards/${self. user}/${name}.html`
}),
}),
}) const Png = Unstructured. UnstructuredBinary(
{ restrictions: [{ mimeType: "image/png" }] })
export const Charts = defineAgent({
name: "ChartAgent",
id: { team: Schema.String },
http: Http. mount( "/charts/{team}"),
methods: {
chart: method({
// over MCP, a chat assistant calls this when a user asks:
description: "Draw a sales chart for one quarter",
input: { quarter: Schema.String },
// the reply is the PNG itself, over HTTP or MCP:
success: Png,
http: [Http. get( "/chart.png?quarter={quarter}" )],
}),
},
}). implement({
init: ({ team }) => Effect. succeed({ team }),
methods: ({ team }) => ({
chart: ({ quarter }) => Effect. gen( function* () {
// the chart is drawn for this call, and no file is written:
const val = drawChart( yield* querySales(team, quarter))
return { _tag: "inline", val, mimeType: "image/png" } as const
}),
}),
}) // asked to export results as CSV, the agent replies with a link:
// the link is /exports/<chat>/<random id>.csv, for that user only:
export const Exports = HttpRouter. define( "ExportsRouter", {
mount: Http. mount( "/exports"),
})
Exports. implement(Effect. succeed(Effect. gen( function* () {
const request = yield* HttpRouter.request
const path = /^\/exports\/( [\w-]+)\/( [\w-]{36})\. csv$/
const m = request.path. match(path)
if (request.method !== "GET" || !m) {
return HttpServerResponse. empty({ status: 404 })
}
// that user's agent builds the CSV now and streams it:
const analyst = yield* Analyst.client. get({ chat: m[ 1] })
const csv = yield* analyst. exportCsv({ id: m[ 2] })
return HttpRouter. withRawHeaders(
HttpServerResponse. stream(csv, { status: 200 }), [
header( "content-type", "text/csv"),
// the browser saves it as a download:
header( "content-disposition",
'attachment; filename="results.csv"' ),
])
}))) export const Billing = defineAgent({
name: "BillingAgent",
id: { org: Schema.String },
// Golem checks the user's sign-in before any code here runs:
http: Http. mount( "/billing/{org}", { auth: true }),
methods: {
ask: method({
// Golem passes in the user who signed in:
input: { question: Schema.String,
caller: Principal.PrincipalSchema },
success: Schema.String, http: [Http. post( "/ask")],
}),
},
}). implement({
init: ({ org }) => Effect. succeed({ org }),
methods: ({ org }) => ({
ask: ({ question, caller }) => Effect. gen( function* () {
if (caller.tag !== "oidc") throw new Error( "sign-in required")
// the issuer can be Google, GitLab or any OIDC provider:
const { issuer, sub } = caller.val
// the model makes a page from that user's own invoices:
const invoices = yield* invoicesOf(org, issuer, sub)
return yield* pageFor(question, invoices)
}),
}),
}) const Png = Unstructured. UnstructuredBinary(
{ restrictions: [{ mimeType: "image/png" }] })
const User = Principal.PrincipalSchema
export const PrivateCharts = defineAgent({
name: "PrivateChartsAgent",
id: { team: Schema.String },
http: Http. mount( "/private/{team}", { auth: true }),
methods: {
ask: method({ input: { question: Schema.String, caller: User },
success: Schema.Number, http: [Http. post( "/ask")] }),
open: method({ input: { n: Schema.Number, caller: User },
success: Unstructured. Option(Png),
http: [Http. get( "/charts/{n}")] }),
},
}). implement({
init: () => Effect. succeed({ charts: [] as Chart[] }),
methods: ({ charts }) => ({
// the agent charts a signed-in user's question for that user:
ask: ({ question, caller }) => Effect. map(
answerWithChart(question),
( png) => charts. push({ askedBy: userKey(caller), png })),
open: ({ n, caller }) => Effect. sync(() => {
const made = charts[n - 1]
// anyone else, even signed in, gets a 404:
if (made?.askedBy !== userKey(caller)) return Option. none()
return Option. some({ _tag: "inline", val: made.png,
mimeType: "image/png" } as const)
}),
}),
}) #[agent_definition(mount = "/chats/{chat}",
// Golem serves files this agent writes, never half-written:
filesystem_bindings = [( "/*", "/out/$1")])]
pub trait AnalystAgent {
fn new(chat : String) -> Self;
#[endpoint(post = "/ask")]
async fn ask( &self, question: String)
-> Result<String, AppError>;
}
struct AnalystAgentImpl { chat: String }
#[agent_implementation]
impl AnalystAgent for AnalystAgentImpl {
fn new(chat : String) -> Self { Self { chat } }
async fn ask( &self, question: String)
-> Result<String, AppError> {
// the model calls this tool when a user asks for a graph:
let chart = ToolDefinition { name: "chart". into(),
description: Some(
"Chart sales for a quarter and link to it". into()),
parameters_schema: Quarter::SCHEMA. into() };
run_tool_loop( &question, model( "gpt-4.1" ), chart,
async |Quarter(q) | {
let name = Uuid::new_v4();
fs::create_dir_all( "/out")?;
fs::write( format!( "/out/{name}.png" ),
chart_png( &q).await? )?;
fs::write( format!( "/out/{name}.html" ), page_for( &q, name))?;
Ok( format!( "/chats/{}/{name}.html" , self. chat))
}).await
}
} // AppAgent's model calls this tool to save an app it just wrote:
#[tool_definition(requires_filesystem = true)]
pub trait Present {
#[arg(name, regex = r"^[a-z0-9-]{1,40}$")]
fn present( &self, name: String, html: String)
-> Result<String, AppError>;
}
#[tool_implementation]
impl Present for PresentImpl {
fn present( &self, name: String, html: String)
-> Result<String, AppError> {
// the app is saved in the calling agent's own files:
fs::create_dir_all( "/out")?;
fs::write( format!( "/out/{name}.html" ), html)?;
Ok( format!( "{name}.html" ))
}
}
#[agent_definition(mount = "/apps/{chat}",
filesystem_bindings = [( "/*", "/out/$1")])]
pub trait AppAgent {
fn new(chat : String) -> Self;
// the app's buttons fetch('sales?region=...') from here:
#[endpoint(get = "/sales?region={region}")]
fn sales( &self, region: String) -> Rows;
}
#[agent_implementation]
impl AppAgent for AppAgentImpl {
fn new(_chat : String) -> Self { Self }
// each click runs this as durable code in the agent's own VM:
fn sales( &self, region: String) -> Rows { query_sales( ®ion) }
} // the path names the agent: /dashboards/ana is Ana's own agent:
#[agent_definition(mount = "/dashboards/{user}",
auth = true,
filesystem_bindings = [( "/*", "/out/$1")])]
pub trait DashboardAgent {
fn new(user : String) -> Self;
#[endpoint(post = "/ask")]
async fn ask( &mut self, request: String)
-> Result<String, AppError>;
}
struct DashboardAgentImpl { user: String }
#[agent_implementation]
impl DashboardAgent for DashboardAgentImpl {
// the first request to /dashboards/ana creates her agent:
fn new(user : String) -> Self { Self { user } }
async fn ask( &mut self, request: String)
-> Result<String, AppError> {
let name = Uuid::new_v4();
fs::create_dir_all( "/out")?;
// the model builds this dashboard for her one request:
fs::write( format!( "/out/{name}.html" ),
dashboard_for( &self. user, &request).await? )?;
// what it made for her is served under her path:
Ok( format!( "/dashboards/{}/{name}.html" , self. user))
}
} #[derive( AllowedMimeTypes, Clone, Debug)]
pub enum PngType { #[mime_type( "image/png")] Png }
type Png = UnstructuredBinary<PngType>;
#[agent_definition(mount = "/charts/{team}")]
pub trait ChartAgent {
fn new(team : String) -> Self;
// over MCP, a chat assistant calls this when a user asks:
#[description( "Draw a sales chart for one quarter")]
#[endpoint(get = "/chart.png?quarter={quarter}" )]
async fn chart( &self, quarter: String)
// the reply is the PNG itself, over HTTP or MCP:
-> Result<Png, AppError>;
}
struct ChartAgentImpl { team: String }
#[agent_implementation]
impl ChartAgent for ChartAgentImpl {
fn new(team : String) -> Self { Self { team } }
async fn chart( &self, quarter: String)
-> Result<Png, AppError> {
// the chart is drawn for this call, and no file is written:
let rows = query_sales( &self. team, &quarter).await? ;
let data = draw_chart( &rows);
Ok( Png::from_inline(data, PngType::Png))
}
} struct ExportsRouter;
// asked to export results as CSV, the agent replies with a link:
// the link is /exports/<chat>/<random id>.csv, for that user only:
#[http_router(name = "ExportsRouter", mount = "/exports")]
impl HttpRouter for ExportsRouter {
type Config = ();
fn new(_ : Config<()>) -> Self { Self }
async fn handle( &self, request: HttpRequest) -> HttpResponse {
export(request) .await. unwrap_or_else(failed)
}
}
async fn export(request : HttpRequest)
-> Result<HttpResponse, AppError> {
let path = Regex::new( r"^/exports/([\w-]+)/([\w-]{36})\.csv$" )?;
let m = path. captures( &request. path);
let Some(m) = m. filter( |_| request. method == "GET") else {
return Ok( HttpResponse { status: 404, headers: vec![],
body: empty() });
};
// that user's agent builds the CSV now and streams it:
let csv = AnalystAgentClient::get(m[ 1]. to_string()) ?
. export_csv(m[ 2]. to_string()) .await? ;
Ok( HttpResponse { status: 200, headers: vec![
header( "content-type", "text/csv"),
// the browser saves it as a download:
header( "content-disposition",
"attachment; filename=\"results.csv \""),
], body: csv })
} // Golem checks the user's sign-in before any code here runs:
#[agent_definition(mount = "/billing/{org}", auth = true)]
pub trait BillingAgent {
fn new(org : String) -> Self;
#[endpoint(post = "/ask")]
// Golem passes in the user who signed in:
async fn ask( &self, question: String, caller: Principal)
-> Result<String, AppError>;
}
struct BillingAgentImpl { org: String }
#[agent_implementation]
impl BillingAgent for BillingAgentImpl {
fn new(org : String) -> Self { Self { org } }
async fn ask( &self, question: String, caller: Principal)
-> Result<String, AppError> {
let Principal::Oidc(user) = caller else {
return Err( "sign-in required". into());
};
// the issuer can be Google, GitLab or any OIDC provider:
let (issuer, sub) = (user . issuer, user. sub);
// the model makes a page from that user's own invoices:
let invoices = invoices_of( &self. org, &issuer, &sub).await? ;
page_for( &question, &invoices).await
}
} #[derive( AllowedMimeTypes, Clone, Debug)]
pub enum PngType { #[mime_type( "image/png")] Png }
type Png = UnstructuredBinary<PngType>;
#[agent_definition(mount = "/private/{team}", auth = true)]
pub trait PrivateChartsAgent {
fn new(team : String) -> Self;
#[endpoint(post = "/ask")]
async fn ask( &mut self, question: String, caller: Principal)
-> Result<u32, AppError>;
#[endpoint(get = "/charts/{n}")]
fn open( &self, n: u32, caller: Principal) -> Option<Png>;
}
struct PrivateChartsAgentImpl { charts: Vec<Chart> }
#[agent_implementation]
impl PrivateChartsAgent for PrivateChartsAgentImpl {
fn new(_team : String) -> Self { Self { charts: vec![] } }
async fn ask( &mut self, question: String, caller: Principal)
-> Result<u32, AppError> {
// the agent charts a signed-in user's question for that user:
let png = answer_with_chart( &question).await? ;
self. charts. push( Chart { asked_by: user_key( &caller)?, png });
Ok( self. charts. len() as u32)
}
fn open( &self, n: u32, caller: Principal) -> Option<Png> {
let made = self. charts. get(n . checked_sub( 1)? as usize)?;
// anyone else, even signed in, gets a 404:
if made. asked_by != user_key( &caller). ok() ? { return None; }
Some( Png::from_inline(made . png. clone(), PngType::Png))
}
} @agentDefinition(
mount = "/chats/{chat}",
// Golem serves files this agent writes, never half-written:
exposeFiles = Array(( "/*", "/out/$1")),
)
trait AnalystAgent extends BaseAgent:
class Id( val chat: String)
@endpoint( "POST", "/ask")
def ask( question: String): Future[String]
@agentImplementation()
final class AnalystAgentImpl( chat: String) extends AnalystAgent:
def ask( question: String): Future[String] =
// the model calls this tool when a user asks for a graph:
val chart = FunctionTool(name = "chart",
description = "Chart sales for a quarter and link to it",
parameters = Quarter.schema, parse = Quarter.parse,
function = q =>
val key = HostApi.generateIdempotencyKey()
val name = Uuid.toStandardString(key)
fs.mkdirSync( "/out", literal(recursive = true))
chartPng(q).map : png =>
fs.writeFileSync( s"/out/$name.png" , png)
fs.writeFileSync( s"/out/$name.html" , pageFor(q, name))
s"/chats/$chat/$name.html"
)
val run = openai.chat.completions.runTools( ToolRunParams(
model = "gpt-4.1" , tools = Seq(chart),
messages = Seq( ChatMessage( "user", question)),
), RunnerOptions(maxChatCompletions = 5))
run.finalContent().toFuture // AppAgent's model calls this tool to save an app it just wrote:
@toolDefinition(name = "present", requiresFilesystem = true)
trait Present:
def present( name: String, html: String): Either[BadName, String]
@toolImplementation()
final class PresentImpl extends Present:
def present( name: String, html: String): Either[BadName, String] =
if !Name.matches(name) then Left( BadName. Invalid)
else
// the app is saved in the calling agent's own files:
fs.mkdirSync( "/out", literal(recursive = true))
fs.writeFileSync( s"/out/$name.html" , html)
Right( s"$name.html" )
@agentDefinition(
mount = "/apps/{chat}",
exposeFiles = Array(( "/*", "/out/$1")),
)
trait AppAgent extends BaseAgent:
class Id( val chat: String)
// the app's buttons fetch('sales?region=...') from here:
@endpoint( "GET", "/sales?region={region}")
def sales( region: String): Future[List[Row]]
@agentImplementation()
final class AppAgentImpl( chat: String) extends AppAgent:
// each click runs this as durable code in the agent's own VM:
def sales( region: String): Future[List[Row]] = querySales(region) @agentDefinition(
// the path names the agent: /dashboards/ana is Ana's own agent:
mount = "/dashboards/{user}",
auth = true,
exposeFiles = Array(( "/*", "/out/$1")),
)
trait DashboardAgent extends BaseAgent:
class Id( val user: String)
@endpoint( "POST", "/ask")
def ask( request: String): Future[String]
// the first request to /dashboards/ana creates her agent:
@agentImplementation()
final class DashboardAgentImpl( user: String) extends DashboardAgent:
def ask( request: String): Future[String] =
val key = HostApi.generateIdempotencyKey()
val name = Uuid.toStandardString(key)
fs.mkdirSync( "/out", literal(recursive = true))
// the model builds this dashboard for her one request:
dashboardFor(user, request).map : page =>
fs.writeFileSync( s"/out/$name.html" , page)
// what it made for her is served under her path:
s"/dashboards/$user/$name.html" type Png = UnstructuredBinary["image/png"]
@agentDefinition(mount = "/charts/{team}")
trait ChartAgent extends BaseAgent:
class Id( val team: String)
// over MCP, a chat assistant calls this when a user asks:
@description( "Draw a sales chart for one quarter")
@endpoint( "GET", "/chart.png?quarter={quarter}" )
// the reply is the PNG itself, over HTTP or MCP:
def chart( quarter: String): Future[Png]
@agentImplementation()
final class ChartAgentImpl( team: String) extends ChartAgent:
def chart( quarter: String): Future[Png] =
// the chart is drawn for this call, and no file is written:
querySales(team, quarter).map : rows =>
val png = drawChart(rows)
UnstructuredBinary.fromInline(png, "image/png") // asked to export results as CSV, the agent replies with a link:
// the link is /exports/<chat>/<random id>.csv, for that user only:
@httpRouter(typeName = "ExportsRouter", mount = "/exports")
trait ExportsRouter extends BaseAgent:
@httpHandler def serve( request: HttpRequest): Future[HttpResponse]
@agentImplementation()
final class ExportsRouterImpl extends ExportsRouter:
private val Csv = """^/exports/([\w-]+)/([\w-]{36})\.csv$""" .r
def serve( request: HttpRequest): Future[HttpResponse] =
(request.method, request.path) match
case ( "GET", Csv(chat, id)) =>
// that user's agent builds the CSV now and streams it:
AnalystAgentClient.get(chat).exportCsv(id).map : csv =>
HttpResponse( UShort( 200), List(
HttpHeader.ascii( "content-type", "text/csv"),
// the browser saves it as a download:
HttpHeader.ascii( "content-disposition",
"""attachment; filename="results.csv"""" ),
), csv)
case _ =>
Future.successful( HttpResponse( UShort( 404), Nil, empty())) @agentDefinition(
// Golem checks the user's sign-in before any code here runs:
mount = "/billing/{org}", auth = true,
)
trait BillingAgent extends BaseAgent:
class Id( val org: String)
@endpoint( "POST", "/ask")
// Golem passes in the user who signed in:
def ask( question: String, caller: Principal): Future[String]
@agentImplementation()
final class BillingAgentImpl( org: String) extends BillingAgent:
def ask( question: String, caller: Principal): Future[String] =
caller match
case user: Principal. Oidc =>
// the issuer can be Google, GitLab or any OIDC provider:
val (issuer, sub) = (user.issuer, user.sub)
// the model makes a page from that user's own invoices:
invoicesOf(org, issuer, sub).flatMap : invoices =>
pageFor(question, invoices)
case _ => Future.failed( Exception( "sign-in required")) type Png = UnstructuredBinary["image/png"]
@agentDefinition(mount = "/private/{team}", auth = true)
trait PrivateChartsAgent extends BaseAgent:
class Id( val team: String)
@endpoint( "POST", "/ask")
def ask( question: String, caller: Principal): Future[Int]
@endpoint( "GET", "/charts/{n}")
def open( n: Int, caller: Principal): Option[Png]
@agentImplementation()
final class PrivateChartsAgentImpl( team: String)
extends PrivateChartsAgent:
private val charts = ArrayBuffer.empty[ Chart]
def ask( question: String, caller: Principal): Future[Int] =
// the agent charts a signed-in user's question for that user:
answerWithChart(question).map : png =>
charts += Chart(askedBy = userKey(caller), png)
charts.length
def open( n: Int, caller: Principal): Option[Png] =
// anyone else, even signed in, gets a 404:
charts.lift(n - 1)
.filter(_.askedBy == userKey(caller))
.map(c => UnstructuredBinary.fromInline(c.png, "image/png")) #derive.agent
#derive.mount ( "/chats/{chat}")
// Golem serves files this agent writes, never half-written:
#derive.expose_files ( "/*", "/out/$1")
struct AnalystAgent {
chat : String
}
fn AnalystAgent::new(chat : String) -> AnalystAgent {
{ chat, }
}
#derive.endpoint ( post="/ask")
pub async fn AnalystAgent::ask(
self : Self, question : String,
) -> String {
// the model calls this tool when a user asks for a graph:
let chart = async fn(q : String) -> String {
guard is_quarter(q) else { fail( "not a quarter: \{q}") }
let name = @api. generate_idempotency_key_string()
let root = @fs. get_root_dir(). unwrap()
root. create_directory_at( "out") |> ignore
@fs. write_bytes(root, "/out/\{name}.png" , chart_png(q)). unwrap()
@fs. write_string(root, "/out/\{name}.html" ,
page_for(q, name)). unwrap()
"/chats/\{self.chat }/\{name}.html"
}
run_model_loop(question, chart, max_steps =5,
tool_description="Chart sales for a quarter and link to it",
)
} // AppAgent's model calls this tool to save an app it just wrote:
#derive.tool ( "present", requires_filesystem=true)
struct Present {}
#derive.arg ( "name", regex="^[a-z0-9-]{1,40}$")
pub async fn Present::present(
name : String, html : String,
) -> String {
// the app is saved in the calling agent's own files:
let root = @fs. get_root_dir(). unwrap()
root. create_directory_at( "out") |> ignore
@fs. write_string(root, "/out/\{name}.html" , html). unwrap()
"\{name}.html"
}
#derive.agent
#derive.mount ( "/apps/{chat}")
#derive.expose_files ( "/*", "/out/$1")
struct AppAgent { chat : String }
fn AppAgent::new(chat : String) -> AppAgent {
{ chat, }
}
// the app's buttons fetch('sales?region=...') from here:
#derive.endpoint ( get="/sales?region={region}")
pub async fn AppAgent::sales(
self : Self, region : String,
) -> Array[Row] {
// each click runs this as durable code in the agent's own VM:
query_sales(region)
} #derive.agent
// the path names the agent: /dashboards/ana is Ana's own agent:
#derive.mount ( "/dashboards/{user}")
#derive.mount_auth ( true)
#derive.expose_files ( "/*", "/out/$1")
struct DashboardAgent { user : String }
// the first request to /dashboards/ana creates her agent:
fn DashboardAgent::new(user : String) -> DashboardAgent {
{ user, }
}
#derive.endpoint ( post="/ask")
pub async fn DashboardAgent::ask(
self : Self, request : String,
) -> String {
let name = @api. generate_idempotency_key_string()
let root = @fs. get_root_dir(). unwrap()
root. create_directory_at( "out") |> ignore
// the model builds this dashboard for her one request:
@fs. write_string(root, "/out/\{name}.html" ,
dashboard_for(self.user, request)). unwrap()
// what it made for her is served under her path:
"/dashboards/\{self.user }/\{name}.html"
} struct Png {}
impl @schema. AllowedMimeTypes for Png with fn mime_types() {
["image/png"]
}
#derive.agent
#derive.mount ( "/charts/{team}")
struct ChartAgent {
team : String
}
fn ChartAgent::new(team : String) -> ChartAgent {
{ team, }
}
// over MCP, a chat assistant calls this when a user asks:
/// Draw a sales chart for one quarter
#derive.endpoint ( get="/chart.png?quarter={quarter}" )
pub async fn ChartAgent::chart(
self : Self, quarter : String,
// the reply is the PNG itself, over HTTP or MCP:
) -> @schema. UnstructuredBinary[Png] {
// the chart is drawn for this call, and no file is written:
let bytes = draw_chart( query_sales(self.team, quarter))
Inline(bytes~, mime_type =Some( "image/png"))
} // asked to export results as CSV, the agent replies with a link:
// the link is /exports/<chat>/<random id>.csv, for that user only:
#derive.http_router
#derive.mount ( "/exports")
struct ExportsRouter {}
fn ExportsRouter::new() -> ExportsRouter { ExportsRouter::{ } }
#derive.http_handler
pub async fn ExportsRouter::serve(
_self : Self, request : @http. HttpRequest,
) -> @http. HttpResponse {
guard request.method == "GET" &&
request.path is [.. "/exports/", .. rest, .. ".csv" ] &&
rest. split( "/"). collect() is [chat, id] &&
is_word(chat) && is_word(id) && id. length() == 36 else {
return { status: 404, headers: [], body: empty() }
}
// that user's agent builds the CSV now and streams it:
let csv = AnalystAgentClient::get(chat. to_string())
. export_csv(id. to_string())
{
status: 200,
headers: [
header( "content-type", "text/csv"),
// the browser saves it as a download:
header( "content-disposition",
"attachment; filename=\"results.csv \""),
],
body: csv,
}
} #derive.agent
#derive.mount ( "/billing/{org}")
// Golem checks the user's sign-in before any code here runs:
#derive.mount_auth ( true)
struct BillingAgent { org : String }
fn BillingAgent::new(org : String) -> BillingAgent {
{ org, }
}
#derive.endpoint ( post="/ask")
pub async fn BillingAgent::ask(
self : Self, question : String,
// Golem passes in the user who signed in:
caller : @common. Principal,
) -> String {
guard caller is Oidc(oidc) else { fail( "sign-in required") }
// the issuer can be Google, GitLab or any OIDC provider:
let { issuer, sub, .. } = oidc
// the model makes a page from that user's own invoices:
let invoices = invoices_of(self.org, issuer, sub)
page_for(question, invoices)
} struct Png {}
impl @schema. AllowedMimeTypes for Png with fn mime_types() {
["image/png"]
}
#derive.agent
#derive.mount ( "/private/{team}")
#derive.mount_auth ( true)
struct PrivateChartsAgent { team : String; charts : Array[Chart] }
fn PrivateChartsAgent::new(team : String) -> PrivateChartsAgent {
{ team, charts: [] }
}
#derive.endpoint ( post="/ask")
pub async fn PrivateChartsAgent::ask(self : Self, question : String,
caller : @common. Principal) -> Int {
// the agent charts a signed-in user's question for that user:
let png = answer_with_chart(question)
self.charts. push({ asked_by: user_key(caller), png })
self.charts. length()
}
#derive.endpoint ( get="/charts/{n}")
pub async fn PrivateChartsAgent::open(self : Self, n : Int,
caller : @common. Principal) -> @schema. UnstructuredBinary[Png]? {
guard self.charts. get(n - 1) is Some(made) else { return None }
// anyone else, even signed in, gets a 404:
guard made.asked_by == user_key(caller) else { return None }
Some( Inline(bytes =made.png, mime_type =Some( "image/png")))
} type AnalystID struct{ Chat string }
type AskIn struct{ Question string }
var Analyst = golem. DefineAgent[AnalystID]( golem. Spec{
Name: "AnalystAgent",
HTTP: &golem. Mount{Path: "/chats/{chat}",
// Golem serves files this agent writes, never half-written:
ExposeFiles: []golem. FileMapping{
{Route: "/*", Path: "/out/$1"}}}})
var Ask = Analyst. Method[AskIn, string]( "ask",
golem. HTTP(golem. POST( "/ask")))
var _ = Analyst. Implement( func( id AnalystID) *AnalystID {
return &id
}). Handle(Ask, golem. Adapt( func( a *AnalystID, in AskIn) string {
// the model calls this tool when a user asks for a graph:
chart := ai. NewTool( "chart",
"Chart sales for a quarter and link to it",
func( _ *ai. ToolContext, args Quarter) ( string, error) {
q, name := args.Quarter, uuid. NewString()
golem. Must0(os. MkdirAll( "/out", 0o755))
golem. Must0(os. WriteFile( "/out/"+name+".png" ,
chartPng(q), 0o644))
golem. Must0(os. WriteFile( "/out/"+name+".html" ,
[]byte( pageFor(q, name)), 0o644))
return "/chats/" + a.Chat + "/" + name + ".html" , nil
})
return golem. Must(genkit. GenerateText(ctx, g,
ai. WithModelName(model), ai. WithPrompt(in.Question),
ai. WithTools(chart), ai. WithMaxTurns( 5)))
})) type AppID struct{ Chat string }
type SalesIn struct{ Region string }
// AppAgent's model calls this tool to save an app it just wrote:
var Present = tool. DefineTool[Presenter]( "present", tool. Spec{
RequiresFilesystem: true,
}). Body[Args, string]( func( a *Args, s *tool. CommandSpec) {
s. Positional( &a.Name)
s. Positional( &a.HTML)
})
var _ = Present. Handle(
func( _ *tool. Context, a Args) ( string, error) {
// the app is saved in the calling agent's own files:
golem. Must0(os. MkdirAll( "/out", 0o755))
golem. Must0(os. WriteFile( "/out/"+string(a.Name) +".html" ,
[]byte(a.HTML), 0o644))
return string(a.Name) + ".html" , nil
})
var Apps = golem. DefineAgent[AppID]( golem. Spec{
Name: "AppAgent",
HTTP: &golem. Mount{Path: "/apps/{chat}",
ExposeFiles: []golem. FileMapping{
{Route: "/*", Path: "/out/$1"}}}})
var Sales = Apps. Method[SalesIn, []Row]( "sales",
// the app's buttons fetch('sales?region=...') from here:
golem. HTTP(golem. GET( "/sales?region={region}")))
var _ = Apps. Implement( func( id AppID) *AppID { return &id }). Handle(
// each click runs this as durable code in the agent's own VM:
Sales, golem. Adapt( func( _ *AppID, in SalesIn) []Row {
return querySales(in.Region)
})) type UserID struct{ User string }
type AskIn struct{ Request string }
type dashboards struct{ User string }
var Dashboards = golem. DefineAgent[UserID]( golem. Spec{
Name: "DashboardAgent",
// the path names the agent: /dashboards/ana is Ana's own agent:
HTTP: &golem. Mount{Path: "/dashboards/{user}",
Auth: true,
ExposeFiles: []golem. FileMapping{
{Route: "/*", Path: "/out/$1"}}}})
var Ask = Dashboards. Method[AskIn, string]( "ask",
golem. HTTP(golem. POST( "/ask")))
// the first request to /dashboards/ana creates her agent:
var _ = Dashboards. Implement( func( id UserID) *dashboards {
return &dashboards{User: id.User}
}). Handle(Ask, golem. Adapt(( *dashboards).ask))
func ( d *dashboards) ask( in AskIn) string {
name := uuid. NewString()
golem. Must0(os. MkdirAll( "/out", 0o755))
// the model builds this dashboard for her one request:
golem. Must0(os. WriteFile( "/out/"+name+".html" ,
[]byte( dashboardFor(d.User, in.Request)), 0o644))
// what it made for her is served under her path:
return "/dashboards/" + d.User + "/" + name + ".html"
} type pngType struct{}
func ( pngType) MimeTypes() [] string { return []string{"image/png"} }
type Png = golem. UnstructuredBinary[pngType]
type ChartID struct{ Team string }
type ChartIn struct{ Quarter string }
var Charts = golem. DefineAgent[ChartID]( golem. Spec{
Name: "ChartAgent",
HTTP: &golem. Mount{Path: "/charts/{team}"}})
var Chart = Charts. Method[ChartIn, Png]( "chart",
// over MCP, a chat assistant calls this when a user asks:
golem. Description( "Draw a sales chart for one quarter"),
golem. HTTP(golem. GET( "/chart.png?quarter={quarter}" )))
var _ = Charts. Implement( func( id ChartID) *ChartID {
return &id
}). Handle(Chart, golem. Adapt(( *ChartID).chart))
// the reply is the PNG itself, over HTTP or MCP:
func ( c *ChartID) chart( in ChartIn) Png {
// the chart is drawn for this call, and no file is written:
val := drawChart( querySales(c.Team, in.Quarter))
return Png{Data: val, MimeType: "image/png"}
} // asked to export results as CSV, the agent replies with a link:
// the link is /exports/<chat>/<random id>.csv, for that user only:
var Exports = golem. DefineHTTPRouter( golem. RouterSpec{
Name: "ExportsRouter", Mount: "/exports"})
var csvPath = regexp. MustCompile(
`^/exports/([\w-]+)/([\w-]{36})\.csv$` )
var _ = Exports. HandleRaw( func( _ context. Context,
req golem. HTTPRequest) golem. HTTPResponse {
m := csvPath. FindStringSubmatch(req.Path)
if req.Method != "GET" || m == nil {
return golem. HTTPResponse{Status: 404, Headers: nil,
Body: golem. StreamOf[[]byte]()}
}
// that user's agent builds the CSV now and streams it:
csv := ExportCsv. MustCall(Analyst. Get( AnalystID{Chat: m[1]}),
ExportIn{ID: m[2]})
return golem. HTTPResponse{
Status: 200,
Headers: []golem. HTTPHeader{
{Name: "content-type", Value: []byte( "text/csv")},
// the browser saves it as a download:
{Name: "content-disposition", Value: []byte(
`attachment; filename="results.csv"` )}},
Body: csv,
}
}) type OrgID struct{ Org string }
var Billing = golem. DefineAgent[OrgID]( golem. Spec{
Name: "BillingAgent",
// Golem checks the user's sign-in before any code here runs:
HTTP: &golem. Mount{Path: "/billing/{org}", Auth: true}})
type AskIn struct {
Question string
// Golem passes in the user who signed in:
Caller golem. Principal
}
var Ask = Billing. Method[AskIn, string]( "ask",
golem. HTTP(golem. POST( "/ask")))
var _ = Billing. Implement( func( id OrgID) *OrgID {
return &id
}). Handle(Ask, golem. Adapt(( *OrgID).ask))
func ( b *OrgID) ask( in AskIn) string {
user, ok := in.Caller.( golem. OidcPrincipal)
if !ok {
panic( "sign-in required")
}
// the issuer can be Google, GitLab or any OIDC provider:
issuer, sub := user.Issuer, user.Sub
// the model makes a page from that user's own invoices:
invoices := invoicesOf(b.Org, issuer, sub)
return pageFor(in.Question, invoices)
} type AskIn struct {
Question string
Caller golem. Principal
}
type OpenIn struct {
N int32
Caller golem. Principal
}
var PrivateCharts = golem. DefineAgent[TeamID]( golem. Spec{
Name: "PrivateChartsAgent",
HTTP: &golem. Mount{Path: "/private/{team}", Auth: true}})
var Ask = PrivateCharts. Method[AskIn, int32]( "ask",
golem. HTTP(golem. POST( "/ask")))
var Open = PrivateCharts. Method[OpenIn, *Png]( "open",
golem. HTTP(golem. GET( "/charts/{n}")))
var impl = PrivateCharts. Implement(newBook)
var _ = impl. Handle(Ask, golem. Adapt( func( b *book, a AskIn) int32 {
// the agent charts a signed-in user's question for that user:
png := answerWithChart(a.Question)
b.Charts[ int32( len(b.Charts) +1)] = Chart{userKey(a.Caller), png}
return int32( len(b.Charts))
}))
var _ = impl. Handle(Open, golem. Adapt( func( b *book, o OpenIn) *Png {
made := b.Charts[o.N]
// anyone else, even signed in, gets a 404:
if made.AskedBy != userKey(o.Caller) {
return nil
}
return &Png{Data: made.PNG, MimeType: "image/png"}
})) Probably not. Each agent's whole execution is durable, and each agent has its own durable file system and can have its own database. Golem Social, a whole social network, runs on Golem with no external database. Keep existing databases where other systems share the data.
Yes. A framework shapes the agent loop and usually saves the conversation between turns. It runs inside a Golem agent, which keeps the agent's whole execution durable, in-progress tool calls included, and enforces permissions in the runtime.
Actor platforms persist each entity's state. Golem keeps the agent's whole execution durable, including its call stack, files and environment, so it continues in the middle of a call. Tool calls run exactly once, and the runtime enforces permissions.
Workflow engines usually ask you to split work into activities they record. In Golem, ordinary code is durable as it runs, with no restructuring, and each agent also gets its own VM, with a durable file system, and runtime-checked permissions.
No. The agent makes a new chart, page, file or small app for one user's request and shows it to that user. Golem serves it directly from the agent. The agent does not change an existing app and needs no access to your build or deploy infrastructure.
Yes. Golem uses the BUSL-1.1 license, which converts to Apache-2.0 after four years. Self-hosting for production workloads is permitted.
Install one binary and run the platform locally.