localharness/lib.rs
1//! # localharness — Rust-native, model-agnostic agent SDK
2//!
3//! Build production agents with streaming text, custom tools, safety
4//! policies, and background triggers — all from a single `cargo add`,
5//! zero external binaries.
6//!
7//! ## Quick start
8//!
9//! ```rust,no_run
10//! use localharness::{Agent, GeminiAgentConfig};
11//!
12//! # async fn run() -> localharness::Result<()> {
13//! let cfg = GeminiAgentConfig::new(std::env::var("GEMINI_API_KEY").unwrap())
14//! .with_system_instructions("You are a concise code reviewer.");
15//!
16//! let agent = Agent::start_gemini(cfg).await?;
17//! let response = agent.chat("What is 2+2?").await?;
18//! println!("{}", response.text().await?);
19//! agent.shutdown().await?;
20//! # Ok(())
21//! # }
22//! ```
23//!
24//! ## Layers
25//!
26//! | Layer | Type | Purpose |
27//! |-------|------|---------|
28//! | 1 | [`Agent`] | High-level facade: connect, chat, shutdown. |
29//! | 2 | [`Conversation`] / [`ChatResponse`] | Stateful session, multi-cursor streams. |
30//! | 3 | [`connections::Connection`] | Transport abstraction. |
31//! | aux | [`Filesystem`] | What the 6 fs-shaped built-in tools call into; swap the impl to target OPFS, an in-memory FS, etc. |
32//!
33//! [`Agent`]: agent::Agent
34//! [`Conversation`]: conversation::Conversation
35//! [`ChatResponse`]: conversation::ChatResponse
36//! [`connections::Connection`]: connections::Connection
37//! [`Filesystem`]: filesystem::Filesystem
38
39// On wasm32 the crate is single-threaded (browser) and intentionally
40// uses `Arc` over non-Send/Sync values via the `MaybeSendSync` marker
41// (see `runtime.rs`). Clippy's `arc_with_non_send_sync` fires on every
42// such use; it's by design on this target, so silence it crate-wide for
43// wasm rather than peppering `#[allow]` across the modules.
44#![cfg_attr(target_arch = "wasm32", allow(clippy::arc_with_non_send_sync))]
45
46// On wasm32 the upper architecture (Agent → Conversation → Connection)
47// is temporarily gated behind `native` because its trait bounds require
48// `Send` futures, which reqwest's browser fetch can't satisfy. The wasm
49// surface exposes `error`, `content`, `types`, and the low-level
50// `backends::gemini::api::GeminiClient` so a web demo can drive the
51// Gemini REST API directly. Lifting the gate is M2.5: thread a
52// `MaybeSend` shim through the Tool/Connection/Hook traits.
53/// Layer-1 agent facade: connect, chat, shutdown.
54pub mod agent;
55/// Backend implementations (Gemini, MCP).
56pub mod backends;
57/// The crate-wide built-in tool registry (fs tools, ask_question, finish,
58/// call_agent, ...) — backend-neutral; every backend registers from here.
59/// Formerly `backends::gemini::tools` (a re-export shim remains there).
60pub mod builtins;
61/// Transport abstraction traits.
62pub mod connections;
63/// Multimodal input primitives (text, images, documents, audio, video).
64pub mod content;
65/// Stateful conversation session with multi-cursor streaming.
66pub mod conversation;
67/// Typed error hierarchy.
68pub mod error;
69/// The one `LHxxxx` error-code registry (compile / runtime / tx-revert).
70pub mod error_codes;
71/// Filesystem abstraction for built-in fs tools.
72pub mod filesystem;
73pub(crate) mod runtime;
74/// Hook traits for observing and gating agent events.
75pub mod hooks;
76/// Declarative tool-execution policy engine.
77pub mod policy;
78/// Custom tool registration and dispatch.
79pub mod tools;
80/// Background triggers that push messages into the agent.
81pub mod triggers;
82/// Public boundary types (steps, tool calls, usage, config, etc.).
83pub mod types;
84
85/// Rust-subset to wasm compiler.
86pub mod rustlite;
87
88/// Pure framebuffer rasterization + `Viewport` (the host::compose geometry
89/// foundation; native-testable, used by `app::display`). See `src/raster.rs`.
90pub mod raster;
91
92/// Compositor scheduling for `host::compose` — the deferred-mutation module
93/// table (native-testable control flow). See `src/compose.rs`.
94pub mod compose;
95
96/// Pure hex / address / amount encoding helpers (native-testable). Hoisted out
97/// of `app::events` so they run under `cargo test`. See `src/encoding.rs`.
98pub mod encoding;
99
100/// Pure, deterministic CONVERGENT reconcile for cross-device shared-folder sync
101/// (native-testable). Hoisted out of `app::sharedfs_sync` so the convergence /
102/// symmetry property runs under `cargo test`. See `src/sharedfs_reconcile.rs`.
103pub mod sharedfs_reconcile;
104
105/// Pure signed-envelope layer for on-chain WebRTC signaling blobs — the SDP
106/// sealing/sender-authentication core (native-testable; needs `wallet` for
107/// k256). Hoisted out of `app::teams_sync` so the seal/unseal round-trip and
108/// tamper/forgery rejection run under `cargo test`. See `src/signaling_seal.rs`.
109#[cfg(feature = "wallet")]
110pub mod signaling_seal;
111
112/// Pure Last-Writer-Wins key/value CRDT for SessionRoom shared state (#22):
113/// folds a set of decrypted ops into a converged map (order-independent,
114/// idempotent, optional TTL). Native-testable. See `src/kv_reduce.rs`.
115pub mod kv_reduce;
116
117/// SessionRoom op sealing/opening + deterministic per-room key derivation (#22):
118/// AES-256-GCM confidentiality under `K_room` inside a writer-signed,
119/// room-bound `signaling_seal` envelope. Needs `wallet` for k256/keccak.
120/// Native-testable. See `src/kv_room.rs`.
121#[cfg(feature = "wallet")]
122pub mod kv_room;
123
124/// Pure typed-confirmation challenge gate for destructive tools
125/// (native-testable, `turn_flow` hoisting pattern): single-use random nonce
126/// bound to exact tool+args, valid only when typed by the USER. Enforced by
127/// `app::chat::confirm_guard` at the dispatch layer. See `src/confirm.rs`.
128pub mod confirm;
129
130/// Pure turn-outcome classification for the continuous-execution chat loop
131/// (native-testable). Hoisted out of `app::chat` so its guard tests run under
132/// `cargo test`. See `src/turn_flow.rs`.
133pub mod turn_flow;
134
135/// Pure state machine for the turn-stage micro-pipeline ("paying → thinking
136/// → streaming") shown inside a pending assistant turn (native-testable,
137/// same hoisting pattern as `turn_flow`). See `src/turn_stage.rs`.
138pub mod turn_stage;
139
140/// Pure lessons-blob merging + prompt-section composition for the agent
141/// LESSONS LOOP (native-testable). The browser `record_lesson` tool, the
142/// headless CLI `call`, and the proxy scheduler worker all fold its output
143/// into the system prompt. See `src/lessons.rs`.
144pub mod lessons;
145
146/// Pure subdomain-name validation (native-testable) — the single source of
147/// truth shared by the browser create tools and kept in sync with the
148/// on-chain `LocalharnessRegistryFacet._isValidName` rule. See `src/subdomain.rs`.
149pub mod subdomain;
150
151// Inline SVG QR-code generation for the app's share surfaces (device
152// pairing, publish share, `?invite=` links). Feature-gated like `app`
153// but NOT wasm-gated, so its unit test runs under a native
154// `cargo test --features browser-app` (the `turn_flow` hoisting pattern).
155#[cfg(feature = "browser-app")]
156mod qr;
157
158// Apex fresh-visitor landing markup — hoisted out of the wasm-gated `app/`
159// tree (the raster.rs/compose.rs pattern) so the SHIPPING markup also
160// renders natively: `cargo test --features browser-app landing_preview`
161// writes `target/landing-preview.html` for screenshot review. The `test`
162// arm keeps non-test native builds free of dead-code (only the wasm app
163// and the preview test consume it).
164#[cfg(all(feature = "browser-app", any(target_arch = "wasm32", test)))]
165mod landing;
166
167// The browser-resident IDE. Gated on the `browser-app` feature AND a
168// wasm target, so a native `cargo add localharness` never compiles it.
169#[cfg(all(feature = "browser-app", target_arch = "wasm32"))]
170mod app;
171
172// M6 spike: in-browser secp256k1 keypair via alloy's local signer.
173// Pure-compute (no HTTP, no JS deps), so it builds on every target.
174/// Secp256k1 keypair, BIP-39 mnemonics, and RLP encoding.
175#[cfg(feature = "wallet")]
176pub mod wallet;
177
178// JSON-RPC client for the `LocalharnessRegistry` diamond on Tempo
179// Moderato. Read-only views (`check_name`, `owner_of_name`,
180// `tba_of_name`, `list_owned_tokens`) work on every target; the
181// sponsored writes sign with a `k256` key (needs the wallet feature)
182// and use `tokio::time::sleep` on native / `setTimeout` on wasm to
183// poll the receipt. The diamond's address is baked in as
184// `registry::REGISTRY_ADDRESS`; the RPC URL is `registry::RPC_URL`.
185/// JSON-RPC client for the on-chain registry diamond.
186#[cfg(feature = "wallet")]
187pub mod registry;
188
189// Tempo Transaction encoder (tx type 0x76). Implements Tempo's native
190// account-abstraction tx format so users can pay fees in $LH instead
191// of native and so a project-controlled fee_payer can sponsor user
192// txs without users holding any balance. Wire format per
193// docs.tempo.xyz/protocol/transactions/spec-tempo-transaction.
194/// Tempo Transaction (tx type 0x76) encoder for native account abstraction.
195#[cfg(feature = "wallet")]
196pub mod tempo_tx;
197
198/// App-injected x402 payment-signing hook (lets the backend `call_agent`
199/// tool sign payments using the app-layer wallet).
200pub mod x402_hook;
201
202pub use agent::{Agent, AgentConfig, GeminiAgentConfig, MockAgentConfig};
203#[cfg(feature = "anthropic")]
204pub use agent::AnthropicAgentConfig;
205#[cfg(feature = "openai")]
206pub use agent::OpenAiAgentConfig;
207#[cfg(feature = "local")]
208pub use agent::LocalAgentConfig;
209pub use backends::gemini::{
210 decode_transcript_bytes, GeminiBackendConfig, GeminiConnection, GeminiConnectionStrategy,
211};
212pub use backends::mock::{
213 MockConnection, MockConnectionBuilder, MockConnectionStrategy, MockRunners, ScriptedTurn,
214};
215#[cfg(feature = "anthropic")]
216pub use backends::anthropic::{
217 AnthropicBackendConfig, AnthropicConnection, AnthropicConnectionStrategy, AnthropicRunners,
218};
219#[cfg(feature = "openai")]
220pub use backends::openai::{
221 OpenAiBackendConfig, OpenAiConnection, OpenAiConnectionStrategy, OpenAiRunners,
222};
223#[cfg(feature = "native")]
224pub use backends::mcp::{McpBridge, McpClient, McpToolDecl};
225pub use connections::{Connection, ConnectionStrategy};
226pub use content::{Content, Media, MediaKind, Part};
227pub use conversation::{ChatCursor, ChatResponse, Conversation};
228pub use error::{Error, Result};
229pub use filesystem::{DirEntry, EntryKind, Filesystem, Metadata, SharedFilesystem, WalkEntry};
230#[cfg(feature = "native")]
231pub use filesystem::NativeFilesystem;
232pub use hooks::{
233 HookContext, HookRunner, OnSessionEndHook, OnSessionStartHook, OperationContext,
234 PostToolCallHook, PostTurnHook, PreToolCallDecideHook, PreTurnHook, SessionContext,
235 TurnContext,
236};
237pub use policy::{
238 allow_all, deny_all, enforce, evaluate, is_path_in_workspace, secure_normalize_path,
239 workspace_only, AskUserHandler, Decision, Policy, Predicate,
240};
241pub use tools::{ClosureTool, Tool, ToolContext, ToolRunner};
242pub use triggers::{every, Trigger, TriggerContext, TriggerRunner};
243pub use types::{
244 BuiltinTool, CapabilitiesConfig, HookResult, Step, StepSource, StepStatus, StepTarget,
245 StepType, StreamChunk, SystemInstructions, ThinkingLevel, ToolCall, ToolResult,
246 TranscriptEntry, TranscriptRole, TriggerDelivery, UsageMetadata,
247};