Skip to content
Merged
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: 2 additions & 1 deletion .agents/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ This crate holds only the *music* part of the agent. The ACP server, session/tur
- `src/tools/`: `MusicTools` is one `Toolset` exposing six `theory_*` tools, `analyze_audio` and `list_separations`. All are read-only and never prompt for approval. New tools need an entry in `definitions()`, `handles()` and `describe()`.
- `src/theory/` (pitch, interval, scale, chord, key, tempo) and `src/audio/` (symphonia decode, loudness/tempo/key analysis) are the pure computation behind the tools. The design principle is that the model calls these instead of recalling answers, so results must be exact (correct enharmonic spelling, e.g. C# major has E# and B#).
- `src/lib.rs` vs `src/main.rs`: the crate is both a library (`Profile` for hosts that embed the agent over an in-memory channel with `ed_acp::serve`) and the `splitfire-agent` binary. The `cli` feature (default) pulls in `ed-acp-tui` and the MCP test stub; build with `--no-default-features` to check the embeddable library alone.
- `src/embed/` (behind the off-by-default `embed` feature) is the SplitFire-only glue a Tauri host needs beside the published `ed-acp-tauri` crate: `embed::fetch_key` (SplitFire's `/agent/key`), `embed::splitfire_mcp` (the SplitFire API's MCP server), and `embed::trim` (drop `run_command` and swap the stems paragraph on platforms that can't start processes). It has no `tauri` dependency — the app identifier comes in as a `&str`. The `NO_STEMS_START`/`NO_STEMS_END` markers `embed::trim::replace_no_stems` keys off are defined in `src/prompt.rs`, which writes the paragraph, so the two can't drift.
- The binary has three modes: TUI when stdin is a terminal; ACP over stdio with `--acp` or when stdin is not a terminal; plus `--setup` / `--list-models`. The TUI is itself an ACP client that spawns `splitfire-agent --acp` as a subprocess.

## Tests
Expand All @@ -35,7 +36,7 @@ Integration tests run the real binary against a scripted mock model and a test M
## Conventions

- Edition 2024. Per `.agents/skills/rust-coding-standard`: no `.unwrap()`/`.expect()` outside `#[cfg(test)]`, don't silently discard errors with `let _ =`.
- `registry/` holds the ACP registry submission (`agent.json`) (its `version` is currently 1.0.0, behind `Cargo.toml`'s 1.1.0, so check it when releasing).
- `registry/` holds the ACP registry submission (`agent.json`); keep its `version` in step with `Cargo.toml` when releasing (both at 1.2.0).

## Remembered notes

Expand Down
103 changes: 81 additions & 22 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 14 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "splitfire-agent"
version = "1.1.1"
version = "1.2.0"
edition = "2024"
license = "Apache-2.0"
description = "Music-domain ACP agent: theory, production, industry, culture and live shows, backed by any OpenAI-compatible chat completions API"
Expand All @@ -11,6 +11,10 @@ default = ["cli"]
# The `splitfire-agent` binary: its terminal UI, plus the MCP stub the integration tests run.
# A host embedding the library turns this off.
cli = ["dep:ed-acp-tui", "ed-mcp/test-server"]
# The `embed` module: the SplitFire-only pieces a Tauri host needs beside `ed-acp-tauri`
# (key fetch, the SplitFire MCP server, the iOS prompt/toolset trimming). Off by default so
# the plain library and the binary don't pull in `reqwest`.
embed = ["dep:reqwest"]

[[bin]]
name = "splitfire-agent"
Expand All @@ -24,15 +28,20 @@ required-features = ["cli"]

[dependencies]
# Onde Agent Platform: the ACP server, terminal UI and MCP layer, from the onde-ed workspace.
ed-acp = { version = "1.2.2" }
ed-acp-tui = { version = "1.2.2", optional = true }
ed-mcp = { version = "1.2.2" }
ed-acp = { version = "1.3.0" }
ed-acp-tui = { version = "1.3.0", optional = true }
ed-mcp = { version = "1.3.0" }
agent-client-protocol = "2.2"
anyhow = "1.0.104"
async-trait = "0.1"
directories = "6.0.0"
ebur128 = "0.1.10"
realfft = "3.5.0"
# Only for `embed::fetch_key` and `embed::splitfire_mcp`, gated behind the `embed` feature.
reqwest = { version = "0.12", default-features = false, features = [
"json",
"rustls-tls-native-roots",
], optional = true }
serde = { version = "1.0.229", features = ["derive"] }
serde_json = "1.0.151"
symphonia = { version = "0.6.1", features = ["mp3", "flac", "ogg", "vorbis", "wav", "pcm", "aac", "isomp4", "aiff", "alac", "mkv"] }
Expand All @@ -41,6 +50,6 @@ tracing = "0.1.44"
uuid = { version = "1.27.0", features = ["v4"] }

[dev-dependencies]
ed-acp-testkit = { version = "1.2.2" }
ed-acp-testkit = { version = "1.3.0" }
base64 = "0.23.1"
hound = "3.5.1"
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,34 @@ as `mcp__<server>__<tool>`, ask for permission unless the server marks them read
progress into the tool call, and are cancelled when you cancel the turn. A server that fails to
start is skipped.

## Embedding

A sandboxed app (an App Store build) can't launch the agent as a subprocess, so it runs it
in-process: it serves [`SplitFire`] with `ed_acp::serve` over an in-memory channel and talks to
it as a regular ACP client. The app-neutral half of that — the client, sessions, permissions,
the key store — lives in the published [`ed-acp-tauri`](https://crates.io/crates/ed-acp-tauri)
crate. The SplitFire-only pieces live here, behind the off-by-default `embed` feature:

```toml
splitfire-agent = { version = "1.2", default-features = false, features = ["embed"] }
ed-acp-tauri = "1.3"
ed-acp = "1.3"
```

`splitfire_agent::embed` has no `tauri` dependency; anything that needs the app's identifier
takes it as a `&str`:

- `embed::fetch_key(url, access_token, app_identifier)` asks the SplitFire API for the signed-in
user's Onde key, rejecting with `SIGN_IN_REQUIRED` or `UNAVAILABLE`.
- `embed::splitfire_mcp(url, access_token, app_identifier)` builds the SplitFire API's MCP server
for a session (nothing without a token).
- `embed::trim` drops `run_command` from the toolsets (`without_commands`) and swaps the stems
paragraph (`replace_no_stems`) for a profile that runs where it can't start processes, e.g.
iOS.

All embedding apps must resolve a single `ed-acp` 1.3.x, so an `Arc<dyn Profile>` from this crate
matches the one `ed-acp-tauri` expects.

## Install and set up

```sh
Expand Down
2 changes: 1 addition & 1 deletion registry/agent.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "splitfire-agent",
"name": "SplitFire AI",
"version": "1.1.1",
"version": "1.2.0",
"description": "Music assistant for musicians, producers and engineers: exact music theory tools (scales, chords, keys, tempo maths), audio analysis (loudness, tempo and key estimates), optional stem separation, and project file tools.",
"repository": "https://github.com/splitfireai/splitfire-agent",
"authors": [
Expand Down
Loading
Loading