defineAgent

Every option an agent takes.

defineAgent declares an agent: its model, instructions and what it may do. Pass a config object, or a function that returns one when the agent depends on who is asking.

ts
import { defineAgent } from "@intentface/latch-core";

// The same for everyone:
const support = defineAgent({
  model: anthropic("claude-sonnet-4-6"),
  instructions: "You answer support questions.",
  tools: { search_docs, create_ticket },
  toolApproval: { create_ticket: "user-approval" },
});

// Depends on the caller: built per turn from the context.
const assistant = defineAgent(({ context }) => ({
  model: context.plan === "pro" ? anthropic("claude-sonnet-4-6") : anthropic("claude-haiku-4-5"),
  instructions: `You work for ${context.orgName}.`,
}), { title: "Assistant", description: "Answers questions about your account." });

Options

OptionTypeDefaultDetails
configVersion?number—
modelLanguageModel—
instructions?string—
tools?Record<string, unknown>—
toolSources?ToolSource<P>[]—
connections?string[]—
toolApproval?unknown—
smoothStream?union—
maxSteps?number | "unlimited"20
enabledTools?string[]all tools
toolDescriptions?Record<string, string>—
subagents?string[]—
sandbox?boolean—
sandboxProvider?string—
defaultTools?boolean | string[]—
renderTools?boolean—
disableTools?string[]—
providerWebTools?boolean—
effort?"high" | "low" | "medium" | "minimal"the provider's default
memory?MemoryConfig—
extensions?Extension[]—

Params

An agent that's always about something declares it in the second argument, with a schema. The chat's first message passes the value; every turn after gets it. When to use params: Principal, context and params.

ts
const Ticket = z.object({ ticketId: z.string() });

const support = defineAgent<Principal, Ctx, z.infer<typeof Ticket>>(
  async ({ context, params }) => {
    const ticket = await context.db.tickets.get(params.ticketId);
    return { model, instructions: `You help with ticket ${ticket.id}.`, tools: { reply_to_ticket } };
  },
  { title: "Support", params: Ticket },
);

runtime.handleChat({ agent: "support", chatId, principal, message, params: { ticketId: "T-9" } });

Refusals: params that don't match the schema, or sent to an agent without one, get 400 invalid_params; different params for an existing chat get 409 params_fixed.

What the function receives

OptionTypeDefaultDetails
contextRuntimeContext—
principalP—
messageContext?unknown—
turnContext?unknown—
params?Params—

Display, params and message context

The second argument says how the agent appears in a picker, what its chats are about, and how it reads the message's data-context:

OptionTypeDefaultDetails
title?stringthe agent's key in `agents`
description?string—
hidden?boolean—
params?FlexibleSchema—
messageContext?object—