Skip to content

Rulvar API reference


Rulvar API reference / @rulvar/core / RunAgentOptions

Interface: RunAgentOptions<S>

Defined in: packages/core/src/runtime/agent-loop.ts:573

Type Parameters

Type ParameterDefault type
S extends SchemaSpecJsonSchema

Properties

PropertyTypeDescriptionDefined in
adapterProviderAdapter-packages/core/src/runtime/agent-loop.ts:578
agentType?string-packages/core/src/runtime/agent-loop.ts:905
billing?{ onProviderCall: (record) => void | Promise<void>; onProviderIntent?: (intent) => void | Promise<void>; }The incremental billing seam (RV2008): called with every ProviderCallRecord the moment the wire call settles and the record is minted, so the caller can journal it while the invocation is still running. The parity rerun lost ~$0.99 of root dispatches because records rode ONLY the terminal entry and the process died before one existed; with the seam the crash window shrinks to the single in-flight turn. Restored records (a checkpoint reboot) never re-emit: they were journaled by the segment that minted them. A returned promise is AWAITED before the loop proceeds (RV3405, the awaited receipt posture): the caller decides the durability, the loop honors it; a void return keeps the RV2008 fire and forget byte for byte.packages/core/src/runtime/agent-loop.ts:818
billing.onProviderCall(record) => void | Promise<void>-packages/core/src/runtime/agent-loop.ts:819
billing.onProviderIntent?(intent) => void | Promise<void>The pre-wire intent (RV4006): invoked strictly BEFORE every dispatched wire attempt, after admission and any quota reservation, with the coordinates the settled record will carry (ordinal, role, servedBy, attempt) and the built request for fingerprinting. A returned promise is AWAITED before the wire dispatches (intent before effect, the RV601 precedent), and a rejected append refuses the dispatch: a wire whose intent could not be made durable must not be able to bill. Quota denials and pre-dispatch aborts never reach it, exactly like the settled record they never mint.packages/core/src/runtime/agent-loop.ts:832
budget?BudgetHooks-packages/core/src/runtime/agent-loop.ts:777
cache?CachePolicyThe prompt-cache policy (RV2006): resolved by the ctx layer from the call opts, the agentType profile, and the engine defaults, in that order. Absent means 'auto': the loop attaches CacheHint breakpoints (after tools, after system, and the sliding deepest message) on every turn served by an adapter that declares ModelCaps.promptCaching 'explicit', and attaches nothing anywhere else. See applyCachePolicy for the exact shape.packages/core/src/runtime/agent-loop.ts:803
canonicalSchema?JsonSchemaCanonicalized JSON Schema projection of schema (precomputed for identity).packages/core/src/runtime/agent-loop.ts:577
checkpoint?{ load: Promise< | CheckpointState | undefined>; save: Promise<void>; }Turn-boundary checkpointing (M3-T02). load() restores the last boundary on a dangling-dispatch resume; save() persists each boundary where the loop continues. The separate extract invocation is not checkpointed in v1: an extract-phase crash re-pays from the last loop boundary.packages/core/src/runtime/agent-loop.ts:694
checkpoint.loadPromise< | CheckpointState | undefined>-packages/core/src/runtime/agent-loop.ts:695
checkpoint.savePromise<void>-packages/core/src/runtime/agent-loop.ts:696
compaction?{ threshold?: number; }Per-profile compaction config; threshold default 0.8 (Appendix A).packages/core/src/runtime/agent-loop.ts:686
compaction.threshold?number-packages/core/src/runtime/agent-loop.ts:686
escalation?{ minSpendUsd: number; }Escalation opt-in (M3-T07): the loop intercepts accepted calls to the escalate tool and terminates with status 'escalated'; the in-run minSpend gate rejects early scope_bigger escalations with a "keep working" error tool result (M3-T09).packages/core/src/runtime/agent-loop.ts:853
escalation.minSpendUsdnumber-packages/core/src/runtime/agent-loop.ts:853
events?RuntimeEventSink-packages/core/src/runtime/agent-loop.ts:840
evidenceContract?{ enforce?: "warn" | "refuse"; minEntries: number; }The resolved evidence contract of the invocation (RV507): under enforce 'refuse' an ok settle whose message window carries fewer successful record_evidence executions (result recorded: true) than minEntries is refused as a typed 'terminal' error carrying the machine-readable counter and threshold. Window-derived exactly like the terminal partial, so live and resumed segments count the same total. Absent, and under 'warn', the loop is byte-identical to before.packages/core/src/runtime/agent-loop.ts:709
evidenceContract.enforce?"warn" | "refuse"-packages/core/src/runtime/agent-loop.ts:709
evidenceContract.minEntriesnumber-packages/core/src/runtime/agent-loop.ts:709
exposureWait?boolean | "child"The exposure-wait posture (RV1902): an in-flight exposure refusal on this invocation parks until a live hold releases and retries pre-wire, instead of settling a budget error. true is set only by the orchestrate-owned root dispatches (the coordination loop, the synthesis invocation, the forced-finish wake), whose settle would tear down the run its own admitted children are still funding. 'child' (RV2002) rides on orchestrator-spawned children: the same park-and-retry, but the drained arm (no live holder left to wait out) dies as the typed cheap 'exposure-drained' refusal instead of the raw budget error, so the orchestrator can tell a starved seat apart from a crashed child and re-spawn it; the third parity rerun terminally killed three mid-research workers on exactly this path.packages/core/src/runtime/agent-loop.ts:793
extract?PhaseTarget & { fallbacks?: PhaseTarget[]; }Separate final extract invocation, present only when the role trigger protocol demands one: schema set AND (routing directs extract to a different model OR the loop model's caps cannot serve the required tier OR finalize is routed). Otherwise the schema rides the last loop turn (the necessity rule is decided by the ctx layer via model/roles.ts).packages/core/src/runtime/agent-loop.ts:651
fallbacks?PhaseTarget[]Transport failover chain for the loop phase (M4-T04): resolved fallback targets tried in order on transport or rate-limit failures after retries exhaust. Failover is sticky and changes only servedBy, never the content key.packages/core/src/runtime/agent-loop.ts:592
finalize?PhaseTarget & { fallbacks?: PhaseTarget[]; }Finalize synthesis invocation (M4-T01), present only when the role trigger protocol fires it: configured in routing AND the toolset is non-empty. Runs after tools stop with toolChoice 'none' over the full transcript plus a deterministic synthesis instruction appended to the REQUEST only (the durable transcript keeps the raw history); its text becomes the output for schema-less calls, a non-truncated empty synthesis falls back to the loop turn's text, and a schema-bearing call always pairs it with a separate extract (the ctx layer guarantees extract is present in that case). Like extract, the finalize invocation is not checkpointed in v1.packages/core/src/runtime/agent-loop.ts:664
label?string-packages/core/src/runtime/agent-loop.ts:908
limitsEffectiveUsageLimits-packages/core/src/runtime/agent-loop.ts:698
modelRetryAttempts?numberBounded ModelRetry conversions per tool call chain; default 2 (Appendix A).packages/core/src/runtime/agent-loop.ts:846
now?() => number-packages/core/src/runtime/agent-loop.ts:909
policyFacts?booleanOpt-in policy-facts digest (RV709): when true AND a finalize invocation fires, one additional REQUEST-ONLY user message precedes the synthesis instruction, carrying the deterministic runtime facts the loop observed (quota denials and recoveries, tool budget pressure, the finalization window, recorded spend with its cost basis), so the final model can cite the run's own live evidence instead of underclaiming it. Never touches the durable transcript, never enters spawn identity; unset keeps the finalize request byte identical.packages/core/src/runtime/agent-loop.ts:676
priceUsd?(servedBy, usage) => number | undefined-packages/core/src/runtime/agent-loop.ts:842
promptstring-packages/core/src/runtime/agent-loop.ts:574
providerSlot?<T>(key, fn, signal?) => Promise<T>Per-provider keyed limiter hook (M4-T07): wraps every wire dispatch under the serving adapter's key; absent = unlimited (Appendix A). signal is the agent-level abort: an aborted caller leaves the key's queue without a slot (v1.34.0 review P2-4).packages/core/src/runtime/agent-loop.ts:609
quota?{ maxDenials?: number; onLimiterError: "allow" | "deny"; reconcile: (reservationId, usage, actual?) => Promise<void>; release?: (reservationId) => Promise<void>; reserve: (request) => Promise<QuotaDecision>; reserveContinuations?: boolean; }The shared quota limiter hook (RV-215): consulted before EVERY live wire dispatch (initial attempts, transport retries, and failover takeovers alike, in every phase). A denial becomes a synthetic rate-limit-class WireError the retry and failover engine treats exactly like a provider 429, except no wire call was paid: retryAfterMs drives the interruptible backoff, denied turns stay bounded by their OWN maxDenials budget (RV1601; RetryPolicy.attempts counts dispatched tries only), and exhaustion of either budget fails over (the takeover reserves under its own model). Granted reservations are reconciled with the attempt's actual usage after the outcome settles. Live-only by construction: replayed calls never reach this seam, and nothing here is journaled.packages/core/src/runtime/agent-loop.ts:625
quota.maxDenials?numberThe per-target denial retry budget (RV1601); default 8.packages/core/src/runtime/agent-loop.ts:637
quota.onLimiterError"allow" | "deny"Limiter infrastructure failure policy; a denial is unaffected.packages/core/src/runtime/agent-loop.ts:633
quota.reconcile(reservationId, usage, actual?) => Promise<void>-packages/core/src/runtime/agent-loop.ts:627
quota.release?(reservationId) => Promise<void>Cancels an unused admission; absent = window age-out.packages/core/src/runtime/agent-loop.ts:639
quota.reserve(request) => Promise<QuotaDecision>-packages/core/src/runtime/agent-loop.ts:626
quota.reserveContinuations?booleanPre-wire continuation admission (RV1013); default post-hoc.packages/core/src/runtime/agent-loop.ts:635
quotaDeniedAgentError?booleanThe versioned compat flag (RV1810): emit the legacy agent:error twin beside quota:denied for recoverable pre-wire quota waits. Default off: the wait speaks its own type only.packages/core/src/runtime/agent-loop.ts:585
resolvedResolvedInvocation-packages/core/src/runtime/agent-loop.ts:579
retry?{ policy?: RetryPolicy; random?: () => number; sleep?: (ms) => Promise<void>; }Transport RetryPolicy (M4-T05): lives UNDER the journal, wired around every adapter.stream dispatch. sleep and random are injectable for tests; the core owns wall-clock.packages/core/src/runtime/agent-loop.ts:598
retry.policy?RetryPolicy-packages/core/src/runtime/agent-loop.ts:599
retry.random?() => number-packages/core/src/runtime/agent-loop.ts:601
retry.sleep?(ms) => Promise<void>-packages/core/src/runtime/agent-loop.ts:600
role?"loop" | "orchestrate" | "plan" | "synthesize"The primary invocation role of the tool loop; default 'loop' (M6-T05; RV-211 adds synthesize).packages/core/src/runtime/agent-loop.ts:907
schema?S-packages/core/src/runtime/agent-loop.ts:575
schemaRetryAttempts?numberBounded schema re-prompt attempts; default 2 (Appendix A).packages/core/src/runtime/agent-loop.ts:844
signal?AbortSignalHost or sibling cancellation.packages/core/src/runtime/agent-loop.ts:776
stream?booleanEmits agent:stream deltas when true (telemetry only).packages/core/src/runtime/agent-loop.ts:774
summarize?PhaseTarget & { fallbacks?: PhaseTarget[]; }Summarize invocation target for compaction (M4-T03): resolved through the chain with role 'summarize', falling back to the loop model when routing resolves nothing. Compaction is ON by default; absence of this option disables it (direct runAgent callers).packages/core/src/runtime/agent-loop.ts:684
terminalTool?{ name: string; repairTurnReserve?: number; validate?: (call) => Promise< | { ok: true; resolved?: { result: unknown; }; } | { feedback: Record<string, unknown>; ok: false; }>; }Terminal-tool interception (M6-T07): an accepted call to the named tool ends the loop with status ok; the call's validated result argument becomes the agent output (the orchestrator finish tool). The tool's execute never runs, mirroring escalate. validate is the optional host judgment over a schema valid call (the RV-204 finish validators): ok finishes as before; a rejection becomes the call's error tool result and the turn continues, so the model can repair and call the terminal tool again. The hook owns bounding and journaling; the loop stays policy only and never throws.packages/core/src/runtime/agent-loop.ts:866
terminalTool.namestring-packages/core/src/runtime/agent-loop.ts:867
terminalTool.repairTurnReserve?numberThe repair reserve (the v1.71 experiment review, P0.4): max EXTRA turns the loop may grant past limits.maxTurns, one per rejected terminal-tool exchange, schema-invalid arguments and host validation rejections alike. The grant count derives from the message window itself (error tool results named after the terminal tool, clamped to the reserve), so a resumed segment that restored the window mid-exchange re-derives the same grants and nothing needs journaling. Zero (or absent) keeps the ceiling byte identical to the pre 1.73 loop.packages/core/src/runtime/agent-loop.ts:903
terminalTool.validate?(call) => Promise< | { ok: true; resolved?: { result: unknown; }; } | { feedback: Record<string, unknown>; ok: false; }>-packages/core/src/runtime/agent-loop.ts:868
toolBudgetDurability?{ onExtensionGrant?: (grant) => Promise<void>; onWindowEntry?: (entry) => Promise<void>; restored?: { cap?: number; extensionsGranted: number; finalizationWindowEntered: boolean; }; }The durable parallel of the tool budget summary (RV509): the caller journals an extension grant and the finalization-window entry as decision entries at the moment each fires, and hands the state read back from those entries into restored on a dangling-dispatch resume. A restored grant is honored as granted (the model was already promised the raised cap), never re-admitted or re-announced, and a restored window entry keeps the summary's finalizationWindowEntered truthful even when a later grant moved the counts back out of the window. Both hooks are AWAITED before the thing they authorize becomes observable (RV601): a grant lifts no expiry and queues no notice until its decision is durable, and the window regime binds no call until its entry is. A rejected append therefore leaves the grant unissued and the entry unrecorded, and the rejection propagates exactly like a failed boundary checkpoint rather than being swallowed. Pressure notices stay events and are never journaled. Absent, the loop is byte-identical to before.packages/core/src/runtime/agent-loop.ts:730
toolBudgetDurability.onExtensionGrant?(grant) => Promise<void>-packages/core/src/runtime/agent-loop.ts:749
toolBudgetDurability.onWindowEntry?(entry) => Promise<void>-packages/core/src/runtime/agent-loop.ts:757
toolBudgetDurability.restored?{ cap?: number; extensionsGranted: number; finalizationWindowEntered: boolean; }-packages/core/src/runtime/agent-loop.ts:731
toolBudgetDurability.restored.cap?numberThe effective cap the journaled grant announced (RV602). It anchors the resumed ceiling, because the live maxToolCalls and increment are not part of the dispatch identity and may legitimately drift between segments: without the anchor the two recovery paths (pure replay, which reads the journal, and live resume, which recomputed) disagreed, and a promise already made to the model could be silently revoked. Validated as a persistent inlet: a non-integer, or one below the base cap, is ignored with a warning, leaving the count derivation as the floor. Grants taken AFTER the restore point still measure the current increment from this anchor.packages/core/src/runtime/agent-loop.ts:747
toolBudgetDurability.restored.extensionsGrantednumber-packages/core/src/runtime/agent-loop.ts:732
toolBudgetDurability.restored.finalizationWindowEnteredboolean-packages/core/src/runtime/agent-loop.ts:733
tools?ToolRuntimeThe resolved toolset; absent = no tools declared.packages/core/src/runtime/agent-loop.ts:642
transcript?{ mintRef: string; put: Promise<void>; }-packages/core/src/runtime/agent-loop.ts:841
transcript.mintRefstring-packages/core/src/runtime/agent-loop.ts:841
transcript.putPromise<void>-packages/core/src/runtime/agent-loop.ts:841