Agents
In development. None of this is in the macOS app yet. A command line prototype, smalti-conductor, implements the agent library, lessons and the Conductor; the last section describes what it does today. Details will change.
Most work in a software factory repeats: fix a failing test, review a change, upgrade a dependency, ship a release. Smalti is designed around reusable agents for that work, defined once and adjusted after each run, rather than a new prompt in a new session every time.
An agent definition
A small folder of plain files, kept under version control like code:
- a role prompt;
- the tools it may use and the policy for using them;
- the checks that decide when its work is done;
- the skills and MCP servers it needs;
lessons.md, the things it has learned from earlier runs.
Definitions live in a repository under .smalti/agents/ or in a personal library. The prototype seeds the personal library with three: fix-failing-tests, review-change and ship-release.
How an agent improves
After each run Smalti collects the evidence of where the run went wrong: redirects and interrupts from the decision timeline, denied actions, checks that failed before they passed, and corrections the person made. The agent then proposes a change to its own definition, usually a new lesson or a tighter instruction, as a diff.
You accept, edit or reject the diff. Where possible, a proposed change is replayed against recent tasks of the same kind before it is kept, and its effect on the check pass rate and the number of interruptions is shown. A definition never changes without you accepting a diff.
Agents you bring
Smalti does not require its own format. The plan is to:
- import Claude Code subagents (
.claude/agents/*.md), skills and MCP servers as they are; - run other agent command line tools (Codex, Gemini, local models, or any command that reads a prompt and works in a directory) through an adapter, so they get the same inbox, policy, checks and action log as Claude Code;
- let imported agents opt in to lessons, without ever rewriting them unless you accept the diff.
With the Conductor
The Conductor will pick an agent from the library for each task, start it with its definition, and record the outcome against that version of the agent. Over time the library shows which agents do their job and which do not.
The prototype today
smalti-conductor starts Claude Code with an MCP server of fleet tools and only read-only built-in tools, so everything it changes goes through those tools.
- Workers. Each worker is a tmux session running an agent, in the repository or in a new Git worktree, after running
.smalti/setup.shif the repository has one. Finished worktrees are archived (refused if anything is uncommitted); the branch stays. Pull requests are opened withgh. - Policy. A
policy.tomlsets allow, ask or deny per tool and argument pattern, plus a budget of concurrent workers. The most restrictive matching rule wins. Destructive tools (approving a worker's permission request, freezing processes, opening a pull request, accepting a lesson) are denied unless a rule names them. - Action log. Every call, the policy's decision and the result are appended to
actions.jsonl. - Missions. A goal and a tree of up to 50 tasks, each with its worker, branch, agent version and last check result. A task cannot be marked done unless a passing check ran after it last started: the first working form of "done means verified". Checks are the project's own commands plus CI status from GitHub Actions or Commitwell.
- Lessons. Proposals are written as diffs to a definition and wait for
smalti-conductor acceptorreject. Outcomes are recorded per agent version in a ledger.
Everything is stored as plain files in ~/.smalti/conductor. smalti-conductor serve shows the factory and each mission as a web page, on port 47822 of this machine and its Tailscale addresses, protected by a token. The Mac app already embeds it as the Mission panel; the iOS and Windows apps are meant to do the same.