Skip to main content

supercode_harness/
lib.rs

1//! # Volter Harness
2//!
3//! A lightweight, fully-customizable AI coding-agent SDK in Rust.
4//!
5//! `supercode` is a native agent loop — it talks directly to any model through
6//! [OpenRouter](https://openrouter.ai) (or any other OpenAI-compatible endpoint),
7//! drives a configurable set of tools, and is designed to be a superset of what
8//! tools like Claude Code and Codex can do: every prompt, every tool description,
9//! and every tool's on/off state is yours to control.
10//!
11//! ## Quick start
12//!
13//! ```no_run
14//! use supercode_harness::{Agent, Config};
15//!
16//! # async fn run() -> supercode_harness::Result<()> {
17//! // Reads OPENROUTER_API_KEY from the environment by default.
18//! let config = Config::builder()
19//!     .model("anthropic/claude-opus-4-8")
20//!     .system_prompt("You are a terse, expert pair programmer.")
21//!     .build();
22//!
23//! let mut agent = Agent::new(config)?;
24//! let reply = agent.send("List the files in the current directory.").await?;
25//! println!("{reply}");
26//! # Ok(())
27//! # }
28//! ```
29//!
30//! ## Design
31//!
32//! - [`Config`] — the single knob box: model, endpoint, credentials, sampling,
33//!   the system prompt, and per-tool overrides (enable/disable + custom
34//!   descriptions).
35//! - [`Provider`] — the model transport. [`OpenAiProvider`] speaks the
36//!   OpenAI chat-completions wire format and defaults to OpenRouter, so it
37//!   reaches Claude, GPT, Gemini, Llama, and anything else OpenRouter exposes.
38//! - [`Tool`] / [`ToolRegistry`] — the capability surface. Built-ins cover
39//!   file read/write/edit, directory listing, glob, content search, and shell
40//!   execution. Register your own to extend it.
41//! - [`Agent`] — the loop that ties it together: it streams a turn, runs any
42//!   tool calls the model requests, feeds results back, and repeats until the
43//!   model produces a final answer.
44
45#![warn(missing_docs)]
46
47#[cfg(feature = "adapter-acp")]
48/// The supercode ontology (`docs/ONTOLOGY.md`): the one model under sessions and the
49/// orchestration.
50pub use supercode_interchange::ontology;
51/// The orchestration piece of the ontology: a harness's operational home as one typed value.
52pub use supercode_interchange::orchestration;
53
54#[cfg(feature = "adapter-acp")]
55pub mod acp_frontend;
56#[cfg(feature = "adapter-acp")]
57pub mod acp_server;
58mod agent;
59pub mod agent_package;
60pub mod approvals;
61pub mod audit;
62pub mod channels;
63pub mod checkpoint;
64pub mod claude_compat;
65pub mod claude_peer;
66pub mod claude_relay;
67pub mod claude_runtime_state;
68pub mod codex_peer;
69mod config;
70pub mod config_schema;
71pub mod configfile;
72pub mod context_injection;
73mod error;
74pub mod formatters;
75#[cfg(feature = "adapter-api")]
76pub mod frontend;
77#[cfg(not(feature = "adapter-api"))]
78#[allow(dead_code)]
79mod frontend;
80#[allow(missing_docs)]
81mod frontend_contract_generated;
82pub mod git_metadata;
83pub mod goals;
84pub mod harness_auth;
85pub mod harness_command;
86#[cfg(feature = "adapter-api")]
87pub mod harness_service;
88#[cfg(not(feature = "adapter-api"))]
89#[allow(dead_code)]
90mod harness_service;
91pub mod hermes_import;
92pub mod human_export;
93pub mod interop_settings;
94pub mod jobs;
95pub mod jobs_control;
96pub mod jobs_notepad;
97pub mod live_runtime;
98pub mod lsp;
99#[cfg(feature = "adapter-api")]
100pub mod mail_agent;
101#[cfg(feature = "adapter-api")]
102pub mod mail_mcp;
103pub mod mail_question;
104#[cfg(feature = "adapter-api")]
105pub mod mail_route;
106#[cfg(feature = "adapter-api")]
107pub mod mail_send;
108pub mod mail_transcript;
109#[cfg(feature = "adapter-api")]
110pub mod mail_watch;
111pub mod mailbox;
112pub mod mcp;
113pub mod mcp_oauth;
114pub mod memory;
115pub mod model_catalog;
116pub mod model_change;
117pub mod modules;
118mod native_materialize;
119pub mod orchestration_doors;
120pub mod orchestrator;
121pub mod orchestrator_door;
122pub mod output_style;
123pub mod parity;
124pub mod path_rules;
125pub mod permissions;
126pub mod plugins;
127pub mod presets;
128pub mod pricing;
129pub mod pricing_ref;
130pub mod profiles;
131pub mod profiles_control;
132mod provider;
133pub mod reduce;
134#[cfg(feature = "adapter-api")]
135pub mod relay_endpoint;
136mod residue_store;
137pub mod routes;
138pub mod runs;
139pub mod runtime;
140pub mod runtime_lease;
141#[cfg(feature = "adapter-api")]
142pub mod runtime_mail;
143#[cfg(feature = "adapter-api")]
144pub mod runtime_registry;
145mod safe_path;
146pub mod sandbox;
147pub mod schema;
148pub mod sdk;
149#[cfg(feature = "adapter-api")]
150pub mod server;
151#[cfg(not(feature = "adapter-api"))]
152#[allow(dead_code, unused_imports)]
153mod server;
154pub mod session_activity;
155pub mod session_index;
156pub mod session_journal;
157pub mod session_title;
158pub mod sessions_control;
159pub mod skills;
160pub mod skills_control;
161pub mod slow_log;
162pub mod startup_prompts;
163pub mod store;
164pub mod subagents;
165pub mod support;
166pub mod teams;
167pub mod tools;
168pub mod triggers;
169pub mod trust;
170pub mod tui;
171pub mod turn_record;
172pub mod usage_log;
173pub mod workflow_doors;
174
175#[cfg(feature = "adapter-acp")]
176pub use acp_frontend::{AcpFrontendCheckpoint, AcpFrontendConnectOptions, AcpFrontendRuntime};
177pub use agent::{Agent, ContextUsage};
178pub use approvals::{
179    approval_harnesses, lists_approvals, plan_reply, ApprovalChoice, ApprovalDecision,
180    ApprovalDoor, ApprovalKind, ApprovalOption, ApprovalRegistry, ApprovalResolution,
181    ApprovalResolveError, ApprovalRow, ApprovalStatus, ApprovalsQuery, ApprovalsResolveParams,
182};
183pub use channels::{
184    channel_status, list_channels, ChannelError, ChannelRow, ChannelStatus, CHANNELS_SCHEMA,
185    CHANNEL_HARNESSES,
186};
187pub use claude_peer::{
188    read_claude_peer_settings, read_registry as read_claude_peer_registry, resolve_live_session,
189    update_claude_peer_settings, user_settings_path as claude_user_settings_path,
190    write_claude_peer_settings, ClaudeCrossSessionInbound, ClaudePeerRefusal,
191    ClaudePeerRefusalError, ClaudePeerSession, ClaudePeerSettings, ClaudePeerSettingsError,
192    ClaudePeerStatus,
193};
194pub use claude_runtime_state::{
195    ClaudeBackgroundChild, ClaudeBackgroundState, ClaudeCronJob, ClaudeQueueState,
196    ClaudeRuntimeManifest, ClaudeRuntimePosture, ClaudeRuntimeResidue, ClaudeWakeup,
197    CLAUDE_RUNTIME_MANIFEST_VERSION,
198};
199pub use config::{
200    project_root_for, ApprovalPolicy, CachePlan, Config, ConfigBuilder, ConfigFile, ConfigProfile,
201    ContextInjectionBlock, HookDecision, LifecycleEvent, LifecycleHook, PreToolOutcome,
202    SteeringMode, StopGateHook, ToolAdvertising, ToolOverride, ToolOverrideProfile,
203    DEFAULT_SYSTEM_PROMPT,
204};
205pub use configfile::HarnessConfig;
206pub use interop_settings::{
207    configure_harness_interop_settings, inspect_harness_interop_settings, HarnessAdvisorySeverity,
208    HarnessInteropAdvisory, HarnessInteropControl, HarnessInteropSettingsError,
209    HarnessInteropSettingsReport, HarnessSettingChange, HarnessSettingChoice,
210    HarnessSettingRecommendation, HarnessSettingScope, CLAUDE_CROSS_SESSION_INBOUND_KEY,
211    HARNESS_INTEROP_SETTINGS_SCHEMA,
212};
213pub use jobs::{
214    get_job, list_jobs, supports_jobs, JobDeliver, JobPayload, JobSchedule, JobScope, JobSource,
215    JobsListing, JobsQuery, ScheduledJob, CLAUDE_SESSION_SCAN_LIMIT, JOB_HARNESSES,
216};
217pub use jobs_control::{
218    harness_program, mutate, supports_job_control, JobControlError, JobDeliverSpec, JobMutation,
219    JobMutationOutcome, JobPayloadSpec, JobScheduleSpec, JobVerb, CONTROLLED_JOB_HARNESSES,
220};
221pub use jobs_notepad::{JobNotepad, JobNotepadEntry, JobNotepadRequest};
222pub use live_runtime::{
223    discover_live_runtime, find_live_runtime, forget_live_runtime, list_live_runtimes,
224    register_live_runtime, register_live_runtime_with_metadata, resolve_live_runtime,
225    LiveRuntimeEndpoint, LiveRuntimeMetadata, LiveRuntimeReceiptError, LiveRuntimeRecord,
226    LiveRuntimeRegistration, LiveRuntimeSource, LiveRuntimeSupervisor, ResolvedLiveRuntime,
227};
228pub use memory::{
229    search_memory, show_memory, supports_memory, MemoryDocument, MemoryError, MemoryMatch,
230    MemoryQuery, MemoryScope, MemorySearchQuery, MEMORY_HARNESSES, MEMORY_SCHEMA,
231};
232pub use modules::{ModuleActivation, ModuleId};
233pub use orchestrator::{
234    clear_lease, daemon_entry, install_service, live_lease, lock_path, read_lease, resolve_program,
235    service_name, service_status, service_unit, uninstall_service, write_lease, write_unit, Lease,
236    OrchestratorError, ServiceState, ServiceUnit, DAEMON_ENTRY, LOCK_FILE, SERVICE_DIR,
237    SERVICE_NAME,
238};
239pub use orchestrator_door::{
240    daemon_is_live, socket_path, Door, DoorAnswer, DoorError, NODE_BIN_ENV, SOCKET_FILE,
241};
242pub use profiles::{
243    get_profile, list_profiles, ProfileError, ProfileKind, ProfileRow, HERMES_DEFAULT_PROFILE,
244    PROFILES_SCHEMA, PROFILE_HARNESSES,
245};
246pub use profiles_control::{
247    supports_profile_control, ProfileControlError, ProfileMutation, ProfileMutationOutcome,
248    ProfileVerb, CONTROLLED_PROFILE_HARNESSES,
249};
250pub use routes::{list_routes, RouteError, RouteMatch, RouteRow, ROUTES_SCHEMA, ROUTE_HARNESSES};
251pub use runs::{
252    get_run, list_runs, supports_runs, HarnessRun, RunDelivery, RunSource, RunsListing, RunsQuery,
253    RUN_HARNESSES,
254};
255pub use sessions_control::{
256    controlled_verbs, supports_session_control, SessionControlError, SessionDoor, SessionMutation,
257    SessionMutationOutcome, SessionVerb, CONTROLLED_SESSION_HARNESSES,
258};
259pub use supercode_interchange::catalog::{
260    orchestrator_profile_dirs, DiscoveryPage, DiscoveryQuery, HarnessCatalog, HarnessHomes,
261    HarnessId, SessionDescriptor, SessionLocator, StorageLocator,
262};
263pub use teams::{
264    connector_service_binary as teams_connector_service_binary,
265    connector_service_name as teams_connector_service_name,
266    connector_service_status as teams_connector_service_status,
267    connector_service_unit as teams_connector_service_unit,
268    install_connector_service as install_teams_connector_service,
269    install_service as install_teams_service, service_status as teams_service_status,
270    service_unit as teams_service_unit, teams_entry, teams_home,
271    uninstall_connector_service as uninstall_teams_connector_service,
272    uninstall_service as uninstall_teams_service, write_unit as write_teams_unit, TeamsError,
273    SERVICE_DIR as TEAMS_SERVICE_DIR, SERVICE_NAME as TEAMS_SERVICE_NAME, TEAMS_ENTRY,
274    TEAMS_PACKAGE,
275};
276pub use triggers::{
277    list_triggers, TriggerError, TriggerKind, TriggerRow, TRIGGERS_SCHEMA, TRIGGER_HARNESSES,
278};
279
280/// Format an agent's final reply for output. `json` wraps it as
281/// `{"result": "..."}`; otherwise the reply is returned as-is. The
282/// stream-json form is the live [`AgentEvent`] stream via an [`EventSink`].
283pub fn format_reply(reply: &str, json: bool) -> String {
284    if json {
285        serde_json::json!({ "result": reply }).to_string()
286    } else {
287        reply.to_string()
288    }
289}
290pub use error::{Error, Result};
291#[cfg(feature = "adapter-api")]
292pub use frontend::HttpFrontendRuntime;
293pub use frontend::{
294    FrontendActions, FrontendApprovalDecision, FrontendAttachSnapshot, FrontendAttachment,
295    FrontendCommandDescriptor, FrontendConnectionState, FrontendDisplayCapabilities,
296    FrontendElicitationAction, FrontendEvent, FrontendOperationDescriptor,
297    FrontendOperationInvocation, FrontendOperationKind, FrontendOperationResult, FrontendRequest,
298    FrontendRequestKind, FrontendResponse, FrontendRuntime, FrontendRuntimeDescriptor,
299    FrontendRuntimeError, FrontendRuntimeMetadata, FrontendTurnState, FRONTEND_REPLAY_CAPACITY,
300    FRONTEND_RUNTIME_SCHEMA_VERSION,
301};
302pub use frontend_contract_generated::{
303    FrontendFacadeMethod, FrontendFacadeTransport, GeneratedFrontendClient,
304};
305pub use harness_auth::{
306    harness_authentication_methods, harness_authentication_plan, inspect_harness_authentication,
307    HarnessAuthenticationEnvironment, HarnessAuthenticationError, HarnessAuthenticationInteraction,
308    HarnessAuthenticationLaunch, HarnessAuthenticationMethod, HarnessAuthenticationMethodId,
309    HarnessAuthenticationPlan, HarnessAuthenticationReport, HarnessAuthenticationState,
310    HarnessBrowserBehavior, HARNESS_AUTHENTICATION_SCHEMA,
311};
312pub use harness_service::{
313    DetachedAnswer, DetachedCall, HarnessSessionService, OpenedRuntime, ReturnedRuntime,
314    RuntimeOpen, DETACHED_CALL_DEADLINE, DETACHED_METHODS, HARNESS_SERVICE_VERSION,
315    RUNTIME_CONTROL_DEADLINE, RUNTIME_EVENT_METHOD, RUNTIME_OPEN_DEADLINE, RUNTIME_OPEN_METHODS,
316    SESSION_ACTIVITY_EVENT_METHOD, SESSION_DISCOVER_DEADLINE, SESSION_EVENT_METHOD,
317    SESSION_INDEX_EVENT_METHOD,
318};
319pub use provider::{
320    model_context_limit, ChatRequest, OpenAiProvider, PromptTokensDetails, Provider, RetryLog,
321    RetryNotice, ToolSchema, Usage, SERVED_MODEL_KEY, UNKNOWN_MODEL_CONTEXT_FLOOR,
322};
323#[cfg(feature = "adapter-api")]
324pub use runtime::SupercodeHttpRuntimeBackend;
325pub use runtime::{
326    AcpRuntimeBackend, BearerToken, ClaudeCodeRuntimeBackend, CodexRuntimeBackend, HarnessEvent,
327    McpServerLaunch, OpenCodeRuntimeBackend, PiRuntimeBackend, ResolvedRuntimeConnection,
328    RuntimeAttachRequest, RuntimeBackend, RuntimeCapabilities, RuntimeConnectLaunch,
329    RuntimeConnection, RuntimeEndpoint, RuntimeHandle, RuntimeInput, RuntimeLaunch,
330    RuntimeStartRequest,
331};
332pub use runtime_lease::{
333    CoordinatedRuntime, CoordinatedRuntimeClient, RuntimeAuthorization, RuntimeClientId,
334    RuntimeControllerLease, RuntimeLeaseCoordinator, RuntimeLeaseError, RuntimeLeaseSnapshot,
335    RuntimeObserverLease, RuntimePermission, DEFAULT_RUNTIME_LEASE_TTL_MS,
336};
337#[cfg(feature = "adapter-api")]
338pub use runtime_registry::{
339    LocalRuntimeRegistry, RuntimeRegistryEntry, RuntimeRegistryEvent, RuntimeRegistryOwner,
340    RuntimeRegistryQuery, RuntimeRegistryState, RuntimeRegistryWatch,
341};
342pub use sandbox::{landlock_available, netns_available, SandboxEnvPolicy, SandboxEscalation};
343pub use sdk::{
344    create_agent, discover_session_page, discover_sessions, load_session, load_session_path,
345    resume_agent, show_model_input, submit_agent, submit_agent_with_images, RuntimeSubmitError,
346    SdkAgent, SdkCapabilities, SdkError, SdkErrorCode, SdkEvent, SdkOperation, SdkPromptSource,
347    SdkRequest, SdkRuntime, SdkRuntimeEvent, SdkService, SDK_SCHEMA_VERSION,
348};
349pub use server::RpcEngine;
350pub use session_activity::{
351    SessionActivity, SessionActivityEvidence, SessionPresence, SessionTurnState,
352};
353pub use session_index::{SessionIndexChange, SessionIndexDelta, SessionIndexKey};
354pub use skills::{
355    declared_skill_name, list_skills, skill_roots, writable_skill_roots, SkillHomes, SkillRow,
356    SkillScope, SkillsQuery, SKILL_HARNESSES,
357};
358pub use skills_control::{
359    mutate_skill, supports_skill_control, SkillControlError, SkillMutation, SkillMutationOutcome,
360    SkillVerb, CONTROLLED_SKILL_HARNESSES,
361};
362pub use store::{SessionInfo, SessionStore};
363pub use supercode_interchange::session::{
364    CrossSurface, OrchestrationNouns, Recurrence, Session, SessionFormat, SessionMeta,
365    SessionSource, SurfaceKey, Trigger, WorkspaceKind, WorkspaceRef,
366};
367pub use supercode_interchange::watch::{SessionFollower, SessionSnapshotReason, SessionWatchEvent};
368pub use supercode_interchange::{
369    core_messages, measure_fidelity, messages_equal, messages_equal_multimodal, replay_eligible,
370    Fidelity, FidelityMetric, FidelityResidue,
371};
372pub use supercode_interchange::{
373    is_tool_error, mark_tool_error, mark_tool_outcome_unknown, tool_outcome, ChatMessage,
374    FunctionCall, Role, ToolCall, ToolOutcome, TOOL_ERROR_METADATA_KEY,
375    TOOL_OUTCOME_UNKNOWN_METADATA_KEY,
376};
377pub use supercode_runtime::{AgentEvent, EventSink};
378pub use support::{
379    harness_support, harness_support_registry, HarnessSupportDescriptor, ImplementationKind,
380    NativeSupport, RuntimeSupport, SupportRegistryReport, SUPPORT_REGISTRY_SCHEMA,
381};
382pub use tools::{
383    shell_sandbox_unenforceable, SandboxPolicy, SchemaTier, Tool, ToolContext, ToolRegistry,
384    WriteObserver,
385};
386
387/// The checkout this binary was built from, when it runs as a build of it. An installed binary (one inside a
388/// `node_modules`, where npm and a local install place it) never reads it: on the machine that built it the checkout
389/// still exists, and its source tree would resolve its packages against whatever `node_modules` sits above it.
390pub fn build_checkout() -> Option<std::path::PathBuf> {
391    let exe = std::env::current_exe().ok()?;
392    if exe
393        .ancestors()
394        .any(|dir| dir.file_name().is_some_and(|name| name == "node_modules"))
395    {
396        return None;
397    }
398    std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
399        .ancestors()
400        .nth(2)
401        .map(std::path::Path::to_path_buf)
402}