apps/sidecar/pi-agent, Bun + TypeScript) and talks to the frontend over an
NDJSON protocol on stdin/stdout. The direct benefit: long jobs never block the
interface, and a frontend crash never kills the work in progress.
The life of one turn
You will understand the agent’s behaviour faster from this loop than from any single module:1
Assemble context
Pull the session history, inject long-term memory and loaded skills, attach the
content of any @-mentioned files and any attachments. Whether compaction is needed
depends on how much of the window is already used.
2
Call the model
Hand the assembled context to the currently selected model and receive incremental
text or a set of tool calls. Switching models affects only this step.
3
Adjudicate each tool call
Every call passes the session mode first (may this tier write?), then the
approval level (must this tier ask?). Both must allow it before it proceeds.
4
If approval is needed, suspend
Suspending is not discarding: the sidecar holds the turn state, pushes an approval
event to the frontend, and resumes from exactly that step when your answer arrives.
A denied call comes back to the model as a blocked result so it can take another
route instead of deadlocking.
5
Execute and feed results back
The tool runs inside the sidecar, its result is appended to the context as a tool
message, and the loop returns to step 2. The turn ends when the model stops asking
for tools.
6
Persist
Transcript, tool calls, and checkpoints are written to session storage; a long
session triggers compaction, folding early turns into a summary.
Only step 2 belongs to the model. Everything else is deterministic local logic.
That is why switching models never changes permission behaviour — approval,
compaction, and subagent scheduling all live outside the model.
Built-in tools
The sidecar registers roughly these categories:Question deserves a mention: it is the channel through which the agent asks the user
something, suspending the turn when it needs to clarify a requirement or confirm a
decision, instead of guessing and continuing.
Session mode: whether it may write
agent
The full toolset. The default — reads, writes, and runs commands.
plan
Structurally read-only. It can read and plan, then uses
plan_exit to get approval before handing back to agent to implement.ask
A read-only subset with no
bash. For “just tell me, don’t touch anything”.goal
Full toolset plus cross-turn autonomy, for “finish this objective” work.
plan is separating planning from implementation by permission:
the reading phase gets read-only rights, and write rights only come back at the
implementation phase.
Approval level: whether it must ask
ask
write / edit / bash / config tools — every single one asks.
workspace-write
Inside the workspace and listed writable roots needs no approval; bash still asks, with “remember this class of command” support.
auto-edit
Writes and edits are fully auto-approved, including outside the workspace; bash still asks.
auto
Everything auto-approved.
Shift+Tab cycles
through them).
The approval card’s three buttons
“Allow and remember” generates a prefix rule, not an exact match:
(Exact-match rules without
* are still supported — that is the form you hand-write into
the list; the UI only ever produces prefix rules.)
Interpreter and destructive commands (node / bash / npx / pnpm dlx / sudo /
rm / cp / find) are not remembered — everything after the first word is the code
to execute or an arbitrary path, so one click would mean a permanent free pass. Their card
has no third button and simply says the approval is for this one time.
The writable-root list (directories that need no approval outside the workspace)
Inside the workspace writes need no approval and outside it they ask — but real work spans projects (a frontend and a backend directory, a shared library outside the monorepo), and asking every time is pure noise. The list fixes that: directories you put on it are treated as “inside the workspace”. Three files, unioned:
“Allow and remember” on the approval card writes into the machine-local file (local).
In other words: every file that can authorize anything lives on your own machine, so there
is no trust state a repository could forge — a root declared by the project layer only
shows up on the card as “this project requests X”.
The two dimensions are separate
Mode decides whether it may change things; approval level decides whether it must ask. They are independent dropdowns, and the combination is the actual behaviour. Collapsing them is the most common misunderstanding about Kova.Context compaction
Long conversations always hit the window limit. Compaction folds older turns into a summary while preserving key decisions, files already produced, and unfinished objectives, so a task keeps going instead of losing its thread.Long-term memory
Beyond in-session compaction, Kova keeps a layer of cross-session memory. Entries are viewable and editable under Settings → Memory. Memory lives in two layers: a global one at~/.kova/memory (shared across sessions and
workspaces) and a workspace one at <workspace>/.kova/memory. The sessions themselves
(transcript and traces) are not there — they live under sessions/ in the app data
directory.
Subagents
Subagents let the agent split large work and process it in parallel instead of grinding through it serially in one context.1
Task
Dispatch a subagent with its own context and toolset.
2
TaskWait
Wait for a subagent to finish and collect its result.
3
TaskList
List live subagents and their status.
4
TaskStop
Terminate a running subagent.
~/.kova/subagents (machine-wide) or <workspace>/.kova/subagents
(committable, shared with your team), both managed under Settings → Subagents. Discovery has
three layers: built-in, user-defined, and plugin-provided. Subagent activity is
persisted, so a long job survives a restart
(see docs/subagent-activity-persistence-design.md).
Scheduled tasks and unattended runs
Automations are scheduled with croner. Scheduled runs use a dedicated automation approval level — clearly, the most dangerous levels cannot be the default when nobody is watching. Results can be pushed to any service over webhooks.Next
Beyond tools, there are workspaces you can actually open.
