Local-first harness for running agentic coding workflows from a Web UI.
Bundled desktop app: v0.1.1 · Python backend 0.1.1 ·
Frontend 0.1.1 — prebuilt for macOS Apple Silicon:
Kajas-0.1.1-macos-arm64.dmg (see Desktop App for the unsigned-install steps).
Kajas runs on your own machine. Pick a project and a workflow, write
a task prompt, review the generated plan when configured to pause, then
let the configured agent profiles do the implementation while Kajas
records state, logs, approvals, and token usage in project-local
files. Everything stays on disk under <repo>/.kajas/ — no cloud, no
telemetry.
See docs/kajas-v1-design.md for the full
V1 design.
The same UI runs in the browser (kajas serve) and inside the bundled
desktop app.
Score local OpenAI-compatible models (llama.cpp, Ollama, LM Studio, …) across tool calling, context retrieval, coding, and latency, then compare saved runs. Scoring is out of 100: tool calling 50, context retrieval 25, coding 10, and latency/reliability 15. The tool-calling score includes a 17-scenario agency suite worth 35 points — a fixed Halcyon Systems sandbox grading tool selection, scheduling, multi-step incident chains, currency conversion, restraint, and focus against decoy tools.
Benchmark — compare saved runs by score, runtime, model, and context size.
Run Benchmark — start or inspect runs; score breakdown, per-test details (tool schema, multistep, JSON, and the agency.* scenarios), and history.
More views
![]() |
![]() |
| Projects — register repos so Kajas can run workflows against them. | Health — backend self-checks and opt-in tool smoke tests. |
- Backend: Python 3.11+ / FastAPI / uvicorn.
- Frontend: React 18 / Vite / Tailwind CSS / TypeScript, served through the FastAPI process in production.
- Desktop: Tauri v2 shell that launches the Python backend as a sidecar and opens a native webview on the served UI.
- Adapters: Codex CLI, Pi CLI, plus a built-in
fakeadapter for tests and demos. - Auth: Argon2id passphrase + signed session cookie (HttpOnly, SameSite=Lax).
- State: global YAML config at
~/.config/kajas/config.yaml, per-project config at<repo>/.kajas/config.yaml. Per-run artifacts under<repo>/.kajas/runs/<id>/.
# 1. Install backend deps and the kajas package
pip install -e backend/
# 2. Install frontend deps
cd frontend && npm install && cd ..
# 3. Write a starter global config and set a passphrase
kajas init # writes config.yaml; prompts for a passphrase
# 4. Start the dev server (backend on :8765, Vite on :5173)
make dev # or: ./kajas --devOpen http://127.0.0.1:5173 and sign in with the passphrase you set in step 3.
Dev server settings can be adjusted with environment variables:
| Variable | Default | Purpose |
|---|---|---|
KAJAS_VITE_HOST |
0.0.0.0 |
Host Vite binds to. |
KAJAS_VITE_PORT |
5173 |
Vite dev server port. |
KAJAS_VITE_ALLOWED_HOSTS |
unset | Comma-separated host allow-list for Vite. Use * to allow any host. |
KAJAS_API_PROXY_TARGET |
http://127.0.0.1:8765 |
Backend target for Vite's /api proxy. |
For example, to reach Vite through a specific hostname:
KAJAS_VITE_HOST=0.0.0.0 KAJAS_VITE_ALLOWED_HOSTS=my-host.example.com make devThe starter global config includes the default real workflow:
agents:
planner:
tool: codex
model: gpt-5.5
role: planner
coder:
tool: pi
model: Qwen3.6-35B-A3B-Claude-4.7-Opus-Reasoning-Distilled.IQ4_XS.gguf
role: implementor
extra:
local_model: Qwen3.6-35B-A3B-Claude-4.7-Opus-Reasoning-Distilled.IQ4_XS.gguf
workflows:
default:
planner: planner
implementor: coderFor production-ish use, build the frontend once and serve it from the FastAPI process:
cd frontend && npm run build && cd ..
kajas serve --frontend-dir frontend/distKajas ships as a single native desktop app under
frontend/src-tauri/. The Tauri v2 shell starts the Python backend on
localhost, serves the built Vite UI through that backend, and opens a
native webview window against it — so the desktop app and the Web UI
are the same UI.
The bundled app version lives in three places that are kept in sync:
| File | Field |
|---|---|
frontend/src-tauri/tauri.conf.json |
version |
frontend/src-tauri/Cargo.toml |
version (the kajas-desktop crate) |
frontend/package.json |
version (matches the UI build) |
backend/pyproject.toml |
version (the Python backend/sidecar) |
All read 0.1.1 for this release.
A prebuilt macOS disk image is published on GitHub Releases for each version. The current release is v0.1.1:
→ Kajas-0.1.1-macos-arm64.dmg (Apple Silicon, size recorded on release)
| Target | macOS 11+ on Apple Silicon (arm64) |
| SHA-256 | recorded in releases/README.md after the release asset is built |
| Signing | ad-hoc signed — not notarized |
The build is unsigned, so Gatekeeper will block a double-click open. There is no Intel (x86_64) prebuilt yet — see Building from source below.
Installing on macOS (unsigned app):
- Download the
.dmgabove and verify the checksum:shasum -a 256 Kajas-0.1.1-macos-arm64.dmg # compare with the SHA-256 in releases/README.md for v0.1.1 - Open the
.dmg, drag Kajas into Applications. - Clear the quarantine attribute before first launch (one-time):
…or right-click Kajas.app → Open → confirm Open at the Gatekeeper prompt the first time.
xattr -cr /Applications/Kajas.app
- Launch Kajas. It starts the local Python backend and opens the webview.
⚠️ Because the app is ad-hoc signed, any macOS user can run it, but they must perform step 3 above. A notarized build requires an Apple Developer ID; seereleases/README.mdfor the notarization TODO.
Desktop builds package the Python backend as a Tauri sidecar with
PyInstaller, so the installed app does not need python3 -m kajas.cli
or any Python environment at runtime. The sidecar binary lives under
frontend/src-tauri/binaries/.
tauri.conf.json sets bundle.targets: "all", so per platform you get:
| OS | Bundles |
|---|---|
| Linux | .deb, .rpm, and .AppImage |
| macOS | .app and .dmg |
| Windows | .msi and .exe (NSIS) |
- Rust/Cargo via https://rustup.rs/.
- Python 3 plus
venvsupport for the local PyInstaller packaging environment. - The frontend dependencies installed with
npm installinfrontend/.
From the frontend/ package:
# Run the desktop app against the current source (live backend sidecar build)
npm run desktop:dev
# Produce a local desktop binary without bundling installers
npm run desktop:build
# Produce all configured installers for this host (deb/rpm/AppImage on Linux, etc.)
npm run desktop:bundle
# Linux packages only (deb + rpm)
npm run desktop:bundle:linux-packagesBy default the wrapper launches python3 -m kajas.cli serve with
PYTHONPATH pointed at ../backend only when running from source or
when KAJAS_BACKEND_CMD is set. Packaged apps launch the bundled
kajas-backend sidecar. Override KAJAS_DESKTOP_PORT if port 8765
is not available.
To produce the same .dmg locally (e.g. for Intel Macs, or to inspect
the build):
cd frontend
npm install
npm run tauri -- build --bundles dmg
# -> frontend/src-tauri/target/release/bundle/dmg/Kajas_0.1.1_aarch64.dmgOverride --target for a different architecture if your toolchain
supports it. See releases/README.md for the full publish recipe.
kajas init # write starter global config, set passphrase
kajas serve [--host H] [--port P] [--frontend-dir DIR]
kajas init-project NAME PATH [--no-bootstrap-dir]
kajas run --project NAME --workflow NAME --prompt "..." [--delete]
kajas doctor [--tool-smoke | --no-tool-smoke]
kajas run is a headless, auto-approving version of the Web UI's
"New Run" flow. It is convenient for smoke tests and CI.
The full HTTP/SSE surface is mounted under /api:
| Method | Path | Purpose |
|---|---|---|
POST |
/api/auth/login |
sign in with the local passphrase |
POST |
/api/auth/logout |
sign out |
POST |
/api/auth/bootstrap |
first-run passphrase setup |
GET |
/api/auth/status |
is auth enabled? do we need to bootstrap? |
GET |
/api/dashboard |
recent runs across all projects |
GET |
/api/projects |
list registered projects |
POST |
/api/projects |
register a project and bootstrap .kajas/ |
DELETE |
/api/projects/{name} |
unregister (keeps files) |
GET |
/api/config/global |
read global config |
PUT |
/api/config/global |
write global config |
GET |
/api/config/project?project=… |
read project config |
PUT |
/api/config/project?project=… |
write project config |
GET |
/api/config/merged?project=… |
read merged config (read-only) |
POST |
/api/runs |
create + start a run |
GET |
/api/runs/{id} |
run summary + persisted state |
GET |
/api/runs/{id}/events/stream |
SSE event stream |
POST |
/api/runs/{id}/approve-plan |
approve (optionally edit) the plan |
POST |
/api/runs/{id}/cancel |
graceful cancel |
DELETE |
/api/runs/{id} |
delete run folder |
GET |
/api/benchmarks |
list saved local model benchmark runs |
POST |
/api/benchmarks |
create + start a benchmark run |
GET |
/api/benchmarks/{id} |
benchmark summary, tests, raw request/response data |
DELETE |
/api/benchmarks/{id} |
delete saved benchmark run |
GET |
/api/health |
basic checks |
POST |
/api/health/tool-smoke |
opt-in tool smoke checks |
backend/kajas/
cli.py # argparse CLI
server.py # FastAPI app
auth.py # argon2 + session cookie
config.py # YAML schemas, deep merge, validation
projects.py # project registry + bootstrap
runs.py # run orchestrator + state machine
run_store.py # persistent run store
benchmarks.py # benchmark runner (tooling, context, coding, latency)
agency_bench.py # agency tool-use scenario suite (Halcyon Systems sandbox)
doctor.py # basic + tool-smoke checks
adapters/
base.py # Adapter / NormalizedEvent / HealthResult
fake.py # in-process fake (Milestone 1)
codex.py # codex exec --json (Milestone 2)
pi.py # pi --mode json (Milestone 2)
frontend/src/
App.tsx
main.tsx
lib/{api,types,format}.ts
pages/{Dashboard,Projects,Config,NewRun,RunDetail,Health,Login,Benchmark,BenchmarkRun}.tsx
components/StatusPill.tsx
frontend/src-tauri/ # Tauri v2 desktop shell + sidecar packaging
docs/kajas-v1-design.md
tests/ # pytest suite (config, auth, runs, API)
python3 -m pytest tests/The test suite uses a fake workflow and the FastAPI TestClient; it
does not invoke real Codex or Pi. The fake adapter supports hints
embedded in the prompt, e.g. <!-- kajas:fake mode=fail --> to
exercise the failure path.
- M1 (delivered): vertical skeleton with fake adapters, full config + auth + project model, dashboard / projects / config / new run / run detail / health UI, plan-approval gate, cancellation, restart-as-interrupted, basic doctor checks.
- M2 (delivered): real Codex and Pi adapters (best-effort
translation of the tool-specific event formats into
NormalizedEvent). - M3 (partial): verification command execution and recording, plan amendment flow. Resume/rerun from plan or implementation is intentionally left as a follow-up.
MIT — see the license field in backend/pyproject.toml.







