Handler slots: description / agent
This page documents the description and agent fields available on
r.writeHandler(...) and r.queryHandler(...). It is hand-written, not
generated: agent isn’t a pattern kind and there is no r.agent(...)
builder, the two fields just live on the handler definitions themselves.
Overview
Section titled “Overview”description and agent are optional fields in a handler definition.
Together they control what the AI-agent tool manifest says about that
handler.
type AgentHandlerHints = { expose?: boolean; risk?: "low" | "mid" | "high";};expose is a plain boolean, not a union, and risk defaults per handler
kind: "low" for r.queryHandler(...), "mid" for r.writeHandler(...).
Visibility is fail-closed
Section titled “Visibility is fail-closed”Whether a handler shows up in the agent manifest follows one rule:
expose = agent.expose ?? (description !== undefined)A handler with no description and no agent.expose stays invisible to
the model. Adding a description is what makes it visible by default:
r.queryHandler("widget:list", z.object({ id: z.string() }), async () => ({}), { access: { roles: ["admin"] }, description: "Lists widgets for a workspace.",});agent.expose: true makes a handler visible even without a description
(there’s just no prose for the model to read). agent.expose: false hides
it even when a description is present, an explicit opt-out that wins
over having written a description:
r.queryHandler("widget:internalDebug", z.object({ id: z.string() }), async () => ({}), { access: { roles: ["admin"] }, description: "Internal debug dump, not for agent use.", agent: { expose: false },});The access role check runs independently of this, and after it: an
exposed handler only appears in a given caller’s manifest if that caller’s
roles satisfy the handler’s access rule.
risk classifies the blast radius of the action, "low" | "mid" | "high".
Set it explicitly when the default for the handler kind doesn’t fit:
r.writeHandler("workspace:createStep", z.object({ name: z.string() }), async () => ({}), { access: { roles: ["user"] }, description: "Creates a step.", agent: { expose: true, risk: "high" },});Screen opt-out
Section titled “Screen opt-out”Screens can also declare agent: { expose: false } (or agent: { expose: true }) in their definition (added in fw#2690):
r.screen({ id: "internal-debug", type: "dashboard", description: "Internal diagnostics dashboard.", agent: { expose: false }, panels: [],});This prevents the screen from entering the agent navigation/tool surface even when a description is authored.
Boot check and lint
Section titled “Boot check and lint”The bundled agent-tools feature reports documentation gaps at boot
(a warning, not a startup failure). findAgentDocGaps() from
@cosmicdrift/kumiko-bundled-features/agent-tools reports the same gaps on
demand, see Make your app agent-ready for the
script. A gap is:
- a handler with no
descriptionthat wasn’t deliberately excluded viaagent: { expose: false }, - a custom screen with no
description, - an entity with no
descriptionthat’s reachable through an agent-visible handler.
There is no separate rule for high-risk handlers, the same gap check
applies regardless of risk.
See also r.writeHandler(...) and
r.queryHandler(...).