Skip to content

Releases: thelabs-id/cempala

v0.5.2 — the mailbox delivers

Choose a tag to compare

@caesarlab caesarlab released this 31 Aug 14:36

The mailbox delivers

Handing a task to another agent used to record it and nothing more. Nothing started that agent, nothing told it, and the reaper — which only sweeps running tasks — could not see that the task was there. So an assignment sat unread indefinitely while whoever made it waited for work that was never going to begin. The database that prompted this release had 77 messages sent and 77 unread, claim_task never once called, and the single mailbox task ever created still pending hours later.

create_task now runs the assignee in the background and returns immediately. You get your turn back, the other agent starts on it, and you collect the outcome with check_task whenever you're ready.

  • Delivery runs under dispatch's policy, by the same code — the denylists in the same order, the trust boundary, the argv baseline, the wait ceiling and the network label all apply. Work cannot reach an agent on easier terms by being assigned instead of dispatched.
  • The result says what actually happened. delivery: "dispatched" means the assignee was started, and carries the network_enforcement that was applied — which matters most for Antigravity, where "no network" cannot be enforced at all and the result now says so. delivery: "queued" means nothing was started, and names why: no assignee, deliver: false, an agent with no CLI mapping, that CLI not installed, a task an agent filed for itself, or a chain already one delivery deep.
  • The row is the unit of work. Because delivery runs on the task's own row, that row is settled from the agent's output whether or not the agent ever calls complete_task — and if it does call it, its own verdict stands. Every reconciliation path only writes a row still marked running, so the first writer to reach a terminal state owns it. The same guard means a claim_task that lands first wins, and nothing is spawned over it.
  • Delivery goes one hop. An agent working a delivered task can still create tasks, but they are recorded rather than started. One handoff is a handoff; a chain that delivers every time it is delivered to spends real provider quota without bound.

Work nobody can be started for is still surfaced

Any MCP client can join the mailbox — identities are created on first use — but only the four mapped CLIs can be spawned, so a task assigned to anyone else would simply sit there. Nothing in MCP lets a server interrupt an agent that is not currently asking it something, and none of these agents is a daemon.

So the reply to a call the agent did make now carries an inbox: its unread message count, and the tasks waiting for it described well enough to act on — id, description, folder, and who asked. It rides on every tool, not just the mailbox ones, and it is absent when there is nothing waiting, so its presence is the signal. This is a pull model told honestly: an agent that never calls Cempala at all still learns nothing, which is exactly why delivery exists alongside it.

Also in this release

Policy is now decided before the task row is written, and the row is inserted already carrying its verdict. A pending row is claimable the instant it exists, so recording first and judging afterwards published work whose denial had not been decided yet. Judging first removes that window rather than guarding it, and keeps SQLite's single writer lock away from the filesystem calls the trust boundary makes.

An allowed verdict hands back a capability bound to the exact task, prompt, canonical folder and resolved command line it judged. There is no general-purpose "spawn this row" function to reach for instead, so approved work cannot be swapped for something else after the fact, or recorded against a task that describes something different.

CI moved off the retired Node 20 runtime.

Upgrading

Re-run the installer, then restart your agent sessions — a running client holds the old binary open as an MCP server until it exits.

Verification

Type checking, 386 unit tests, the installer and uninstaller suites, six-target builds, and smoke tests on matching hardware for Linux (x64/ARM64), macOS (Intel/Apple Silicon) and Windows x64.

Delivery was additionally verified end to end against all four supported CLIs on a real machine — Codex, Claude, Antigravity and OpenCode each pick up an assigned task and complete it with nobody claiming it by hand, each reporting the network enforcement its own argv actually imposes. The implementation was reviewed across eight adversarial rounds before it landed.

The published binaries are byte-identical to the artifacts those smoke tests ran against; checksums.txt matches the hashes produced by the build job.

v0.5.1 — progress heartbeats for long dispatches

Choose a tag to compare

@caesarlab caesarlab released this 31 Aug 10:27

Progress heartbeats for long dispatches

A dispatch that ran longer than the caller's own MCP timeout looked non-responsive, even though it had completed. This release fixes that.

