Skip to content

SuperInstance/clark-agent

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

clark-agent

A small, typed, hookable agent loop — provider-agnostic, sandbox-agnostic, tooling-agnostic.

What This Gives You

  • Typed agent loopcontext → LLM → tool batch → results → repeat, with clean termination semantics
  • Plugin system — 6 capability traits (BeforeToolCall, AfterToolCall, ContextTransform, EventObserver, SteeringSource, FollowUpSource) for cross-cutting extension
  • Stream abstractionStreamFn trait: swap LLM providers, inject fixtures, replay scenarios, proxy remotely
  • Tool registryAgentTool trait + ToolRegistry; tools own their schema, validation, and execution
  • Builder APIAgentBuilder composes stream, tools, plugins, and config in one call
  • CancellationCancellationToken for graceful shutdown

Shape

context → LLM (StreamFn) → tool batch → results appended → repeat

Termination is a tool decision (ToolResult::terminate = true, unanimous across the batch). The runtime owns execution and event emission; tools own semantics; plugins own cross-cutting extension.

Quick Start

use std::sync::Arc;
use clark_agent::{AgentBuilder, AgentContext, AgentMessage, ToolRegistry, UserContent};
use tokio_util::sync::CancellationToken;

let registry = ToolRegistry::new()
    .with(Arc::new(my_shell_tool()))
    .with(Arc::new(my_file_tool()));

let config = AgentBuilder::new()
    .stream(Arc::new(my_provider()))
    .tools(registry)
    .before_tool_call(my_security_gate())
    .after_tool_call(my_repeat_detector())
    .context_transform(clark_agent::budget::TokenBudget::default())
    .max_iterations(50)
    .build()?;

let outcome = clark_agent::run(
    vec![AgentMessage::User {
        content: UserContent::Text("List files in /tmp".into()),
        timestamp: None,
    }],
    AgentContext::new("You are a helpful assistant."),
    &config,
    CancellationToken::new(),
).await;

Layers

Layer Purpose
types AgentMessage, content blocks, StopReason
event AgentEvent enum + EventSink trait (ChannelSink, FanOutSink, NoopSink)
tool AgentTool trait + ToolRegistry
stream StreamFn trait — swappable LLM transport
plugin Plugin + 6 capability traits
config LoopConfig + AgentBuilder
run run / run_continue — canonical loop
exec Parallel + sequential tool dispatch, hook plumbing
budget Token-budget context transform

Plugin Extension Points

Trait When it runs
BeforeToolCall After validation, before tool.execute. May block.
AfterToolCall After tool.execute. May override result, vote terminate.
ContextTransform Before each LLM call. Window management, redaction.
EventObserver On every AgentEvent. Logging, telemetry, persistence.
SteeringSource Between batches. Inject extra messages mid-run.
FollowUpSource After natural stop. Re-start if more is queued.

Examples

# Minimal agent with fixture provider
cargo run --example minimal

# Tool call demo
cargo run --example tool_call

How It Fits

Testing

26 integration tests covering agent loop, tool dispatch, plugin hooks, graceful turn limits, sandbox isolation, max token recovery, sibling abort, and message persistence round-trips.

cargo test

Installation

[dependencies]
clark-agent = { git = "https://github.com/SuperInstance/clark-agent" }
git clone https://github.com/SuperInstance/clark-agent.git
cd clark-agent
cargo build

License

MIT

Part of the SuperInstance OpenConstruct ecosystem.

About

A small, typed, hookable agent loop. Provider-agnostic, sandbox-agnostic, tooling-agnostic. Battle tested on clarkchat.com

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Rust 100.0%