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
59 changes: 59 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [Unreleased]

### Added

- **Token economy — finish the task on fewer tokens** ([`electron/tuning.js`](electron/tuning.js)). Everyone now draws from the same free tiers, so the edge is tokens per finished task, not access. Six levers, all opt-outable by env:
- **Gateway compression on by default.** The sidecar turns on OmniRoute's RTK compression when the gateway is healthy. It rewrites tool-result text — the bulk of an agent's context — with cheap heuristics, no model call, and keeps failed-command output verbatim. Measured **9,812 → 3,170 tokens (68%)** on a realistic grep result. `OMNIWORK_COMPRESSION=off` disables it.
- **Housekeeping off the main model.** Titles, memory capture and compaction summaries run on the fast free pool (`auto/best-fast`), never the session's chosen model. `OMNIWORK_UTILITY_MODEL` overrides.
- **Step-level tiers.** With `auto`, grunt work runs on the fast pool and escalates to the coding pool (`auto/best-coding`) only when the fast model stalls — two failed steps, or six steps without finishing. A pinned model never tiers. `OMNIWORK_MODEL_TIERS=off` disables.
- **Rate-limit round-robin.** A 429 cools that model for a minute and rotates to the next connected free provider for the step, without a permanent switch — so stacked free tiers share the load instead of one draining first. Hard failures still switch as before.
- **Cheap delegate verifier.** An MCP delegation ends with a PASS/FAIL grade from the utility model, so the orchestrator re-delegates only when the work fell short — the expensive path is the caller re-reading and re-issuing.
- **Prompt-cache affinity.** Every request in a session carries a stable `x-session-id`, so the gateway keeps the unchanged system+history prefix cached upstream.
- **First-run nudge.** On first launch with nothing connected, the desktop app opens the free-models panel once so the OpenRouter catalog is one click away.

## [0.12.0] — 2026-09-15

### Added

