to="caller" to ask:
to="caller" is always mode="async" and requires an ambient state context — the
ask parks the asking run and returns a suspension sentinel at once. The answer is
handed back by whoever resolves the park, never by an answer door. A caller ask may
carry a structured payload the resolving run reads in place of (or beside) the
question; payload is forbidden on a to="user" ask.
Parks on the subject
A caller ask parks on the asking run’s subject — the same subject a state binding resolves. Every park on that subject is one parked entry, and the run that owns the subject sees the whole set. An entry carries itsid and status and, while it is a live ask, its
addressing and question fields:
status is one of asking (a pending ask), running (an ask being resumed),
finished (a resolved run’s waiting outcome holding a result), or failed (a
resolved run’s waiting outcome that raised). An entry’s unset optional fields are
absent, never present as null keys.
A tool run through the background submit door answers a park exactly as the
synchronous run-tool door does: its run record ends status: parked carrying the
asks entries above when the tool asked its caller — else the suspension sentinel
when only a to="user" ask is open — alongside the resumed_interactions it
resolved on the way.
The door contract
A door that can drive a parkable run — a conversation route, a hook, or a schedule — carries up to four optional jq expressions, the door contract. Each runs over the door’s own input document with the run’s parked entries bound as the jq variable$parked, so an author
branches on what is already parked:
Each expression yields one value; a program that emits more than one, or a result
outside the shape its column names, is a loud error, never a silent default. An
undeclared
$name is refused at save.
Resume, take, and cancel
- Resume an
askingcaller ask by naming itsidwith apayload: the payload is the answer, the parked run resumes under its own identity, and its next outcome — a result, fresh caller asks, or a re-park — comes back. - Take a
finishedwaiting outcome by naming its bareid: the resolved run’s result is returned. Taking afailedoutcome raises, so the taker fails as the resolved run did. - Cancel any parked interaction by naming its
id: it and every run linked above it are torn down for good. A run a door started with a receiver is delivered a single failure.
Waiting outcomes
When a resumed run finishes but the run that resumed it cannot take the result inline — a hook or a schedule fire has no receiver — the terminal waits on the subject as afinished (or failed) parked
entry. The subject’s next owner takes it. A waiting outcome never taken is dropped
past a retention horizon and the drop is surfaced as a platform event, never lost
silently.
Extras
extras_expr builds an extras mapping handed to the started target beside its
kwargs. A target declares the extras keys it reads at registration; the visit refuses
an undeclared key before the run starts, so an extras injection never reaches a
target that never asked for it. extras ride only a start — never a resume, take,
or cancel.
The generic tools
A run inspects and resolves the parks on its own subject with three builtin tools — the LLM-facing face of the same door contract:
They read and write the same subject a door contract does, so an agent resolves a
caller ask by hand exactly as a route resolves one by jq.
Hidden from the inbox
A caller ask is not a human question. Every user-facing read surface hides it and every user-facing resolution door refuses it: it never appears in the interactions inbox or its stream, and the human answer door rejects it — a caller ask is resolved only by its calling run resuming, never by a person.The concurrency cap
Caller asks — and the waiting outcomes they leave behind — are bounded by their own ceiling,INTERACTIONS_MAX_CONCURRENT_CALLER (default 10000), independent of
INTERACTIONS_MAX_CONCURRENT, which bounds to="user" asks. A caller ask at the cap
is refused loudly, exactly as a user ask is at its cap; a finished run’s waiting
outcome is counted here but never refused, so a result is never lost.
Expiry
A caller ask carries a deadline like any async park, and itson_expiry decides what
a lapsed deadline does: kill (the default) tears the whole run chain down, while
resume resumes the parked run with the reserved expiry marker so it takes its
expiry branch. See async parks.
See also
- Interactions — the human ask this parallels.
- jq variables —
$parkedand every other variable a door jq binds. - Conversation bridge — a route as a parkable door.
- Hooks and scheduling a tool — the other parkable doors.

