types/contracts

The important architecture lives in small contracts. Provider components, protocol drivers, tool definitions, output contracts, plugin hooks, store commits, process admins, trigger operations, read projections, AgentFrames, and observability sinks: each a named seam.

Core Contracts

These abstractions define the system more precisely than crate names do.

AbstractionOwnerRole
TurnMachine, Effect, Response, EffectId, TurnCheckpointlash-sansioPure turn state machine and response protocol. Checkpoints are a sans-IO primitive for machine round-trips and tests; runtime crash recovery is owned by effect hosts and committed state stores.
EffectHost, ExecutionScope, ScopedEffectController, RuntimeEffectEnvelope, RuntimeEffectLocalExecutorlash-coreTyped durable effect boundary for LLM calls, direct completions, individual tool calls, durable tool steps and waits, process admin, retry sleeps, RLM exec, checkpoints, and execution-surface sync. Envelopes carry a serialized RuntimeInvocation with scope, subject, optional causal parent, and replay.key; durable hosts such as Temporal, Restate, or another workflow runtime own effect replay and timers.
TurnOutcome, TurnFinish, TurnStoplash-sansioThree-class turn result: Finished{AssistantMessage|FinalValue|ToolValue}, AgentFrameSwitch{frame_id, task}, Stopped{Cancelled|Incomplete|InvalidInput|MaxTurns|ToolFailure|ProviderError|PluginAbort|RuntimeError|SubmittedError{...}|ToolError{...}}.
ProtocolBuildInput, TurnDriverPreamble, ContextProjector, ProtocolDriverHandlelash-core + lash-sansio + protocol cratesProtocolBuildInput carries the effective tool catalog and generic prompt/protocol context from the runtime into mode preamble builders. Protocol-specific prompt surfaces, including RLM's Lashlang host environment, are built by the protocol/runtime crates. TurnDriverPreamble, projectors, and protocol drivers stay mode-agnostic in lash-sansio.
LashCore, SessionBuilder, LashSession, TurnBuilderlashApp-facing facade for shared core configuration, session opening/resume, the stable host-owned session handle, and semantic turn streaming.
Session, AgentFrameRecord, SessionGraphlash-coreSession is the durable work identity; AgentFrameRecord is the current agent assignment/execution frame inside that session; graph nodes carry agent_frame_id so export/debug can show frame boundaries without turning them into child sessions.
SessionRelation::{Root, Child}lash-coreHow a new session relates to its parent: top-level root, or child session with optional typed CausalRef. continue_as appends an agent frame inside the same session instead of creating a related session.
PluginOptions, RlmCreateExtras, RlmProjectedSeedSnapshotlash-core + lash-rlm-typesPlugin-owned session-creation payloads. RLM carries a termination policy and an optional projected-binding seed for AgentFrame switches and spawned agents.
PluginSessionContext, SubagentSessionContextlash-corePer-session context handed to plugin factories, plus the marker that identifies a session as a subagent and tracks parent session id, capability, and depth.
SessionSnapshot, RuntimeSessionStatelash-corePublic serializable session snapshots and runtime-private mutable state: graph, current frame id, frame records, policy, turn index, usage, store refs, and store head metadata. Execution snapshots are frame-owned, not one global session VM blob.
AgentFrameRun, stream_turn_with_agent_frameslash-coreInternal AgentFrame switch-following runtime primitive used by the lash facade. App hosts normally use TurnBuilder::stream_to, TurnBuilder::run, or pull-style TurnBuilder::stream.
ToolContext, RuntimeSessionServiceslash-coreThe runtime-session boundary is an internal capability set (RuntimeSessionServices and its capability handles). Public tool code reaches the same powers through explicit ToolContext capability methods (sessions(), processes(), direct_completions(), attachments(), durable_effects()) instead of receiving the runtime handle.
TurnBoundary, TurnCommitDraft, TurnGraphEditorlash-coreTurn-scoped typed graph writes, checkpoint, usage, and read-state commit policy.
SessionGraph, SessionReadView, ChronologicalProjectionlash-coreDurable event storage, public read seam, and semantic chronological projection, all in lash-core. The CLI's UiTimeline is a render projection downstream of ChronologicalProjection.
RuntimePersistence, RuntimeCommit, GraphCommitDeltalash-coreRuntime persistence contract for the per-session execution lease, fenced graph/head commits, current AgentFrame metadata, pending turn input, queued work, committed checkpoints, and final turn commit idempotency. The session execution lease is the normal concurrency primitive; head CAS is the stale-writer backstop inside the same commit transaction.
RuntimePersistence implementationshost / store cratesThe store contract is database-agnostic. First-party implementations include lash-sqlite-store for local durable hosts and lash-postgres-store for shared workers; hosts can provide another database-backed implementation when it preserves the same session-lease, CAS, queue, process, trigger, artifact, and blob semantics.
ProcessStartRequest, ProcessService::start_from_requestlash-coreCanonical process-start request path. Facade, runtime, and tool controls pass a typed request into the service helper; only the service reconstructs the internal ProcessRegistration and ProcessStartOptions.
ProcessCancelAbility, ProcessCancelRequest, ProcessCancelAllRequestlash-coreHost-owned intentional cancellation seam. Single-handle and cancel-all requests carry a ProcessCancelSource plus reason, and cancel-all returns ProcessCancelSummary by cancelling each visible live handle through the same ability path.
ProviderHandle, ProviderComponentslash-coreRuntime-only provider handle split by state, auth, readiness, transport, and model policy. Session persistence records only provider_id and rebinds it to the live handle supplied by the host.
ToolDefinition, ToolCatalog, ToolOutputContract, ToolCallOutput, ToolCallRecordlash-sansioTool catalog membership (a tool is callable iff it is a catalog member), model schema, examples, compact docs, dynamic return contracts, and canonical tool-call output records. App-facing tool implementations return lash::tools::ToolResult / lash_core::ToolResult, which core converts into this canonical output shape.
PluginFactory, SessionPlugin, PluginRegistrarlash-corePlugin lifecycle and hook registration for tools, prompts, modes, runtime events, and host-callable RPCs.
LlmToolsPluginFactory, DirectCompletionClient::direct_completionlash-llm-tools + lash-corellm_query tool registration, output-schema handling, direct completion, and usage relay.
RlmControlToolsProviderlash-protocol-rlmMode-provided RLM control tools such as continue_as, which produces a TurnOutcome::AgentFrameSwitch.
LashlangHostEnvironment, ModuleArtifact, ProcessDefinitionIdentity, compile_modulelashlang + lash-lashlang-runtime + lash-protocol-rlmThe linked host surface gates resource operations, process declarations/starts, process sleep/signals, trigger registry operations, and optional language features such as @label annotations. Disabled features parse but fail during linking before runtime side effects. Hosts compile modules through the facade/runtime integration to get content-addressed artifact refs, stable introspection, and exported process definition identities. Stored Lashlang process input is an opaque ProcessInput::Engine { kind: "lashlang", payload }; the Lashlang process engine loads the artifact by module_ref before compiling the selected process_ref.
TriggerHostOperation, TriggerRegistrationRequest, ProcessDefinitionIdentitylashlang + lash-lashlang-runtime + lash-coreCanonical trigger register/list/cancel contract. Lashlang owns operation strings, resource operation shapes, request decoding, process-definition identity, trigger compatibility checks, alias resolution, and event/input assignability; the Lashlang runtime bridges those operations to core's durable registration storage and source-key matched trigger delivery.
RlmProjectedBindings, ProjectionRef, ProjectionResolver, ProjectionRegistrylash-protocol-rlmPublic RLM projection API. Inline bindings carry JSON/lashlang values; lazy bindings carry stable refs that resolve to low-level projected values immediately before execution.
ProjectedBindings, ProjectedValue, ProjectedHostDescriptor, ProjectedReadRequest, ProjectedReadResponselashlangLow-level read-only Lashlang projection protocol. ProjectedValue can carry opaque projection-ref metadata for snapshot/seed rehydration; assignment over a projected name is rejected at runtime.
ToolResultProjector, ToolOutputBudgetPluginFactory, ToolOutputBudgetConfiglash-plugin-tool-output-budgetSingle exclusive tool-result projector (registered via reg.tool_results().projector(...)) that derives the budgeted model-facing ModelToolReturn from each completed call's full ToolCallOutput, with ToolOutputBudgetMode::{Bytes, Tokens} limits (defaults: 16 KiB, 400 lines).
ToolDiscoveryIndex, CatalogTool, llm_rerank_requestlash-cli MCP discovery exampleHost-owned search/load tool discovery pipeline split across catalog preview, ranking, rerank, and schema-index modules.
TraceSink, TraceEvent::LashlangExecution, OtelTraceSinklash-trace + lash-coreRuntime observability contracts. Ordinary traces flow through the configured trace sink; Lashlang execution graphs use a separate opt-in sink with execution maps, node, branch, and child-link records. Static @label metadata is emitted once in execution-map node label_metadata; OpenTelemetry export is feature-gated.
UiTimelineItem, ActivityBlock, TuiExtensionslash-cli / lash-tui-extensionsCLI render projection, tool activity display, and host-provided surface slots.

