Yoke.sh
SDK

Context API

ActorCtx and PortalCtx — append, state, instances, portals, conversations, schedules, correlation, and llm.

ctx is the runtime handle passed to actor process(), portal start(), route handlers, and lifecycle hooks. The SDK wraps yoke ops into a typed surface — user code never calls Rust directly.

ActorCtx

Full surface for actor handlers:

type ActorCtx<TConfig, TState, TPortals, TEventMap> = {
  instanceId: string;
  config: TConfig;
  sqliteStoreId: string;

  state: DurableState<TState>;
  sqlite: SQLiteDatabase;

  correlation: CorrelationContext;

  append(input, options?): Promise<AppendResult>;
  instances: InstanceManager;
  portals: PortalClients<TPortals>;
  conversations: ConversationClient;
  schedules: ScheduleManager;
  llm: LlmClient;  // requires inferenceStack on runtime
};

instanceId and config

async process(ctx, event) {
  console.log(ctx.instanceId);  // UUID
  console.log(ctx.config.model); // validated against configSchema
}

state and sqlite

See State & SQLite.

await ctx.state.update((s) => ({ ...s, count: s.count + 1 }));
await ctx.sqlite.exec("SELECT 1");

append

await ctx.append({
  type: "work.assigned",
  payload: { role: "coder", lang: "typescript" },
});

await ctx.append(
  { type: "urgent.alert", payload: {} },
  { correlation: "new", priority: 0, groupId: "alerts" },
);

Typed for events in defineEvents(). Source is set automatically (kind: "actor", instanceId).

correlation

const id = ctx.correlation.inherit();
await ctx.append({ type: "step.done", payload: {} }); // inherits id
MethodBehavior
currentCorrelation id of event being processed
inherit()Reuse current or create new
create()Always new id

instances

const handle = await ctx.instances.create("helper", {
  config: { shard: "us-east" },
  filter: { type: "work.assigned" },
});

await ctx.instances.destroy(handle.id);

Typed when using defineFor — definition names autocomplete.

portals

await ctx.portals.slack.sendMessage("#general", "Done!");

Typed from portal interface definitions. Routes through portal_call → portal_invoke.

conversations

Conversation store CRUD — available when tenant has conversation migrations:

const actor = await ctx.conversations.createActor({ name: "alice" });
const room = await ctx.conversations.createConversation({ title: "support" });
const p = await ctx.conversations.addParticipant(room.id, actor.id);
await ctx.conversations.postMessage(room.id, {
  parts: [{ type: "text", text: "Hello", format: "plain" }],
}, { authorParticipantId: p.id, type: "message" });

Prefer createChatClient(ctx.conversations) for ergonomic wrappers.

schedules

await ctx.schedules.schedule({
  key: "retry:42",
  delay: { seconds: 30 },
  event: { type: "retry.tick", payload: { id: "42" } },
});

See Schedules.

llm

Requires .inferenceStack() on the runtime builder:

const outputs = await ctx.llm.infer(messages, {
  model: "gpt-4o",
  temperature: 0.7,
  maxTokens: 1024,
  tools: resolvedTools,
});

See Inference.

PortalCtx

Portals receive a subset — no state, sqlite, or llm:

type PortalCtx = {
  instanceId: string;
  config: TConfig;

  correlation: CorrelationContext;
  append(input, options?): Promise<AppendResult>;
  instances: InstanceManager;
  conversations: ConversationClient;
  schedules: ScheduleManager;
};

Route handlers and start() use PortalCtx:

routes: [{
  method: "POST",
  path: "/webhooks/shopify",
  handler: async (req, ctx) => {
    await ctx.append({ type: "ingress.shopify.order", payload: parsed });
    return { status: 200, body: "ok" };
  },
}],

Lifecycle hooks

defineActor({
  async onCreate(ctx) {
    await ctx.sqlite.exec("CREATE TABLE IF NOT EXISTS ...");
  },
  async onDestroy(ctx) {
    // cleanup before teardown
  },
  async process(ctx, event) { /* ... */ },
});

onCreate runs once after SQLite provisioning. onDestroy runs before instance rows are deleted.

Type flow

defineFor(events, portals) threads types through:

defineRuntime().events(events).portals(...).actors(...)
  → ActorCtx.config: CoderConfig
  → ActorCtx.state: CoderState
  → ActorCtx.portals.slack: SlackApi
  → process event payload: narrowed by EventMap

On this page