MCP clients time out tool calls on a clock of their own — 60 seconds by default in the MCP SDK, which OpenCode inherits unless a per-server timeout is configured. A dispatch may legitimately wait up to 600. Nothing reconciled the two, so a long handoff played out as: Cempala runs the target agent to completion, parses its result, writes the task row — and the caller gave up minutes earlier and reported the agent as unreachable. Both sides behaved exactly as documented, and the handoff still died. The work was never lost, but you had to know to go looking for it with check_task.

  • A tools/call carrying a progressToken now receives a notifications/progress heartbeat every 10 seconds until the tool settles. This is the protocol's own answer rather than a workaround: clients that reset their request timeout on progress — OpenCode enables resetTimeoutOnProgress for every MCP tool call — keep the request alive for as long as the work honestly continues.
  • Callers that send no token are unaffected, and get no notifications. Nothing is invented on their behalf; for those, ask for a short wait_seconds and poll check_task.
  • The heartbeat stops when the tool settles, and goes quiet if the client cancels. Its timer never holds the server process open, and a fast tool stops it before it can fire — so in practice only a waiting dispatch produces any traffic at all.

No configuration is needed, and no timeout field has to be added to an OpenCode MCP entry. The README documents the interaction, including the manual fallback for a client too old to send a progress token.

Upgrading

Re-run the installer, then restart your agent sessions — a running client holds the old binary open as an MCP server, so it keeps serving the previous version until it exits.

Verification

Type checking, 369 unit tests, the installer and uninstaller suites, six-target builds, and smoke tests on matching hardware for Linux (x64/ARM64), macOS (Intel/Apple Silicon) and Windows x64.

The fix carries an end-to-end test that drives the real stdio JSON-RPC surface: heartbeats must arrive before the response and strictly increase, and an equally slow dispatch sent without a token must stay silent for its whole run.

The published binaries are byte-identical to the artifacts those smoke tests ran against; checksums.txt matches the hashes produced by the build job.

v0.5.0 — OpenCode auto-registration

Choose a tag to compare

@caesarlab caesarlab released this 14 Aug 13:27

OpenCode auto-registration

OpenCode was the one supported agent installing left alone. Cempala could already dispatch work to it, but making it call back required a manual config edit. Installing now registers all four.

  • Registration runs the CLI's own opencode mcp add cempala -- <bin>, exactly as Claude Code and Codex are registered. It is idempotent, so re-running the installer to upgrade re-points the entry instead of duplicating it, and it preserves the comments in the JSONC config it edits.
  • Uninstalling removes the entry again. OpenCode ships no mcp remove, so cempala --unregister-opencode edits the config directly: it cuts out the single mcp.cempala member and copies every other byte through — comments, indentation and line endings intact. Install then uninstall returns the file to its original bytes.
  • All three files OpenCode reads are cleaned: opencode.json, opencode.jsonc, and the legacy config.json it still loads but no longer writes.
  • A config that cannot be scanned end-to-end is left untouched and reported, and an ambiguous one — duplicate mcp or cempala keys — is refused rather than half-cleaned.

Also fixed

Two defects in the shared registration code, both reachable from the existing Antigravity paths:

  • A lock-contended unregistration told the user to re-run cempala --register-antigravity — the wrong tool, and the opposite direction. It is the only actionable line printed after a run that has already exited non-zero and kept the binary.
  • The "did our failed write damage this config?" check used a strict JSON.parse, which is false for any file containing a comment. Its guarantee that a concurrent writer's valid config survives was therefore inert for JSONC, and could have restored a pre-edit snapshot over someone else's write and then deleted the backup.

Verification

Type checking, 368 unit tests, the installer and uninstaller suites (172 checks), six-target builds, and smoke tests on Linux (x64/ARM64), macOS (Intel/Apple Silicon) and Windows x64.

The published binaries are byte-identical to the artifacts those smoke tests ran against; checksums.txt matches the hashes produced by the build job.

v0.4.0 — OpenCode MCP support

Choose a tag to compare

@caesarlab caesarlab released this 14 Aug 06:42

OpenCode MCP support

Cempala now supports OpenCode in both directions:

  • OpenCode can use Cempala's MCP server and all eight Cempala tools.
  • Cempala can dispatch work to opencode run, with optional model selection.
  • OpenCode JSONL output is parsed into task status, results, and retained logs.
  • Network-disabled OpenCode dispatches use an explicit tools-only policy.

This release also adds OpenCode to the default agent configuration and database seed data, plus end-to-end protocol coverage.

Verification

The release passed type checking, unit tests, install/uninstall tests, six-target builds, and smoke tests on Linux (x64/ARM64), macOS (Intel/Apple Silicon), and Windows x64.

v0.3.0 — uninstaller

Choose a tag to compare

@caesarlab caesarlab released this 11 Aug 13:07

