Architecture chapter 07

deps/edges

Lash keeps external systems at explicit edges — LLM transports, OAuth flows, MCP servers (stdio / streamable-http / sse), Tavily search/extract, models.dev metadata, trace sinks, file watchers, and SQLite-backed session storage.

External Integration Map

The runtime uses provider handles and dynamic tool providers to isolate external protocols from turn logic.

Runtime dependency edges
flowchart TD CLI["lash-cli"] --> Config["LashConfig"] Config --> Providers["ProviderSpec -> ProviderHandle"] Providers --> OpenAI["OpenAI-compatible
Responses + Chat Completions"] Providers --> Codex["OpenAI Codex OAuth
chatgpt.com backend responses"] Providers --> Anthropic["Anthropic Messages
/v1/messages"] Providers --> Google["Google Code Assist
generateContent / streamGenerateContent"] Config --> MCP["MCP servers
stdio / streamable-http / sse"] Config --> Tavily["Tavily search / extract"] Config --> Models["models.dev catalog
file cache + bundled fallback"] CLI --> FileIndex["lash-file-index
ignore + notify + nucleo"] Runtime["LashRuntime"] --> StoreContract["RuntimePersistence
lash-core/src/store.rs"] StoreContract --> Store["lash-sqlite-store
session graph + blobs + usage"] Runtime --> Trace["lash-trace schema v2
standard + extended JSONL"] Runtime --> Export["lash-export
DB + provider trace"] MCP --> Dynamic["DynamicToolProvider"] DefaultTools["lash-standard-plugins"] --> Tavily Dynamic --> Runtime DefaultTools --> Runtime

Providers

Four first-party provider crates ship five provider kinds: lash-provider-openai (direct openai Responses plus openai-compatible Chat Completions), lash-provider-codex (codex OAuth device-code flow), lash-provider-anthropic (anthropic), and lash-provider-google (google_oauth). They share one runtime interface (ProviderHandle) but own their own auth, readiness, model policy, transport, and cache-marker placement.

See the Providers chapter for the OpenAI Responses vs Chat split, Anthropic cache_control placement rules, RLM prompt-cache strategy, normalized LlmUsage shape, and the components needed to add a new provider.

Local And Optional Dependencies

These dependencies are important operationally because they affect startup, discovery, observability, or tool availability.

MCP

lash-plugin-mcp wraps the official rmcp SDK and registers an McpPluginFactory with the lash plugin system. The factory owns a shared per-LashCore connection pool, so stdio servers spawn once and every session built from the same core reuses the same client. McpServerConfig supports three transports: stdio (child process), streamable_http, and sse. Tools are surfaced under mcp__<server>__<tool> names, preserving input and output schemas.

Tavily

Web search and URL extraction register only when LashConfig.auxiliary_secrets.tavily_api_key is present.

models.dev

CachedModelCatalog reads a file cache, can fetch https://models.dev/api.json, and has a bundled snapshot fallback.

Tracing

lash-trace defines schema version 2 JSONL records. Standard records cover turns, LLM starts/completions, tools, and summaries; extended records preserve provider/stream details. The optional otel cargo feature pulls in opentelemetry 0.31 and maps those events to spans. lash-trace-viewer is a standalone binary crate that consumes lash-trace JSONL directly, independent of lash-export.

File index

lash-file-index combines ignore walking, notify watching, and nucleo matching for async @ completion.

Store

lash-core/src/store.rs defines the runtime persistence contract. lash-sqlite-store is the local implementation for session heads, graph nodes, checkpoint blobs, usage deltas, tombstones, and garbage-collection metadata.

Export

lash-export renders the SQLite session plus provider trace JSONL when present, so transcript views can include prompt snapshots, context percentage, cached-token percentage, and exact token counts.