Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
d19e1ba
fix(security): confine file tools by real path, not text path
inquerium Oct 15, 2026
e5ee830
fix(security): approval modes fail closed; gate network and persistence
inquerium Oct 15, 2026
47e4e51
fix(security): validate ACP session ids and cwd
inquerium Oct 15, 2026
1911907
fix(gateway): only quarantine storage that SQLite says is corrupt
inquerium Oct 15, 2026
29007a7
fix(security): explicit environments for commands and the gateway
inquerium Oct 15, 2026
3c7d3b8
fix(security): gateway requires the per-install key; guard Host and O…
inquerium Oct 15, 2026
af190db
feat: tool-free completion mode (ACP, MCP, CLI) with JSON Schema output
inquerium Oct 15, 2026
170c071
fix(privacy): no prompt bodies on disk; owner-only gateway files
inquerium Oct 15, 2026
3075385
fix: treat a pool's 502/504 as no model; don't walk a failed pool twice
inquerium Oct 15, 2026
b29b732
fix(gateway): stop keyless OpenCode impersonation; explain pool failures
inquerium Oct 15, 2026
195655a
fix(acp): per-tool permissions, client MCP servers, failures as stop …
inquerium Oct 15, 2026
6e9bb5c
fix(mcp): negotiate the protocol version
inquerium Oct 15, 2026
d3e9f0f
fix(security): repository files are lower-trust context, not instruct…
inquerium Oct 15, 2026
880c9f1
fix(providers): find local servers where they are, and say why one is…
inquerium Oct 15, 2026
87504cd
fix(gateway): one gateway store for the desktop app and headless servers
inquerium Oct 15, 2026
eadc54d
fix(supply-chain): pin the gateway engine and verify its download
inquerium Oct 15, 2026
3996512
test(storage): give the stand-in gateway time to come up on a busy ma…
inquerium Oct 15, 2026
3939db2
fix: review findings in ACP permissions, schema patterns, local probing
inquerium Oct 15, 2026
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
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,19 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Bounded metadata-only request/tool traces for durable workers, with paged retrieval, repeated-read fingerprints, usage coverage and phase timings. Benchmarks retain trace artifacts.
- Opt-in token/time reserves stop restricted OpenCode workers, confirm idle, and verify their patches before the hard limit. Failed checks use the existing bounded repair; hard budgets, errors, cancellation and ownership remain authoritative.

