Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .cargo-husky/hooks/pre-commit
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
#!/bin/sh
set -e
make ci
exec make ci
69 changes: 69 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Release

on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+*"

permissions:
contents: write

env:
CARGO_TERM_COLOR: always

jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Extract version from tag
id: version
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"

- name: Extract changelog
id: changelog
run: |
notes=$(awk '/^## \[${{ steps.version.outputs.version }}\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
{
echo "notes<<CHANGELOG_EOF"
echo "$notes"
echo "CHANGELOG_EOF"
} >> "$GITHUB_OUTPUT"

- name: Create GitHub Release
run: |
gh release create "$GITHUB_REF_NAME" \
--title "$GITHUB_REF_NAME" \
--notes "${{ steps.changelog.outputs.notes }}"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2

- name: Publish mixtape-anthropic-sdk
run: cargo publish -p mixtape-anthropic-sdk
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

- name: Publish mixtape-core
run: cargo publish -p mixtape-core
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

- name: Publish mixtape-tools
run: cargo publish -p mixtape-tools
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

- name: Publish mixtape-cli
run: cargo publish -p mixtape-cli
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

- name: Publish mixtape-server
run: cargo publish -p mixtape-server
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
/target
Cargo.lock

# Rust artifacts
**/*.rs.bk
*.pdb

# IDE
.idea/
.vscode/
Expand All @@ -15,3 +19,6 @@ Cargo.lock
*.profraw
*.profdata
lcov.info

# cargo-mutants artifacts
**/mutants.out*/
29 changes: 28 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- **mixtape-server**: HTTP server and AG-UI protocol support for the mixtape agent framework

## [0.2.1] - 2026-01-05

### Fixed

- Use i64 for rusqlite in session store for cross-platform compatibility
- Use i64 for rusqlite COUNT queries for cross-platform compatibility

## [0.2.0] - 2026-01-05

### Added

- Claude Sonnet 4.5 1M model support
- Tool grouping exports for filesystem and process modules
- `builder.add_trusted_tool()` convenience method
- Animated spinner for thinking indicator in CLI
- Improved tool execution event model for approval

### Changed

- Updated non-interactive examples to use `add_trusted_tool()`

## [0.1.1] - 2026-01-04

### Added
Expand All @@ -27,5 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- AWS SigV4 authenticated requests
- **mixtape-cli**: Session storage and REPL utilities for interactive agents

[Unreleased]: https://github.com/adlio/mixtape/compare/v0.1.1...HEAD
[Unreleased]: https://github.com/adlio/mixtape/compare/v0.2.1...HEAD
[0.2.1]: https://github.com/adlio/mixtape/compare/v0.2.0...v0.2.1
[0.2.0]: https://github.com/adlio/mixtape/compare/v0.1.1...v0.2.0
[0.1.1]: https://github.com/adlio/mixtape/releases/tag/v0.1.1
10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
[workspace]
members = ["mixtape-core", "mixtape-anthropic-sdk", "mixtape-tools", "mixtape-cli"]
members = ["mixtape-core", "mixtape-anthropic-sdk", "mixtape-tools", "mixtape-cli", "mixtape-server"]
resolver = "2"

[workspace.package]
version = "0.2.1"
edition = "2021"
license = "MIT"
homepage = "https://github.com/adlio/mixtape"
repository = "https://github.com/adlio/mixtape"

[workspace.dependencies]
Expand All @@ -14,14 +15,21 @@ mixtape-core = { version = "0.2.1", path = "./mixtape-core" }
mixtape-anthropic-sdk = { version = "0.2.1", path = "./mixtape-anthropic-sdk" }
mixtape-tools = { path = "./mixtape-tools" }
mixtape-cli = { path = "./mixtape-cli" }
mixtape-server = { path = "./mixtape-server" }

# Async runtime
tokio = { version = "1.41", features = ["full"] }
tokio-test = "0.4"
tokio-stream = "0.1"
async-trait = "0.1"
async-stream = "0.3"
futures = "0.3"

# HTTP Server
axum = { version = "0.7", features = ["macros"] }
tower = { version = "0.5", features = ["util"] }
tower-http = { version = "0.6", features = ["cors", "trace"] }

# Serialization
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
Expand Down
16 changes: 12 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

.DEFAULT_GOAL := help

.PHONY: help test coverage coverage-html build build-release clean fmt fmt-check lint check doc doc-check all ci ensure-tools
.PHONY: help test coverage coverage-html coverage-ci build build-release clean fmt fmt-check clippy clippy-fix lint check doc doc-check all ci ensure-tools

# Tool installation helpers
CARGO_NEXTEST := $(shell command -v cargo-nextest 2>/dev/null)
Expand Down Expand Up @@ -31,6 +31,9 @@ coverage: ensure-tools ## Show coverage summary in console
coverage-html: ensure-tools ## Generate HTML coverage report and open
cargo llvm-cov nextest --workspace --all-features --html --open

coverage-ci: ensure-tools ## Generate LCOV coverage for CI upload
cargo llvm-cov nextest --workspace --all-features --lcov --output-path lcov.info

build: ## Build debug
cargo build --workspace --all-targets --all-features

Expand All @@ -49,9 +52,14 @@ fmt: ## Format code
fmt-check: ## Check formatting
cargo fmt --all -- --check

lint: ## Run clippy
clippy: ## Run clippy with warnings as errors
cargo clippy --workspace --all-targets --all-features -- -D warnings

clippy-fix: ## Run clippy and auto-fix
cargo clippy --workspace --all-targets --all-features --fix --allow-dirty -- -D warnings

lint: clippy ## Alias for clippy

clean: ## Clean build artifacts
cargo clean

Expand All @@ -61,6 +69,6 @@ doc: ## Generate docs
doc-check: ## Check docs build without warnings
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps

all: ensure-tools fmt lint build test ## Format, lint, build, and test
all: ensure-tools fmt clippy build test ## Format, lint, build, and test

ci: ensure-tools fmt-check lint build doc-check test ## Check formatting, lint, build, docs, test (for CI/hooks)
ci: ensure-tools fmt-check clippy build doc-check test ## Check formatting, lint, build, docs, test (for CI/hooks)
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Mixtape

[![Crates.io](https://img.shields.io/crates/v/mixtape-core.svg)](https://crates.io/crates/mixtape-core)
[![Documentation](https://docs.rs/mixtape-core/badge.svg)](https://docs.rs/mixtape-core)
[![CI](https://github.com/adlio/mixtape/actions/workflows/ci.yml/badge.svg)](https://github.com/adlio/mixtape/actions/workflows/ci.yml)
[![Coverage](https://codecov.io/gh/adlio/mixtape/branch/main/graph/badge.svg)](https://codecov.io/gh/adlio/mixtape)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![codecov](https://codecov.io/gh/adlio/mixtape/graph/badge.svg)](https://codecov.io/gh/adlio/mixtape)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)

An agentic AI framework for Rust.

Expand Down
2 changes: 2 additions & 0 deletions mixtape-anthropic-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ version = "0.2.1"
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
description = "Minimal Anthropic API client for the mixtape agent framework"
documentation = "https://docs.rs/mixtape-anthropic-sdk"
readme = "README.md"
keywords = ["anthropic", "claude", "llm", "ai", "api"]
categories = ["api-bindings", "asynchronous"]
exclude = [".cargo-husky/", ".claude/", ".github/", ".idea/"]

[features]
default = []
Expand Down
2 changes: 2 additions & 0 deletions mixtape-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
description = "Session storage and REPL utilities for the mixtape agent framework"
documentation = "https://docs.rs/mixtape-cli"
readme = "README.md"
keywords = ["ai", "agents", "cli", "repl", "session"]
categories = ["command-line-utilities", "development-tools"]
exclude = [".cargo-husky/", ".claude/", ".github/", ".idea/"]

[dependencies]
mixtape-core = { workspace = true, features = ["session"] }
Expand Down
3 changes: 3 additions & 0 deletions mixtape-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@ version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
description = "An agentic AI framework for Rust"
documentation = "https://docs.rs/mixtape-core"
readme = "../README.md"
keywords = ["ai", "agents", "llm", "anthropic", "tools"]
categories = ["development-tools", "api-bindings", "asynchronous"]
exclude = [".cargo-husky/", ".claude/", ".github/", ".idea/"]

[features]
default = []
session = []
bedrock = ["dep:aws-config", "dep:aws-sdk-bedrockruntime", "dep:aws-smithy-types"]
anthropic = ["dep:mixtape-anthropic-sdk", "dep:base64"]
mcp = ["dep:rmcp", "dep:reqwest", "dep:shellexpand"]
test-utils = []

[dependencies]
async-stream.workspace = true
Expand Down
4 changes: 3 additions & 1 deletion mixtape-core/src/agent/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::AtomicU64;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::RwLock;
Expand Down Expand Up @@ -559,7 +560,8 @@ impl AgentBuilder {
system_prompt: self.system_prompt,
max_concurrent_tools: self.max_concurrent_tools,
tools: self.tools,
hooks: Arc::new(parking_lot::RwLock::new(Vec::new())),
hooks: Arc::new(parking_lot::RwLock::new(HashMap::new())),
next_hook_id: AtomicU64::new(0),
authorizer: Arc::new(RwLock::new(authorizer)),
authorization_timeout: self.authorization_timeout,
pending_authorizations: Arc::new(RwLock::new(HashMap::new())),
Expand Down
30 changes: 23 additions & 7 deletions mixtape-core/src/agent/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ pub use types::{
pub use types::SessionInfo;

use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::{mpsc, RwLock};

use crate::conversation::BoxedConversationManager;
use crate::events::{AgentEvent, AgentHook};
use crate::events::{AgentEvent, AgentHook, HookId};
use crate::permission::{AuthorizationResponse, ToolCallAuthorizer};
use crate::provider::ModelProvider;
use crate::tool::DynTool;
Expand Down Expand Up @@ -68,7 +69,8 @@ pub struct Agent {
pub(super) system_prompt: Option<String>,
pub(super) max_concurrent_tools: usize,
pub(super) tools: Vec<Box<dyn DynTool>>,
pub(super) hooks: Arc<parking_lot::RwLock<Vec<Arc<dyn AgentHook>>>>,
pub(super) hooks: Arc<parking_lot::RwLock<HashMap<HookId, Arc<dyn AgentHook>>>>,
pub(super) next_hook_id: AtomicU64,
/// Tool call authorizer (always present, uses MemoryGrantStore by default)
pub(super) authorizer: Arc<RwLock<ToolCallAuthorizer>>,
/// Timeout for authorization requests
Expand All @@ -95,7 +97,9 @@ pub struct Agent {
}

impl Agent {
/// Add an event hook to observe agent execution
/// Add an event hook to observe agent execution.
///
/// Returns a [`HookId`] that can be used to remove the hook later via [`remove_hook`](Self::remove_hook).
///
/// Hooks receive notifications about agent lifecycle, model calls,
/// and tool executions in real-time.
Expand All @@ -116,16 +120,28 @@ impl Agent {
/// .bedrock(ClaudeSonnet4_5)
/// .build()
/// .await?;
/// agent.add_hook(Logger);
/// let hook_id = agent.add_hook(Logger);
///
/// // Later, remove the hook
/// agent.remove_hook(hook_id);
/// ```
pub fn add_hook(&self, hook: impl AgentHook + 'static) {
self.hooks.write().push(Arc::new(hook));
pub fn add_hook(&self, hook: impl AgentHook + 'static) -> HookId {
let id = HookId(self.next_hook_id.fetch_add(1, Ordering::SeqCst));
self.hooks.write().insert(id, Arc::new(hook));
id
}

/// Remove a previously registered hook.
///
/// Returns `true` if the hook was found and removed, `false` otherwise.
pub fn remove_hook(&self, id: HookId) -> bool {
self.hooks.write().remove(&id).is_some()
}

/// Emit an event to all registered hooks
pub(crate) fn emit_event(&self, event: AgentEvent) {
let hooks = self.hooks.read();
for hook in hooks.iter() {
for hook in hooks.values() {
hook.on_event(&event);
}
}
Expand Down
6 changes: 6 additions & 0 deletions mixtape-core/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,12 @@ where
}
}

/// Unique identifier for a registered hook.
///
/// Used to remove hooks via [`crate::Agent::remove_hook`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HookId(pub(crate) u64);

#[cfg(test)]
mod tests {
use super::*;
Expand Down
5 changes: 4 additions & 1 deletion mixtape-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ pub mod mcp;
#[cfg(feature = "session")]
pub mod session;

#[cfg(feature = "test-utils")]
pub mod test_utils;

pub use agent::{
Agent, AgentBuilder, AgentError, AgentResponse, ContextConfig, ContextError, ContextLoadResult,
ContextSource, PermissionError, TokenUsageStats, ToolCallInfo, ToolInfo,
Expand All @@ -154,7 +157,7 @@ pub use conversation::{
TokenEstimator,
};
pub use error::{Error, Result};
pub use events::{AgentEvent, AgentHook, TokenUsage};
pub use events::{AgentEvent, AgentHook, HookId, TokenUsage};

pub use model::{
AnthropicModel, BedrockModel, InferenceProfile, Model, ModelRequest, ModelResponse,
Expand Down
Loading