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