Cempala can now be uninstalled properly. Everything else is unchanged from 0.2.0.

Why this needed a release

Deleting ~/.cempala/ was never enough, and the README used to say it was. Installing writes to four places, three of which are other programs' config files:

~/.cempala/ the binary, the config, the database
a shell startup file the PATH export
Claude Code and Codex an MCP server registration each
Antigravity an entry in ~/.gemini/config/mcp_config.json

Remove only the directory and those three registrations survive, each pointing at a binary that no longer exists — so every launch of every agent CLI reports cempala as a failed server, indefinitely, until you find and edit three config files by hand.

Uninstalling

curl -fsSL https://raw.githubusercontent.com/thelabs-id/cempala/main/scripts/uninstall.sh | bash
irm https://raw.githubusercontent.com/thelabs-id/cempala/main/scripts/uninstall.ps1 | iex
  • Your data is kept. ~/.cempala/ holds the task history and audit log; that's a record, not installation debris. --purge (-Purge on Windows) removes it too.
  • --dry-run prints exactly what would happen and changes nothing.
  • Registrations go through each CLI's own mcp remove, so their config files stay theirs. Only Antigravity, which ships no such command, is edited directly — one key removed, every other server and setting untouched, and the file never deleted.
  • The PATH export is removed only where the installer's marker comment is still present. A block you've edited, a commented-out copy, or a file that merely mentions the same path is left alone. Install then uninstall returns a startup file to its original bytes.
  • A step that can't be completed reports a partial uninstall — non-zero exit, no success banner — and keeps the binary so you can retry. --purge is refused outright in that case, so a half-undone system never loses its database.

Two new flags support this and can be run on their own: cempala --unregister-antigravity and cempala --remove-path-block <rc-file>....

Also in this release

  • The installer can't be hung by an older binary. A cempala predating --register-antigravity doesn't reject the flag — it starts the stdio server and reads stdin. Both installers now close the child's input.

Verifying these binaries

Every asset is listed in checksums.txt, and the installers refuse to install without a matching line. These are the exact bytes CI smoke-tested on matching hardware for each platform — verified reproducible: a local build produced identical SHA-256 digests for all six targets.

cempala-windows-arm64.exe is deliberately not published; it has no smoke-test runner, and Windows on ARM runs the x64 build under emulation, which the installer selects for you.

v0.2.0 — Antigravity support

Choose a tag to compare

@caesarlab caesarlab released this 11 Aug 11:01

