An agent harness is the runtime around a language model: it calls the model and its tools, keeps the conversation state, applies policy, and carries a task through to the end. Karya is a small one that runs in your terminal, built so the engineer stays in the control seat.
go install github.com/prateek041/karya/cmd/karya@latestKarya keeps one model request in flight per session. Reads proceed on their own. Anything that writes a file or runs a command stops at the approval gate. A denial goes back to the model as a structured result, so it can try another approach or stop.
write, edit and bash show a unified diff or the exact command, then stop and wait. Try it: press a or d on your keyboard, or click a key.
read runs automatically. write and edit show a unified diff, bash shows the exact command and its timeout, and each waits for you to press a key.
read cmd/list.go offset=14 limit=3
14 │ cmd := &cobra.Command{Use: "list"}
15 │ cmd.RunE = func(c *cobra.Command, …
16 │ tasks := store.List()cmd/.list_test.go.tmp → cmd/list_test.go mode 0644 kept · atomic replace
edit cmd/list.go error ambiguous_match old_text found 2 times
exit 1 · 6.2s · timed_out false output truncated, 48,213 bytes original --- FAIL: TestListJSON (0.00s)
Press d and the call does not run. The model receives a structured result saying so, and can propose something else or stop. Nothing runs that you did not see first.
tool_result → model
{ "tool": "bash", "status": "denied", "by": "user" }The agent loop depends on a single internal provider interface. Set HARNESS_MODEL and the loop, tools and approvals stay the same.
Sessions are stored in a canonical message format, never in a provider's wire types, so a log does not depend on the provider that wrote it.
$ export HARNESS_MODEL=claude-sonnet-5 $ export HARNESS_MAX_COST=1.00 $ karya
Every bash command, and every process it starts, runs under the operating system's sandbox: sandbox-exec on macOS, bubblewrap with seccomp on Linux. Writes are limited to the project root and a per-session temp directory. Network egress is denied unless you enable it.
The file tools are confined to the root. They reject absolute paths, paths that escape with .., and symlinks that resolve outside it.
Sessions are append-only JSONL logs stored outside the project. They are keyed to the git remote URL rather than the local path, and the log contains no absolute paths or hostnames. On resume, Karya compares the recorded HEAD and dirty-tree flag with the live repository and tells the model what changed.
Each turn records the model name, input and output tokens, estimated cost, duration and tool outcomes, in the session and in structured local logs. HARNESS_MAX_COST and HARNESS_MAX_STEPS stop the loop safely, say which limit was hit, and leave the session resumable.
| turn | model | in | out | cost | duration | tool | outcome |
|---|---|---|---|---|---|---|---|
| 1 | claude-sonnet-5 | 812 | 188 | $0.0034 | 4.2s | read | ok · auto |
| 2 | claude-sonnet-5 | 1,046 | 421 | $0.0061 | 9.8s | edit | approved |
| 3 | claude-sonnet-5 | 1,297 | 96 | $0.0034 | 18.3s | bash | approved · exit 0 |
| 4 | claude-sonnet-5 | 1,657 | 388 | $0.0071 | 8.7s | none | final answer |
| total | 0 | 0 | $0.0000 | 0.0s |
stopped HARNESS_MAX_COST reached (1.00) session 7f3a2c is resumable: karya --resume 7f3a2c
Four tools, one loop, one provider interface, one binary. The source is on GitHub under the license in the repository. Karya is pre-1.0, and interfaces may change between releases.
Issues and pull requests are welcome. For anything larger than a bug fix, open an issue first so the change can be weighed against the roadmap.
go install github.com/prateek041/karya/cmd/karya@latest