- **ACP server** — OmniWork now speaks the [Agent Client Protocol](https://agentclientprotocol.com)
Expand All @@ -25,6 +40,50 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
skills are published as slash commands, and `session/load` replays a transcript so a crashed
harness resumes where it left off. Approval modes (`ask`, `edits`, `auto`, `plan`) are exposed as
ACP session modes; `ask` is the default, because in ACP the client owns the permission boundary.
- **OpenCode as an engine** — OpenCode Zen's free models (Nemotron 3 Ultra, Nemotron 3.5
Lightning, MiMo V2.5, Ling 3.0 Flash, Big Pickle) are real, keyless and tool-capable, and Zen
serves them only to OpenCode itself. So OmniWork runs OpenCode: `electron/opencode-engine.js`
starts `opencode serve` once per process and `OpenCodeAgent` wraps a session in the same
interface as the gateway agent — text deltas, tool calls, permission prompts and errors all map
onto OmniWork's events (reasoning on its own lane, so it never reads as the reply), and the
workspace rides on the official `x-opencode-directory` header — which also scopes the event
stream, one subscription per workspace. Nemotron 3.5 Lightning is the default because it answers
in seconds; Nemotron 3 Ultra stays available by name for when strength beats speed.
`opencode/<model>` is a model like any other in the desktop menu, ACP config options and
`delegate`; and when no gateway model answers, desktop sessions, ACP turns and MCP delegations
finish on the engine and say so. Getting OpenCode never involves npm -g or a PATH edit: a clone
brings it in as an optional dependency (`opencode-ai`, the platform binary), the installers stage
it per platform at build time next to the bundled Node runtime, and the free-models panel,
`npm run providers connect opencode`, or the ACP `opencode` auth method download the same npm
package (~45 MB) into OmniWork's data folder, verified against the sha512 pinned in
`package-lock.json` before it runs — always from a user gesture, never an agent tool call.
OmniWork launches it by absolute path wherever it landed; the server it starts is protected by a
per-process secret; a mid-session switch to the engine carries the conversation with it;
`OMNIWORK_ENGINE_FALLBACK=off` opts out of the automatic switch.
- **Free models that stay up** — the gateway's built-in keyless pool turned out to be a set of
unofficial endpoints (a scraped hobby site, a restaurant's chat widget, a CLI shim) that
providers shut off without notice; on 2026-09-15 every one of them was dead and `auto` returned
503 after 29 attempts. OmniWork now makes the durable kind of free one click away: a
*🆓 free models* panel in the app, `npm run providers` (also `npx omniwork-providers`), MCP tools
`list_providers` / `connect_provider`, and ACP auth methods `openrouter` / `local`. OpenRouter
connects through its PKCE flow (browser sign-in, no key to paste); Ollama Cloud, Kilo, Groq,
Cerebras, NVIDIA, Gemini and Mistral take a pasted key that is exercised once before it is kept;
Ollama, LM Studio, llama.cpp and vLLM are auto-detected and registered. Whatever is connected
becomes the default fallback chain for the MCP and ACP servers and for every desktop session,
live — strongest coder first, local last — so a retired free model is a hop, not a dead turn.
All of it goes through OmniRoute's own management API on loopback; nothing new is stored.
- **Pick the model, keep the answer** — headless agents no longer live and die by one model id.
ACP sessions expose the gateway catalog as a `model` config option (`session/set_config_option`,
the older `session/set_model`, or `_meta.model` on `session/new`), and the MCP `delegate` tools
take `model` / `fallback_models`, with a `list_models` tool to choose from. Behind both is a
fallback chain (`OMNIWORK_MODEL_FALLBACKS`): when the chosen model fails — a free catalog entry
retired upstream, a provider key you don't have, a quota — the same request goes to the next
model, the one that answers sticks for the rest of the session, and the switch is reported back
(`config_option_update` on ACP, a `[model: …]` note on MCP results). When every model fails the
error names each one and why, instead of a bare `Gateway 401`. A harness can now say "this free
coder, else this paid one" and stop babysitting the catalog. A dead gateway is still reported as
such — no model on the same socket would do better — and a provider that merely rejects
streaming still gets its non-streaming retry on the same model.
- **Add to chat** — highlight any text in the transcript and a small pill offers to quote it
into the composer, or press `⌘L` / `Ctrl+L`. The quote lands in the prompt as visible `> `
lines you can read and edit before sending, dimmed by a highlight layer behind the
Expand Down
94 changes: 85 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ delegate** *inside* Claude Code or Codex.

| | |
|---|---|
| 🆓 **Free & keyless** | Free models via `auto` routing, out of the box. Never rate-limited (auto-fallback across providers). |
| 🆓 **Free models** | `auto` routes the gateway's free pool out of the box, and one click connects a free provider that *stays* up (OpenRouter, Ollama Cloud, Groq, Gemini…) — see [Free models that stay up](#free-models-that-stay-up). |
| 🖥️ **Claude Code UI** | A clean terminal: `⏺`/`⎿` tool calls, `✻` thinking, `>` prompt, `@`-file mentions, per-turn time + token counts. |
| 📁 **Projects & memory** | Sessions live under projects. Durable `MEMORY.md` per project plus a global scope — it remembers what you teach it. |
| 🎓 **Skills** | Claude Code–compatible `SKILL.md` packs, loaded on demand. Ships with Anthropic's public set; the agent can write and install more. |
Expand Down Expand Up @@ -93,10 +93,12 @@ xattr -dr com.apple.quarantine /Applications/OmniWork.app
## Use OmniWork *inside* Claude Code / Codex — token saver 🪙

Don't want to fully switch? Keep your premium agent as the orchestrator and let it **delegate the
token-heavy grunt work to OmniWork's free models.** OmniWork ships an MCP server exposing two tools:
token-heavy grunt work to OmniWork's free models.** OmniWork ships an MCP server exposing these tools:

- `delegate(task, cwd?)` — OmniWork does the subtask autonomously on free models and returns a summary + changes
- `delegate_parallel(tasks[], cwd?)` — fan a batch out to parallel free-model subagents
- `delegate(task, cwd?, model?, fallback_models?)` — OmniWork does the subtask autonomously (free models by default) and returns a summary + changes
- `delegate_parallel(tasks[], cwd?, model?, fallback_models?)` — fan a batch out to parallel subagents
- `list_models()` — what the gateway can route right now, so you can pin a model and name what to try if it fails
- `list_providers()` / `connect_provider(provider)` — see which free providers are connected and connect one (OpenRouter opens the browser; `local` registers Ollama & co.). It takes no API key on purpose: keys are pasted in the app or the terminal, never by a model.

Your expensive model spends tokens on the hard reasoning; OmniWork burns **free** tokens on the
mechanical work, in the same project folder.
Expand Down Expand Up @@ -151,6 +153,58 @@ matters — free models drift on instruction details.
Treat the returned summary as a **claim, not evidence**, and verify before calling the work done.
`npm run connect` installs this guidance so your agent applies it automatically.

## Fewer tokens per task 🗜️

Free tiers are the same for everyone now, so OmniWork competes on how little it spends finishing a task. The gateway compresses tool output by default (a real grep result: **9,812 → 3,170 tokens**), housekeeping and mechanical steps run on the fast free pool while real reasoning escalates to the coding pool, a rate-limited provider rotates instead of draining, and every session reuses one id so the gateway caches its prefix. All on by default, all opt-outable:

| Env | Default | Effect |
|-----|---------|--------|
| `OMNIWORK_COMPRESSION` | on | `off` disables gateway-side tool-output compression |
| `OMNIWORK_MODEL_TIERS` | on | `off` runs `auto` on one tier instead of fast-then-escalate |
| `OMNIWORK_UTILITY_MODEL` | `auto/best-fast` | model for titles, memory, compaction summaries |

## Free models that stay up 🆓

The gateway's built-in keyless pool is a set of unofficial endpoints that providers shut off
without notice — on a bad day every one of them is gone and `auto` returns 503. So OmniWork
makes the durable kind of free easy: real free tiers behind a free account, no card.

| Provider | Free tier | Connect |
|---|---|---|
| **OpenRouter** | 20 free models, 18 with tool calling · 20 req/min, 50 req/day (1,000/day after a one-time $10 top-up) | one click — browser sign-in, no key to paste |
| **Ollama Cloud** | DeepSeek V4, Kimi K2.6, GLM 5.1, Gemma 4 on a "light usage" tier | paste a key |
| **Kilo Gateway** | `kilo-auto/free` router + Nemotron / MiniMax `:free` | paste a key |
| **Groq** | gpt-oss-120b at 30 req/min, 1,000 req/day; Qwen 3 32B | paste a key |
| **Cerebras**, **NVIDIA NIM**, **Gemini**, **Mistral** | free developer tiers | paste a key |
| **OpenCode engine** | Zen's free models with **no account**: Nemotron 3.5 Lightning (default, answers in seconds), MiMo V2.5, Big Pickle, Ling 3.0 Flash, Nemotron 3 Ultra (strongest, ~100 s a step) | ships with a clone and with the desktop app; otherwise one click downloads it (~45 MB) |
| **Ollama / LM Studio / llama.cpp / vLLM** | whatever runs on your machine | auto-detected |

Three ways in, all the same code path:

- **App** — *🆓 free models* in the sidebar. Connected providers become every session's fallback chain on the spot.
- **Terminal** — `npm run providers` shows status; `npm run providers connect openrouter` opens the browser; `npm run providers connect groq <key>`; `npm run providers connect local`; `npm run providers connect opencode` downloads the engine if a clone or installer didn't bring it. Also `npx omniwork-providers`.
- **From a harness** — the MCP server has `list_providers` / `connect_provider`; the ACP server offers `openrouter` and `local` as auth methods.

A pasted key is exercised once before it is kept, so a bad key never lands in the pool. What is
connected becomes the default fallback chain for the MCP and ACP servers (unless you set
`OMNIWORK_MODEL_FALLBACKS` yourself), ordered strongest coder first, local last.

**The OpenCode engine is the floor.** OpenCode Zen only serves its free tier to OpenCode itself, so
OmniWork doesn't pretend to be OpenCode — it runs it. And nobody has to install anything or touch
their PATH: `npm install` brings OpenCode in as an optional dependency (`opencode-ai`, the binary
for your platform), the desktop installers stage it per platform at build time, and if it is
missing anyway one click fetches the same npm package (~45 MB) into OmniWork's data folder —
verified against the sha512 pinned in `package-lock.json` before a byte of it runs. OmniWork
always launches it by absolute path, and the server it starts answers only requests carrying a
per-process secret. Set `OMNIWORK_ENGINE_FALLBACK=off` if a failed turn should stay failed rather
than finish on the engine. With OpenCode installed, `opencode/<model>`
runs a session on OpenCode's own headless server (`opencode serve`, the same thing Zed and acpx
drive) and its own tools, scoped to your workspace, streamed back as OmniWork events — text as
text, the model's reasoning on its own lane, tool calls as tool calls. Pick one in the model
menu, pass `model: "opencode/nemotron-3.5-lightning-free"` to `delegate`, or do nothing: when no
gateway model answers, the turn finishes on the engine and says so. What you give up there is
OmniWork's own skills and memory — OpenCode runs its tools, not ours.

