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
21 changes: 21 additions & 0 deletions .agents/skills/release/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ explicitly:
ed-agent = { version = "X.Y.Z", path = "crates/ed-agent" }
ed-mcp = { version = "X.Y.Z", path = "crates/ed-mcp" }
ed-acp = { version = "X.Y.Z", path = "crates/ed-acp" }
ed-acp-tauri = { version = "X.Y.Z", path = "crates/ed-acp-tauri" }
ed-acp-tui = { version = "X.Y.Z", path = "crates/ed-acp-tui" }
ed-acp-testkit = { version = "X.Y.Z", path = "crates/ed-acp-testkit" }
ed-onde-account = { version = "X.Y.Z", path = "crates/ed-onde-account" }
ed-claude = { version = "X.Y.Z", path = "crates/ed-claude" }
```

Bump all of these together. The crate manifests under `crates/*/Cargo.toml`
Expand Down Expand Up @@ -146,6 +148,25 @@ here — this is the safety net for step 6.
release URL + checksum, copies the generated `ed_agent_ffi.swift`, commits,
pushes to `ed-swift` `main`, and tags it `vX.Y.Z`.

## Adding a new crate

crates.io only allows a trusted publisher on a crate that already exists
(RFC 3691), so a new crate's first version goes up by hand, before the tag that
would publish it:

1. Create a token with scope `publish-new` only, crate pattern set to the new
crate, expiry 1 day. Keep it in `CARGO_REGISTRY_TOKEN` for one command; never
write it to `~/.cargo/credentials.toml`.
2. Publish the first version. If the crate needs an API that the last published
`ed-acp` lacks, it won't compile against the registry, so publish a
placeholder `0.0.1` (empty `lib.rs`, same license and metadata) from a
scratch directory. A caret requirement such as `"1.4"` never selects it.
3. Revoke the token.
4. On the crate's Settings → Trusted Publishing, add a GitHub publisher: owner
`ondeinference`, repository `ed`, workflow `release-sdk-rust.yml`, and the
same environment as `ed-acp`. Check the owners match `ed-acp`'s.
5. Tag as usual. The real crate then publishes through OIDC.

## Manual / recovery runs

Both workflows have a `workflow_dispatch` with a `publish` boolean and a
Expand Down
20 changes: 10 additions & 10 deletions Cargo.lock

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

18 changes: 9 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ members = [
# Shared across every crate in the workspace so a release moves one number.
# `sync-version` tooling (see the Makefile) reads these.
[workspace.package]
version = "1.3.5"
version = "1.4.0"
edition = "2021"
rust-version = "1.94"
license = "MIT OR Apache-2.0"
Expand Down Expand Up @@ -60,14 +60,14 @@ directories = "6"
base64 = "0.23"

# Internal
ed-agent = { version = "1.3.5", path = "crates/ed-agent" }
ed-mcp = { version = "1.3.5", path = "crates/ed-mcp" }
ed-acp = { version = "1.3.5", path = "crates/ed-acp" }
ed-acp-tauri = { version = "1.3.5", path = "crates/ed-acp-tauri" }
ed-acp-tui = { version = "1.3.5", path = "crates/ed-acp-tui" }
ed-acp-testkit = { version = "1.3.5", path = "crates/ed-acp-testkit" }
ed-onde-account = { version = "1.3.5", path = "crates/ed-onde-account" }
ed-claude = { version = "1.3.5", path = "crates/ed-claude" }
ed-agent = { version = "1.4.0", path = "crates/ed-agent" }
ed-mcp = { version = "1.4.0", path = "crates/ed-mcp" }
ed-acp = { version = "1.4.0", path = "crates/ed-acp" }
ed-acp-tauri = { version = "1.4.0", path = "crates/ed-acp-tauri" }
ed-acp-tui = { version = "1.4.0", path = "crates/ed-acp-tui" }
ed-acp-testkit = { version = "1.4.0", path = "crates/ed-acp-testkit" }
ed-onde-account = { version = "1.4.0", path = "crates/ed-onde-account" }
ed-claude = { version = "1.4.0", path = "crates/ed-claude" }

# gemm-common (via candle, via onde) depends on sysctl for every
# `target_vendor = "apple"`, but sysctl 0.6.0 only has a `sys` module behind
Expand Down
142 changes: 71 additions & 71 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,41 @@
# Ed

**Embed Ed.** Your app gets a brain. Your users keep their data.

Ed is an agent you drop into an application. It loads an open-weights model,
keeps the conversation, calls only the host tools you register, and hands back
replies. The model runs on the user's device — there is no metered inference
API and private context does not need to leave the device.

## Why it exists

Seven applications needed the same thing: load a model, know whether it's ready,
keep a conversation, hand back a reply. Seven times somebody wrote it again. Two
of them ended up byte-identical in three files.

None of that code was interesting. It was the wiring between an inference engine
that reports status by polling and a user interface that needs to be told —
plus the flattening of `Result<InferenceResult, _>` into one payload a view can
render either way. Ed is that layer, written once.

It deliberately doesn't own sessions, persistence, model catalogues, or
downloads. Those differ per application and stay yours.

## Onde runs the model. Ed makes it a feature.

[Onde](https://ondeinference.com) is the inference engine: weights, quantisation,
Metal and CPU backends, tokens out. Ed is the part above it your users touch.

## See it live
<p align="center">
<img src="https://raw.githubusercontent.com/ondeinference/onde/refs/heads/main/assets/onde-inference-logo.svg" alt="Onde Inference" width="96">
</p>

<h1 align="center">Ed</h1>

<p align="center">
Embed Ed. Your app gets a brain. Your users keep their data.
</p>

<p align="center">
<a href="https://crates.io/crates/ed-agent"><img src="https://img.shields.io/crates/v/ed-agent?style=flat-square&labelColor=17211D&color=235843&label=ed-agent" alt="ed-agent on crates.io"></a>
<a href="https://crates.io/crates/ed-acp"><img src="https://img.shields.io/crates/v/ed-acp?style=flat-square&labelColor=17211D&color=235843&label=ed-acp" alt="ed-acp on crates.io"></a>
<a href="https://github.com/ondeinference/ed-swift"><img src="https://img.shields.io/github/v/release/ondeinference/ed-swift?style=flat-square&labelColor=17211D&color=235843&label=Swift" alt="Swift"></a>
<a href="https://github.com/ondeinference/ed/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/ondeinference/ed/ci.yml?style=flat-square&labelColor=17211D&color=235843&label=CI" alt="CI"></a>
<a href="#license"><img src="https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-235843?style=flat-square&labelColor=17211D" alt="License"></a>
</p>

Ed is the agent layer above [Onde](https://ondeinference.com). It loads an
open-weights model, keeps the conversation, calls only the host tools you
register and hands back replies. The model runs on the user's device, so there
is no metered inference API and private context stays put. The `ed-acp` crates
build ACP agents (Onde Code, SplitFire Agent, siGit) on the same base.

Ed doesn't own sessions, persistence, model catalogues or downloads. Those
differ per application and stay yours.

## Install

```sh
cargo add ed-agent # embedded chat
cargo add ed-acp # ACP agent
```

- **[smbCloud MailX](https://smbcloudmail.com/en-us)** — siMail drafts replies,
summarises long threads, and helps triage the inbox. The mail never leaves the
machine to get read.
- **[Siti AI](https://github.com/getsigit/siti)** — a private assistant for
macOS, iOS, and Android, and the open reference app: its source is public, so
it's the codebase to read for a full integration.
- **[Pepak Basa Jawa Komplit](https://apps.apple.com/se/app/pepak-basa-jawa-komplit/id6752241812)**
— Joko, a Javanese tutor who answers offline, which matters where the app is
used.
- **[Rumi Learn Persian](https://apps.apple.com/se/app/rumi-l%C3%A4r-dig-persiska/id6753832408)**
— Rumi, a Persian tutor, same model, same device.
For Swift, add the package from [`ondeinference/ed-swift`](https://github.com/ondeinference/ed-swift).

## Quick start

```toml
[dependencies]
ed-agent = "1.2"
```

```rust
use ed_agent::{Ed, GgufModelConfig};

Expand All @@ -57,39 +46,50 @@ let reply = ed.send("Summarise this thread.").await;
println!("{}", reply.reply.unwrap_or_default());
```

Stream a reply with `Ed::stream` (plain chat) or let `Ed::run` forward deltas to
`EventSink::text_delta`. `Ed::restore_history` loads a saved conversation,
`Ed::generate` is a one-shot call that leaves the history alone, and
`Ed::set_system_prompt` / `set_sampling` change the model's settings. These
three need a loaded model. Persistence stays yours; implement `SessionStore`
over whatever you already have.
Streaming, history, tools and approvals are covered in [`crates/ed-agent`](crates/ed-agent).

Construct with `Ed::with_agent` to provide the executor for registered tools
and the approval handler for mutating tools. Read-only tools run directly;
mutating tools must receive `AllowOnce` or `AllowForSession` first.
## Use it your way

## Crates
- **In your app:** [`ed-agent`](crates/ed-agent), plus [`ed-agent-tauri`](crates/ed-agent-tauri) for Tauri and [`ed-agent-ffi`](crates/ed-agent-ffi) for Swift.
- **As an ACP agent:** [`ed-acp`](crates/ed-acp) with a `Profile`, and [`ed-acp-tui`](crates/ed-acp-tui) for a terminal UI.
- **Inside a Tauri app:** [`ed-acp-tauri`](crates/ed-acp-tauri) runs an ACP agent in process or an external stdio agent.
- **With Claude:** [`ed-claude`](crates/ed-claude), a profile on the Anthropic API with the user's key.
- **With MCP servers:** [`ed-mcp`](crates/ed-mcp). Onde accounts: [`ed-onde-account`](crates/ed-onde-account). Tests: [`ed-acp-testkit`](crates/ed-acp-testkit).

| Crate | What it's for |
| --- | --- |
| [`ed-agent`](crates/ed-agent) | The agent. No framework dependency. |
| [`ed-agent-ffi`](crates/ed-agent-ffi) | UniFFI bridge used by the Swift XCFramework. |
| [`ed-agent-tauri`](crates/ed-agent-tauri) | Tauri bindings: a sink that emits to the webview, the commands it invokes, and `EdSessions` for apps with a chat history and webview-answered approvals. |
## In production

The Onde Agent Platform crates, for agents that editors drive over the Agent Client Protocol
(ACP). SplitFire Agent is built on them.
[smbCloud MailX](https://smbcloudmail.com/en-us), [Siti AI](https://github.com/getsigit/siti) (the open reference app),
[Pepak Basa Jawa Komplit](https://apps.apple.com/se/app/pepak-basa-jawa-komplit/id6752241812) and
[Rumi](https://apps.apple.com/se/app/rumi-l%C3%A4r-dig-persiska/id6753832408).

| Crate | What it's for |
| --- | --- |
| [`ed-acp`](crates/ed-acp) | An ACP v1 server: sessions, auth, the turn loop on Onde Cloud, tool approval, workspace tools and MCP. A product supplies a `Profile`. |
| [`ed-acp-tui`](crates/ed-acp-tui) | A terminal UI for any ACP agent. |
| [`ed-mcp`](crates/ed-mcp) | MCP client glue over `rmcp`, the official Rust SDK: stdio and streamable HTTP servers, bearer tokens, tool namespacing, timeouts, progress and cancellation. With the `agent` feature, `McpExecutor` serves a toolset to Ed as tools. |
| [`ed-onde-account`](crates/ed-onde-account) | Onde Inference account client: sign in, activate an app and get its Onde Cloud API key through ondeinference.com, with no Onde secrets in the host app. |
| [`ed-acp-testkit`](crates/ed-acp-testkit) | Mock Onde Cloud endpoint, raw ACP client, the ACP registry probe and the ACP v1 conformance suite. |
## Docs

Free the intelligence.
Each crate has its own README:
[`ed-agent`](crates/ed-agent/README.md),
[`ed-agent-tauri`](crates/ed-agent-tauri/README.md),
[`ed-agent-ffi`](crates/ed-agent-ffi),
[`ed-acp`](crates/ed-acp/README.md),
[`ed-acp-tui`](crates/ed-acp-tui/README.md),
[`ed-acp-tauri`](crates/ed-acp-tauri),
[`ed-claude`](crates/ed-claude),
[`ed-mcp`](crates/ed-mcp/README.md),
[`ed-onde-account`](crates/ed-onde-account/README.md),
[`ed-acp-testkit`](crates/ed-acp-testkit/README.md).

## Contributing

Open pull requests against `development`. Before you push:

```sh
cargo fmt --all -- --check && cargo clippy --locked --workspace --all-targets -- -D warnings && cargo test --locked --workspace
```

## License

MIT or Apache-2.0, at your option. See [LICENSE-MIT](LICENSE-MIT) and [LICENSE-APACHE](LICENSE-APACHE).

## Copyright

Free the intelligence.

© 2026 [Onde Inference](https://ondeinference.com).
Licensed under MIT or Apache-2.0.
Loading