leonard_agent_io library
I/O-only harness entrypoint for hosts that create VM-service connections.
This re-exports the web-safe leonard_agent.dart API and adds the owning
connection functions used by command-line and native hosts. Web consumers
should continue to import leonard_agent.dart and supply a borrowed
VmService connection.
Classes
- ActionSchema
- JSON Schema (draft-07) constraining a single model decision.
- ActionValidator
- Five-pass validator for candidate actions.
- AnthropicBackend
-
Anthropic (Claude) via stock dartantic
AnthropicChatModel. - AssistantTurn
- An assistant-role turn: the thinking trace (empty when absent) and the tool call the model chose.
- ChangedValue
- One key-level change inside a structured extension diff.
- ConversationBuilder
- ConversationSnapshot
- Immutable snapshot of a chat-shape conversation at a point in time.
- ConversationTurn
- Sealed turn hierarchy for the append-only chat conversation (chat-shape rebuild).
- CoreDiff
- Diff over the core fragment.
- CoreFragment
- Core fragment: route stack + semantics nodes (id-keyed) + recent errors.
- DartanticModelProvider
-
A ModelProvider that drives ANY dartantic
ChatModelbuilt from a ModelBackendSpec (ADR 0003, lenny-4dhv.3). - DefaultLoopHost
- ExtensionAutoDisabled
- Emitted when LeonardSession.disableExtension auto-disables a extension.
- ExtensionBusy
- Subset of the binding's per-extension "busy at termination" descriptor.
- ExtensionDiff
- Sealed extension diff; one of ExtensionDiffStructured, ExtensionDiffOpaque, ExtensionDiffAdded, ExtensionDiffRemoved.
- ExtensionDiffAdded
-
Extension namespace appears in
currbut not inprev. - ExtensionDiffOpaque
-
Opaque pair (
previous,current). Used when either side is notdeltaFriendly. - ExtensionDiffRemoved
-
Extension namespace appears in
prevbut not incurr(e.g. extension auto-disabled). - ExtensionDiffStructured
-
Key-level structured diff. Selected when both
prevandcurrdeclareddeltaFriendly: true. - ExtensionDisabledEvent
- ExtensionFailureTracker
- ExtensionFragment
- One extension's contribution to the observation bundle.
- ExtensionManifestEntry
- One entry in the handshake extension manifest.
- ExtensionManifestRecord
- HandshakeResult
-
Result of the
ext.leonard.core.handshakeexchange. - HostAttachment
-
One host to attach: its diagnostic label + the ws:// uri to reach
it. Exported so CLI callers can name endpoints (e.g.
flutter/native). - JsonObservationRenderer
-
Renders Observation as compact JSON. Top-level keys are stable
(
core,extensions,stability) so ajsonDecodeof the output is structurally identical to the wire format the binding produced (modulo screenshot, which the renderer omits — providers add the image as a separate content part). - LeonardConfig
- Per-session configuration consumed by later harness stories (turn budgets, session budgets, max turns).
- LeonardSession
- Owns the run lifecycle: connect, start, observe, act, end.
- LoopDriver
- LoopHost
- ModelBackendSpec
- Declarative description of a model backend the agent can drive, independent of how it is wired (ADR 0003, lenny-4dhv.2).
- ModelCapabilities
- Capabilities advertised by a ModelProvider.
- ModelDecision
- One decision returned by ModelProvider.decide.
- ModelProvider
- Provider contract for model backends used by the Leonard agent.
- MultiHostSession
-
N-host generalization of LeonardSession. Implements SessionSurface
so
DefaultLoopHost/bringUpSessiondrive it identically to a single-host session. - NodeChange
-
One semantics node whose content differs between
prevandcurr. - Observation
- Top-level typed observation bundle.
- ObservationDiff
- Top-level diff: per-turn delta over Observation.
- ObservationDiffer
- Stateless differ. All routines are pure functions of the two inputs.
- ObservationRenderer
-
Interface used by
ConversationBuilderto flatten an Observation into the text body of aUserTurnfor any provider. - OpenAIBackend
-
OpenAI (or any OpenAI-compatible endpoint) via stock dartantic
OpenAIProvider/OpenAIChatModel. - OpenAiModel
- One OpenAI model entry — id + capabilities.
- RouteChange
- One route-stack change: previous full stack -> current full stack.
- RuntimeError
- One runtime error captured in the binding's error ring buffer.
- SemanticsNode
- One semantics node from the captured tree.
- SessionEnded
- Emitted exactly once when LeonardSession.end runs.
- SessionHeader
- SessionProgressEvent
- Sealed base type for events emitted on LeonardSession.progress.
- SessionStarted
- Emitted exactly once when LeonardSession.start succeeds.
- SessionSurface
- The contract the loop driver's host adapter relies on.
- SessionTermination
- Structured return value from LoopDriver.runSession.
- StabilityMetadata
- Wire-typed mirror of the binding's stability block.
- SwiftInferBackend
- swift-infer (local MLX, Anthropic-compatible wire) via the custom SwiftInferChatModel — the backend that needed lenient SSE parsing and the Qwen sampling knobs (lenny-4dhv.1).
- SwiftInferChatModel
-
A dartantic
ChatModelthat speaks swift-infer's Anthropic-compatible/v1/messageswire (ADR 0003, lenny-4dhv.1). - SwiftInferChatOptions
-
Generation options for
SwiftInferChatModel. - SwiftInferConfig
- Configuration values for the dartantic SwiftInferChatModel seam.
- ThinkingDelta
- One delta emitted on the ModelProvider.thinking stream.
- ToolDescriptor
- Description of a single tool available to the model on a given turn.
- TrajectoryReader
- Decodes JSONL trajectories into TrajectoryRecords.
- TrajectoryRecord
- Wire format for the trajectory JSONL stream (PRD §14).
- TrajectorySink
-
Append-only line sink for trajectory writers. Implementations live
outside
leonard_agent(e.g.FileTrajectorySinkusingdart:ioin the CLI,DtdTrajectorySinkusingpackage:dtdin DevTools). - TrajectoryWriter
- Serializes typed trajectory records to JSONL via a TrajectorySink.
- TurnActionDecided
- The action the loop chose for this turn (post-validation).
- TurnBegan
- Emitted at the start of each turn.
- TurnComplete
- End-of-turn marker.
- TurnEvent
- One event in the per-turn stream.
- TurnRecord
- TurnThinking
-
One reasoning delta from the model provider's
thinking()stream. - TurnUsage
- Per-turn token-usage snapshot. Emitted just before TurnComplete at each turn boundary. Carries the whitespace-split estimate and the trim-budget ceiling so DevTools consumers can display used/ceiling without reading internal ConversationBuilder state.
- TurnValidation
- Validation outcome for the turn's chosen action.
- UnknownTrajectoryRecord
-
Fallback when the
typediscriminator is absent or unrecognized. - UserTurn
-
A user-role turn: one observation + diff from the loop driver, plus
an optional tool-result map for error feedback (schema/validation retry
or failed action). trimmed is set by
ConversationBuilder.trimIfOverBudget. - ValidationLoopResult
- Result of a successful decideAndValidate call.
- ValidationOk
- The candidate action passed all three validation passes.
- ValidationReject
- The candidate action was rejected by one of the three passes.
- ValidationResult
- Outcome of validating a candidate action against the merged tool list and the current observation.
- VmServiceClient
- Typed VM-service client used by LeonardSession.
Enums
- HarnessError
-
Sub-classification of a session that ended with
SessionOutcome.harnessError. Mirrors the wire-level
harness_errorfield carried in the trajectory footer. - SessionOutcome
- StabilityPolicy
-
Wire-name mapping for the request's
policyparameter. MirrorskStabilityPolicyWireNameson the binding side (packages/leonard_flutter/.../observation_request.dart). - SwiftInferReasoningEffort
-
How much reasoning swift-infer's chat template budgets for a turn
(
reasoning_effort). Non-Anthropic-standard. - SwiftInferToolChoice
- How swift-infer should be told to pick a tool each turn.
Extensions
- HarnessErrorWire on HarnessError
-
Wire name for HarnessError values, as written to the trajectory
footer's
harness_errorstring.
Constants
-
kAnthropicVisionModels
→ const Set<
String> - Set of Claude model ids that accept image inputs (PRD §22). Public so a host can advertise vision support without instantiating a provider. (Relocated here from the deleted hand-rolled Anthropic provider — dartantic cutover, ADR 0003 / lenny-4dhv.4.)
- kDefaultAgentsMd → const String
- The bundled operating guide. See the library doc for provenance.
-
kIoTransportExceptionNames
→ const Set<
String> -
Runtime-type names of the
dart:iotransport exceptions treated as provider transport failures. - kMaxDetailLength → const int
- Maximum length of a footer detail message, so one long exception cannot bloat the trajectory footer.
- kQwen38IdPrefix → const String
-
Model-id prefix whose chat template runs
xhighwhenreasoning_effortis absent. Mirrors thestartsWith('qwen3')test incapabilitiesFor. - kSwiftInferDriverMaxTokens → const int
-
Driver default for
max_tokenson the swift-infer tier. -
openAiModels
→ const Map<
String, OpenAiModel> - Known GPT-5-class models. Keyed by wire id.
Properties
- kDefaultAgentsMdHash → String
-
Stable provenance hash of kDefaultAgentsMd for trajectory headers.
final
Functions
-
bringUpSession(
{required SessionSurface session, required String goal, required StabilityPolicy policy, required String modelIdentifier, required String buildIdentifier, required String harnessVersion, required List< ToolDescriptor> coreTools, required Map<String, List< extensionTools, required String agentsMd, String agentsMdHash = '', Map<ToolDescriptor> >String, dynamic> ? extraConfig}) → Future<BringUpResult> - Build the extension manifest, assemble a SessionHeader, compose a DefaultLoopHost, and return both.
-
buildBackendChatModel(
ModelBackendSpec spec, {required String model, List< Tool< ? tools, Client? client}) → ChatModel<Object> >ChatModelOptions> -
Builds a configured dartantic
ChatModelforspec. -
buildExtensionTools(
{required Iterable< String> requested, required List<ExtensionManifestEntry> handshake}) → Map<String, List< ToolDescriptor> > -
Build the
extensionToolsmap from a caller-supplied namespace whitelist and the binding's handshake manifest. See library doc for semantics. -
capabilitiesFor(
String providerId, String modelId) → ModelCapabilities? - Resolve capabilities for the given (provider, model) pair.
-
connectLeonardSession(
Uri vmServiceUri) → Future< LeonardSession> - Opens an owned VM-service connection and wraps it in a LeonardSession.
-
connectMultiHostSession(
List< HostAttachment> hosts) → Future<MultiHostSession> - Opens one owned VM-service connection for each host, in attach order.
-
connectVmServiceClient(
Uri wsUri) → Future< VmServiceClient> -
Opens an owned VM-service connection to
wsUriand pins its first isolate. -
decideAndValidate(
{required ModelProvider provider, required ConversationSnapshot baseSnapshot, required ActionSchema schema, required ActionValidator validator, required Observation observation, required List< ToolDescriptor> mergedTools, int maxValidationRetries = _kDefaultMaxValidationRetries}) → Future<ValidationLoopResult> - Run one decide-and-validate cycle for a single turn against a chat-shape ConversationSnapshot.
-
defaultReasoningEffortFor(
String modelId) → SwiftInferReasoningEffort? -
SwiftInferReasoningEffort.medium for a
qwen3.8node, elsenull— an unset effort is omitted from the body, leaving the node's card default. -
defaultSwiftInferOptions(
String modelId, {int? maxTokens, SwiftInferReasoningEffort? reasoningEffort, double? temperature, double? presencePenalty}) → SwiftInferChatOptions -
The options lenny's drivers send for
modelId; each supplied override wins over the default. -
describeThrowable(
Object? error) → String -
Footer-safe rendering of
error: its runtime type, then its credential-scrubbed message clipped to kMaxDetailLength. -
encodeToolName(
String dotted) → String -
Encode a dotted ToolDescriptor name (
core.tap) into the wire form accepted by Anthropic's tool API (core_tap). -
fnv1a32Hex(
String s) → String -
Web-safe 32-bit FNV-1a hash (hex) — masks to 32 bits each step so it is
deterministic on both the VM and
dart2js/dart2wasm(unlike the CLI's native 64-bit variant, which relies on two's-complement overflow). Used only as a stable provenance stamp forSessionHeader.agentsMdHash; cross-harness equality is not required. -
isProviderTransportError(
Object error) → bool -
True when
erroris a provider TRANSPORT failure — the request or its response stream died before the model produced output. -
lookupTool(
List< ToolDescriptor> tools, String wireName) → ToolDescriptor? -
Look up a ToolDescriptor by its wire-encoded name (dotted name with
.replaced by_). Returnsnullwhen no match exists. -
manifestToolDescriptors(
ExtensionManifestEntry entry) → List< ToolDescriptor> - Project one handshake entry into qualified model tool descriptors.
-
mergeObservations(
List< Observation> perHost) → Observation -
Merge
perHostobservations (in attach order, primary/Flutter first) into one Observation: -
scrubCredentials(
String text) → String -
Replaces credential-shaped substrings of
textwith<redacted>. -
unknownExtensionNamespaces(
{required Iterable< String> requested, required List<ExtensionManifestEntry> handshake}) → List<String> -
Names the caller listed in
requestedthat are not present in the handshake manifest (i.e. the binding does not have an extension with that namespace loaded). Returned in iteration order ofrequestedso warnings match caller input. -
unknownToolRejection(
String wireName, List< ToolDescriptor> tools, {required Map<String, Object?> rawPayload}) → SchemaRejection - Build the canonical SchemaRejection for "model emitted a tool name the prompt did not offer". All frontier providers share this shape so the loop driver and any panel-side observer see the same surface.
-
validateToolArgs(
ToolDescriptor tool, Map< String, dynamic> args) → void -
Validate
argsagainsttool.inputSchemausing the samepackage:json_schemadraft-07 path asActionSchema.
Typedefs
- BringUpResult = ({SessionHeader header, DefaultLoopHost host})
- Result returned by bringUpSession.
-
ValidatorAction
= ({Map<
String, dynamic> args, String tool}) - Shape of the action handed to ActionValidator.validate.
Exceptions / Errors
- BindingNotInitializedError
-
Thrown when the target app's
LeonardBindingis not initialized, i.e. theext.leonard.core.handshakeservice extension is absent. - InvalidActionExhausted
- Thrown by decideAndValidate when the validator rejected the model output 3 times (PRD §17). Carries the structured rejection messages the driver writes into the failed-turn record.
- MultiHostNamespaceCollision
-
Thrown by
MultiHostSession.startwhen two attached hosts each report the SAME manifest namespace in their handshake. - MultiHostUnknownNamespace
-
Thrown synchronously by
MultiHostSession.executeAction(before any wire call) when an action's namespace is owned by no attached host. - ObservationEnvelopeError
-
Thrown when
get_stable_observationdoes not return an observation envelope this harness can decode. - ProviderTransportFailure
-
Raised by
decideAndValidatewhenModelProvider.decidefailed with a transport fault rather than a model-output fault.LoopDriver.runTurnconverts it into aprovider_transportTurnFailure. - SchemaExhausted
- Thrown by decideAndValidate when the provider threw SchemaRejection twice in a row (PRD §17 schema budget = 1).
- SchemaRejection
- Thrown when a model response fails JSON-Schema validation.
- SwiftInferHttpException
- Thrown when swift-infer returns a non-2xx HTTP status. The seam maps this to a failed turn; it is distinct from a schema/validation rejection.
- TurnFailure
- Thrown by LoopDriver.runTurn when a turn fails for a reason that counts toward a failure budget (PRD §17).
- TurnTimeoutError
- Thrown by the per-turn budget when a single turn exceeds the wall- clock budget (PRD §10, default 30s).
- VmServiceConnectionLost
- Raised by LoopDriver.runSession when the underlying VM-service transport emits an unrecoverable error mid-session (e.g. the websocket dropped). The driver translates this into a HarnessError.connectionLost termination.