Build Agents with the SDK

Navigate the SDK contracts used to author Agent behavior, durable execution, Tools, Resources, and analytics.

Use @constal/sdk inside the immutable Agent package. The smallest useful Agent reads accepted history, makes one model turn, and returns a result that the runtime records durably:

Minimal Agent

ts
import { agent, type HistoryView } from "@constal/sdk";

export default agent({
  id: "support", version: "1.0.0", model: "model",
  async onMessage(message, ctx) {
    const history = await ctx.ledger.view<HistoryView>("history");
    const turn = await ctx.turn({
      system: "Resolve the request accurately.",
      objective: message,
      context: { priorFacts: history.facts },
    });
    return { answer: turn.message.content };
  },
});

model is a logical Resource binding, not caller-selected provider input. ctx.resources contains only accepted CRNs, and Credential bytes never enter Agent code.

Interpret a model turn

ctx.turn() returns the provider's result even when it has no visible text or Tool calls. Inspect turn.message.content, turn.toolCalls, and turn.completion to decide what the Agent should do next. turn.refusal and turn.reasoningContent carry provider-exposed text when available. turn.cost.tokens.output records output-token usage; it can be positive even when the visible message is empty. An incomplete model response does not by itself finish the Agent's task.

Judge structured state

Use ctx.evaluate() when a bound evaluation Model should answer typed Choice, Score, or Noul questions about one state. It returns constrained answers and settled cost without creating a conversational turn. Bind the evaluation Model under a logical name such as judge, then pass model: "judge" in the call. See Evaluate state with a Model for a complete manifest, Agent, answer shapes, and a direct Gateway option.

Add a durable boundary

ts
async onMessage(message, ctx) {
  if (message.kind === "checkpoint") {
    return ctx.commit({ kind: "checkpoint", payload: message.payload });
  }
  return { accepted: true };
}

Restore a sandbox from a recorded image

pool.createImage(sandbox) returns a pool-owned image whose id an Agent can record, for example in a Fact. A later Run addresses that image with pool.image(id) without invoking the pool. To recreate the Session's sandbox from it, the Run deletes the current sandbox, then passes the image to pool.createSandbox(agent, session, { image }); the pool refuses an image while the Session's sandbox is live. image.delete() removes the image once it is no longer needed. An addressed image does not know its cache key (cacheKey is null), and deleting it removes a setup-cache image too, so address only images the Agent recorded.

Schedule a follow-up

Use ctx.schedule(name, { after: "10m", input }) to record a future invocation that survives the current Run. every maintains a cadence; ctx.listSchedules() and ctx.cancelSchedule(name) manage the Session's schedules. See Schedule Agent follow-ups for timing, destinations, replay, and cancellation semantics.

Use Durable Agent patterns for waits and subtasks, Resources SDK guide for effects, and Analytics SDK guide for typed events. Deployment remains a Console, CLI, or Platform API operation; it is intentionally absent from Ctx.