provider

package
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 4, 2026 License: Apache-2.0 Imports: 43 Imported by: 0

Documentation

Overview

Package provider is codeaf's model adapter: the one place in the process that speaks to an OpenAI-compatible endpoint.

It exists because provider economics are request-shape decisions, not loop decisions. The transcript layer earns a byte-stable prompt prefix; only the adapter can make a provider actually pay for that stability, by carrying the cache key, the usage-accounting opt-in, and the per-phase reasoning knob that the pinned AgentField SDK's Request type has no field for. Everything else in codeaf — the scheduler, the tool registry, the TUI — keeps seeing the SDK's neutral message and response types and never learns a wire detail.

Index

Constants

View Source
const (
	// DirectUserAgent is the identity codeaf gives a service it reaches itself.
	// The historical name distinguishes it from pretending to be a vendor's
	// supported client; the same product name also belongs on routed requests.
	DirectUserAgent = "codeaf"

	// AppURL is the HTTP-Referer OpenRouter groups a RELEASE binary's usage
	// under — stable and release candidates — and it is the app's identity: a
	// request without it is attributed to nobody, whatever else it carries.
	AppURL = "https://agentfield.ai"

	// AppName is the display title OpenRouter shows for the release's app. It
	// also RENAMES the app page when it changes, so it is not a string to vary
	// per caller or per rig.
	AppName = "AgentField AI"

	// AppCategories are the marketplace categories the app is filed under, in
	// the lowercase hyphenated spellings OpenRouter's published category list
	// recognizes ("cli-agent" under Coding, "programming-app" under Coding).
	// An unrecognized word is dropped by the router without an error, so these
	// are copied from that list rather than invented. Every identity carries
	// them: a channel build is the same kind of program as the release.
	AppCategories = "cli-agent,programming-app"

	// StagingAppURL and StagingAppName are the app a staging build reports as.
	StagingAppURL  = "https://staging.codeaf.agentfield.ai"
	StagingAppName = "codeaf staging"

	// DevAppURL and DevAppName are the app a dev build reports as, and so does
	// every build the release workflow did not cut.
	DevAppURL  = "https://dev.codeaf.agentfield.ai"
	DevAppName = "codeaf dev"
)
View Source
const (
	// RescueSlow is a lane that was late. Something is being done about it.
	RescueSlow = "slow"
	// RescueRefused is a lane that said no. Nothing more will be asked of it.
	RescueRefused = "refused"
)

The two words a rescue is drawn with. They are constants because three packages spell them — the transport writes them, internal/session carries them, internal/tui3 draws them — and a word spelled in three places is a word that gets reworded in one.

View Source
const (
	// LagTTFT is the wait before the first token that counts as slow. Two
	// seconds is roughly four times what a warm endpoint takes to start
	// answering, so crossing it is a claim about the endpoint rather than about
	// a large prompt.
	LagTTFT = 2000 * time.Millisecond

	// LagRate is the sustained output rate, in tokens per second, below which
	// an endpoint is slow. Thirty is about a third of what the small fast models
	// this surface rides sustain, and comfortably under the slowest large one.
	LagRate = 30.0

	// LagGap is the widest quiet stretch INSIDE a successful answer that still
	// counts as streaming. Above it the endpoint is assembling the reply
	// server-side and delivering it in lumps — a shape measured across one
	// model's sixteen endpoints on 2026-08-24, where every endpoint that
	// streamed stayed under four seconds between deltas and every one that
	// buffered sat at twelve seconds or worse, up to fifty. Fifteen sits in
	// the empty middle. A lumped answer that arrives is still an answer, which
	// is why this is a lag strike and never a cut: the lane is demoted below
	// the endpoints that stream, and the stall guard's patience (streamguard.
	// go's bufferedQuietBound) is what keeps the lump survivable meanwhile.
	LagGap = 15 * time.Second
)
View Source
const DefaultRouting = RoutingSimple

DefaultRouting is what a client asks for when nobody has chosen: no caller handed it a row and this process installed none (InstallRouting).

IT IS THE SAME ANSWER FOR EVERY CALL, and that is the point. The old default read who was waiting and asked the router to sort by speed for a person's own turn and by price for an errand — a decision made per request, out of sight, that the picker and the record could then disagree with. Under this one the wire carries the person's own row and nothing this build inferred, so what is shown, what is chosen and what is written down are the same fact.

It is spelled again in internal/config ([config.DefaultRouting]) because that package owns the word on disk and this one owns the word on the wire; a test there holds the two together.

View Source
const PhaseWindow = 15 * time.Second

PhaseWindow is how long a phase still describes the present: past it a surface draws nothing rather than a clock for work that may be over.

IT IS THE CONTRACT BETWEEN WHOEVER POSTS A PHASE AND WHOEVER DRAWS ONE, so it is spelled once, here, with the vocabulary — and every beat that keeps a phase alive is DERIVED from it rather than written down beside it. Two packages with two ideas of how long a phase lasts is a stage that goes dark while it is still running, which is the defect this constant was moved out of internal/tui3 to end.

Both beats sit comfortably inside it: a request says its phase again at most once a second while it lasts ([phaseBeat] below), and a turn holding a phase open says it again every third of this window (internal/session's [phaseHeldBeat]). Fifteen seconds is therefore a wide margin on either — wide enough that a busy frame or a machine under load never blinks the segment, and short enough that a posting layer whose goroutine was killed without saying so takes its clock off the screen while a person is still looking at it.

View Source
const ReasoningReplayPolicy = "pass every retained assistant message's reasoning back unmodified under the field it arrived on"

ReasoningReplayPolicy is the wire law for model working carried across a tool loop. The wording lives once because a weaker caller-specific rule is exactly how an older assistant step would quietly lose its continuation.

View Source
const (

	// ReceiptWait is the longest one receipt can take to be answered once it is
	// queued: the whole schedule's ceiling, counted from the queue however long
	// the receipt waited there for a worker ([receiptWork.deadline]), so a
	// waiter that starts after every receipt it is owed was queued sees each one
	// answered within it. It is exported for work that waits
	// for the receipts it is owed before it closes its books
	// ([WithReceiptPending]), so that wait and this schedule are one figure and
	// widening the schedule widens the wait with it.
	ReceiptWait = receiptFetchTimeout
)
View Source
const RescueRetired = "retired"

RescueRetired is the word a retirement travels under: the machine a person pinned said it will not serve this model, so nothing more is demanded of it and this model routes on auto until they pin again.

IT IS A REFUSED REASON AND NOT A THIRD KIND OF WAIT. RescueRefused is the same wire fact about a machine the RACE demanded; this one is that fact about a machine a PERSON demanded, and only a person's own row earns a sentence about their own row. It is spelled here rather than beside its two neighbours in refusalobject.go because it is the pin's word and that file holds no pin policy.

View Source
const WallCeiling = streamWallCeiling

WallCeiling is [streamWallCeiling] for a reader outside the transport: the longest one request is allowed to stay open, whatever its lane's history claims. A surface quoting a wait longer than this is quoting arithmetic on a belief that has forgotten, not a wait anybody could have sat through (internal/tui3's lanes.go draws no tail past it).

Variables

View Source
var ErrNoAPIKey = errors.New("no API key: this session has not been given one yet")

ErrNoAPIKey is what a request meets on a client built without a key and not yet handed one (Client.SetAPIKey). It is a value so the session can name the state to a person in its own words rather than matching a sentence.

View Source
var ErrVideoTimeout = errors.New("video generation timed out")

Functions

func AnswerOffer

func AnswerOffer(ask string, yes bool) bool

AnswerOffer answers an open offer and reports whether one was still open.

FALSE IS A REAL ANSWER AND NOT A FAILURE: the lane came good while the person was reaching for the key, or the request finished, or the offer aged out. A surface that is told false draws nothing and says nothing, because the thing it would have said is no longer true.

The rescue runs OUTSIDE the lock. It starts a request, and a registry held across that would be a lock held across a handshake.

func ApplyAttribution

func ApplyAttribution(header http.Header)

ApplyAttribution writes this binary's attribution set onto a request's headers.

IT IS EXPORTED BECAUSE THIS PACKAGE HAS NOT ALWAYS BEEN THE ONLY ONE THAT POSTS TO THE ROUTER. v1's microphone client reached /audio/transcriptions on its own — that package is gone now, and transcribe.go is the one transcription transport — but it spent a release writing out its own half of this set by hand — a referer and the old title spelling, with no X-OpenRouter-Title and no categories at all — so every word a person spoke to v1 was attributed as an unclassified app while every word they typed was attributed correctly.

So: a package that talks to OpenRouter calls this. It does not write header names and it does not carry the values.

func BaseTakesLaneChoice

func BaseTakesLaneChoice() bool

BaseTakesLaneChoice reports whether the base this process is talking to has said it will carry a routing preference.

It is what a SURFACE asks — internal/tui3's lane row, so that `pinned: X` never stands on a screen as a claim about a request that did not carry it — and it takes no argument because a panel holds no client and so has no base URL of its own to name.

func CacheControlRejected

func CacheControlRejected(model string) bool

CacheControlRejected reports that this model's endpoint has refused a cache breakpoint. Like ReasoningMandatory it is learned rather than published, so it is empty until some call has been told no — which is exactly what a surface should show: a fact when there is one, and nothing when there is not.

func CacheKeyFrom

func CacheKeyFrom(ctx context.Context) string

CacheKeyFrom returns the run's stable cache key, empty when unset.

func CallNodeFrom

func CallNodeFrom(ctx context.Context) string

CallNodeFrom is the node WithCallNode named, empty when nothing did.

func ContextSafetyTokens

func ContextSafetyTokens(window int) int

ContextSafetyTokens leaves room for tokenizer and chat-template differences. It grows with small windows and is bounded on million-token models.

func DecodeJSONObject

func DecodeJSONObject(text string, destination any) error

DecodeJSONObject accepts the small formatting failures common at provider boundaries: a code fence, or a sentence wrapped around an otherwise valid object. It still requires the selected object itself to be strict JSON.

It lives here rather than beside any one caller because those failures are a property of the boundary, not of the pass that happens to be crossing it. The head learned this first — a router that silently falls back to a model without structured-output support answers in prose — and the planner paid for not knowing it: every contract call on a fenced-JSON model returned "invalid character 'B' looking for beginning of value", the money was spent, and the leaf ran with no working method and said nothing about it. One extractor, one tolerance, every structured call.

func Emit

func Emit(ctx context.Context, kind StreamEventKind, delta string)

Emit hands one event to whatever observer is listening on this context, stamped with the room the turn is answering for.

It is the door for the boundaries no adapter can report: a TOOL CALL is something the caller above the client does, and until this existed the only way to tell a surface about one was to invent a second channel beside the token feed. Everything the surface needs to interleave the two — order, and the room key — is already the property of this one.

Nothing listening is the ordinary case (every headless run), and it costs one context lookup. The observer contract is unchanged and still synchronous: a caller emitting from inside a read loop is paying for it in that loop.

func EmitEvent

func EmitEvent(ctx context.Context, event StreamEvent)

EmitEvent is Emit for a kind that carries more than a string — today only StreamToolCallForming, whose call index, id and name are fields. The Session is stamped here from the context, so a caller never sets it and cannot set it to the wrong room.

func EmitReasoning

func EmitReasoning(ctx context.Context, field, delta string, details json.RawMessage)

EmitReasoning is the test-double and adapter-neutral door for a reasoning delta whose wire identity must survive beyond the display event.

func EmptyAtCeiling

func EmptyAtCeiling(response *ai.Response, ceiling int) bool

EmptyAtCeiling is the one answer shape that says more room would change the result: no text, a "length" finish, and the whole ceiling spent. It is deliberately narrower than "blank" — a refusal or a cut stream also has no text, but neither of those is cured by a larger max_tokens. It is the signature of a thinking pass that ate the answer, and it is defined once so the adapter's recovery, the reflex's, and the bill that journals a paid call all call the same thing empty.

func Evidence

func Evidence(err error) taxonomy.Evidence

Evidence reads one failed request into the vocabulary the response boundary reasons in, and DECIDES NOTHING.

── WHY IT IS HERE AND NOT AT THE CALL SITE ─────────────────────────────────

A 404 used to be classified by three unrelated rules: internal/taxonomy's `classOf`, this file's `refusalKind`, and internal/session's `providerCouldNotServe`. Each was correct about the half it had been told, each grew a special case every time a wave met a new shape — #835 added `Evidence.Routing` to work around a fourth — and the class that mattered most fell through all of them: an account's privacy setting reached a person as `the request itself was refused`, with two other machines idle.

So the split is the other way round now. THIS TRANSPORT KNOWS FACTS and says them: what status it wore, who refused, whether a list emptied the set, whose list it was, whether the model is still carried, whether the request fitted. ONE FUNCTION TURNS FACTS INTO A MOVE (taxonomy.Classify), and it is the only place in the build that may. A law test holds the line (internal/provider/classifier_law_test.go).

The facts themselves were all decided at the refusal door, once, on the way past ([markRefusal], [apiError]); nothing is re-read from a sentence here. What is NOT on this evidence is everything the transport cannot see — whether a stream guard cut, how many attempts this model has had, whether the caller has another model — and those are the caller's to add before it classifies.

func FailedLane added in v0.3.0

func FailedLane(err error) string

FailedLane is the upstream lane a failed call names, "" when the failure implicates nobody — the wire fact a retry turns into a lane to avoid. A 5xx relayed from a named upstream names that provider, a cut stream names the provider the stream named; a router's own refusal carries no provider name, a transport fault names no machine, and an empty name is a fact the retry keeps: the retry goes where it always went.

func FinishReason

func FinishReason(response *ai.Response) string

FinishReason reports the provider's own word for how a completion ended, or "" when it never said. It reads the first choice because that is the only one the adapter assembles: the streaming path builds exactly one, and single responses are requested with n=1.

func ImageExtension

func ImageExtension(data []byte, declared string) string

ImageExtension names an image from its bytes, using the provider's declared type only when the bytes do not identify a supported format. The saved suffix must describe what a file contains even when a provider mislabels its reply.

func InstallLaneProber

func InstallLaneProber(c *Client, waiting lanes.ProbeGate) bool

InstallLaneProber wires this client's transport into the registry's prober, and reports whether it did.

waiting is the caller's own answer to "is anybody waiting on this model right now" — the λ of the design, which only a surface can know. Nil is "always", which is the right reading for a headless run that probes at all.

IT REFUSES A CLIENT WHOSE BASE WILL NOT CARRY A DEMAND. A probe's whole body is a `provider.only`, and a probe without it measures whatever endpoint happened to answer, which is a number worse than none. Since issue #433 that is the base's own answer and never its hostname: a base nobody has asked is wired, because the asking is the sending — and nothing is ever bought there until a lane exists to name, which is the real floor (Client.ProbeLanes reads the frontier and an empty one buys nothing).

func InstallRouting

func InstallRouting(strategy RoutingStrategy)

InstallRouting states the routing row this process's clients answer to, for every client that is handed no RoutingSource of its own. The empty strategy is NOBODY HAVING WRITTEN ONE and puts the knob back to exactly that, which is what an unwritten row resolves to — DefaultRouting, the same answer for every call.

func IsConnectionUnavailable

func IsConnectionUnavailable(err error) bool

IsConnectionUnavailable distinguishes a spent connection wait from a model failure. There is no underlying provider error for a fallback to repair.

func KeyExpiredFrom

func KeyExpiredFrom(err error) bool

KeyExpiredFrom keeps the expiry fact after the engine wire has flattened a refusal into text, so hosted and local conversations refresh the same reading.

func LaneGuardOn

func LaneGuardOn() bool

LaneGuardOn reports whether the speed guard is on.

func LaneSheetCertain

func LaneSheetCertain(base string) bool

LaneSheetCertain reports whether base is KNOWN to publish a lane sheet without anybody having to ask it: the shipped router, recognised by its hostname.