### Security
- Approval modes fail closed. An unknown or misspelled mode blocks every tool instead of running as `auto`, and an unclassified tool is blocked. `ask`, `edits` and `plan` now ask before `web_fetch`, `web_search`, `browse_page`, `open_url`, `spawn_subagents` and MCP tools. `save_skill`, `install_skills` and global `save_memory` ask even in `auto` and are blocked in `plan`; `OMNIWORK_AUTO_PERSIST=1` restores the previous `auto` behaviour for one release. The desktop ignores an unknown mode instead of switching to `auto`.
- Prompts no longer land on disk. OmniRoute wrote every request's full prompt and reply to `call_logs/` (world-readable, kept a week) and ACP transcripts were world-readable. OmniWork's key is now marked no-log, so bodies are never written (verified against the real gateway); the app log file is off and metadata is kept a day. The gateway runs with umask 077, and files from earlier versions — call logs, database backups, ACP transcripts — are made owner-only. `OMNIWORK_GATEWAY_LOGS=on` keeps bodies when you want them. Adopting a gateway an older OmniWork started (keyless, logging) now says so.
- Lite builds verify the OmniRoute engine they download: the tarball was streamed from the registry straight into `tar`, unverified, for a floating `^3.8.x` version. `omniroute` is now pinned exactly, its sha512 is pinned beside it in `package.json` (a regression test keeps it equal to `package-lock.json`), and the download is checked before anything is unpacked.
- A repository's instruction files no longer speak with the user's authority. `AGENTS.md`, `CLAUDE.md`, `CLAUDE.local.md`, `.omniwork.md` and `.cursorrules` were appended to the system prompt under "Project instructions"; they now go in a labelled, lower-trust "Repository files" section (instructions you set on the project page keep their authority, saved memory is framed as notes), are read through the workspace confinement — an `AGENTS.md` symlinked to `~/.ssh/id_rsa` put the key in the prompt sent to the provider — and can be left out: `OMNIWORK_PROJECT_FILES=off`, or `_meta.omniwork.projectFiles: false` on ACP. A repository skill can no longer shadow a global skill of the same name, and is marked as coming from the repository.
- The gateway requires a key. OmniRoute ran keyless — an invalid bearer fell back to anonymous — with no Host-header check, so other local accounts and, by DNS rebinding, web pages could use every connected provider; with any `Authorization` header, a cross-site page could even read `/v1` responses. OmniWork now starts it with `REQUIRE_API_KEY` and a per-install key (`~/.omniwork/gateway-key.txt`, owner-only) that every OmniWork client sends, and a guard preloaded into the gateway refuses a foreign `Host` or a cross-site `Origin` (the dashboard's own requests still pass). The port and key are recorded in `~/.omniwork/gateway.json`, so every entry point adopts a running gateway — including one that had to move off 20128 — with the key it accepts. **Tools that call the gateway directly must now send `Authorization: Bearer <key>`**; `OMNIWORK_GATEWAY_OPEN=1` keeps it keyless for one release. `OMNIWORK_GATEWAY_ALLOWED_HOSTS` admits extra Host names (e.g. `host.docker.internal`).
- Child processes get an explicit environment instead of OmniWork's own. `run_command` (and acceptance checks) receive `PATH`, home/temp dirs, locale, proxy/CA settings and toolchain locations, but no tokens or keys from the shell, and run in a non-login shell so the profile isn't re-sourced; `OMNIWORK_COMMAND_ENV_PASSTHROUGH` adds variables. The gateway gets the same base plus its own settings, and never `CORS_ALLOW_ALL`/`CORS_ORIGIN`. `OMNIWORK_COMMAND_ENV=inherit` and `OMNIWORK_GATEWAY_ENV=inherit` restore the old behaviour for one release.
- A gateway boot that fails no longer wipes provider connections. `storage.sqlite` was renamed to a `.corrupt-<ts>.bak` after *any* failed first boot (a slow disk, a cold start, a port race); now it is set aside only when SQLite's `quick_check` reports corruption, together with its WAL, and the user is told. Backups are owner-only (existing ones are tightened on every start), only the newest two are kept when a new one is made, and the retry waits for the old process to exit before respawning.
- ACP `session/load` accepts only session ids of the shape the server mints, so an id can no longer point outside the session store (`../../elsewhere/file`) and load an arbitrary JSON file as a session. `cwd` on `session/new` and `session/load` must be an existing absolute directory, and a saved session's unknown mode falls back to the default instead of being trusted.
- File tools compare real paths, so a symlink inside a workspace no longer carries `read_file`, `write_file`, `edit_file` or `list_dir` outside it. Writes refuse any symlink as the final path component (use the link's target path instead). Approval previews, ACP diffs and `/undo` use the same check.

### Added
- **Tool-free completion** for untrusted text — one request in, one completion out — as an ACP `completion` session mode (`_meta.omniwork.mode: "completion"` on `session/new`, which reports `toolFree: true` and an empty tool list), an MCP `complete` tool, and `npm run complete` / `omniwork-complete`. The request carries no `tools` or `tool_choice`, the system prompt is used verbatim, no workspace, memory or skills are read and nothing is written to disk. With a JSON Schema the reply is requested as structured output (`response_format` / Ollama `format`), validated, and retried once with the errors — never coerced to `{}`. Results report the model used, usage, a `truncated` flag and every attempt with its reason. `ollama/<model>` runs on Ollama's native API (honouring `OLLAMA_HOST`) with a context sized to the prompt, and completion only falls back to models the caller names. ACP sessions now report `toolFree` and their tool list in `_meta.omniwork`, expose the mode as a `mode` config option, and `OMNIWORK_ACP_MODE=completion` makes completion the default.
- Optional scoped OpenCode workers using the supported custom-agent API: model-specific instructions, local tools, preserved repository rules, no automatic skills catalog, and a fixed 24-step limit. The full `standard` agent remains the default; `focused` adds an experimental compact prompt.
- Separate engine uncached/cache-read/cache-write/reasoning/request metrics, accumulated across repairs. Paired repository-regression benchmark with withheld evaluator checks and a real-engine local request-size probe.
- Shared persistent coding worker service with eight MCP job tools and an `omniwork-jobs` CLI. Durable batch admission, reconnectable results, provider capacity and pacing, explicit free/paid policy, and paged artifacts.
Expand All @@ -22,6 +34,14 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Required deterministic regression jobs for CI and release builds.

### Fixed
- One gateway store. The desktop app kept its gateway store in its app-data folder and headless servers (MCP, ACP, CLI, jobs) in `~/.omniwork`, so a provider connected in the app was missing whenever a headless server had started the gateway. Headless servers now use the app's data folder; an old `~/.omniwork` store is copied over once if the shared one has none (never deleted). With the running gateway recorded in `~/.omniwork/gateway.json`, every entry point also finds it on whatever port it ended up on. (On Linux, packaged builds keep their own `OmniWork` folder for now.)
- Local servers are found where they actually are: `OLLAMA_HOST` (parsed as the Ollama CLI does), `LMSTUDIO_HOST`, `LLAMA_CPP_HOST` and `VLLM_HOST` are honoured when probing and registering. A server that isn't usable is explained rather than reported as "not running": nothing listening vs. listening but erroring or hanging and, for Ollama, a models folder linked to a missing drive, the last errors in its server log, and a runaway log that means it is crash-looping — in `npm run providers`, the MCP `list_providers`/`connect_provider` tools and tool-free completion. Status also warns that Ollama's default context silently cuts longer prompts. `OMNIWORK_PREFER_LOCAL=1` puts local models first (and makes one the primary where `auto` would have been, on headless servers).
- With nothing connected, every `auto/*` request failed with a 502 wall of upstream errors: the keyless pools went to OmniRoute's "OpenCode Free" provider, which presents itself as the OpenCode client (on by default) and which upstream now refuses ("only from within OpenCode"). OmniWork now turns that header synthesis off and blocks the keyless provider once per gateway store (a user who re-enables it in the dashboard is not overridden; `OMNIWORK_KEYLESS_OPENCODE=on` opts out). Keyed `opencode-zen`/`opencode-go` connections are untouched, and OpenCode's free Zen models keep working through the real OpenCode binary (`opencode/…`). A pool failure now says why — "pool of 3; 3 tried; 1 deprecated, 2 needs key", from OmniRoute's diagnostics — and an `auto/*` pool with nothing to route to says to connect a local model or a key.
- The MCP server negotiates `protocolVersion` (2024-11-05 through 2025-11-25) instead of always answering 2024-11-05.
- ACP permissions: "allow always" switched the whole session to `auto`, so approving one edit meant every later command ran unasked. "Always allow" and the new "never allow" now apply to that tool (a command: that exact command line) for the session. A subagent's permission request names a tool call the client has been shown, and that call is closed out afterwards. A `cancelled` answer stops the turn.
- ACP accepts the client's stdio `mcpServers` on `session/new` / `session/load` (they were silently ignored); http/sse servers are refused with an error. `_meta.omniwork.honorClientCapabilities` / `OMNIWORK_ACP_STRICT_CAPABILITIES=1` drop file and shell tools for capabilities the client didn't declare.
- ACP model failures end the turn with a stop reason, the error in the transcript and in `_meta.omniwork.error`, instead of a JSON-RPC error a client reads as a protocol failure. `OMNIWORK_ACP_ERRORS=rpc` keeps the old behaviour for one release.
- A gateway 502 or 504 — what an `auto/*` pool returns when every member failed or timed out — now counts as "no model answered": the OpenCode engine fallback runs (desktop excepted until it shares the check), ACP adds the how-to-connect hint, and a failed streaming request against a pool (502/503/504) is no longer retried without streaming, which walked the whole failing pool twice.
- Failed writes and intermediate commentary no longer masquerade as completed delegations. Summary-only PASS/FAIL grading is replaced by explicit check results or an unverified label.
- Workers inherit parent permissions, memory, and skills. More than eight tasks are queued rather than silently dropped.
- Rate-limit cooldowns survive subsequent steps, honor Retry-After, and avoid duplicate streaming retries. Failed exact edits trigger escalation.
Expand Down
Loading
Loading