## Drive OmniWork from OpenClaw, Zed, or any ACP harness 🔌

The MCP server makes OmniWork a *tool* your agent calls. The **ACP server** makes it a full
Expand Down Expand Up @@ -183,14 +237,33 @@ What the harness gets:
| **The Agent Deck** | `spawn_subagents` shows up as *N* live parallel tool calls, not one opaque block |
| **Skills as slash commands** | Installed skills are published via `available_commands_update` |
| **Modes** | `ask` (default), `edits`, `auto`, `plan` — switch with `acpx omniwork set-mode plan` |
| **Model selection** | The gateway catalog as a `model` config option — `session/set_config_option`, the older `session/set_model`, or `_meta.model` on `session/new` — with a fallback chain if the pick fails |
| **Auth methods** | `authenticate({ methodId: "openrouter" })` opens the browser for a free OpenRouter key; `"local"` registers a running Ollama / LM Studio / llama.cpp / vLLM; `"opencode"` installs OpenCode for its free Zen models |
| **Resumable sessions** | `session/load` replays the transcript, so a crashed harness picks up where it left off |

Modes default to **ask** because ACP's whole point is that the client owns the permission
boundary. Set `OMNIWORK_ACP_MODE=auto` if you'd rather it run unattended.

By default OmniWork runs on its own bundled OmniRoute gateway (free models). To spend a different
provider's budget instead, point it elsewhere — `OMNIWORK_BASE_URL`, `OMNIWORK_API_KEY`, and
`OMNIWORK_MODEL` apply to both the ACP and MCP servers.
By default OmniWork runs on its own bundled OmniRoute gateway on `auto` (the free pool). To
pin a model — a specific coder, or a paid one you've keyed in the router dashboard — pick it per
session (ACP: the `model` config option, `session/set_model`, or `_meta.model` on
`session/new`; MCP: the `model` parameter on `delegate`) or set `OMNIWORK_MODEL`.

Free catalogs go stale — a pinned model can 401 as "not supported" the day its provider retires
it — so a fallback chain is always in play. By default it is built from whatever free providers
are [connected](#free-models-that-stay-up), strongest coder first, local server last;
`OMNIWORK_MODEL_FALLBACKS` (or `fallback_models` / `_meta.fallbackModels`) replaces it with your
own list, in order. The same request goes to the next model, the one that answers becomes the
session's model, and the switch is reported (a `config_option_update` on ACP, a `[model: …]`
note on MCP results). If every model fails, the error names each one and why. End the chain with
a paid model when the work must complete:

```bash
OMNIWORK_MODEL=oc/some-free-coder OMNIWORK_MODEL_FALLBACKS=auto,anthropic/claude-sonnet-5 npx omniwork-acp
```

To spend a different provider's budget entirely, point the servers elsewhere — `OMNIWORK_BASE_URL`
and `OMNIWORK_API_KEY` apply to both.

## Build from source

Expand Down Expand Up @@ -262,14 +335,17 @@ Locally built `.app`s are unsigned — same right-click → Open dance as above.

## Configuration

Works with zero config. To go beyond the free tier, click **router dashboard** in the app to add
provider keys (stored encrypted, locally), or pick a specific model in the status bar.
Works with zero config. To get a free tier that stays up, use **🆓 free models** in the app (or
`npm run providers`); to add any other provider key, click **router dashboard** (keys are stored
encrypted, locally). Pick a specific model in the status bar.

| Env | Default | Purpose |
|-----|---------|---------|
| `OMNIWORK_GATEWAY_PORT` | `20128` | Gateway port |
| `OMNIWORK_WORKSPACE` | — | Open a folder on launch |
| `OMNIWORK_MODEL` | `auto` | Pin a model (MCP + ACP servers) |
| `OMNIWORK_MODEL_FALLBACKS` | connected providers | Comma-separated models to try, in order, when the model fails (MCP + ACP servers). Unset: built from connected free providers; set empty to disable |
| `OMNIWORK_ENGINE_FALLBACK` | on | `off` keeps a turn that no gateway model could serve as an error instead of finishing it on the OpenCode engine |
| `OMNIWORK_BASE_URL` | — | Run headless servers against another OpenAI-compatible endpoint |
| `OMNIWORK_API_KEY` | `omniwork` | Key for `OMNIWORK_BASE_URL` |
| `OMNIWORK_ACP_MODE` | `ask` | Starting approval mode for ACP sessions |
Expand Down
32 changes: 27 additions & 5 deletions electron-builder-lite.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,48 @@
"!node_modules/@ngrok/**",
"!node_modules/react-dom/**",
"!node_modules/@modelcontextprotocol/**",
"!node_modules/jsdom/**"
"!node_modules/jsdom/**",
"!node_modules/opencode-ai/**",
"!node_modules/opencode-*/**"
],
"extraResources": [
{ "from": "build/runtime", "to": "runtime" }
{
"from": "build/runtime",
"to": "runtime"
}
],
"directories": {
"output": "dist",
"buildResources": "assets"
},
"win": {
"target": [{ "target": "nsis", "arch": ["x64"] }],
"target": [
{
"target": "nsis",
"arch": [
"x64"
]
}
],
"artifactName": "OmniWork-Lite-Setup-${version}.${ext}"
},
"mac": {
"target": [{ "target": "dmg", "arch": ["x64", "arm64"] }],
"target": [
{
"target": "dmg",
"arch": [
"x64",
"arm64"
]
}
],
"artifactName": "OmniWork-Lite-${version}.${ext}",
"category": "public.app-category.developer-tools"
},
"linux": {
"target": ["AppImage"],
"target": [
"AppImage"
],
"artifactName": "OmniWork-Lite-${version}.${ext}",
"icon": "assets/icon.png",
"category": "Development"
Expand Down
Loading
Loading