Installation
The SDK is available as @podium/ts in the Podium monorepo. Agents are deployed as npm packages.
Quick Start
import { BaseAgent, type AgentConfig, type AgentInput, type AgentContext } from '@podium/ts';
class MyAgent extends BaseAgent {
async onMessage(ctx: AgentContext, input: AgentInput) {
const msg = await ctx.sendMessage('Hello from TypeScript!', 'agent');
await msg.streamToken(' Response complete.', { final: true });
}
}
export default (config: AgentConfig) => new MyAgent(config);
Agent Lifecycle
BaseAgent
Every agent extends BaseAgent and implements lifecycle methods:
class MyAgent extends BaseAgent {
// Optional: Define state class for auto-persistence
static State = MyState;
// Called once after runtime is initialized, ctx.state is hydrated
async onInit(ctx: AgentContext): Promise<void> {}
// Handle inbound message — all output via streaming
async onMessage(ctx: AgentContext, message: AgentInput): Promise<void> {}
// Main execution loop (override for custom lifecycle)
async run(ctx: AgentContext): Promise<void> {}
// Called on config updates at runtime
async onConfigChange(ctx: AgentContext, changes: Record<string, unknown>): Promise<void> {}
// Called during graceful shutdown
async onShutdown(ctx: AgentContext): Promise<void> {}
// Called before scale-to-zero (return false to defer)
async onIdleTimeout(ctx: AgentContext): Promise<boolean> { return true; }
}
Event Listeners
Register handlers for custom event types:
async onInit(ctx: AgentContext) {
this.on('steer', this.handleSteer.bind(this));
}
async handleSteer(ctx: AgentContext, data: Record<string, unknown>) {
// Handle mid-turn guidance
}
AgentContext
The context object passed to every lifecycle method:
State
// Access tracked state (mutations auto-generate deltas)
ctx.state.count += 1;
ctx.state.items.push('new item');
// Access underlying DeltaState
ctx.deltaState;
Config
// Current config
ctx.config;
// Get nested config value
ctx.getConfig('ensemble.model', 'claude-sonnet-4-20250514');
// Config change history
ctx.configHistory;
Streaming Output
// Send a message and stream tokens
const msg = await ctx.sendMessage('', 'agent');
await msg.streamToken('Hello ');
await msg.streamToken('world!', { final: true });
// Send structured content
await ctx.emit('tool_call', { name: 'get_weather', arguments: { location: 'SF' } });
Sandboxes
// Access Chronicle-backed sandboxes
ctx.sandboxes;
Events
// Iterate over inbound events (used in run() loop)
for await (const event of ctx.events()) {
// event.type, event.data, event.id, event.timestamp, event.source
}
DeltaState
Define typed state with automatic persistence:
import { DeltaState } from '@podium/ts';
interface MyData {
count: number;
history: string[];
settings: { model: string; temperature: number };
}
class MyState extends DeltaState<MyData> {
static version = 1;
static entityType = 'my_agent';
protected defaults(): MyData {
return {
count: 0,
history: [],
settings: { model: 'claude-sonnet-4', temperature: 0.7 },
};
}
}
State is:
- Hydrated from SQLite on agent startup
- Tracked via JavaScript Proxy — mutations auto-generate JSON Patch deltas
- Persisted after each
onMessage()call - Replicated to S3 via Litestream
Deployment
Package agent as a tar.gz bundle:
my-agent/
├── package.json
├── src/
│ └── index.ts
└── tsconfig.json
Deploy via CLI or API:
podium deploy ./my-agent --version 1.0.0