IT IS A HINT AND NEVER A REFUSAL. This build used to decide whether the endpoints page was fetched at all by this very substring test, so a binary driven through CODEAF_BASE_URL at a proxy, a mirror, a self-hosted router or the router reached by its IP silently got no sheet, an empty frontier and no lane behaviour whatever — nothing errored and nothing logged a refusal, the feature was simply absent (issue #373). A ROUTER IS RECOGNISABLE BY WHAT IT ANSWERS AND NEVER BY A SUBSTRING OF WHERE IT LIVES: every base is wired, and the sheet learns from the base's own first answer whether there is a page there (lanes.ErrNoSheetHere). What this hostname buys is only that the one base everybody already knows about skips straight to "serves", so the shipped path is unchanged in behaviour and pays not one extra round trip.

IT IS THE BASE URL AND NEVER THE MODEL ID, which is where it parts company with [Client.shippedRouterHint]. That one is also true of a client whose model is spelled `openrouter/...` behind somebody's own gateway, and it is right to be: the ledger still learns from what that gateway serves. But the sheet is fetched FROM THE BASE URL, so the base is the thing a hint can be about.

func LaneTalkAsk

func LaneTalkAsk(model string, now time.Time) lanes.Request

LaneTalkAsk is the request A CONVERSATION'S OWN TURN makes, with nothing yet typed into it: one model, a person waiting, an answer they will read.

It gives the picker the same value of waiting and quality requirement as a conversation. The row is a preview: the actual request adds its prompt, cache lineage and learned work size before deciding where to send it.

The prompt's own length is left out and so is its cache prefix: neither is known before somebody has typed. Output size is unknown too: no workload class or reasoning setting has been selected for this preview.

func LeafCacheKey

func LeafCacheKey(run, leaf string) string

LeafCacheKey derives one leaf's affinity key from its run's. It is a pure function of the two identities — no clock, no counter, no attempt number — so every turn of one leaf produces the same key and a retried leaf rejoins the prefix its first attempt warmed.

func LoadQuirks

func LoadQuirks(dir string)

LoadQuirks seeds the process from a profile directory and names the file later discoveries are written to. Empty dir means ~/.codeaf, which is where every other durable codeaf fact lives.

It is called once at startup, before any request is shaped. Calling it twice re-reads the file, which is harmless: the memo only ever grows, and a fact learned in memory is never dropped by a read.

func LostItsThread

func LostItsThread(text string) bool

LostItsThread judges text that has ALREADY streamed — a reply a person stopped by hand — by the same tests the live guard runs, read once over its tail. It exists for the one road junk still had into the transcript: the guard cuts a stream it wins the race against, and a person who stopped the stream first was handed the soup as their own kept reply, replayed on every request after (internal/session's keepPartial). Nothing here is a second opinion — it is the same window, the same floor, the same compressor.

func MachineryLeak

func MachineryLeak(request *ai.Request, response *ai.Response) bool

MachineryLeak reports that a reply is the model's own tool grammar written as text: the request declared tools, the answer called none, and the content is symbol-dense text that spells a declared tool's name fenced in symbol runes.

IT NEVER SPEAKS WHERE A FENCE IS OPEN. A reply that carries ``` anywhere is showing code on purpose — a person asked what a tool call looks like, and the honest answer is symbol-dense and names a tool. The cost of that conservatism is one failure mode deliberately kept: a leak that happens to emit a code fence goes uncut and the person sees it — which is exactly the old behaviour, not a regression.

func ModelsTried

func ModelsTried(ctx context.Context) []string

ModelsTried names every model this question has been put to, in the order it was put to them. It is the FACT a caller choosing the next model reads instead of counting its own hops.

A context with no record answers nothing, which a caller must read as "no model has been ruled out" rather than as an error: a build with no chain, a call made outside a turn, and `--one-model` all arrive here, and each of them wants the move to be ABSENT rather than broken.

func NodeFrom

func NodeFrom(ctx context.Context) string

NodeFrom is the subject in force for ctx, empty when none was said — which reads as the conversation.

func NoteAnswerCut

func NoteAnswerCut(model, lane string, spent int) bool

NoteAnswerCut records that a model ran out of room mid-answer on one lane, and reports whether that is wider than anything already known. Only a wider cut is worth a write: a model that cuts on every call of a lane costs the profile one save, not one per call.

The figure recorded is what the model actually SPENT, never what it was allowed — a provider that stops short of the ceiling has told us where its own wall is, and that is the more useful of the two numbers.

func NoteReasoningDisableIgnored

func NoteReasoningDisableIgnored(model string)

NoteReasoningDisableIgnored remembers that a model accepted the disable but still spent an answer-sized ceiling without returning any answer. The provider adapter cannot infer this from the HTTP exchange alone: the caller owns the promise that the requested ceiling was large enough for its answer.

func NoteServedWindow

func NoteServedWindow(model string, tokens int) bool

NoteServedWindow records that a model REFUSED a prompt of this many tokens for being too long, and reports whether that narrows what was already known.

It is the one way a published window is ever contradicted, and the evidence bar is deliberately high: not a slow answer, not a bad answer, but the endpoint saying in as many words that the request would not fit. Anything less is a claim this process cannot check, and a ceiling built on a guess is how a model with real room gets folded like a small one.

Only a NARROWER figure is worth a write, so a session that keeps overrunning the same wall costs the profile one save rather than one per turn.

func OnPhase

func OnPhase(fn func(PhaseNews)) (previous func(PhaseNews))

OnPhase registers the reader every phase change is told to and hands back the one that was there, so a surface that opens over another can put it back when it closes. A nil function unregisters.

func PersonAtTheDoor

func PersonAtTheDoor() bool

PersonAtTheDoor reports whether a typed command owns this process.

func PinNow

func PinNow(model string) (demanded, standDown string)

PinNow is that answer and the other half of it, read together: the machine a request for model will demand, and the machine a person's row still names after the wire has stopped asking for it ([retirePinnedLane]).

TOGETHER FOR [lanePinFor]'s REASON ONE LAYER UP. The two are one fact about one moment — is the row on the wire, and if it is not, whose name is still on the screen — and a surface that asked them as two questions could draw a machine's name beside a sentence saying nothing is asking for that machine, or draw neither.

AND IT IS THE ONE DOOR EVERY SURFACE THAT NAMES THE PIN COMES THROUGH (internal/tui3's laneInForce). The chip, the model row's tail and the fold's mark were drawn from the SETTINGS ROW while the wire asked this file, so a pairing the wire retired mid-session left `@morph` on the model word over three turns another machine answered (issue #1022). The model is folded here, by [lanePinFor], under the same normaliser the retirement was written with — a surface folding a spelling of its own would rebuild that disagreement one layer down.

A BASE THAT WILL NOT CARRY A LANE CHOICE AT ALL ANSWERS NEITHER, and that is deliberate: no request demands the machine, so nothing may name it, and what a person reads about that is the base's own sentence (UncarriedPinLine) rather than this one.

func PinnedFor

func PinnedFor(model string) string

func PlanPauseSentence

func PlanPauseSentence(reset, overflowDoor string) string

PlanPauseSentence is the one person-facing sentence for a temporarily spent subscription window, shared by live status, the final row, and connection.

func ProseAnswerAsked

func ProseAnswerAsked(ctx context.Context) bool

ProseAnswerAsked reports the mark above. It is exported for MessageReasoningFrom's reason: a Completer test double has to be able to assert the same contract the real adapter reads, without learning this package's private context key.

func ReasoningBudgetRefused

func ReasoningBudgetRefused(model string) bool

ReasoningBudgetRefused reports that this model's endpoint rejected a thinking budget. Like the fact above it is learned rather than published — no catalog row says it — so it is empty until some call has been told no.

func ReasoningDisableIgnored

func ReasoningDisableIgnored(model string) bool

ReasoningDisableIgnored reports that a model accepted the disable but still consumed the caller's whole answer budget before returning any text.

func ReasoningMandatory

func ReasoningMandatory(model string) bool

ReasoningMandatory reports that this model's endpoint has refused to have its reasoning turned off. It is learned rather than published — no catalog field says it — so it is empty until some call has been told no, and then it stays known across processes (see LoadQuirks). A surface should show exactly that: a fact when there is one, and nothing when there is not.

func ReasoningUnavoidable

func ReasoningUnavoidable(model string) bool

ReasoningUnavoidable reports either observed way a model has shown that its thinking pass cannot be removed. Callers that reserve a small answer budget need the combined fact; request encoding still reads the two facts separately because only a rejected disable must be omitted from the wire.

func RepinLane

func RepinLane(pin LanePin)

RepinLane is SetLanePin as A PERSON'S ACT: the picker, `/model @cloudflare`, the `lane` row in the settings panel — anywhere somebody has just said, in their own words, which machine they want.

IT FORGETS EVERY REFUSAL WHATEVER THE ROW SAYS, and that is the whole difference from the setter above. Re-choosing the SAME lane is a row that did not change and an instruction that did, and it is the exact keystroke the manual promises works: "pinning again puts it straight back". A build that compared rows here would answer a person who had just re-pinned coreweave with silence, and go on routing their model on auto — which is the sentence on their screen made into a lie.

AND IT FORGETS WHAT THE ACCOUNT WAS BELIEVED TO EXCLUDE about that machine (internal/lane's account.go), for the same reason and before the lock is taken — a neighbouring package is never called under this file's lock. A person who has just re-chosen the machine the router said their account cannot reach may have changed the setting, and the next request is how to find out.

func Report

func Report(ctx context.Context, reading Reading)

Report records how a unit of work turned out. It is the call site's half of the contract, and it is idempotent: the first reading wins, so an error path that reports and then falls through to a shared return cannot overwrite what it already said. A call nobody reports on stays unverified, which is the honest answer rather than a missing one.

func RetiredPinLine

func RetiredPinLine(lane string) string

retiredPinLine is the whole sentence, and it is spelled ONCE, here.

Two surfaces say it — the status line while the answer is in flight (internal/tui3's laneRider) and the conversation, which keeps it — and a sentence spelled in two places is a sentence that gets reworded in one. The machine is named the way the person spelled it when they pinned it, because that is the row they will go and look at. RetiredPinLine is that sentence for a surface that has to draw it beside its own furniture (internal/tui3's laneRider). It is exported rather than copied for the reason the two words above it are constants: three packages would otherwise spell one sentence, and a sentence spelled in three places is a sentence that gets reworded in one.

func RetiredPinTail

func RetiredPinTail(lane string) string

RetiredPinTail is the same fact in the room a SETTINGS ROW has for it: `(morph cannot serve this model)`, drawn after the word `auto` on the row whose machine is no longer being asked for (internal/tui3's laneWord).

IT IS A THIRD GRAIN OF ONE FACT AND NOT A THIRD CLAIM, which is the same licence the rider and the parked line already take ([retirePinnedLane]): the sentence a person reads in the conversation says what happened and what happens next, and a row they come back to look at has one line to say why the machine they wrote down is not the machine answering. It is spelled here so that all three move together the day the wording moves.

func RetryAvoidFrom added in v0.3.0

func RetryAvoidFrom(ctx context.Context) []string

RetryAvoidFrom is the list of lanes the caller has asked this call's bodies to ignore, nil when the call is not a retry after a named failure. It is the read side of WithRetryAvoid, exported for the layer that composes the retry and asserts what it stamped.

func RoleFrom

func RoleFrom(ctx context.Context) lanes.Role

RoleFrom is the role in force for ctx, [lane.RoleUnknown] when none was said.

func RoutingRefusal

func RoutingRefusal(err error) bool

RoutingRefusal reports that an error is the router saying NOTHING IT CAN REACH will serve this request as it stands — and that somewhere else still can: another machine once the list comes off, another model after that. It is the typed fact APIError.Routing carries, asked from anywhere in a chain.

A SPENT LADDER IS NOT ONE. RefusalError is what the endpoint ladder returns once it has relaxed the request, dropped the ceiling and walked the fallback models without an answer; it wraps the router's last refusal, and reading THAT as somewhere left to go would send a caller round the whole ladder again to be told the same thing. What it carries is a diagnosis for a person.

func RunCacheKey

func RunCacheKey(task, model string) string

RunCacheKey derives a run's cache key from the run's own identity rather than from a clock or a random source. Two requests inside one run must produce the same key or the affinity is worthless, and deriving it from the task and model also lets a repeated identical run reuse the warm prefix instead of paying to write it again.

func ServedWindow

func ServedWindow(model string) int

ServedWindow answers the narrowest prompt this model has ever been refused for, in tokens, and zero when it has never been refused for length.

ZERO MEANS NOTHING WAS LEARNED, which is the honest reading and the one that leaves the model card's own figure standing alone. A caller must not read it as "this model has no window" — that is the emptiness law applied to a measurement, and internal/session's TrustedWindowFor is written to it.

func SessionFrom

func SessionFrom(ctx context.Context) string

SessionFrom is the conversation in force for ctx, empty when none was said.

func SetLaneGuard

func SetLaneGuard(on bool)

SetLaneGuard turns the speed guard on or off, and it is the ONE switch: it moves the rescue and the probe together, because both are the same promise to a person — that this build may spend a little extra to keep an answer moving — and a row that turned off half of it would be a row nobody could reason about.

OFF IS A PURSE THAT REFUSES EVERYTHING rather than a flag the race consults. The purse is already the one gate every rescue passes through ([hedgeRace.affords]), so a purse that says no is the whole of "do not rescue" with no second path to keep in step — and it is written onto the plan where every arm of the question reads it (waitplan.go). The probe reads the flag directly, because a probe is not priced in dollars: it is gated on whether anybody is waiting.

func SetLanePin

func SetLanePin(pin LanePin)

SetLanePin states which machine this process's conversation asked for. It is called from wherever the routing row is resolved — the surface, once, at launch — and again by the picker when somebody pins from it.

func SetPaymentRequiredHook

func SetPaymentRequiredHook(call func(string)) func()

SetPaymentRequiredHook connects any client's 402 to the local chat door. The returned function removes only this registration when that door closes.

func SetPersonAtTheDoor

func SetPersonAtTheDoor(here bool)

SetPersonAtTheDoor states whether somebody typed the command this process is running. It is what lets a headless command's calls — its planning pass and the nodes the session's executor runs for it, whose roles are not ones a person reads — carry the talk pin under `simple` and count as watched (internal/session's someoneIsWatching), without the command opening a conversation it does not have. Tests hand it false again.

func SharedTransport

func SharedTransport() *http.Transport

SharedTransport is the process-wide connection pool for provider traffic. One pool rather than one per client: the clients (talk, work, boost, media, vision) all address the same account at the same host, and a pool each would mean a handshake each.

func StatusOf

func StatusOf(err error) (int, bool)

StatusOf returns the HTTP status a provider failure carried, including the status in the legacy `API error (N)` spelling. The fallback is centralized here because an in-band or wrapped failure can lose its structured status; retry and response boundaries must still make the same decision.

func Streaming

func Streaming(ctx context.Context) bool

Streaming reports whether a call made on this context is served over the event stream. It is what tells an empty finish reason apart: no terminal frame on a stream is a dropped connection, while a single response that omitted the field is just an endpoint being terse.

func SummaryOutputReserve

func SummaryOutputReserve(profile ReasoningProfile, answer int) int

SummaryOutputReserve is the space a summary chunk must leave behind its prompt when the catalog publishes a floor above the summarizer's low ask. The provider's wire sizing uses the same reasoningCeiling calculation.

func UncarriedPinLine

func UncarriedPinLine(lane, base string) string

UncarriedPinLine is that sentence for a surface that has to draw it beside its own furniture, exported for RetiredPinLine's reason: a sentence spelled in two packages is a sentence that gets reworded in one.

func UnclosedJSONObject

func UnclosedJSONObject(text string) (string, bool)

UnclosedJSONObject answers the one question a truncated reply raises: did the model BEGIN an object and get cut off, or did it never start one?

The two failures arrive at the same door and want opposite repairs. A reply that opened a brace and ran out of room is half bought — the expensive tokens are already in hand — and the right move is to ask for the rest of it. A reply with no brace anywhere spent its whole ceiling on something else (prose, or private deliberation) and there is nothing to continue; that one is asked again. Telling them apart is a structural reading of the text and never of the finish reason, which is the same law DecodeJSONObject is written under.

It returns the text from the first unterminated brace to the end, which is exactly the prefix a continuation is appended to. It answers false whenever a complete object is present, so a caller that has already decoded never reaches it, and false when no brace was ever opened.

func UnmeteredReceiptsFrom

func UnmeteredReceiptsFrom(ctx context.Context) bool

UnmeteredReceiptsFrom reports whether WithUnmeteredReceipts armed ctx.

func ValueOfTimeFrom

func ValueOfTimeFrom(ctx context.Context) (float64, bool)

ValueOfTimeFrom answers what a second is worth on the calls made under ctx, and whether anybody said. It is the read half of WithValueOfTime, exported so a surface can assert what its own calls will ask for.

func WidestAnswerCut

func WidestAnswerCut(model, lane string) int

WidestAnswerCut answers what this model has been seen to spend before being cut off on this lane, zero when it has never been cut off here. Zero means "nothing was learned", which is the honest reading and the one that leaves the derived ceiling standing alone.

func WireLaneSheet

func WireLaneSheet(base, key string)

WireLaneSheet points the live lane sheet at a base, with the bearer a fetch should carry. It is the one door through which internal/lane is handed a transport, and it is exported because two callers need it and a second spelling of it would drift: NewClient wires the base its client talks to, and the process's own beat (cmd/codeaf's lanebeat.go) wires the base the settings name, because on three headless doors the beat starts before any client exists and a beat over an unwired sheet fetches nothing at all.

Wiring is not a fetch — it hands the sheet a base, a bearer and something that can open a connection, and nothing goes to the network until a beat calls Refresh. Wiring the same base twice is harmless: the sheet keeps what that base already answered, and forgets it only when the base itself moves.

EVERY NON-EMPTY ROUTER BASE IS WIRED. Whether there is an endpoints page at it is the base's own to say, once, and the sheet remembers (LaneSheetCertain says why the hostname is a hint here and not the decision). A connected direct service never comes through this door because it owns one road and must not repoint the default account's process sheet.

func WithBilling

func WithBilling(ctx context.Context, sink BillingSink) context.Context

WithBilling arms one piece of work's banking. Like the transcript sink it belongs to the work rather than to the client, because one client serves every node in the process.

func WithCacheKey

func WithCacheKey(ctx context.Context, key string) context.Context

WithCacheKey pins one run's provider affinity. It is set once, at the top of a run, and inherited by every nested worker and synthesis call through the ordinary context tree, which is exactly the property a prefix cache needs: one run is one cache lineage, and no request-time randomness can split it.

func WithCall

func WithCall(ctx context.Context, class CallClass) context.Context

WithCall opens a slot for one unit of work and stamps what kind of call it is. The class named here loses to an override set further out — see WithCallClass.

func WithCallAttempt

func WithCallAttempt(ctx context.Context, class CallClass, attempt int) context.Context

WithCallAttempt opens a slot for a retry. The attempt number is how a caller asks for escalation without knowing anything about the panel: attempt 1 means "whatever you chose last time was not good enough", and it is the router's job to decide what that costs.

func WithCallClass

func WithCallClass(ctx context.Context, class CallClass) context.Context

WithCallClass overrides the class every call opened under ctx belongs to.

It exists for the one case where the same code means two different things: a fan-out at the top of a plan is drawing the whole graph from the goal, while the same function inside an expansion is splitting one oversized node against a far narrower premise. Those are different populations, and a ledger that pooled them would learn the average of two things it could have known separately.

func WithCallHorizon

func WithCallHorizon(ctx context.Context, calls int) context.Context

WithCallHorizon states roughly how many more model calls the work under ctx expects to make. It sizes exploration and nothing else: a three-call errand should never pay to find out whether a lane it has not used is quicker, and a five-hundred-call swarm should find out early (Part II §7 of the design).

func WithCallNode

func WithCallNode(ctx context.Context, node string) context.Context

WithCallNode names the work a call belongs to — a plan node's key, a task's id — so a log full of leaf calls can be read one node at a time. It is separate from the tag because the two are known in different places: the tag is a fact about the code making the call, the node a fact about the work.

func WithCallProgress

func WithCallProgress(ctx context.Context, watcher CallWatcher) context.Context

WithCallProgress attaches the one callback this package makes about a call while the call is still running: going out, parked on a provider's pacing, thinking, writing, and how it ended.

WHAT REPORTS IS EVERY STREAMED CALL THAT RUNS UNDER A WAITING CONTROLLER, and that is the honest bound rather than "every call". The report is opened at [Client.completeWithMessagesStreaming], so anything that never reaches that door says nothing; and what fills it comes through a [streamWatch], which is installed from one site — hedge.go's startArm, under [Client.raceFor]'s gate — so a build with no controller installed (`internal/lane`'s seam, which a shipped binary always fills) reports nothing either. A watcher attached to a call that cannot report is SILENT rather than wrong; if that empty state ever becomes a real door rather than a test's, the fix is to give the bare stream loop a watch, not to feed this from somewhere else.

IT IS CALLED SYNCHRONOUSLY FROM THE READ LOOP AND MUST DO NO WORK. See the type's own doc; the same law WithPacingNotice states in patience.go.

A nil watcher is nobody listening and the context comes back unchanged, so a caller with a conditional surface may pass what it has.

func WithCallShape

func WithCallShape(ctx context.Context, class CallClass, attempt int, shape string) context.Context

WithCallShape opens a slot and names the sub-population this call belongs to within its class.

A class is the grain ability varies at across *kinds of work*; a shape is the grain it varies at within one kind. It exists for exactly one class today. `exec.leaf` covers every leaf the executor runs, and arm B measured what that costs: five leaves that exhausted their budget on one oversized task moved the single global leaf rating far enough to reroute the leaves of every other task, including two where the demoted model had never once failed. A rating is only transferable between calls drawn from the same population, and leaves are not one population.

The shape is the call site's to name because only it knows: the router sees a conversation, the scheduler sees the node the conversation is for. Empty means the class is not divided, which is every planning call — those are already one request against one schema, which is as narrow as a population gets.

func WithCallTag

func WithCallTag(ctx context.Context, tag string) context.Context

WithCallTag names what a call is FOR — "turn", "leaf", "compile", "gate", "reflex" — for the one reader that cannot work it out for itself: a person reading the log a fortnight later.

It rides the context for the reason patience does (patience.go): the adapter underneath is shared by every agent in the process, so the call is the only thing that knows whose call it is.

A call that sets no tag is not untagged if it opened a routing slot: the tag falls back to that slot's class, shortened to its own last word (callTag), so `plan.brief` reads as "brief" without anybody spelling "brief" twice.

func WithConfiguredEffortRung

func WithConfiguredEffortRung(ctx context.Context, rung effort.Rung) context.Context

WithConfiguredEffortRung carries a rung a PERSON chose — a dialled conversation, a task somebody set, the install's own default. It is sent even when the catalog cannot vouch for the model, which is the difference WithConfiguredReasoningEffort draws and the reason it exists: an operator's choice is worth one round-trip to discover an endpoint refuses it, and a harness's guess is not.

func WithConfiguredReasoningEffort

func WithConfiguredReasoningEffort(ctx context.Context, effort Effort) context.Context

WithConfiguredReasoningEffort carries an operator-configured effort, which is sent even when the catalog cannot vouch for the model.

func WithContextBudget

func WithContextBudget(ctx context.Context, budget ContextBudget) context.Context

func WithDiscardedUsage

func WithDiscardedUsage(ctx context.Context, observe func(model, tag string, response *ai.Response)) context.Context

WithDiscardedUsage observes a paid response superseded by an internal retry. The final response keeps its own usage: summing attempts into it would make its context size and cache counts describe a different request. Observers compose so the ledger and each budget owner can retain their own accounting. A full WithBilling owner already receives every attempt; ledger observers must defer to that owner, while budget observers still need this notice.

func WithEffortRung

func WithEffortRung(ctx context.Context, rung effort.Rung) context.Context

WithEffortRung carries a rung the HARNESS chose for one phase — a sentinel's yes-or-no, a standing check. The catalog gate drops it on a model that cannot be vouched for, so a default depth can never break a run on an unknown model.

func WithExpectedAnswer

func WithExpectedAnswer(ctx context.Context, tokens int) context.Context

WithExpectedAnswer says how many output tokens this answer is expected to run to. It is the sunk cost in the watch's commitment rule and nothing else.

func WithFirstPrompt

func WithFirstPrompt(ctx context.Context) context.Context

WithFirstPrompt marks this call as a profile's first prompt: nobody has chosen a talk model yet, so a stall must name `/model` instead of sitting silent. Set by the session when [config.FirstPrompt] is true.

func WithHedgeReport

func WithHedgeReport(ctx context.Context, slot *HedgeReport) context.Context

WithHedgeReport asks the adapter to write down, in the caller's own slot, what the race did to the calls made under ctx.

func WithLaneChoice

func WithLaneChoice(ctx context.Context, choice lanes.Choice) context.Context

WithLaneChoice carries one request's lane choice to the transport.

func WithLeafCacheKey

func WithLeafCacheKey(ctx context.Context, leaf string) context.Context

WithLeafCacheKey narrows the run's affinity to one leaf.

A routing key is not a cache: it is the answer to "which replica should serve this?", and a provider with an automatic prefix cache can only hit when the same replica sees the same bytes twice. The run key gets the first half of that right — every turn of every leaf of one run asks for one destination — and the second half wrong, because the six leaves of a fan-out run CONCURRENTLY with six different transcripts. They are then six growing, unrelated prefixes competing for one replica's cache, which is what the ledgers showed: cache reads quantized in 256-token steps, a third of the re-sent dollars missing a cache that a stable prompt should have hit.

Narrowing to the leaf splits those six lineages apart. What it gives up is the shared head — the system message and the tool block, which every leaf of a run really does share byte for byte — now written cold once per leaf instead of once per run. That trade is not close: the head is a few thousand tokens written once per leaf, and the tail it protects is the whole transcript re-sent on every turn of that leaf. On Anthropic-family endpoints it is not even a trade, because their cache is content-addressed and the explicit breakpoint on the head is shared across leaves whatever the routing key says.

The leaf key is derived FROM the run key rather than replacing it, so a run's requests still share a namespace a provider or an operator can see, and a leaf with no run key set stays unkeyed rather than inventing a lineage of its own.

func WithMessageReasoning

func WithMessageReasoning(ctx context.Context, reasoning []MessageReasoning) context.Context

WithMessageReasoning attaches the sidecar for one request. The slice is copied because a request may outlive the transcript lock that assembled it.

func WithModelsTried

func WithModelsTried(ctx context.Context) context.Context

WithModelsTried opens the record for one question. Opening it twice on one chain of contexts keeps the OUTER one, so a turn's record survives every child context its calls and errands derive.

func WithNode

func WithNode(ctx context.Context, subject string) context.Context

WithNode says which subject the calls made under ctx are about — a task node's own identity, and nothing at all for the conversation itself.

func WithPacingNotice

func WithPacingNotice(ctx context.Context, notice func(bool)) context.Context

WithPacingNotice attaches the one callback the retry loop makes: true when a call parks on a provider's pacing, false when it stops being parked — because it got through, or because it gave up.

It is a bool and not a reason, and that is the contract rather than a shortcut. What is upstream of this is a surface drawing a queue, and a surface has no use for a status code; what it needs is whether the thing is moving. The word a person eventually reads is chosen where the words live (internal/session's task_contract.go), not here.

The notice is called from the sending goroutine, synchronously, so it must not work: the one live implementation sets a field and announces, which is the budget it has.

ITS SIBLING IS WithCallProgress (callprogress.go), which carries the other half of a call's life — it went out, it is writing, it ended — under the very same law about doing no work. This one is about the wait BEFORE the request reaches a machine; that one begins where this one ends.

func WithPatientRateLimits

func WithPatientRateLimits(ctx context.Context) context.Context

WithPatientRateLimits marks every call made under ctx as one that MAY WAIT for a window when there is nowhere else to send the request: the bounded 429 patience in retry.go stops applying, the wait is the window the machine itself named (capped at maxProviderWait), and the context is still the only thing that ends the call.

IT IS NOT PERMISSION TO ASK THE SAME MACHINE AGAIN WHILE ANOTHER IS FREE. A patient call walks the machines exactly as a watched one does and reaches a wait by the same road — every machine it may use being held at once.

It says nothing about faults. A timeout, a torn connection and a 500 keep the short patience they always had, here as everywhere: those are the provider failing, and repeating a failure is not patience.

func WithPlanOverflowGuard

func WithPlanOverflowGuard(ctx context.Context) context.Context

WithPlanOverflowGuard gives every request belonging to one turn the same one-shot billing decision. Repair, relaxation, hedge and model-hop re-entry all derive contexts from this one, so none can buy a second metered attempt after another road has already taken the separately authorised overflow.

func WithProseAnswer

func WithProseAnswer(ctx context.Context) context.Context

WithProseAnswer marks a call whose words are A REPLY SOMEBODY READS rather than a value something parses. It is what turns the promotion on.

func WithReasoningEffort

func WithReasoningEffort(ctx context.Context, effort Effort) context.Context

WithReasoningEffort scopes a harness phase default to one call. The harness sets it around the calls whose job is routing or phrasing rather than thinking. EffortNone is itself a request — "send nothing, let the model use its own default" — and it shadows any effort set further out, which is how an inner phase escapes a run-wide economy like EffortOff.

func WithReceiptPending

func WithReceiptPending(ctx context.Context, pending ReceiptPending) context.Context

WithReceiptPending arms one piece of work to be told about every receipt queued on its behalf and when each was answered (ReceiptPending). It changes nothing about how a receipt is fetched or banked: the money still reaches the work through WithReconcile alone.

func WithReconcile

func WithReconcile(ctx context.Context, sink ReconcileSink) context.Context

WithReconcile arms one piece of work for a receipt that arrives after its call has already ended. It is separate from WithBilling so an ordinary usage block and a late receipt can never both bank the same call.

func WithRequiredReasoningEffort

func WithRequiredReasoningEffort(ctx context.Context, effort Effort) context.Context

WithRequiredReasoningEffort carries an effort that is part of the call's correctness rather than an optional economy. A reflex with a tiny answer cap is the measured case: silently dropping its disable leaves a reasoning model no tokens in which to answer, so an unknown catalog row must not erase it.

func WithRetryAvoid added in v0.3.0

func WithRetryAvoid(ctx context.Context, lanes []string) context.Context

WithRetryAvoid asks every request made under ctx to carry the named lanes in its routing preferences' ignore list. Names are trimmed, deduplicated and emptied of blanks; a list that holds nothing changes nothing.

func WithRole

func WithRole(ctx context.Context, role lanes.Role) context.Context

WithRole says who the calls made under ctx are for.

func WithRoutingIntent

func WithRoutingIntent(ctx context.Context, intent RoutingIntent) context.Context

WithRoutingIntent states who is waiting on the calls made under ctx.

IT IS SAID AND NEVER INFERRED. "Nobody is watching this stream" is close to the answer but is not it: a tool that asks a model something takes the observer off the context and the person is still sitting there waiting for the turn it belongs to. Only the call site knows whether anybody is waiting, so only the call site may say — and a call that says nothing keeps the behaviour it has always had.

func WithServedEndpoint

func WithServedEndpoint(ctx context.Context, slot *ServedEndpoint) context.Context

WithServedEndpoint asks the adapter to write down, in the caller's own slot, which endpoint answered each call made under ctx.

func WithSession

func WithSession(ctx context.Context, key string) context.Context

WithSession says which conversation the calls made under ctx belong to.

func WithStreamObserver

func WithStreamObserver(ctx context.Context, observer StreamObserver) context.Context

WithStreamObserver asks the adapter to stream this completion while still returning the ordinary accumulated response to its existing caller.

func WithStreamSession

func WithStreamSession(ctx context.Context, session string) context.Context

WithStreamSession stamps the room a turn is answering for. The caller that owns the turn (the head, one per turn) sets this on the turn's context before making the provider call; every StreamEvent that call emits carries it, so a consumer fed by more than one room can tell them apart.

func WithThinkingWall

func WithThinkingWall(ctx context.Context, wall time.Duration) context.Context

WithThinkingWall carries the wall one completion runs under, so the thinking pass in front of its answer is told how long it has ([Client.wallBudget]).

It keeps whatever effort is already on the context and adds only the wall, and it is applied by the wall itself — last, on the context the completion is actually sent with — because an effort set further in would shadow it.

func WithUnmeteredReceipts

func WithUnmeteredReceipts(ctx context.Context) context.Context

WithUnmeteredReceipts arms one piece of work to have an answer that arrived whole but carried no usage block settled the way a cut one is ([Client.settle]): its receipt is asked for by generation id, or it is told as a call nobody could price. Without it such an answer is billed nowhere and said nowhere, which is every other caller's behaviour, left alone on purpose.

IT IS OPT-IN BECAUSE IT IS NEW MONEY ON AN OLD ROAD. A program's model API arms it (internal/provider/modelapi): its runs are held to a dollar ceiling and read as one account, and true-myth's call 7483768e of 2026-09-23 — a 200 on kimi-k2.6 after nearly eight seconds with no usage block — was in no book at all. A direct service is untouched either way, because settle stops at one: its missing usage block is a subscription's silence, not a charge.

func WithValueOfTime

func WithValueOfTime(ctx context.Context, seconds float64) context.Context

WithValueOfTime states what a second is worth to whoever is waiting on the calls made under ctx, in SECONDS PER DOLLAR.

It is the companion of WithRoutingIntent and it is written under the same law: IT IS SAID AND NEVER INFERRED. The intent says whether somebody is waiting; this says what their wait costs, which is a thing only the graph knows — a chat turn is worth a person's attention ([lane.AttentionValue]), a node on a task's critical path is worth the whole task, and a node with slack off the path is worth nothing at all. [lane.Lambda] is the table that answers it; this is how the answer travels.

A call that says nothing keeps the behaviour it has always had: interactive calls are worth a person's attention, background calls are worth nothing, and a `price` routing row is worth nothing whatever anybody said.

func WithoutBabbleGuard

func WithoutBabbleGuard(ctx context.Context) context.Context

WithoutBabbleGuard takes the degeneration guard off every call made under ctx. The silence watchdog has no switch and is not affected: a request that produced nothing in ninety seconds has failed by any reading, and there is no preference under which sitting on it is the answer.

It rides the context for the reason patience.go's seams do — the adapter is shared by every agent in the process, so a field on the client would make a task node's setting the conversation's.

func WithoutPatientRateLimits

func WithoutPatientRateLimits(ctx context.Context) context.Context

WithoutPatientRateLimits takes the patience off every call made under ctx, whatever an outer context granted: a 429 on every machine the request may use is handed straight back. A crew seat asks this way, because its answer to "not yet" is its next route or its next model, never a wait — a pool at its daily limit that is waited on a minute at a time holds a task for as long as the limit lasts.

AND IT NEVER WAITS ON THE SAME MACHINE AFTER A 429: a free move to another machine is still made at once, but the wait that would follow — the machine's named window, or our doubling — is not sat out; the refusal is handed back instead ([handsBackRateLimits]).

func WithoutStream

func WithoutStream(ctx context.Context) context.Context

WithoutStream takes the observer back off a context, for a call made inside a surface's own context that is not the surface's conversation.

The observer is installed once, on the process's serving context, so anything that borrows that context to ask a model something inherits a live typewriter pointed at the transcript — and a call whose answer is a LABEL rather than a reply would type its label into the room as if somebody were saying it. The head's room-naming clerk is the first such caller (head/scribe.go); a background summarizer would be the second.

A typed nil is stored rather than the key being removed, because a context value cannot be unset — and the reader below already treats a nil observer as "do not stream", which is exactly what this means.

Types

type APIError

type APIError struct {
	// Status is the refusal's HTTP status, or the normalized gateway failure
	// for an in-band error that supplied no status of its own.
	Status int
	// Message is the provider's error text or a description of its terminal
	// failure marker. When it could not be decoded, Body carries it whole.
	Message string
	// Body is the undecoded payload, kept so nothing is lost when the provider
	// answered with something this client does not know the shape of.
	Body string
	// Provider is the UPSTREAM the router handed this request to, exactly as
	// OpenRouter spells it in `error.metadata.provider_name`. It is EMPTY when
	// the router refused on its own account, and that emptiness is a fact rather
	// than a gap — see [APIError.OurRequest].
	Provider string
	// Raw is the upstream's own answer, out of `error.metadata.raw`, clipped to
	// [maxRawClip]. It is the sentence that says what the 400 actually was, and
	// it is the one thing "Provider returned error" never contains.
	Raw string
	// Routing says the ROUTER emptied the endpoint set for this request — a
	// list, a policy, a price ceiling or a demand left it nothing to ask — so
	// another machine or another model can serve the same bytes. It is decided
	// ONCE, by the refusal classifier at the refusal door (refusalobject.go,
	// [Client.refuseUpstream]), and carried here so that nobody downstream has
	// to decide it again from the sentence: it is the difference between "try
	// somewhere else" and "our own request is wrong", which [APIError.OurRequest]
	// and internal/taxonomy both read.
	Routing bool
	// Account says the list that emptied the set is the ACCOUNT'S OWN — a
	// privacy switch, a paid-training guardrail, a standing ignore list — which
	// is true of every model rather than of this one. It is only ever set beside
	// Routing: the MOVE is the same (somewhere else), and what it adds is which
	// list, for the ledger and for the journal line.
	Account bool
	// Withdrawn says THE ROUTER NO LONGER CARRIES THIS MODEL. The machines are
	// fine and the request is fine; the id is gone, so there is no machine to
	// rotate to and no shape to relax, and the only move is another model.
	//
	// Before it, such a 404 was indistinguishable from an emptied set and spent
	// the whole transport budget buying three more identical refusals (#838).
	Withdrawn bool
	// Overflow says the request DID NOT FIT the model's window.
	//
	// IT IS DECIDED FROM STRUCTURE AT THIS DOOR AND NOWHERE ELSE. A seven-branch
	// regex over the provider's prose used to answer it in internal/session, and
	// it ran AFTER the verdict was computed and returned before the verdict could
	// be read — a string deciding what a typed classification had already
	// answered. What sets it now is the request-too-large status and the error
	// envelope's own `code`, with the sentence kept only as a hint for a body
	// that carries neither ([overflowRefusal]).
	Overflow bool
	// Context facts are optional evidence, parsed once at the refusal boundary.
	ContextLimit  int
	InputTokens   int
	OutputTokens  int
	Local         bool
	BudgetChanged bool
	// Code is the error envelope's `code`, as text. The router types that field
	// as a number, as a string, and sometimes omits it, so it is normalised here
	// once rather than decoded at each reader.
	Code string
	// Payment, PlanPaused and PlanUnavailable are the three billing facts the
	// refusal door classified from the vendor's numeric code. They travel on the
	// refusal so the response boundary does not classify the same body again.
	Payment         bool
	PlanPaused      bool
	PlanUnavailable bool
}

APIError is one refusal from the model provider, with the two things about it that are facts rather than prose: the status it came back under, and — when the body decoded — the provider's own sentence about why.

It exists because the only carrier those facts ever had was the formatted string, and everything downstream that wanted to say something honest about a failure had to go mining in it. A room row that reads "API error (404): {"error":{"message":"No endpoints found ...\"sh\"..." is that mining not happening: a JSON blob delivered to a person as an explanation. With the message in a field, the sentence a reader gets is composed from parts rather than cut out of transport.

Error() keeps its OPENING byte-for-byte. That is deliberate and load-bearing: the harness's provider-error taxonomy recovers a status code by reading the text, so a rewording of the `API error (404): …` head would silently disable rate-limit and transient-failure retries. What may be added is a tail, and [APIError.upstream] is the only thing that adds one.

── THE MEASURED FAILURE THAT PUT Provider AND Raw ON HERE ──────────────────

SWE-Marathon run s2, 22:45 UTC: a turn died with the whole of what anybody was ever told being `error: after 3 retries: API error (400): Provider returned error`. That sentence names no provider, carries no upstream body, and left five hours of a benchmark's budget unspent with NOTHING in the session journal to autopsy — no error row, no endpoint, no status. "Provider returned error" is OpenRouter saying that somebody ELSE refused, and the somebody and the refusal are both in the JSON it sent: `error.metadata` carries `provider_name` and `raw`, and this client threw them away.

func RefusalFrom

func RefusalFrom(err error) (*APIError, bool)

RefusalFrom recovers the provider's refusal from anywhere in an error chain, which is how a caller several wraps away asks the two questions above rather than grepping the sentence.

func (*APIError) AccountCannotPay

func (e *APIError) AccountCannotPay() bool

AccountCannotPay reports the terminal authenticated-account refusal shared by connect and the live transport. It reads the original body, never Error's formatted sentence, so a plain pacing 429 cannot become terminal by wording added inside this process.

func (*APIError) Error

func (e *APIError) Error() string

Error keeps the SDK's exact error phrasing, and names the upstream when the router told us there was one.

func (*APIError) FromUpstream

func (e *APIError) FromUpstream() bool

FromUpstream reports that THE ENDPOINT THE ROUTER CHOSE is what refused, not the router. It is the presence of a provider name and nothing else: OpenRouter puts `provider_name` in the metadata exactly when it is relaying somebody else's refusal, and leaves it out when it is answering for itself.

It is the distinction that decides whether another endpoint is worth asking.

func (*APIError) KeyExpired

func (e *APIError) KeyExpired() bool

KeyExpired reports the service refusing the key as expired: a 401 whose sentence says so (OpenRouter answers `API key expired.`). It is a fact read off the wire, stamped here so no caller reads the status for itself; the balance read spells the same fact off its own routes (internal/credits).

func (*APIError) OurRequest

func (e *APIError) OurRequest() bool

OurRequest reports that THE REQUEST IS WHAT IS WRONG, so no endpoint will do better with it.

IT IS A SHAPE, NEVER A STATUS LIST. A 4xx that named an upstream is that upstream's refusal and another one may well serve it; a 4xx that named nobody is the router reading our own bytes and saying no, and asking again — anywhere — spends the deadline to be told the same thing. 429 is excluded because it is pacing rather than a verdict on the request, and it has its own patience (retry.go).

AND A ROUTING REFUSAL IS NOT OURS EITHER (APIError.Routing). It names no upstream for the same reason our own malformed bytes name none — nobody was asked — and that is the only thing the two have in common: this one is a list or a setting that emptied the set, and the measured turn of 2026-09-10 ended on "the request itself was refused" because the two were read as one.

func (*APIError) UpstreamFault added in v0.3.0

func (e *APIError) UpstreamFault() bool

UpstreamFault reports that THE NAMED ENDPOINT FAILED ON ITS OWN ACCOUNT: it was asked, it answered, and its answer was a fault of its own (5xx). It is the one relayed refusal that names a lane worth steering the SAME request away from on its next attempt: a relayed 4xx is that endpoint's reading of the request, which the next endpoint may read the same way, and a 429 is pacing with its own patience (retry.go).

It lives here because a status is a fact only this package and the taxonomy may read; a caller asks the question and never the number.

type App

type App struct {
	URL        string
	Name       string
	Categories string
}

App is one OpenRouter app identity: the values the attribution headers carry.

func AppFor

func AppFor(revision string) App

AppFor names the app a binary stamped with revision reports as. A stable or release-candidate tag is the release's app and nothing else is; a staging tag is the staging app; everything else, recognized or not, is the dev app, so a build nobody tagged can never be counted as the release.

func RunningApp

func RunningApp() App

RunningApp is the app this binary reports as.

func (App) Apply

func (app App) Apply(header http.Header)

Apply writes the whole attribution set onto a request's headers.

type Billed

type Billed struct {
	// Node is what [WithCallNode] named, empty when nothing did.
	Node string
	// Model is who actually served the call — the rung a panel picked or an
	// escalation moved to — rather than the name the caller asked for.
	Model string
	// The four figures the usage table holds. CachedTokens is the share of
	// PromptTokens the provider billed at the cached rate.
	PromptTokens     int
	CompletionTokens int
	CachedTokens     int
	Cost             float64
}

Billed is one model response the provider charged for, as the adapter read it off the wire. It carries the node the call belongs to so a listener does not have to re-derive it: the same WithCallNode the call log is keyed by names the work, and a call made without one is spend nobody can file.

func (Billed) Empty

func (b Billed) Empty() bool

Empty reports a response the adapter cannot bill: the provider sent no usage block, or sent one that says nothing was spent. Neither is written down — guessing at a number is worse than a gap, and the gap is visible as a call the ledger did not see rather than as money the ledger invented.

type BillingSink

type BillingSink func(Billed)

BillingSink is told what one response cost, as soon as it is decoded. An implementation must be safe for concurrent use: a leaf's batch of parallel tools can each be waiting on a call of their own.

func BillingSinkFrom

func BillingSinkFrom(ctx context.Context) BillingSink

BillingSinkFrom and CallNodeFrom read back what a leaf's context was armed with. They exist for the surfaces that arm it and the tests that check they did: arming billing is one line at three call sites, and a call site that silently armed nothing is exactly the shape of the defect this file answers.

type Call

type Call struct {
	// contains filtered or unexported fields
}

Call is one unit of routable work: a single planning call, or a whole exec leaf together with every turn of its tool loop.

It is a mutable slot rather than a plain context value because the two things a router needs from a call site are both writes, and both have to outlive the function that opened them. The router writes down which model it picked, so that the second turn of a leaf goes where the first one went — a leaf that wandered between models would rewrite its prefix cache every turn and mix two lineages into one transcript. And the call site writes down how the work turned out, which it can only know after it has parsed and checked the answer, long after the call itself returned.

A call made without a slot is not an error and not a special case: the slot is absent on every path that has no router, and every method here is a no-op then.

func CallFrom

func CallFrom(ctx context.Context) *Call

CallFrom returns the slot for this unit of work, nil when nothing opened one.

func (*Call) Attempt

func (c *Call) Attempt() int

Attempt reports how many times this unit of work has already been given up on.

func (*Call) Class

func (c *Call) Class() CallClass

Class reports what kind of call this is.

func (*Call) Model

func (c *Call) Model() string

Model reports the model pinned to this unit of work. It is empty when no router served the call, which lets callers degrade to their configured model without making the single-adapter path participate in routing state.

func (*Call) Observe

func (c *Call) Observe(observer func(Reading))

Observe registers the router's side of the reading contract. It is set after the call has been placed, because until then there is nothing to attribute a reading to.

func (*Call) Pin

func (c *Call) Pin(model string) string

Pin fixes the model this unit of work runs on and returns whatever is now fixed. The first caller wins, so every later turn of a leaf's loop is handed back the model the first turn chose rather than choosing again.

func (*Call) Shape

func (c *Call) Shape() string

Shape reports the sub-population within the class, empty when the class is not divided.

type CallClass

type CallClass string

CallClass names the kind of work a call is doing.

It is carried the same way the effort and cache-key knobs are, and for the same reason: only the call site knows what the call is for, and only the adapter can act on it. The classes are the harness's own passes rather than anything generic, because that is the grain ability actually varies at — the router lab found the panel's ordering on structured planning different from its ordering on reasoning, and averaging them would have hidden both.

const (
	ClassPlanSpine       CallClass = "plan.spine"
	ClassPlanGround      CallClass = "plan.ground"
	ClassPlanExpand      CallClass = "plan.expand"
	ClassPlanFanOut      CallClass = "plan.fanout"
	ClassPlanBind        CallClass = "plan.bind"
	ClassPlanSize        CallClass = "plan.size"
	ClassPlanAudit       CallClass = "plan.audit"
	ClassPlanContract    CallClass = "plan.contract"
	ClassPlanBrief       CallClass = "plan.brief"
	ClassPlanRevise      CallClass = "plan.revise"
	ClassPlanEnsemble    CallClass = "plan.ensemble"
	ClassPlanRecalibrate CallClass = "plan.recalibrate"
	ClassExecLeaf        CallClass = "exec.leaf"

	// ClassTaskNode is a WHOLE SETTLED PIECE OF WORK in the chat engine: one
	// task node, from the brief it was handed to the answer its check gave.
	//
	// It is not exec.leaf under another name. A leaf is one turn loop inside the
	// resident's plan and its verdict is the plan's own reading of the answer;
	// a task node is a worker in its own copy of the repository whose outcome
	// somebody else already decided — the check at the end of it — and the whole
	// of what this class exists for is that the decision was already made and
	// paid for (internal/session's taskgrade.go). Pooling the two would rate a
	// model's turn-taking and its finished work as one ability.
	ClassTaskNode CallClass = "task.node"
)

func CallClassFrom

func CallClassFrom(ctx context.Context) CallClass

CallClassFrom returns the class of the call in flight, empty when unstamped.

type CallEnd

type CallEnd string

CallEnd is HOW a call ended, in the four outcomes that are different things to draw. They are deliberately not the taxonomy's causes: a surface drawing a line needs to know whether to leave the answer up, replace it, or say nothing at all, and four words is the whole of that question.

const (
	// CallEndAnswered is the model having finished.
	CallEndAnswered CallEnd = "answered"
	// CallEndCut is the request ending before its answer did — a bound of ours,
	// a torn connection, or this call moving on to another machine.
	CallEndCut CallEnd = "cut"
	// CallEndRefused is the machine saying no.
	CallEndRefused CallEnd = "refused"
	// CallEndCancelled is nobody's fault: the caller left, or this arm lost a
	// race another arm had already won. IT IS NOT A FAILURE and a surface that
	// drew it as one would be drawing this build's own hedging policy as
	// provider weather (armwatch.go's [streamWatch.lost] says what that cost).
	CallEndCancelled CallEnd = "cancelled"
)

type CallPhase

type CallPhase string

CallPhase is where one call is, in the few words a surface can draw.

THE PACING PARK IS A PHASE AND NOT A SECOND CHANNEL. A call that is waiting out a provider's "not yet" is in a state exactly as a call that is thinking is, and it is the state a person waits longest in; WithPacingNotice says the same thing as a bare bool, from the same one site in dispatch.go, and is the older spelling of this phase rather than a second account of it.

const (
	// CallStarted is the request on the wire with nothing back yet.
	CallStarted CallPhase = "started"
	// CallPaced is the request parked before the wire because every machine it
	// may go to is being held (patience.go).
	CallPaced CallPhase = "paced"
	// CallThinking is the endpoint writing where nobody can read.
	CallThinking CallPhase = "thinking"
	// CallWriting is the answer arriving.
	CallWriting CallPhase = "writing"
	// CallEnded is the last report this call makes, and the only one carrying an
	// [CallEnd]. It is said ONCE, when the request has really come back — never
	// for an attempt that is about to be made again on another machine, which is
	// what a person sees as one call still running (see
	// [callProgress.landed] for why an attempt's ending is latched and not
	// spoken).
	CallEnded CallPhase = "ended"
)

type CallProgress

type CallProgress struct {
	// Model is what was asked, and Served the machine that is answering it —
	// empty until the stream names one, which on a cold path is never.
	Model  string
	Served string
	// Attempt names WHICH CONCURRENT REQUEST of this question this is: 0 for the
	// one the caller made, 1 and up for a rescue racing beside it (hedge.go). A
	// reader drawing one line per call keys on it, so that the rescue does not
	// overwrite the request it was sent to save — and so that the loser's
	// [CallEndCancelled] does not read as the question having been abandoned.
	//
	// IT IS NOT A COUNT OF TRIES. A call that walks to a second machine keeps its
	// number and says so with a fresh Started; what changes is where it is, never
	// how many goes it has had.
	Attempt int
	// Started is when THIS ATTEMPT really went out, and it MOVES: a call that is
	// refused and walks to another machine reports [CallStarted] again with a
	// later Started, because the wait a person is sitting through began again.
	// FirstToken is when the first delta of anything — answer or thought — came
	// back, and is ZERO UNTIL IT DOES, which is the state a surface most needs to
	// draw: the gap between the two is the whole of what a person is waiting
	// through.
	Started    time.Time
	FirstToken time.Time
	// Tokens is progress a person could read and Reasoning is the run of thought
	// underneath it, counted apart for the reason [control.Reading] counts them
	// apart: hidden work keeps a stream alive and shows nothing. Both are the
	// stream's own running estimate and neither is the bill — the provider's
	// usage receipt is what money is counted from, always (calllog.go).
	Tokens    int
	Reasoning int
	// Phase is where the call is now, and End is filled in only on the last one.
	Phase CallPhase
	End   CallEnd
	// Err is what ended it, on the endings that carry a reason.
	Err error
}

CallProgress is ONE OUTBOUND CALL WATCHED WHILE IT IS STILL RUNNING, and it is the whole of what this package will say about a call before the call is over.

── WHY IT EXISTS ───────────────────────────────────────────────────────────

A task room can draw a worker's call — the model thinking, the token count climbing, the seconds since it went out — because a worker's turn streams through the session's own observer. Nothing else does. A division, a sizing pass, a mark being read are each ONE call through internal/session's callRole, and every one of them draws nothing at all while it runs: measured on 2026-09-10 those calls ran between 12 and 219 seconds with a blank line over them, which is indistinguishable from a process that has stopped.

The events were never missing. Every streamed call in this process already reports each moment of its stream to a hazard controller ([streamWatch.note] in armwatch.go), which is where the tokens, the first token, the heartbeats and the silences are counted for the waiting policy. This seam forwards what that one place already knows, so there is no second decoder and no second count of anything.

── WHY IT RIDES THE CONTEXT ────────────────────────────────────────────────

The same reason WithPatientRateLimits does, said in patience.go and true here: the adapter is SHARED. A task node's agent talks through the very same *Client the person's conversation does, so a field on the client would have a division's progress drawn over a conversation's. The call is the only thing that knows whose call it is, and the context is what the call already carries — which is also what lets a caller attach this without any file it does not own already being changed.

── IT IS CALLED FROM THE READ LOOP, SO IT MUST DO NO WORK ──────────────────

The one rule this seam has, and it is the same rule WithPacingNotice has: the function is called SYNCHRONOUSLY from the goroutine reading the stream, between two deltas of the person's answer. It may set a field and announce. It may not take a lock somebody else holds, write a file, or call back into this package. Anything it does is time the answer is not being read in.

type CallWatcher

type CallWatcher func(CallProgress)

CallWatcher receives one call's progress synchronously and in order. It is a function and not a one-method interface for the reason StreamObserver is: every watcher in this build is a closure over a surface's own row, and an interface would be a named type each of them had to declare to say the same thing.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is codeaf's model adapter. It satisfies the harness's LoopClient and TextStreamer interfaces structurally, so nothing above it knows a wire format, and it owns the only outbound provider path in the process.

func NewClient

func NewClient(config Config) (*Client, error)

NewClient builds the adapter. It performs no network request.

A CLIENT MAY BE BUILT WITHOUT A KEY. The chat surface opens on a profile with nothing in it and asks for the key on its first screen (internal/tui3's firstrun.go), so the session — and this adapter under it — has to exist before the key does. Every request refuses with ErrNoAPIKey until Client.SetAPIKey lands one; nothing is sent with an empty bearer. The other three fields are still required: they have defaults and a caller with none is a caller with a bug.

func (*Client) CompleteWithMessages

func (c *Client) CompleteWithMessages(ctx context.Context, messages []ai.Message, options ...ai.Option) (response *ai.Response, err error)

CompleteWithMessages performs one completion.

── THE GUARD IS ARMED BY THE CALL, NEVER BY AN AUDIENCE ────────────────────

Every completion is asked for as a STREAM, whether or not anybody attached an observer to watch it. The observer decides who is TOLD what arrives; it has never had anything to do with whether the call is watched, and until 2026-08 it silently decided exactly that.

What that cost, measured: a headless `codeaf do` leaf attaches no observer, so every one of its calls took the request/response path, whose only bound is [adaptiveCompletionTimeout] — a total deadline that caps at fifteen minutes. A DeepSeek endpoint accepted a request and never answered; the leaf sat on it for 15m24s, a twenty-minute claim reaper then took the node away, and the run spent ninety minutes and $0.63 producing nothing. Every detector that would have caught it in ninety seconds already existed — streamguard.go's first delta bound, its mid-stream gap, its wall derived from the lane's own measured history — and every one of them was dormant because a stream nobody was reading was not a stream at all.

A fail-safe that arms only when a person is looking is decoration (docs/design/failsafe/FAILSAFE.md). So the shape of the request is decided here, by what the adapter needs in order to see, and the observer is optional throughout the streamed path.

The accumulated *ai.Response is byte-identical either way, which is what makes this a change of transport and not of contract. The one endpoint that cannot be served this way says so on the wire — a gateway that takes `stream: true` and answers one whole JSON completion — and [Client.unstreamable] remembers it from what actually happened, so the fallback is a memo rather than a guess.

func (*Client) ExecuteToolCallLoop

func (c *Client) ExecuteToolCallLoop(
	ctx context.Context,
	messages []ai.Message,
	tools []ai.ToolDefinition,
	config ai.ToolCallConfig,
	call ai.CallFunc,
	options ...ai.Option,
) (*ai.Response, *ai.ToolCallTrace, error)

ExecuteToolCallLoop satisfies the harness's LoopClient interface. codeaf ordinarily drives its own loop against this adapter, so this is a contract detail rather than the live chat path — but it is a REACHABLE one, and where it goes is the SDK boundary.

── THE SDK BOUNDARY ────────────────────────────────────────────────────────

On OpenRouter nothing below this line is the SDK's. The loop runs over this adapter's own transport (openrouter_client.go), which is the only way a request can carry X-OpenRouter-Categories — the SDK's client has no field for it — and the only way a refused belt reaches the endpoint-refusal ladder instead of ending the turn on a 404.

c.base is only for an operator's non-direct plain OpenAI-compatible endpoint. A connected service is direct even when its wire happens to be compatible: sending that loop through the SDK would bypass both its billing-door answer and the User-Agent that identifies codeaf honestly.

func (*Client) FallbackModels

func (c *Client) FallbackModels(model string) []string

FallbackModels names the models this client would move to when `model` can no longer answer, in order — the SAME chain the two doors above walk, offered to a caller that has to make the decision itself.

Its one caller today is internal/session's turn loop, which owns a failure this package cannot see: a stream that opened, was accepted, and then went quiet often enough to have spent its budget. The precedence and the cap stay here, in [Client.fallbackChain], because a second place that decided which model comes next would be a second answer to drift from this one. AND A MODEL THE ROUTER HAS PUT DOWN IS NOT OFFERED. The chain is where a hop picks its target, so a model already known to have no endpoints must not be on it — otherwise the hop lands on a second 404 and the turn pays twice for one fact (withdrawn.go, measured 2026-09-10 22:39). Dropping it HERE rather than at the caller is what keeps the order deterministic: two turns of one conversation asked the same question and moved to different models, because each rediscovered the withdrawal for itself.

func (*Client) Model

func (c *Client) Model() string

Model reports the adapter's default model slug.

func (*Client) OwnsToolLoop

func (c *Client) OwnsToolLoop() bool

OwnsToolLoop tells the harness that this client's transport is harness-owned, so the bounded safe loop — horizon compaction, stall detection, the tool membrane — drives it rather than a provider-side loop.

func (*Client) ParseDocument

func (c *Client) ParseDocument(ctx context.Context, request DocumentRequest) (*DocumentResponse, error)

ParseDocument performs the completion that activates OpenRouter's file parser and harvests text from file annotations. Native is the sole exception: OpenRouter produces no annotations for native files, so the model is explicitly asked to return extracted text.

The acknowledgement is kept small by the PROMPT — "Acknowledge receipt." — and no longer by a ceiling this file picked (see the const block).

func (*Client) ProbeLanes

func (c *Client) ProbeLanes(ctx context.Context, model string)

ProbeLanes buys a measurement of the two lanes this model's next turn is most likely to use. It is what a keystroke turns into, and it returns before anything has been sent.

THE SHAPE OF THE ASK IS THE TURN'S OWN (LaneTalkAsk), because probing the head of a frontier computed for some other kind of request would measure two machines the turn was never going to use. The head is at most two — the lane the request is going to and the one a rescue would go to — and the prober's own budget decides whether this pair is bought at all: at most one pair every twenty seconds per model, and none when nobody is waiting.

It is a method on the client rather than a package function because the gate it eventually passes is this client's — its routing row, its rate limiter, its base URL — and because a build with no router wired has no probe to buy.

func (*Client) SetAPIKey

func (c *Client) SetAPIKey(key string) error

SetAPIKey hands the adapter the key its requests ride from now on: the one a person pasted on the first-run screen or into the settings row, arriving while this client is already the conversation's.

The SDK client under the plain-OpenAI loop is rebuilt here rather than patched, because it validates its key at construction and holds it privately; while there is no key it is simply absent, and the one path that needs it says so (ExecuteToolCallLoop). The adapter's own transport reads the key per request under the lock, so a request already in flight keeps the bearer it was encoded with and the next one carries the new key.

func (*Client) StreamComplete

func (c *Client) StreamComplete(ctx context.Context, prompt string, options ...ai.Option) (<-chan ai.StreamChunk, <-chan error)

StreamComplete performs one streaming completion over a single user prompt. It mirrors the SDK's channel contract exactly so the harness's stream pump is unchanged.

func (*Client) WithdrawnModel

func (c *Client) WithdrawnModel(model string) bool

WithdrawnModel reports that this client has already been told the router does not carry this model.

It is exported because the layer that owns the one model hop has to be able to skip such a model rather than hop onto it (internal/session's nextFallback reads it through Client.FallbackModels, which does the skipping).

type Config

type Config struct {
	APIKey  string
	BaseURL string
	Model   string
	// Direct says this account is a connected service with one road rather than
	// a router with a set of serving lanes. It suppresses every lane preference,
	// sheet and probe at the transport boundary; a direct service must never be
	// asked for OpenRouter's endpoints document merely because another account
	// in the process uses it.
	Direct bool
	// KeyOptional is true only for a service such as a local Ollama runner that
	// explicitly accepts an empty key. The ordinary keyless client remains the
	// first-run state and refuses before the wire.
	KeyOptional bool
	// BillingDoor is the person-facing name of a bound road. Empty is a service
	// with one road and preserves every older status line.
	BillingDoor string
	// PlanOverflow is the separately billed road a paused subscription may use.
	// It is inert unless OverflowOnPlanPause is true, which is never the default.
	PlanOverflow        string
	PlanOverflowDoor    string
	OverflowOnPlanPause bool
	// Effort is the operator's own pin carried by the model value this client
	// was built from, such as `vendor/model:high`. It belongs to this client
	// rather than a context because one run holds several differently pinned
	// seats, and it outranks the run-wide economy; only an effort required for
	// one call's correctness wins over it. A router copies the pin to every
	// fallback model because the seat keeps doing the same job after a fallback.
	// Zero is the ordinary unpinned case, and the level is NEVER part of Model.
	Effort  Effort
	Timeout time.Duration

	// RouteGate, when set, is asked before every body this client sends, with
	// the model the body names and the call's tag ([WithCallTag]); an error
	// is the call's answer and nothing is sent. It is how a session keeps
	// EVERY road — a helper, a child's loop, a road that carries a client of
	// its own — off a route its route health says will not answer, at the one
	// door every body passes, rather than caller by caller.
	RouteGate func(ctx context.Context, model, tag string) error
	// SupportsParameter answers "does this model accept this request field?"
	// from data already in memory. It must not block or perform I/O; an unknown
	// answer is reported by returning known=false, never by waiting.
	SupportsParameter func(model, parameter string) (bool, bool)

	// ReasoningProfile answers what the provider published about a model's
	// thinking pass — whether it can be turned off, and which effort words the
	// model takes — from data already in memory, under the same contract as
	// SupportsParameter: never blocks, and an unknown is known=false. It is
	// the authority thinking.go reads first; the quirks memo is what stands in
	// when it is silent.
	ReasoningProfile func(model string) (ReasoningProfile, bool)

	// Routing says how this client asks the router to choose among the
	// endpoints serving one model (velocity.go). NIL IS NOBODY'S CHOICE, not a
	// choice of latency: the adapter then decides per request from who is
	// waiting on it, so a caller that has never heard of the row still chases
	// speed on a person's own turn and price on an errand. A caller that HAS
	// heard of it hands down the resolved setting rather than a path to it, and
	// that setting wins over everything.
	Routing RoutingSource

	// ModelPrice is the model's OWN published list price, per token in US
	// dollars, from rows already in memory. It is what the latency ask's price
	// ceiling is derived from (velocity.go's latencyPriceCeiling), and like
	// SupportsParameter it must not block or perform I/O: a catalog that has not
	// resolved answers known=false, which sends no ceiling at all.
	//
	// known=false is the ONLY way to say "no price". A published zero is a real
	// figure — the free variants a router carries — and must not be reported as
	// unknown.
	ModelPrice func(model string) (prompt, completion float64, known bool)

	// Fallbacks are the models to try, in order, when no endpoint serving the
	// configured one will accept the request's shape (endpoints.go). It is the
	// operator's own list and it wins outright over any inference; empty is the
	// ordinary case and means the catalog is asked instead.
	Fallbacks []string

	// NearestModels answers "what else could have taken this conversation?" from
	// data already in memory, and is consulted ONLY when Fallbacks is empty. Like
	// SupportsParameter it must not block or perform I/O — a catalog that has not
	// resolved answers nil, which is one more way of not knowing rather than a
	// reason to wait on the one path where somebody is already watching a failure.
	NearestModels func(model string) []string

	// HTTPClient is optional. Connected services may use it to adapt their wire
	// protocol, and tests use the same seam to keep requests deterministic.
	HTTPClient *http.Client
}

Config configures the adapter. It is deliberately the same shape the AgentField SDK client takes, plus the two resolvers that let the adapter decide a request's economics without ever performing I/O on the hot path — and minus generation controls: absent sampling and output parameters are omitted upstream and the provider's own defaults apply. The SDK's plain OpenAI loop injects config defaults of its own; [withoutInjectedDefaults] clears those before applying the caller's explicit options. It carries no attribution fields on purpose: who this binary reports itself as is a constant (attribution.go), and a config field for it is exactly how a caller ends up sending a different app — or none.

type ConnectionUnavailableError

type ConnectionUnavailableError struct{}

ConnectionUnavailableError ends automatic recovery without inviting a model or endpoint ladder to spend another window on the same unreachable origin.

func (*ConnectionUnavailableError) Error

type ContextBudget

type ContextBudget struct {
	Window      int
	Reserve     int
	PromptFloor int
}

ContextBudget describes the conversation whose next request is being encoded. The provider applies it after tool schemas, replayed reasoning and routing preferences have been assembled, so the check measures what will be sent.

type ContextLimit

type ContextLimit struct {
	Base     string    `json:"base"`
	Model    string    `json:"model"`
	Provider string    `json:"provider,omitempty"`
	Tokens   int       `json:"tokens"`
	At       time.Time `json:"at,omitempty"`
}

ContextLimit is an endpoint's stated total window, never a rejected prompt's size. The key includes the account's base URL and the serving endpoint.

type CutReason

type CutReason int

CutReason says which thing went wrong, and it is the only thing this package decides about a cut. The sentence is composed upstream.

const (
	// CutSilent is a request that produced nothing within firstDeltaBound.
	CutSilent CutReason = iota
	// CutStalled is a request that started writing and then went quiet for
	// midStreamGapBound.
	CutStalled
	// CutBabble is a reply that stopped being language: a repetition loop, or
	// text switching alphabet inside its own words.
	CutBabble
	// CutOverrun is a reply that never stopped: an endpoint that kept writing
	// past the wall its own history earned it WITHOUT KEEPING PACE, or past
	// [streamWallCeiling] whatever its pace. See [wallFor] and
	// [stallWatch.keptPace].
	CutOverrun
	// CutMachinery is a reply that is the model's own tool grammar written as
	// text: the request declared tools, the answer called none, and the content
	// spells a declared tool's name fenced in delimiter bytes inside
	// delimiter-dense text. It means the serving endpoint did not parse its
	// model's chat template, and the reply is unusable no matter how healthy
	// the stream that carried it was. See [MachineryLeak].
	CutMachinery
	// CutTruncated is a stream that ended without an explicit completion marker
	// or a finish reason, so its partial reply cannot be used.
	CutTruncated
)

type DocumentParseEngine

type DocumentParseEngine string

DocumentParseEngine is one verified OpenRouter file-parser engine. Callers must always choose one: omitting the plugin would silently select the provider's native-then-OCR fallback and make cost an accident.

const (
	DocumentParseCloudflare DocumentParseEngine = "cloudflare-ai"
	DocumentParseMistralOCR DocumentParseEngine = "mistral-ocr"
	DocumentParseNative     DocumentParseEngine = "native"
)

type DocumentRequest

type DocumentRequest struct {
	Model     string
	Filename  string
	MediaType string
	Data      []byte
	Engine    DocumentParseEngine
	Question  string
}

type DocumentResponse

type DocumentResponse struct {
	Text  string
	Hash  string
	Usage *ai.Usage
}

type Effort

type Effort string

Effort is OpenRouter's unified reasoning-effort knob. The empty value means "send nothing", which is materially different from "low": omitting the field leaves the model at its own default, while sending it commits us to a shape some models reject outright.

const (
	EffortNone Effort = ""

	// EffortOff is not a quieter setting than low — it is a different request.
	// It sends {"reasoning": {"enabled": false}}, which suppresses the thinking
	// pass outright. On structuring calls that is worth an order of magnitude in
	// latency: the model spends its whole budget on the answer instead of
	// deliberating first, and the answer is the same size either way.
	EffortOff Effort = "off"

	// EffortMinimal is the lowest word the router defines. It is not on the
	// operator's dial — ParseEffort does not take it — because nobody chooses
	// it; it is what the adapter sends to a model that cannot stop thinking
	// when the caller asked for off, if the model lists it (thinking.go).
	EffortMinimal Effort = "minimal"
	EffortLow     Effort = "low"
	EffortMedium  Effort = "medium"
	EffortHigh    Effort = "high"
)

func ParseEffort

func ParseEffort(value string) (Effort, bool)

ParseEffort validates operator-supplied configuration. An unrecognized value is an error rather than a silent downgrade: a knob that would 400 must never reach the wire, and a typo that silently disables an economy the operator asked for is worse than a startup failure.

func ReasoningEffortFrom

func ReasoningEffortFrom(ctx context.Context) Effort

ReasoningEffortFrom returns the effort requested for this call, if any.

type GeneratedImage

type GeneratedImage struct {
	Base64    string `json:"b64_json"`
	MediaType string `json:"media_type"`
}

type HedgeReport

type HedgeReport struct {
	// contains filtered or unexported fields
}

HedgeReport is what one request's rescue cost and bought, written into the caller's own slot exactly like ServedEndpoint.

It is a slot rather than a field on the response for the reason the served endpoint is: the SDK's response type is the OpenAI shape, and racing is this adapter's own bookkeeping. `internal/session`'s usage ledger reads it to write `hedged`, `lane` and `hedge_waste_usd` on the row.

func HedgeReportFrom

func HedgeReportFrom(ctx context.Context) *HedgeReport

HedgeReportFrom returns the slot in force for ctx, nil when none was opened — which every method here answers correctly, so a caller never tests.

func (*HedgeReport) Action

func (h *HedgeReport) Action() string

Action is what was done about the wait — "hedge", "ask", "borrow", "report", "escalate", "commit" — and empty on a call nothing had to be done about.

func (*HedgeReport) Arms

func (h *HedgeReport) Arms() int

Arms is how many requests this one question put on the wire.

func (*HedgeReport) Hedged

func (h *HedgeReport) Hedged() bool

Hedged reports whether a second request went out.

func (*HedgeReport) Lanes

func (h *HedgeReport) Lanes() (winner, loser string)

Lanes are the lane that answered and the lane that was cancelled.

func (*HedgeReport) OnHedgeStart

func (h *HedgeReport) OnHedgeStart(fn func(RescueNews))

OnHedgeStart registers what to do about a rescue while it is still happening: once when one goes out, and once more if the machine it went to fails. It is called from the race's own goroutine and must not block; a nil function unregisters.

IT IS SET BEFORE THE CALL AND NEVER DURING ONE. The slot belongs to the caller and is stamped on the context before the request goes out (WithHedgeReport), which is the only moment at which nothing is reading it.

func (*HedgeReport) PathFault

func (h *HedgeReport) PathFault() bool

PathFault reports whether the act was about a dead path rather than a slow lane. A ledger row that carried this as an ordinary demotion would be blaming an endpoint for somebody's network.

func (*HedgeReport) Primary

func (h *HedgeReport) Primary() string

Primary is the lane the first request was served by, empty when no stream named one. See the field for why it is not HedgeReport.Lanes's loser.

func (*HedgeReport) Reason

func (h *HedgeReport) Reason() string

Reason is the controller's machine word for why something was done.

func (*HedgeReport) Silence

func (h *HedgeReport) Silence() time.Duration

Silence is how long the stream had been silent when it was acted on.

func (*HedgeReport) Waste

func (h *HedgeReport) Waste() float64

Waste is the estimated dollars the arms that did not answer cost.

type ImageReference

type ImageReference struct {
	Type      string            `json:"type"`
	ImageURL  ImageReferenceURL `json:"image_url"`
	FrameType string            `json:"frame_type,omitempty"`
}

ImageReference is the OpenRouter image-ref envelope, and it is ONE envelope for every endpoint that takes a picture as input: the image endpoint's input_references, and the video endpoint's first / last frames and style references. FrameType is a video-only slot and is omitted everywhere else.

func NewImageReference

func NewImageReference(url string) ImageReference

NewImageReference is the one place the envelope is built, so no caller has to remember that the type word is "image_url" and that the URL lives one level down. Every reference on every endpoint goes through here.

type ImageReferenceURL

type ImageReferenceURL struct {
	URL string `json:"url"`
}

type ImageRequest

type ImageRequest struct {
	Model           string           `json:"model"`
	Prompt          string           `json:"prompt"`
	N               int              `json:"n,omitempty"`
	Size            string           `json:"size,omitempty"`
	AspectRatio     string           `json:"aspect_ratio,omitempty"`
	OutputFormat    string           `json:"output_format"`
	InputReferences []ImageReference `json:"input_references,omitempty"`
}

ImageRequest is OpenRouter's non-streaming image generation request.

InputReferences carries the SAME envelope the video endpoint takes and not a bare URL, because that is what /api/v1/images validates against: a plain string comes back as `{"expected":"object","path":["input_references",0]}` with the whole render refused. One reference type for both endpoints, so the shape cannot drift out of step in one of them again.

type ImageResponse

type ImageResponse struct {
	Data  []GeneratedImage `json:"data"`
	Usage *ai.Usage        `json:"usage,omitempty"`
}

type LanePin

type LanePin struct {
	// Lane is the machine named by the row, as the wire spells it. Empty is a
	// row that names none.
	Lane string
	// Borrow is whether a PINNED lane may still be left when it goes slow. It
	// is false unless somebody said so: a pin means the machine they named, and
	// widening it on their behalf is not this build's to do.
	Borrow bool
	// OpenRouter is the row that asks for NO lane at all and lets the router
	// balance on price, which is what this build did before it held an opinion.
	// It is a different fact from `auto`, which asks the belief to choose.
	OpenRouter bool
}

LanePin is a person's answer to "which machine serves this model", already resolved from the settings row.

The three states of the row are three states here, and the zero value is `auto` — nobody said, and the belief chooses per answer.

auto          Lane empty, OpenRouter false   the belief chooses
pinned        Lane named, Borrow false       exactly that machine, or nothing
pinned+borrow Lane named, Borrow true        that machine first, and a rescue may leave it
openrouter    OpenRouter true                no lane choice at all; the router balances

func CurrentLanePin

func CurrentLanePin() LanePin

CurrentLanePin is the pin in force.

type MediaClient

type MediaClient struct {
	// contains filtered or unexported fields
}

MediaClient owns the non-chat OpenRouter endpoints while sharing the adapter's bearer key, attribution headers, timeout, and in-memory test seam.

func NewMediaClient

func NewMediaClient(config Config) (*MediaClient, error)

func (*MediaClient) GenerateImage

func (c *MediaClient) GenerateImage(ctx context.Context, request ImageRequest) (*ImageResponse, error)

func (*MediaClient) GenerateMusic

func (c *MediaClient) GenerateMusic(ctx context.Context, request MusicRequest) (*MusicResponse, error)

GenerateMusic composes one clip and returns it whole.

It is one blocking call, like every other generation call on this client: the stream is an artifact-delivery mechanism the provider insists on, not something a caller watches, and nothing here polls. What differs is who waits. A composition takes most of a minute, so the session's tool (internal/session/tools_music.go) runs this in a job's goroutine and lands the result as a note, the way it does a video render — which is why ctx must be honoured all the way down: `jobs kill` is that context being cancelled, and a call that ignored it would compose on into a job nobody owns. The harness belt (internal/exec) still calls this and waits.

func (*MediaClient) GenerateVideo

func (c *MediaClient) GenerateVideo(ctx context.Context, request VideoRequest) (*VideoResponse, error)

GenerateVideo submits one asynchronous OpenRouter job, waits through its pending/in-progress states, and downloads the first completed artifact. The method is deliberately synchronous: a leaf tool does not return a path until that path names a complete local video.

func (*MediaClient) Speak

func (c *MediaClient) Speak(ctx context.Context, request SpeechRequest) (*SpeechResponse, error)

func (*MediaClient) Transcribe

Transcribe posts one audio file and returns its transcript.

Usage follows the rest of this client's law: the body's own usage object when the deployment sends one, the cost header when it does not, and nil for "unknown" rather than a zero that would read as free.

type MessageReasoning

type MessageReasoning struct {
	Field   string
	Text    string
	Details json.RawMessage
	// Model is the slug this working was produced under. A thinking pass is
	// the endpoint's own — OpenRouter encrypts it, and replaying it to any
	// other model is answered with a 404 ("encrypted payloads can only be
	// replayed to the endpoint that created them"), which is what a /model
	// switch mid-conversation ran into on 2026-08-28. Empty on a sidecar
	// restored from a journal written before the field existed; that one is
	// replayed on trust and repaired on refusal (client.go's sendRepaired).
	Model string
}

MessageReasoning is the provider-only half of one assistant message. The SDK message deliberately has no home for these fields, so callers carry a slice aligned with the messages in one request rather than putting model working in Content, where it would become part of the answer and disturb tool pairing.

func MessageReasoningFrom

func MessageReasoningFrom(ctx context.Context) []MessageReasoning

MessageReasoningFrom returns a copy of the request's aligned sidecar. It is exported so a Completer test double can assert the same contract the real encoder reads without learning the provider's private context key.

type MusicRequest

type MusicRequest struct {
	Model  string
	Prompt string
}

MusicRequest is a composition brief. There is deliberately NO DURATION and no format: the endpoint takes neither, a call returns whatever length the model decides to write, and a field that was accepted and ignored would be a knob every caller reasoned from and nothing honoured.

type MusicResponse

type MusicResponse struct {
	Audio  []byte
	Format string
	Usage  *ai.Usage
}

MusicResponse is the finished clip. Format is what the provider said it sent, lowercased, and empty when it said nothing — the caller decides what an unnamed format is called on disk.

type Phase

type Phase string

Phase is what a request is doing right now.

THE WORDS ARE THE PERSON'S. There is no `awaiting_first_token`, no `hedging`, no `backoff` — a surface that has to translate a machine word is a surface that will translate it differently from the next one (the design law about machinery vocabulary). What is spelled here is what is read out loud.

const (
	// PhaseConnecting is the handshake: DNS, TLS, and the request going out.
	// Nothing has been accepted yet.
	PhaseConnecting Phase = "connecting"
	// PhaseConnectionLost is a reachability wait, not a slow model response.
	PhaseConnectionLost Phase = "waiting for connection"
	// PhaseFirstWord is the wait after the endpoint accepted the request and
	// before it wrote anything — the queue, the router's own fallback walk, a
	// cold model loading. It is the phase a hedge deadline belongs to.
	PhaseFirstWord Phase = "first word"
	// PhaseThinking is a run of reasoning tokens: the endpoint IS writing, and
	// none of it is on the screen.
	PhaseThinking Phase = "thinking"
	// PhaseWriting is the answer arriving.
	PhaseWriting Phase = "writing"
	// PhasePaced is a rate-limit wait. Its deadline is the router's own
	// `Retry-After` and is therefore real.
	PhasePaced Phase = "paced"
	// PhasePlanPaused is a subscription window that will reset. It remains
	// pacing, never a terminal account verdict.
	PhasePlanPaused Phase = "plan paused"
	// PhaseRetrying is the relax ladder: the same question asked again with
	// something dropped from it. Detail carries "2 of 6".
	PhaseRetrying Phase = "trying again"
	// PhaseSwitching is a rescue in flight — a second request to another lane,
	// with nobody committed yet. Then names the lane it went to.
	PhaseSwitching Phase = "switching"
	// PhaseAsking is a wait a person can end: the lane they pinned has gone
	// quiet, there is somewhere else to go, and a pin is asked rather than
	// overridden (offer.go). Detail carries "coreweave is slow", Then the lane
	// the rescue would go to, and Ask the token `y` answers with.
	//
	// IT IS A PHASE AND NOT A SECOND CHANNEL. One sentence about one request,
	// on the seam that already carries every other sentence about it — a second
	// channel for it would be a second thing to keep alive, and the first time
	// one of them stalled the other would still be drawing.
	PhaseAsking Phase = "asking"
	// PhaseAllSlow is the visible half of [control.Report]: every reachable provider
	// is believed slow, so acting would buy nothing and the only honest act left
	// is to SAY the wait is real.
	//
	// SILENCE IS NEVER AN OPTION, and saying nothing was the old behaviour. A
	// person watching a line that says "still working" through a real wait is
	// being told less than this build knows, and what this build knows is that
	// it has weighed the alternatives and there are none.
	//
	// IT KEEPS THE CLOCK OF THE WAIT IT IS ABOUT. The report does not start a
	// new phase in a person's terms — nothing has changed about what the
	// endpoint is doing — so [phaseClock.allSlow] leaves [PhaseNews.Since]
	// exactly where it was and the surface goes on counting up from the moment
	// the wait began.
	PhaseAllSlow Phase = "all providers slow"
	// PhaseBelowPace is the other half of [control.Report], and it is the half
	// this build used to say nothing about: the endpoint IS writing, and it is
	// writing too slowly to be worth reading, and no second machine can be
	// started to fix it.
	//
	// IT IS NOT [PhaseAllSlow] AND IT IS NOT [PhaseWriting]. A person told "all
	// providers slow · still waiting" while words are appearing is being told about a
	// silence they can see is not happening; a person told "writing" while 604
	// tokens take 86 seconds is being told about a stream that is technically
	// alive and practically stopped. On 2026-09-11 that exact call showed a
	// person one nudge and then nothing for eighty-six seconds, because neither
	// of the two existing words was true and the honest third one did not exist.
	//
	// IT IS NOT A WAIT IN [PhaseNews.Waiting]'s sense, deliberately: the answer is
	// arriving, so the surface goes on drawing it and this word sits beside it
	// rather than in place of it.
	PhaseBelowPace Phase = "below pace"
	// PhaseSwitchingModel is the LAST rung of the ladder and the only one that
	// changes what a person asked for: every lane of the model has been tried
	// and a fallback model is being asked instead. Then names it.
	//
	// It is a different word from [PhaseSwitching] on purpose. Changing which
	// machine serves an answer is bookkeeping; changing which model writes it
	// is a different answer, and a surface that spelled the two the same way
	// would be hiding the one that matters.
	PhaseSwitchingModel Phase = "switching model"
	// PhaseRunning, PhaseChecking, PhaseTidying and PhaseBriefing belong to
	// internal/session and are spelled here because there is ONE vocabulary and
	// one reader: a tool executing, a gate reading an answer, a compaction pass,
	// and a turn being written down for whoever takes it over.
	PhaseRunning  Phase = "running"
	PhaseChecking Phase = "checking"
	PhaseTidying  Phase = "tidying"
	// PhaseBriefing is the harness writing the instruction a turn is handed over
	// on, before there is a task to point at (internal/session's checkpoint.go).
	// Detail names who it is for, so the row reads "briefing a worker".
	PhaseBriefing Phase = "briefing"
	// PhasePreparing names bounded context lookup before the main request starts.
	PhasePreparing Phase = "preparing"
	// PhaseTakingStock is the reading a turn stops for at a mark: a second mind
	// is shown an account of the work so far and asked what is left of the ask
	// (internal/session's checkpoint.go, [readMark]). It is a ten-to-thirty
	// second call and it used to draw nothing at all.
	//
	// THE WORD IS THE ONE A PERSON WOULD USE for stopping to see where you are,
	// and it is a different word from `checking` on purpose: checking is a
	// reader deciding whether an answer is finished, and this is a reader
	// weighing the whole ask against everything that has been done. A surface
	// that spelled them the same way would say the same sentence twice for two
	// waits that mean different things.
	PhaseTakingStock Phase = "taking stock"
)

type PhaseNews

type PhaseNews struct {
	// Phase is what is happening. An empty phase is the end of the story —
	// posted when a turn stops, so a surface stops drawing a clock for work
	// that is over.
	Phase Phase
	// Since is when THIS phase began. The surface counts up from it at paint,
	// so nothing here has to tick.
	Since time.Time
	// Deadline is the moment something will be done about it, and Then is what
	// that something is. Both are zero and empty unless a real deadline exists:
	// see the header.
	Deadline time.Time
	Then     string
	// Lane is the machine answering, when one has named itself, and Rate how
	// fast it is writing right now in tokens a second. Zero for both is "not
	// measured", never "nothing".
	Lane string
	Rate float64
	// Door is the billing road in use. It is distinct from Lane, which is the
	// serving machine behind a router, and empty for every one-road service.
	Door string
	// Detail is the phase's own noun, already in a person's words: the tool
	// being run, the rung of the ladder, how long a stall had gone on.
	Detail string
	// Ask is the token naming an open offer, empty when there is none — which
	// is every phase but [PhaseAsking] and the post that withdraws one. A
	// surface hands it back to [AnswerOffer] with the person's answer, so the
	// keystroke lands on the request that raised the question and never on the
	// one that came after it.
	Ask string
	// Model is the model this request is on, and Role who it is for. A surface
	// draws only the roles a person is reading (internal/lane's roles.go): the
	// naming errand and the memory reflex that run beside a talk turn are not
	// the answer somebody is waiting for, and a status line that showed
	// whichever of them answered last was the other half of the reported
	// defect.
	Model string
	Role  lanes.Role
	At    time.Time

	// Session is the conversation this request belongs to ([SessionFrom]), and
	// it is EMPTY IN EVERY BUILD THAT NEEDS NO ANSWER: one process with one
	// window has nothing to disambiguate. An engine that is a separate process
	// from its surfaces reads it to decide which connection a piece of news
	// belongs on, and news that names no conversation is news it cannot place.
	Session string

	// Subject is WHAT THIS NEWS IS ABOUT, and it is a different question from
	// Session, which is whose it is. A conversation runs a talk turn and a tree
	// of task nodes under it; all of them are one Session, and each of them is
	// its own subject.
	//
	// A NEWS ITEM BELONGS TO A SUBJECT, AND A WINDOW DRAWS ITS OWN SUBJECT'S
	// NEWS. That is the law this field exists for, and it is stated here rather
	// than at a drawing site because a surface cannot invent an identity that
	// never left the engine. Until it existed a surface's news desks were keyed
	// by MODEL, which is an address and not an identity: two task nodes running
	// on one model id overwrote each other's phase, and a node's room could
	// never be asked what its own node was doing — the row it drew was
	// whichever of the two had posted last.
	//
	// EMPTY MEANS THE CONVERSATION. Every producer that names no subject, and
	// every older peer across a connection, is talking about the conversation
	// itself, so absence must behave exactly as it did before this field
	// existed — which is what internal/tui3's desks do with it: a subject-less
	// piece of news is filed under its model, as it always was.
	//
	// It is carried on the context ([WithNode]) rather than passed down the
	// call chain for the role's and the session's reason: it belongs to the
	// ERRAND, so it survives a completer wrapper, a retry, a relax rung and a
	// hedge arm without anybody re-stating it.
	Subject string

	// Relayed says this news arrived over a connection from the engine that
	// produced it, rather than off this process's own stream.
	//
	// IT EXISTS TO STOP A LOOP. A build that is both serving and watching —
	// which is every test that drives an engine host inside its own process —
	// would otherwise forward what it just received straight back out of the
	// door it came in, forever. It is never put on the wire: the side that
	// takes a frame off the wire is the only side that can know it, and it
	// stamps it on receipt.
	Relayed bool
}

PhaseNews is one moment of one request's life.

Anything unknown is left zero and draws nothing, which is the emptiness law said at the seam rather than at the surface: no reader has to invent a figure to have something to print.

func (PhaseNews) ControllerActed

func (n PhaseNews) ControllerActed() bool

ControllerActed reports whether this phase is the controller having spoken: the wait said out loud with nowhere better to go, the pace reported on a wire that is writing too slowly to read, or a rescue started — to another lane or to another model. Those four are the phases the controller produces rather than the stream, so they are the ones a test can hold a first word against with the certainty that acting differently trips them.

IT IS A DIFFERENT QUESTION FROM PhaseNews.Waiting, and a phase can carry both. Waiting is what a person sits through; this is what this build DID about it. PhaseFirstWord is a wait nobody has acted on yet, PhaseRetrying is the relax ladder and not the router, and PhaseAsking is the controller deciding NOT to act until a person answers — so none of the three belongs here.

THE LIST LIVES HERE AND NOWHERE ELSE. It used to be spelled out by hand in the hedge fixture that waits on it, and a phase added or a rung renamed left that copy silently short (#970): the failure showed up as a lane holding its first word until the test's deadline, which names nothing. Beside the constants, a new rung is written next to the question it answers.

func (PhaseNews) Waiting

func (n PhaseNews) Waiting() bool

Waiting reports whether this phase is one a person is waiting through with nothing arriving. It is the phase clock's own reading of its own vocabulary, kept here so that two surfaces cannot disagree about it.

type PlanPauseError

type PlanPauseError struct {
	Reset        string
	OverflowDoor string
	Cause        error
}

PlanPauseError is the typed end of a request whose fixed-price window is temporarily unavailable. Cause preserves the vendor refusal for the journal; surfaces read this type so the person sees only the actionable pause sentence rather than a generic API error after it.

func PlanPauseFrom

func PlanPauseFrom(err error) (*PlanPauseError, bool)

PlanPauseFrom recovers a plan-pause ending through the wrappers added by the provider and session loops.

func (*PlanPauseError) Error

func (e *PlanPauseError) Error() string

func (*PlanPauseError) Unwrap

func (e *PlanPauseError) Unwrap() error

type Reading

type Reading string

Reading is what one unit of model work TAUGHT US — the learning record, and nothing whatever to do with what happens next.

── IT IS CALLED A READING BECAUSE IT IS NOT A VERDICT ──────────────────────

It was called `Verdict` until 2026-09-10, and internal/taxonomy's control answer is called taxonomy.Verdict too: one word, two types, in packages that call each other. A reader holding one of them could not tell from the name whether it was looking at the thing that decides what the harness DOES about a failure or at the thing a rating is trained on, and the recovery census found three separate rules deciding what a 404 meant partly because the vocabulary let them all look like the same kind of answer.

So `Verdict` now means exactly one thing in this tree — the control answer, from taxonomy.Classify — and THIS is a reading: an observation about a piece of finished work, filed against a model, read only by the ledgers that learn. A law test holds the line (internal/provider/reading_law_test.go).

It exists because every label the harness already had conflates two different questions. The scheduler's StateDone means "a non-empty string came back", which is equally true of a leaf that finished the job and of one that exhausted its budget mid-edit; profile.Record.Done inherits that conflation. For control flow it is the right call — a dependent still needs whatever text there is — and for learning it is fatal, because anything trained on it would be told that running out of money is a success.

So the reading sits alongside the state rather than replacing it. Nothing here decides what runs next; it only decides what may be learned from.

const (
	// ReadingVerifiedSuccess is the only positive evidence there is: something
	// checked the answer and it held. For a structured call the schema and the
	// pass's own semantic test are that check; for a leaf it would be a test
	// suite. The whole cascade design rests on this verdict being cheap to
	// obtain — the router lab's winning policy beat every single model only
	// because failure was detectable for free.
	ReadingVerifiedSuccess Reading = "verified_success"

	// ReadingUnverifiedSuccess is output nobody could check. It is deliberately
	// evidence in neither direction: the probe lab's lesson restated, which is
	// that when you cannot verify an outcome you must not pretend to have.
	ReadingUnverifiedSuccess Reading = "unverified_success"

	// ReadingFormatFailure is a reply that did not parse or did not carry the
	// fields the schema required. It is the cascade's escalation trigger.
	ReadingFormatFailure Reading = "format_failure"

	// ReadingSemanticFailure is a reply that parsed and was wrong — a cyclic
	// dependency set, an empty stage list, a ruler too thin to use. Only the
	// call site can know this, which is why it is reported back rather than
	// inferred.
	ReadingSemanticFailure Reading = "semantic_failure"

	ReadingBudgetStop Reading = "budget_stop" // the leaf ran out of tokens
	ReadingTurnCap    Reading = "turn_cap"    // the leaf ran out of iterations

	// ReadingProviderFailure is a transport fact — a 429, a 5xx, a timeout — and
	// never ability evidence. Rating a model down because its provider was busy
	// would make the most popular model look like the weakest one.
	ReadingProviderFailure Reading = "provider_failure"

	// ReadingEmptyResponse is the runaway-reasoning mode: the whole completion
	// budget spent on private deliberation, zero visible text returned. The
	// probe lab measured it at 15% of all failures and it is the single most
	// expensive outcome available — full price, nothing delivered — so it is
	// named rather than folded into "produced no result".
	ReadingEmptyResponse Reading = "empty_response"
)

func (Reading) Escalates

func (v Reading) Escalates() bool

Escalates reports whether a verdict is worth re-running on a stronger model. A provider failure is not — the next rung would hit the same weather — and an unverified success is not, because nothing said it was wrong.

func (Reading) Graded

func (v Reading) Graded() (positive bool, graded bool)

Graded reports whether a verdict may move an ability rating, and in which direction.

Two verdicts are deliberately inert. A provider failure says nothing about the model, and an unverified success says nothing about the answer; counting either would teach the ledger something that is not true. Everything else is evidence: budget, turn-cap and empty-response verdicts can only come from a leaf, and all three mean the model could not converge inside what it was given, which is exactly what a rating is for.

func (Reading) Weight

func (v Reading) Weight() float64

Weight is how much of a rating step one graded verdict is worth.

Graded is a yes-or-no question and this is the follow-up: not every failure says the same amount about the model that produced it. A reply that did not parse, was semantically wrong, or was empty is the model failing at the work it was handed. A leaf that ran out of budget or turns may be the same thing — or it may be a leaf that was three nodes' worth of work, which is a fact about the planner and not about the model. BASELINE.md measured that variance directly: a byte-identical brief drew graphs from 5 to 26 nodes and cost tracked node count almost exactly, so sizing dominates what a leaf costs and therefore what exhausts it.

A quarter, rather than zero, because it is still evidence — a model that wanders is a model that runs out — and rather than one, because arm B watched five budget stops on a single oversized task outvote a prior and reroute every leaf on the panel. Weighted at a quarter those five move a rating about as far as one wrong answer does, which is the right size for what they are.

type ReasoningProfile

type ReasoningProfile struct {
	Mandatory bool
	Efforts   []Effort
	Default   Effort
}

ReasoningProfile is the provider's published account of one model's thinking pass, in this package's vocabulary. The catalog carries the same three facts in strings; config translates at the seam so that neither package has to import the other.

type ReceiptPending

type ReceiptPending func() (done func())

ReceiptPending is told the moment a receipt is queued for a call whose stream ended without its usage block, and answers the function to call once that receipt's one answer has reached the ReconcileSink. The answer is called exactly once, found or not, so a count kept with it always comes back to zero.

IT EXISTS FOR WORK WHOSE BOOKS CLOSE. A receipt is fetched in the background on a schedule that runs for seconds after the call returned, and a caller that reads its total and closes its books the moment its last call ends reads a total without that money — the stopped senior-dev runs of 2026-09-23 lost their in-flight call exactly so, about twenty seconds before its receipt arrived. With this armed, such a caller can wait (bounded by ReceiptWait) for what it is still owed before it reads the total.

func ReceiptPendingFrom

func ReceiptPendingFrom(ctx context.Context) ReceiptPending

ReceiptPendingFrom reads back what WithReceiptPending armed, or nil — for a scripted funnel that owes a receipt the way the provider's own does.

type ReconcileSink

type ReconcileSink func(Reconciled)

ReconcileSink is told exactly once what became of an unpriced call. Like a billing sink it must be safe for concurrent use, because separate calls can finish without their usage blocks at the same instant.

func ReconcileSinkFrom

func ReconcileSinkFrom(ctx context.Context) ReconcileSink

ReconcileSinkFrom reads back the receipt sink WithReconcile armed, or nil.

type Reconciled

type Reconciled struct {
	Billed
	// Ref is the provider's generation id, and is empty when the stream never
	// named the call well enough for a receipt to be requested.
	Ref string
	// Reason is the cut's own word, or the ending that left the stream without
	// a usage block.
	Reason string
	// Hedged says this was a rescue arm, so any money on its receipt is waste.
	Hedged bool
	// Found says the provider supplied a receipt. False means the figures above
	// stay empty and the missing price is counted instead.
	Found bool
}

Reconciled is what became of one call the wire never priced. Found says the provider's own receipt supplied the embedded figures; when it is false every figure is zero and nothing may be banked.

type RefusalError

type RefusalError struct {
	// Model is the model the request started on.
	Model string
	// Params is what the first attempt actually carried, in the words the wire
	// spells them.
	Params []string
	// Stripped is what the chain took off, in the order it did.
	Stripped []string
	// Attempts is how many requests were sent in total, the first one included.
	Attempts int
	// Refusal is the provider's own last words, kept whole so nothing this
	// package summarizes can lose them.
	Refusal *APIError
}

RefusalError is the end of the chain: every endpoint serving every model tried refused this request's shape.

It exists as its own type rather than as another APIError because the two say different things. An APIError is a refusal, quoted. This is a DIAGNOSIS: which model, what the request carried, what was taken off and in what order, what else was tried, and the one or two things a person can do about it. A turn that ends in "API error (404): No endpoints found that can handle the requested parameters" tells somebody watching that something is broken and nothing whatever about which knob to turn.

func (*RefusalError) Error

func (e *RefusalError) Error() string

func (*RefusalError) Unwrap

func (e *RefusalError) Unwrap() error

Unwrap keeps the provider's refusal reachable, so a caller that classifies errors by status still finds the 404 under the diagnosis.

type RescueNews

type RescueNews struct {
	// Alt is the machine this news is about: the one a rescue is going to, or
	// the one whose rescue has just died.
	Alt string
	// Reason is why a rescue went out, in the two words a surface draws:
	// [RescueSlow] when a lane was merely late, [RescueRefused] when a lane
	// said no. Empty is a rescue nobody classified, which draws as slow.
	Reason string
	// Failed retracts a claim rather than making one: the `trying X…` this
	// surface is showing is about a machine that has now failed, and a status
	// line that goes on promising it is lying about the present tense.
	Failed bool
}

RescueNews is a rescue as a SURFACE may read it, narrowed from [laneRefusal] so that a status line never has to interpret a transport's vocabulary.

It travels through HedgeReport.OnHedgeStart — see waitreport.go — and it is derived from the classifier rather than assembled by hand, so the word a person reads and the decision the ledger took cannot drift apart.

type RouteFailure

type RouteFailure string

RouteFailure is one kind of route failure.

const (
	// RoutePayment is the account behind the route out of credit (402, an
	// "insufficient credits" refusal): every PAID route on that account is
	// unaffordable until a paid call on it succeeds again.
	RoutePayment RouteFailure = "payment"
	// RouteAuth is the key refused (401): the provider is disconnected until
	// the person reconnects it.
	RouteAuth RouteFailure = "auth"
	// RouteForbidden is this route refusing this model for this account or
	// plan (403, "only available on…", a data-policy exclusion): the route is
	// quarantined; the model may still be reachable elsewhere.
	RouteForbidden RouteFailure = "forbidden"
	// RouteQuota is a limit reached (429, a paused plan window, a daily cap):
	// the route cools down until the reset, when one was said.
	RouteQuota RouteFailure = "quota"
	// RouteUnavailable is the model not served here any more (404, withdrawn):
	// the route cools down and the model is demoted.
	RouteUnavailable RouteFailure = "unavailable"
	// RouteTransient is a single timeout or 5xx: asked again once, no verdict
	// about the route.
	RouteTransient RouteFailure = "transient"
)

func RouteFailureOf

func RouteFailureOf(err error) (RouteFailure, time.Time)

RouteFailureOf reads one call's error as a route failure: the kind, and when the route may be asked again if the provider said (zero when it did not). An error that says nothing about the route — a request nothing could serve, a cancelled context — answers "".

type RoutingIntent

type RoutingIntent int

RoutingIntent says whether a person is waiting on this call.

const (
	// IntentInteractive is a call somebody is watching arrive. It is the zero
	// value, because a call that has said nothing about itself is the
	// conversation's own turn until something says otherwise.
	IntentInteractive RoutingIntent = iota
	// IntentBackground is a call nobody is waiting on. Speed is worth nothing
	// to it and price is worth everything.
	IntentBackground
)

func RoutingIntentFrom

func RoutingIntentFrom(ctx context.Context) RoutingIntent

RoutingIntentFrom answers who is waiting on the calls made under ctx, interactive when nothing said. It is the read half of WithRoutingIntent, exported so a surface can assert what its own calls will ask for without standing up a router.

type RoutingSource

type RoutingSource interface {
	RoutingStrategy() RoutingStrategy
}

RoutingSource answers which strategy is in force.

It is an interface rather than a value on Config for one reason: the answer lives in a settings file, and an adapter that read one would put a disk read on the path of every client construction — in tests, in a leaf, in a subharness process that has no profile directory at all. The surface resolves the row once and hands the answer down; a test hands down StaticRouting and touches nothing.

func StaticRouting

func StaticRouting(strategy RoutingStrategy) RoutingSource

StaticRouting is one already-resolved answer as a source. The empty strategy is NOBODY HAVING CHOSEN rather than a refusal, so a caller that has nothing to say falls to the row this process installed and, with none installed, to DefaultRouting — see [Client.routingChoice].

type RoutingStrategy

type RoutingStrategy string

RoutingStrategy is how a session asks the router to choose among the endpoints serving one model.

It is a CHOICE and not a bool because the two live answers are not opposites: latency wants the fastest endpoint, price wants the cheapest, and they routinely disagree. Off is the third answer and it is a real one — it sends no preference object at all, which is what an operator behind a gateway that does not speak this dialect needs.

const (
	// RoutingLatency asks for the currently-fastest endpoint UNDER A PRICE
	// CEILING (see latencyPriceCeiling). It is opt in — somebody writes the row
	// — and the ceiling is there because being served fastest was never worth
	// being charged anything.
	RoutingLatency RoutingStrategy = "latency"
	// RoutingPrice asks for the cheapest endpoint that can serve the request.
	RoutingPrice RoutingStrategy = "price"
	// RoutingOff sends no preference object, and switches the ledger off with
	// it: an operator who has not asked to be routed has not asked to be
	// measured either, and a demotion nobody can act on is only overhead.
	RoutingOff RoutingStrategy = "off"
	// RoutingSimple sends exactly what the person asked for and nothing else,
	// and IT IS THE ROW THIS BUILD SHIPS ([DefaultRouting]).
	// No lane pinned: the request carries NO provider object at all and the
	// router's own default routing answers — no belief, no sort word, no price
	// ceiling, no hedge. A lane pinned: the request carries that one demand
	// (`provider.only`, fallbacks off) and nothing with it. Every other layer
	// of the routing stack stays compiled in but is disconnected from the
	// request path, so the row a person wrote is the whole algorithm.
	RoutingSimple RoutingStrategy = "simple"
)

func ParseRoutingStrategy

func ParseRoutingStrategy(word string) (RoutingStrategy, bool)

ParseRoutingStrategy reads a settings word. An unrecognized word is NOT an error and NOT off: it falls back to DefaultRouting, because a typo in a config row must not silently put a session on a road nobody asked for, and off is a road somebody chooses rather than one they arrive at by accident.

func RoutingNow

func RoutingNow() RoutingStrategy

RoutingNow is the row in force for a caller that hands down none of its own: what this process installed, and DefaultRouting where nobody installed anything.

It is exported for the gates OUTSIDE this package that have to answer the same question the request path answers ([Client.routingChoice]) — the lane beat in internal/session asks whether the row is `off` before it measures anything. A session that carries no row of its own must read it HERE and not off a field filled in at launch, because the row moves while the process runs: the settings panel writes it and re-installs it in the same keystroke, and a launch snapshot would keep a beat running that a person has just turned off.

type ServedEndpoint

type ServedEndpoint struct {
	// contains filtered or unexported fields
}

ServedEndpoint is a slot one caller opens to be told WHICH endpoint answered its calls.

It exists because LastServed cannot answer that question honestly for a caller: the ledger's latest sighting is process-wide, and two task nodes running the same model concurrently would each read the other's endpoint. The slot is scoped to the context the caller stamped, so what it holds is always an answer to one of that caller's own requests.

It holds the MOST RECENT answer and nothing else. A caller stamps it around a turn and reads it beside each response, which is the grain the journal writes at (internal/session's addUsage).

AND IT COUNTS THE HOPS, because that is the fact a cost autopsy needs and the one nobody could see: eleven moves across six endpoints in a single 41-request turn, every one of them a cold prompt cache (affinity.go). A journal line that records the endpoint alone shows where a request landed; ServedEndpoint.Hops and ServedEndpoint.Pinned show whether it stayed.

func ServedEndpointFrom

func ServedEndpointFrom(ctx context.Context) *ServedEndpoint

ServedEndpointFrom returns the slot in force for ctx, nil when none was opened — which every method here answers correctly, so a caller never tests.

func (*ServedEndpoint) Hops

func (s *ServedEndpoint) Hops() int

Hops is how many times the answering endpoint changed while this slot was open. Every hop is a prompt cache written from cold on the far side, so this is the number a cost autopsy reads first.

func (*ServedEndpoint) Name

func (s *ServedEndpoint) Name() string

Name is the endpoint that answered most recently, empty when nothing has answered yet or when no answer named its server — which is every endpoint that is not a router.

func (*ServedEndpoint) Pinned

func (s *ServedEndpoint) Pinned() bool

Pinned reports whether the most recent answer came from the endpoint its own request asked for by name — that is, whether this lineage kept the machine holding its prompt cache. False is a first request, a lineage with no cache key, a session with routing off, and every hop.

type Sighting

type Sighting struct {
	// Model is the model as it was asked for, normalized.
	Model string
	// Provider is the endpoint the router says served it, exactly as the
	// response spelled it.
	Provider string
	// TTFT is the wait before the first token, zero when unmeasured — which is
	// every non-streamed call, where there is no first token to observe.
	TTFT time.Duration
	// Tokens is what the answer was worth in output tokens, and Elapsed is the
	// window Rate was computed over.
	Tokens  int
	Elapsed time.Duration
	// Rate is output tokens per second, zero when the answer was too short to
	// rate (see [ratedFloor]).
	Rate float64
	// Gap is the widest quiet stretch between two deltas of a streamed answer,
	// zero when unmeasured — every non-streamed call, where the answer has no
	// inside to be quiet in.
	Gap time.Duration
	// Laggy is the verdict this sighting earned under the law above.
	Laggy bool
	// At is when the answer finished.
	At time.Time
}

Sighting is one timed answer: who served it, and how fast.

It is the ledger's unit and the HUD's fact, which is why it carries both the raw measurements and the verdict — a surface must not have to re-derive "was this slow" from thresholds it would then own a second copy of.

func LastServed

func LastServed(model string) (Sighting, bool)

LastServed is the most recent sighting for a model, false when this process has not seen one. It is the HUD's read and it is a copy: nothing a surface does can reach the ledger's state.

type SpeechRequest

type SpeechRequest struct {
	Model          string `json:"model"`
	Input          string `json:"input"`
	Voice          string `json:"voice,omitempty"`
	ResponseFormat string `json:"response_format"`
}

type SpeechResponse

type SpeechResponse struct {
	Audio []byte
	Usage *ai.Usage
}

type StreamCut

type StreamCut struct {
	Reason CutReason
	// Waited is how long the stream was quiet, on the two silence reasons, and
	// zero when no timer made the cut (CutBabble and CutTruncated). It is the
	// constant that fired rather than a measurement
	// — the plain bound on an outright silence, [bufferedQuietBound] when
	// keepalives bought the stream its full patience and it still never wrote —
	// because the timer is what decided, and the timer's own bound is the
	// honest figure.
	//
	// On CutOverrun it is THE WALL THAT FIRED, which is derived rather than
	// constant ([wallFor]): the same rule, that the figure a person is told is
	// the figure the timer was set to. A wall re-armed on a stream that kept pace
	// names the whole bound it reached, measured from when the stream opened —
	// "ran past 7m30s" after two re-arms of a 2m30s wall, never the 2m30s.
	Waited time.Duration
	// Provider is the endpoint the stream named as serving it, "" when no chunk
	// ever did. Ran is how long the request had been open, and Tokens is how
	// much the model had WRITTEN — answer, thought and tool-call arguments alike
	// — both measured rather than derived.
	//
	// TOKENS IS THE WATCH'S OWN COUNT, the one the wall's pace test read
	// ([stallWatch.tokens]), so a row can be checked against the decision it
	// records. It used to count answer text only, and a cut tool call — ten
	// minutes of a `write` streaming at pace — was journaled as zero tokens,
	// which is the one figure that cannot tell "producing nothing" from
	// "producing forever". Thought is in it for the reason the provider's own
	// output count has it: it is billed, streamed work, and a row comparable
	// with the call rows beside it has to count what they count.
	//
	// THE THREE OF THEM EXIST FOR THE JOURNAL. A cut is the one failure that got
	// somewhere before it failed, and the autopsy question about it — was this
	// endpoint producing nothing, or producing forever? — cannot be answered
	// from a reason word alone. internal/session's journalFailedCall writes them
	// onto the error row.
	Provider string
	Ran      time.Duration
	Tokens   int
	// Rerouted says the ledger ACTED on this cut: the endpoint that went quiet
	// was named on the wire and struck out of the (model, endpoint) lane, so the
	// very next attempt is encoded away from it (velocity.go's noteCutProvider).
	//
	// It exists for one question a layer above has to answer before it moves a
	// turn to another MODEL — has endpoint diversity actually been tried?
	//
	// TWO THINGS USED TO MAKE THE ANSWER NO AND NEITHER DOES ANY MORE. `routing
	// off` switched the ledger off entirely, and a stream that died before any
	// chunk named its provider had nothing to strike — so the one stall shape
	// that most needed to reroute was the one that could not, and asking the same
	// model again landed on the same lane deterministically. The ledger is no
	// longer switched off by configuration (velocity.go's [Client.refuseLane]),
	// and a cut that named no server is now filed against the machine this
	// request ASKED FOR, which this process wrote itself and therefore knows
	// ([Client.noteCutProvider]). What is left as false is a request that
	// expressed no preference at all — a build with no router behind it, where
	// there is one machine and diversity is not a thing that exists. The decision
	// itself is not this package's — internal/session's loop.go states the rule —
	// and this is the one fact it cannot see.
	Rerouted bool

	// OneMachine says this request had NO ENDPOINT DIVERSITY TO TRY: it named no
	// machine and none named itself, which is a build with no router behind it
	// and a set of one — a person's own base url, a local server, a single
	// connected service.
	//
	// IT IS THE THIRD CAUSE OF [StreamCut.Rerouted] BEING FALSE, and it wants
	// the opposite answer to the other two. Routing switched off, and a stream
	// that died before naming its server, both leave a POOL that the next
	// attempt draws from by the same rules, so asking again buys little and the
	// allowance above narrows. Here there is no pool: nothing moved because
	// there is nothing to move to, the next attempt is the only move there is,
	// and the only thing that mends a machine which answered nothing is time.
	// A layer above spends a different allowance on it and waits in front of it
	// (internal/taxonomy's transportBudget and waitFor).
	OneMachine bool
}

StreamCut is the error a guarded stream fails with. It is a distinct type rather than a message because the decision upstream — retry, and how many times — is made on the reason, and a decision made by matching substrings of an error string is a decision that breaks the next time somebody rewords it.

func CutFrom

func CutFrom(err error) (*StreamCut, bool)

CutFrom reports whether an error is a guarded stream's cut, and which kind.

func (*StreamCut) Error

func (c *StreamCut) Error() string

type StreamEvent

type StreamEvent struct {
	Kind    StreamEventKind
	Delta   string
	Session string

	// ReasoningField and ReasoningDetails preserve the assistant continuation's
	// wire signature on StreamReasoning. They are metadata, never display text;
	// Delta remains the only part a surface shows.
	ReasoningField   string
	ReasoningDetails json.RawMessage

	// FromAnswer marks working that was carved out of the ANSWER channel rather
	// than delivered on a reasoning field — a `<think>` region the endpoint did
	// not strip (answer.go). It is DISPLAY ONLY: there is no field it arrived
	// under, so there is no field to replay it under, and a continuation that
	// invented one would hand the endpoint back a message it never sent
	// (internal/session's [reasoningBuffer.write] is where the line is drawn).
	FromAnswer bool

	// Index, ID and Tool name the tool call a StreamToolCallForming event is
	// about, and are zero on every other kind. They are fields rather than a
	// JSON payload in Delta — the shape StreamToolCallReady uses — because a
	// forming event is raised per fragment and a marshal per token is work the
	// read loop does not have the budget for.
	//
	// ID and Tool are empty until the wire has said them: an endpoint sends the
	// id and the name on the first fragment of a call, but "sends them first" is
	// a convention rather than a guarantee, and a consumer that assumed it would
	// key its rows on "".
	Index int
	ID    string
	Tool  string
}

StreamEvent carries provider text as it arrives. Delta is populated only for StreamDelta; the terminal events deliberately carry no provider error text because the normal completion return remains the error authority. Session names the room this call's turn belongs to (empty in the one caller — the belt/router tests — that streams without ever setting one). Today there is exactly one room, so every event's Session is the same value; keyed events are the prerequisite, not a multi-room consumer.

type StreamEventKind

type StreamEventKind int

StreamEventKind names one boundary in an observed provider stream. The observer is opt-in through context, so every existing completion remains byte-for-byte non-streaming unless its caller is an interactive surface.

const (
	StreamStarted StreamEventKind = iota
	StreamDelta
	// StreamThinking says the model is producing reasoning rather than answer.
	// It carries no text: reasoning tokens are the model's own working and are
	// never shown, so the only thing that leaves the provider is that the wait
	// has a reason. It is raised once per run of reasoning, not per token.
	StreamThinking
	StreamFinished
	StreamFailed
	// The TOOL ACTIVITY boundaries. They are not produced by the adapter — no
	// endpoint reports them — but by whoever is RUNNING the tool loop above it,
	// through [Emit]. They ride this vocabulary rather than a second channel
	// because the person is watching one turn: a read the head performed and a
	// word it streamed are the same turn happening, and two feeds would have to
	// be re-interleaved by every surface that drew them.
	//
	// StreamToolBegin carries a person-readable gloss of the call in Delta —
	// "searching for «navctx»", never the raw arguments. The two ends carry a
	// short result hint, which is very often empty.
	//
	// THE OUTCOME IS A KIND AND NOT A FIELD, so the event struct stays three
	// strings wide and every bridge between vocabularies stays a copy.
	StreamToolBegin
	StreamToolEnd
	StreamToolFailed
	// StreamReasoning carries one chunk of the model's reasoning TEXT in Delta.
	//
	// It is the companion of StreamThinking and not its replacement: thinking
	// says a run of reasoning has begun and is raised once, this is raised per
	// delta and carries the words. Both are emitted, in that order, because a
	// surface that only draws "thinking…" must keep working unchanged and a
	// surface that wants the text must not have to infer where the run started.
	//
	// The wire spells it two ways — OpenRouter's "reasoning" and the
	// "reasoning_content" the DeepSeek-family endpoints send — and sse.go reads
	// both into one field, so what leaves here is one vocabulary regardless.
	StreamReasoning
	// StreamToolCallReady says ONE tool call has finished streaming, before the
	// response it belongs to has. Delta is that call JSON-marshaled — an
	// ai.ToolCall object, id and function and all, not a gloss — because the
	// consumer is code deciding whether to start work, not a person reading a
	// line.
	//
	// It is raised when the stream opens a LATER call (the one before it can
	// receive no more fragments) and, for whatever is still open, once the
	// stream ends cleanly. A call is only announced when its name is known and
	// its arguments parse as JSON: a fragment boundary misread would otherwise
	// hand a consumer a truncated instruction, and "not yet" is always a safe
	// answer here — the response's own ToolCalls() remains the authority.
	//
	// NOTHING IS PROMISED ABOUT WHO RUNS IT. This is an early sighting, not a
	// dispatch: see the safety law in session/loop.go for which calls may act on
	// one and which must wait for the response.
	StreamToolCallReady
	// StreamToolCallForming says ONE tool call is still ARRIVING: the model has
	// begun spelling it out and has not finished. Index, ID and Tool name the
	// call as far as the wire has said them — the index is always known, the id
	// and the name arrive on the first fragment or shortly after — and Delta
	// carries the call's ACCUMULATED ARGUMENTS TEXT so far, raw and partial.
	//
	// IT IS RAW ON PURPOSE. The arguments of a half-sent call are not JSON yet,
	// so nothing here may be unmarshaled and nothing downstream may treat this as
	// an instruction. It is the answer to "what is it doing right now" for the
	// seconds between the model starting a long write and the call being whole —
	// seconds a surface with only StreamToolCallReady has to draw as silence.
	//
	// It is raised PER FRAGMENT, which is per token for the endpoints that stream
	// arguments a token at a time. That rate is deliberate and the consumer's
	// problem: this vocabulary reports the wire, and whoever is drawing decides
	// how often a person needs to see it (session/toolhint.go throttles it).
	//
	// EVERY CALL THAT FORMS IS LATER READY, in that order, unless the stream dies
	// mid-call — the same condition under which StreamToolCallReady says nothing
	// either. A non-streaming endpoint raises none of these at all.
	StreamToolCallForming
	// StreamNotice carries one line ABOUT the call rather than from it, in Delta.
	//
	// It is the only kind the adapter itself raises that is not the model
	// speaking, and it exists for exactly one thing today: the endpoint-refusal
	// chain saying which attempt it is on and what it just took off the request
	// (endpoints.go). A retry that reshapes a person's request has to be visible
	// or it is an adapter answering a different question from the one it was
	// asked — and the only channel that reaches the room in order is this one.
	//
	// It is raised BEFORE the stream opens, so a surface may receive notices on a
	// turn that goes on to produce no StreamStarted at all.
	StreamNotice
	// StreamReplaced says the answer the person has been reading is being
	// REPLACED, and carries in Delta the one line telling them so.
	//
	// It is StreamNotice's sibling and deliberately not StreamNotice itself: a
	// notice is a line to print, while a replacement is that line AND an
	// instruction to throw away what is above it. It is raised only when text
	// had actually been shown. Everything the consumer drew or buffered for this
	// response is void, and the response the call returns is the replacement's.
	StreamReplaced
	// StreamRowNews carries one line about A PERSON'S OWN ROW in Delta — a pin
	// the wire has refused and this build has stopped sending (lanepin.go), a
	// base that has said it will not carry one at all (prefcarry.go).
	//
	// IT IS [StreamNotice]'s SIBLING AND DELIBERATELY NOT StreamNotice ITSELF,
	// for the reason [StreamReplaced] is not: a notice is the adapter saying
	// what it did to the person's REQUEST to get it accepted, and a surface is
	// free to fold that away with the rest of the machinery once the answer has
	// landed. This is the adapter saying that a SETTING they wrote is no longer
	// being sent, and there is nothing to fold it into — it is the only account
	// they will get of why the machine they named stopped appearing.
	//
	// THE MEASURED FAILURE (2026-09-13). The retirement sentence went out on
	// StreamNotice, arrived as a note, and was swallowed whole by the chat's
	// work chip: one drive, `@deepseek` gone from the model word, another
	// machine answering, and `▸ worked 1.6s · thought 0.2s · ctrl+e` where the
	// explanation should have been.
	//
	// It is raised BEFORE the stream opens, exactly as StreamNotice is, so a
	// surface may receive one on a turn that goes on to produce no
	// StreamStarted at all.
	StreamRowNews
)

type StreamObserver

type StreamObserver func(StreamEvent)

StreamObserver receives provider deltas synchronously and in order.

type TranscriptionRequest

type TranscriptionRequest struct {
	Model    string
	Data     []byte
	MIME     string
	Filename string
	Language string
}

TranscriptionRequest is one audio file on its way to /audio/transcriptions.

Data is the bytes rather than a path because this package never opens a person's files — the caller that has the path also has the size limit it wants to enforce and the sentence it wants to say about it. MIME and Filename are two ways of answering the same question (which format word rides the wire) and either alone is enough; Language is the endpoint's optional hint and is omitted when empty.

type TranscriptionResponse

type TranscriptionResponse struct {
	Text  string
	Usage *ai.Usage
}

TranscriptionResponse is what came back: the words, and what they cost.

type VideoRequest

type VideoRequest struct {
	Model           string           `json:"model"`
	Prompt          string           `json:"prompt"`
	Duration        int              `json:"duration,omitempty"`
	Resolution      string           `json:"resolution,omitempty"`
	AspectRatio     string           `json:"aspect_ratio,omitempty"`
	FrameImages     []ImageReference `json:"frame_images,omitempty"`
	InputReferences []ImageReference `json:"input_references,omitempty"`
	GenerateAudio   *bool            `json:"generate_audio,omitempty"`
	Seed            *int             `json:"seed,omitempty"`
}

type VideoResponse

type VideoResponse struct {
	Video []byte
	Usage *ai.Usage
}

Directories

Path Synopsis
Package modelapi is the model API codeaf serves each run of a program it carries (internal/delegate): an OpenAI-style chat-completions endpoint on this machine, opened by one token, whose every call goes through codeaf's own model funnel — refused at the run's ceiling, priced, logged, and written down as one turn of the program's conversation with codeaf.
Package modelapi is the model API codeaf serves each run of a program it carries (internal/delegate): an OpenAI-style chat-completions endpoint on this machine, opened by one token, whose every call goes through codeaf's own model funnel — refused at the run's ceiling, priced, logged, and written down as one turn of the program's conversation with codeaf.
Package pool is the surface's provider seam: the model-switchable client a slot holds, the per-model clients a pinned job is served by, and the billing that makes every structuring call land on the same rail its leaves land on.
Package pool is the surface's provider seam: the model-switchable client a slot holds, the per-model clients a pinned job is served by, and the billing that makes every structuring call land on the same rail its leaves land on.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL