CLI host
lash-cli composes providers, plugins, MCP servers, traces, and terminal rendering around a live LashRuntime.
The async runtime is the effect controller boundary around the pure turn machine. It manages residency, persistence, plugin hooks, provider calls, background work, usage accounting, and read projections while keeping CLI rendering and the app-facing lash API outside the core session model.
LashRuntime holds the live session registry, managed child-session state, AgentFrame-following turn execution, process grants, pending user turn input, queued background work, and a usage ledger. Sans-IO computes protocol effects, RuntimeEffectController handles nondeterministic host work, plugins contribute behavior, providers complete requests, and persistence records committed session state plus final turn commit idempotency.
RuntimeTurnDriver only polls TurnMachine, builds a RuntimeEffectEnvelope, invokes the active ScopedEffectController, and feeds the returned response back. The default InlineEffectHost creates in-process scopes; durable adapters create handler-scoped controllers for stable ExecutionScope values.
Every invocation carries a RuntimeInvocation: session id, optional turn id/index, protocol iteration, subject, optional typed causal parent, stable replay.key, and ref-only request data. Cancellation, streams, provider trace sinks, and attachment byte hydration stay in the local executor or host workflow. The boundary covers turn LLM calls, direct/plugin LLM completions, individual tool calls, durable tool steps and waits, process admin, retry sleeps, RLM exec, checkpoints, and execution-surface sync. Process registry presence enables the generic process admin plane, while the process work driver owns execution and waits; protocol/language runtimes decide how that plane becomes authored process/lifecycle abilities.
RuntimeHostConfig::new, RuntimeEnvironmentBuilder::with_effect_host, EmbeddedRuntimeBuilder::with_effect_host, and the facade's AdvancedLashCoreBuilder::effect_host configure the deployment effect host. Durable workflow adapters pass a handler-scoped controller through the scoped turn entrypoints so the workflow controller and stable turn identity are explicit for that run. After a crash or worker move, the workflow handler reruns and the durable host replays effect outcomes before Lash retries the final commit. Restate is the first-party adapter; Temporal or another workflow engine can implement the same controller boundary.
Lash can run behind the CLI or inside any long-lived host application. The same session machinery supports active sessions, managed child sessions, parked runtimes, and resumable hosts.
lash-cli composes providers, plugins, MCP servers, traces, and terminal rendering around a live LashRuntime.
lash exposes LashCore/LashSession for long-lived host applications. Hosts open sessions from ids and stores; parking/resume remains runtime plumbing behind the facade.
Services can compose the same core with their own providers, plugins, stores, traces, and validation around product-specific session ids.
The internal RuntimeSessionServices capability set is projected through thin wrappers (RuntimeSessionStateService, RuntimeSessionLifecycleService, RuntimeSessionGraphService, RuntimeSessionProcessService) that implement the split host-boundary traits SessionStateService, SessionLifecycleService, SessionGraphService, and ProcessService with typed operations for plugin and tool needs. Tool authors do not receive the runtime handle directly; ToolContext projects host capabilities into explicit methods such as sessions() (whose ToolSessionAdmin exposes model() and tool_catalog()), processes(), direct_completions(), attachments(), and durable_effects(). Process starts enter through ProcessStartRequest, and intentional cancellation enters through the host-owned ProcessCancelAbility.
| Host-service area | Runtime responsibility |
|---|---|
| Session snapshots and catalogs | Read current state and tool catalogs through stable projections. |
| Tool state | Read and mutate per-session Tool Catalog membership without exposing the inner registry. |
| Session lifecycle and turns | Create child sessions, fork sessions, append AgentFrames, and stream managed turns. |
| Session graph writes | Append plugin-authored nodes to a session graph through a guarded contract. |
| Processes | Register durable processes from typed start requests, grant process handles, deliver wakeups, and route user-facing cancellation through the configured cancel ability. |
| Trace access | Expose trace sinks to hooks without exposing session internals. Human input is provided by host tools such as the CLI ask implementation, not by a runtime prompt event. |
| Direct completions | Run one-shot completions for helper tools such as llm_query and account for their usage. |
A turn finishes as one of three outcomes. App hosts usually call session.turn(input).stream_to(&sink) or .run(); durable handlers add .turn_id(...).effects(&controller). Those facade calls follow AgentFrame switches and return the final TurnResult. Raw stream_turn and stream_turn_with_agent_frames are runtime internals for lower-level hosts.
| Outcome | Shape | Meaning |
|---|---|---|
TurnOutcome::Finished | TurnFinish::AssistantMessage { text }, TurnFinish::FinalValue { value }, or TurnFinish::ToolValue { tool_name, value } | The turn produced a final answer. AssistantMessage is the sole terminal prose result; live prose is only AssistantProseDelta, and the runtime commit materializes the settled transcript message once. FinalValue and ToolValue are typed terminal values authored by RLM finish or terminal tool controls. |
TurnOutcome::AgentFrameSwitch | { frame_id, task } | RLM continue_as appended a new AgentFrame inside the same session with a fresh prompt window, explicit { task, seed } nodes, and frame-scoped execution state. |
TurnOutcome::Stopped | TurnStop::{Cancelled, Incomplete, InvalidInput, MaxTurns, ToolFailure, ProviderError, PluginAbort, RuntimeError, SubmittedError{...}, ToolError{...}} | The turn ended without a clean answer. SubmittedError and ToolError carry terminal errors authored by failing control paths, such as submit_error, so hosts can surface them. |
A single exclusive projector runs once per completed tool call. The durable graph keeps the full ToolCallOutput; the projector derives the budgeted ModelToolReturn that the live model prompt and rolled-up history see.
Defaults: ToolOutputBudgetMode::Bytes at 16 KiB and 400 lines, configurable via ToolOutputBudgetConfig. RLM piggybacks on the same projector for its print observations.
Persistence is turn-scoped. Commits update the session graph, checkpoint blobs, session heads, usage ledger, and settled assistant prose together so read views and host reports agree. Modes describe outcomes; the shared runtime commit path handles final transcript materialization.