Adding Typed Configuration to an Effect Golem Agent
Effect agents model configuration as an Effect service created by defineConfig. Each field is
declared with Effect Schema, and the service is attached to the agent definition with the
config property. The runtime supplies the service to the constructor Effect and every method
handler.
Steps
- Define nested configuration records with
Schema.Struct. - Create a config service class with
defineConfig(name, fields). - Attach the class to
defineAgentasconfig: MyAgentConfig. - Keep durable identity in
id; config is not a agent id field. - Yield the config service and its field Effects inside method handlers.
- Set values in
golem.yaml, at agent creation, or through typed RPC overrides.
Agent with Typed Config
import { Effect, Schema } from "effect";
import {
defineAgent,
defineConfig,
method,
WitTypes,
} from "@golemcloud/effect-golem";
const ServerConfig = Schema.Struct({
host: Schema.String,
port: WitTypes.Int32,
});
export class MyAgentConfig extends defineConfig("MyAgent.Config", {
appName: Schema.String,
maxRetries: WitTypes.Int32,
server: ServerConfig,
}) {}
const Settings = Schema.Struct({
appName: Schema.String,
maxRetries: WitTypes.Int32,
serverHost: Schema.String,
serverPort: WitTypes.Int32,
});
export const MyAgent = defineAgent({
name: "MyAgent",
mode: "durable",
config: MyAgentConfig,
id: {
name: Schema.String,
},
methods: {
getSettings: method({
input: {},
success: Settings,
}),
},
}).implement({
init: ({ name }) => Effect.succeed(name),
methods: (name) => ({
getSettings: () =>
Effect.gen(function* () {
const config = yield* MyAgentConfig;
const appName = yield* config.appName;
const maxRetries = yield* config.maxRetries;
const serverHost = yield* config.server.host;
const serverPort = yield* config.server.port;
return { appName, maxRetries, serverHost, serverPort };
}).pipe(Effect.annotateLogs({ agentName: name })),
}),
});
Import the implementation module from the component entry point so the top-level
.implement({ init, methods }) call registers it:
// src/main.ts
import "./my-agent.js";
How Config Reaches the Implementation
init receives only the decoded id record. There is no positional Config<T> input: the complete
example above retains name as state and yields MyAgentConfig from the handler Effect. Do not
invent a config argument; yield the config service from an Effect.
Prefer yielding the service inside a handler when methods should observe configuration changes. Non-secret leaves are loaded when their Effects are evaluated, cached within that invocation, and read from a fresh config service on the next invocation.
Providing Config Values
Set defaults under the agent in golem.yaml:
agents:
MyAgent:
config:
appName: "My Application"
maxRetries: 3
server:
host: "localhost"
port: 8080
Override values when creating an individual agent. Dot-separated keys address nested fields:
golem agent new 'MyAgent("agent-1")' \
--config appName="CLI Application" \
--config maxRetries=5 \
--config server.host=example.com \
--config server.port=8443
For an RPC-created remote agent, use the typed overrides option. Overrides are recursively
partial, apply when the remote agent is first created, and cannot override secret fields:
const program = Effect.gen(function* () {
const remote = yield* MyAgent.client.get(
{ name: "agent-2" },
{
overrides: {
appName: "RPC Application",
server: { host: "rpc.example.com" },
},
},
);
return remote;
});
Schema and Loading Rules
- Use
Schema.String,Schema.Boolean, andSchema.Array(...)for ordinary values. - Use
WitTypes.Int32for a WITs32; plainSchema.Numbermaps tof64. - A
Schema.Structdirectly insidedefineConfigbecomes nested config paths such asserver.host; access each leaf as an Effect such asyield* config.server.host. - Use
Schema.Option(inner)for an optional config leaf. Evaluating it yields an EffectOption.Option<T>rather than trapping when the key is absent. - Config keys retain TypeScript camelCase in
golem.yamland CLI paths. - Config source precedence is
componentTemplates→components→agents→presets, with agent-creation or RPC overrides taking precedence over manifest defaults. - Do not use
Config<T>from@golemcloud/golem-ts-sdk, manually provide a config Layer, or implement handlers as plainasyncfunctions. - Run
golem buildafter changing the schemas or agent definition.