Plugin Registration

Plugins are assembled per session. Each plugin can contribute protocol drivers, native tools, tool-catalog entries, prompt text, lifecycle hooks, host-callable RPCs, and UI activity.

Plugin contract shape
flowchart LR Factory["PluginFactory"] --> SessionPlugin["SessionPlugin"] SessionPlugin --> Registrar["PluginRegistrar"] Registrar --> Mode["mode hooks
session + driver + native tools"] Registrar --> Tools["ToolProvider
ToolCatalogContributor"] Registrar --> Prompt["PromptContributor
context transforms"] Registrar --> Runtime["runtime events
plugin actions"] Registrar --> UI["UI activity / panels"] Mode --> Session["Session"] Tools --> Session Prompt --> Session Runtime --> Session UI --> CLI["lash-cli projection"]

Boundary Rules

The current codebase relies on these separations staying explicit.

  1. No UI vocabulary in core runtime.

    UiTimelineItem and rendering stay in lash-cli.

  2. Provider quirks stay at provider boundary.

    OpenAI-compatible schema normalization belongs in request builders, not canonical tool definitions.

  3. Graph storage is not chronological policy.

    Dedupe and ordering live in projections such as ChronologicalProjection.

  4. Background work is handle-shaped.

    Started tool calls, including spawn_agent fan-out, are rediscovered through processes.list.

  5. Host bindings are projected, not copied.

    RLM exposes history and any host globals to Lashlang via RlmProjectedBindings. Lazy values are passed as ProjectionRefs and resolved at execution time; snapshot restore rehydrates ref-backed projected values instead of copying or materializing them.

read on ·