Decisions.ask sends some state and one or more typed questions to a decision model and returns typed answers with probabilities. It never returns free text, so there is nothing to parse. Two providers sit behind one shape, TypeSafe Jev by default and OpenAI Decisions when the call carries images, sets region: 'eu' or sets zeroRetention, or when Jev is down. Available in tools, jobs, webhooks, triggers, processors and workflow code steps.
Verified against lua-cli 3.48.0.
Quick example
{ type: 'refused' }, so check type before you read its fields.
A single question returns its answer directly instead of a map.
Question types
Each question type is named after the field its answer carries.options is an object of label to description, and the answer’s choice is typed to those labels. levels is an ordered list from low to high, either strings or { label, description }. score is a position along the levels and can fall between two of them; level is the nearest one. probabilities holds one entry per option or level and they sum to 1.
Any answer can also be { type: 'refused' } when the provider declines to judge the content. Only OpenAI returns refused. Jev never does, but a call reaches OpenAI through images, region, zeroRetention or a fallback, so handle it on every call.
Methods
ask(request)
string | string[] | object
required
What the questions are about. Use an object with descriptive keys for anything beyond one piece of text, and refer to the keys in the instructions.
Record<string, DecisionQuestion>
required
One to 64 questions built with
Decisions.odds, Decisions.choice and Decisions.score. The answer keys match the question keys.{ data: string, mimeType: string }[]
Up to 4 base64 images (
image/png, image/jpeg, image/webp, image/gif), 5 MB each. Only OpenAI takes images, so they route the call to OpenAI.'auto' | 'jev' | 'openai'
default:"auto"
auto picks Jev unless the call carries images, sets region: 'eu' or sets zeroRetention. A pinned provider is never switched silently. It also overrides the image, region and zero-retention rules: provider: 'jev' with images fails with decision_rejected, and provider: 'jev' with region: 'eu' runs on Jev.string
A model of the chosen provider,
jev-latest, jev-preview or gpt-6-luna. Defaults to the provider’s current model. A model of the other provider fails with decision_invalid. After a fallback, the other provider uses its own default model.'jev' | 'openai' | 'none'
default:"the other provider"
The provider to try once when the first one times out or is unavailable. It defaults to the other provider, and to none when
provider is pinned. none keeps the call on one provider. Calls with images, region: 'eu' or zeroRetention never fall back to Jev.'eu'
Routes the call to OpenAI’s EU endpoint.
boolean
default:"false"
Routes the call to OpenAI. No retention setting is sent with the call.
number
default:"0.7"
Choice and score answers below this confidence come back with
uncertain: true so your code can escalate instead of acting.number
default:"3000"
Per-call timeout in milliseconds. A value outside 100 to 15000 is clamped to that range, not rejected.
{ ttlSeconds: number }
Caches identical calls per agent for up to a day. A longer
ttlSeconds is capped at 86400. A cache hit costs nothing and carries cached: true.'summary' | 'full' | 'none'
default:"summary"
How much of
state the log row keeps. summary records its size and top-level keys. full records the first 8,000 characters of the state as sent, with no redaction, and sets truncated when the state was longer. none records nothing.DecisionResult.
ask(state, question, options?)
options takes every field above except state, questions and images, and the promise resolves to that one answer.
odds(instructions, criteria?)
criteria spells out what counts as yes and what counts as no when the instruction alone is ambiguous.
choice(instructions, options)
score(instructions, levels)
Examples
Route a message before the agent sees it
Gate a webhook on a yes-or-no
Judge an image
damaged.type === 'refused' too.
Errors
Decisions.ask and Decisions.run throw a DecisionError with these fields.
Limits and cost
A decision costs 0.005 actions when Jev answers and 0.01 actions when OpenAI answers. A call that falls back is charged once, at the price of the provider that answered. Cached hits and failed calls cost nothing.
Every call writes one row to the agent’s logs under the
decisions source, visible with lua logs --type decisions. The row carries the provider, the routing rule, every answer with its probabilities, the tokens, costUsd and any fallback. costUsd is the provider’s token-based cost estimate, not what you are charged.
Related
LuaDecisiondeclares a named, versioned decision on the agent and runs it withDecisions.run.AIfor free-text generation.