Cempala now works with Antigravity (Google's agy CLI) alongside Claude Code and Codex — in both directions. dispatch can target it, and Antigravity can call Cempala's eight tools itself.

Antigravity

  • target_agent: "antigravity" runs agy -p headless and returns the result inline, like the other two.
  • Registration is automatic. Antigravity has no mcp add, so the installer merges an entry into ~/.gemini/config/mcp_config.json — covering both the Antigravity IDE and the CLI. Every other server and top-level key in that file is preserved, and a config that can't be parsed is left untouched with the snippet printed for you. Re-runnable on its own with cempala --register-antigravity.

Two things about this agent are worth knowing before you rely on it:

  • allow_network: false cannot be enforced for Antigravity. agy has no argv-level network switch — its reach is governed by read_url / execute_url in your own agy settings, which Cempala neither reads nor writes. Rather than borrow a label it can't back up, the result reports a fourth value, network_enforcement: "not_enforceable". Codex still reports "sandboxed" and Claude "tools_only".
  • Shell commands are auto-denied in headless mode unless you add an allow-rule under permissions.allow in ~/.gemini/antigravity-cli/settings.json. File edits work out of the box. Cempala will not pass --dangerously-skip-permissions to get around this.

Fixes that affect Claude and Codex too

  • A run that produced nothing is no longer reported as a success. Every path that settled a task read stderr only when the run had already failed, so a run that exits 0 with an empty answer came back completed with an empty result — and the one line explaining why was discarded. An empty answer now earns the stderr read, and an agent explicitly stating it produced no output is recorded as failed.
  • Dispatched work lands in the cwd you asked for. agy resolves relative paths against its own workspace, not the process cwd, so a dispatch could write into ~/.gemini/antigravity-cli/scratch instead of your project. The baseline now anchors it with --add-dir <validated cwd>.
  • The reaper reports what actually happened instead of falling through to a canned message when stdout was empty.
  • The installer can't be hung by an older binary. A cempala predating --register-antigravity doesn't reject the flag — it starts the stdio server and reads stdin. Both installers now close the child's input.

Upgrading

Re-run the installer; it's idempotent and replaces the binary in place even while it's running.

curl -fsSL https://raw.githubusercontent.com/thelabs-id/cempala/main/scripts/install.sh | bash

Restart any Claude Code, Codex or Antigravity session that's already open so it picks up the registration.

Verifying these binaries

Every asset is listed in checksums.txt, and the installers refuse to install without a matching line. These are the exact bytes CI smoke-tested on matching hardware for each platform. cempala-windows-arm64.exe is deliberately not published — it has no smoke-test runner yet, and Windows on ARM runs the x64 build under emulation, which the installer selects for you.

v0.1.0

Choose a tag to compare

@caesarlab caesarlab released this 30 Jul 17:11

First release of Cempala — a local MCP server that lets Claude Code and Codex hand work to each other.

What's published

Platform Asset Verified on
Linux x64 cempala-linux-x64 Linux 6.6 (WSL2) and a CI x64 runner
Linux arm64 cempala-linux-arm64 a real ARM64 CI runner
macOS Apple Silicon cempala-darwin-arm64 macOS 15 arm64
macOS Intel cempala-darwin-x64 macOS 15 x86_64
Windows x64 cempala-windows-x64.exe Windows 11

"Verified" means the published binary was run on that platform — not that it compiled. Each one starts, writes its config, completes an MCP handshake, advertises all eight tools, and round-trips a message through SQLite (scripts/smoke-test.sh, run by CI against the exact artifact attached here).

Windows on ARM is the one gap: the installer fetches the x64 build, which runs under emulation. A native windows-arm64 binary compiles but isn't published, because there was no ARM64 Windows machine to run it on.

Install

Linux / macOS:

curl -fsSL https://raw.githubusercontent.com/thelabs-id/cempala/main/scripts/install.sh | bash

Windows (PowerShell):

irm https://raw.githubusercontent.com/thelabs-id/cempala/main/scripts/install.ps1 | iex

Either installer verifies the SHA-256 below before doing anything with the binary, installs it (~/.cempala/bin on Linux and macOS, %LOCALAPPDATA%\Cempala\bin on Windows), adds it to PATH for this shell and future ones, writes a default ~/.cempala/config.toml, and registers itself with whichever of claude and codex it finds. Re-running it is safe.

Requirements

Cempala holds no API keys and never talks to a model itself — it shells out to the agent CLIs and lets each use its own credentials. So:

  • claude and/or codex on PATH
  • each one signed in. This is the thing people trip over. If a CLI's session has expired, every handoff to that agent fails, usually with a 401. Check with claude -p "reply with OK" and codex exec "reply with OK" before assuming Cempala is at fault.

What's in it

Eight MCP tools: send_message, check_messages, create_task, claim_task, complete_task, dispatch, check_task, approve_path.

Handoffs run offline by default, and every result reports which enforcement actually applied — sandboxed (OS-level, Codex), tools_only (Claude's Bash can still reach the network) or allowed. The distinction is reported honestly rather than flattened into a reassuring default.

Work is confined to your home directory unless you explicitly approve a path, a baseline denylist (~/.ssh, ~/.aws, ~/.gnupg, …) can't be weakened by editing config, and every call is written to an audit log.

Known limitations

  • A dispatched agent that genuinely dies without producing output can read as running for up to 30 minutes before the reaper clears it. This is deliberate: the alternative — treating a dead process as a failed task — reported failures for work that had actually completed, which is worse, because the calling agent then redoes it.
  • claude and codex credentials expire on their own schedule. Cempala surfaces the resulting 401 rather than hiding it.
  • No native Windows ARM64 build yet (see above).

Verifying the download

f28b795293d632660b535958e7bcbe6943a4aecce858581a4060bc50b4cce62b  cempala-darwin-arm64
ed3459a9b005f66611f5e27aaa30984036ab15a779db1909ae29775c7ca912e6  cempala-darwin-x64
55ab78922f25a7e017710349bff1054467bbe9b6000d81501f4c86e96e586dcf  cempala-linux-arm64
f95b942782badbdea39de1ccac993b7c6b2425d43e563943d99a1b0c66868101  cempala-linux-x64
ab11e8f277ecb34a0bd500ebc3ea920e3467ad08a62bcb329a216584469df0a9  cempala-windows-x64.exe
sha256sum -c checksums.txt --ignore-missing
(Get-FileHash .\cempala-windows-x64.exe -Algorithm SHA256).Hash.ToLower()