📦 Mini Me
931b4ab parent: 5a8a838 added
.tickets/issues/0016-check-and-review-the-compress-mechanism.yaml +13 -0 | new file mode 100644 | ||
| @@ -0,0 +1,13 @@ | ||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | |
| 2 | +id: 16 | |
| 3 | +title: Check and review the compress mechanism | |
| 4 | +state: open | |
| 5 | +author: | |
| 6 | + name: k33g | |
| 7 | + email: ph.charriere@gmail.com | |
| 8 | +createdAt: 2026-09-17T06:17:29.481Z | |
| 9 | +updatedAt: 2026-09-17T06:17:29.481Z | |
| 10 | +labels: [] | |
| 11 | +body: "" | |
| 12 | +tasks: [] | |
| 13 | +comments: [] | |
| new file mode 100644 | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | ||
| 2 | +id: 16 | ||
| 3 | +title: Check and review the compress mechanism | ||
| 4 | +state: open | ||
| 5 | +author: | ||
| 6 | + name: k33g | ||
| 7 | + email: ph.charriere@gmail.com | ||
| 8 | +createdAt: 2026-09-17T06:17:29.481Z | ||
| 9 | +updatedAt: 2026-09-17T06:17:29.481Z | ||
| 10 | +labels: [] | ||
| 11 | +body: "" | ||
| 12 | +tasks: [] | ||
| 13 | +comments: [] | ||
modified
README.md +92 -1 | @@ -1,4 +1,95 @@ | ||
| 1 | 1 | # Mini Me |
| 2 | 2 | |
| 3 | -## ACP with Mini Me and Zed | |
| 3 | +> No, no. We don't gnaw on our kitty. | |
| 4 | 4 | |
| 5 | +**Mini Me** (`mm`) is a minimalist coding agent for **local LLMs**, written in Go, that runs in your terminal or inside an editor such as **Zed**. | |
| 6 | + | |
| 7 | +## What is mini-me? | |
| 8 | + | |
| 9 | +Mini Me started as the preparation of a *Tools-in-Action* talk for **Devoxx Belgium 2026**, where I explain how an **agent loop** works with a single tool: `bash`. The model asks for a command, the agent runs it, the output goes back to the model, and so on until the model answers. | |
| 10 | + | |
| 11 | +From there it grew into the coding agent I wanted for local models (Docker Model Runner, llama.cpp): small enough to be read in an afternoon, and careful about what a small model does wrong: | |
| 12 | + | |
| 13 | +- **hallucinations** — the agent shows the real output of every command before the model comments on it, and the prompt forbids stating anything a command did not return; | |
| 14 | +- **silent failures** — a failing command, a refused edit or a rejected permission goes back to the model as text, so it reads why and tries something else; | |
| 15 | +- **endless loops** — the same action with the same result, repeated, is detected and the model is told to change approach; a watchdog catches a connection that went silent. | |
| 16 | + | |
| 17 | +What makes Mini Me a bit special is that it was designed from the start to be used **from an editor through the [Agent Client Protocol (ACP)](https://agentclientprotocol.com)**: the same loop runs in a terminal REPL, in a full-screen terminal window, and inside Zed. | |
| 18 | + | |
| 19 | +## Install | |
| 20 | + | |
| 21 | +You need Go 1.25+ and a model server that can emit tool calls: Docker Model Runner (default) or Llamacpp. | |
| 22 | + | |
| 23 | +```bash | |
| 24 | +git clone https://git.rickub.com/bots-garden/mini-me.git | |
| 25 | +cd mini-me | |
| 26 | +./install.sh # go build, then copies mm to /usr/local/bin | |
| 27 | +mm -h | |
| 28 | +``` | |
| 29 | +> `install.sh` works only on macOS and Linux | |
| 30 | + | |
| 31 | +Or download a binary from the [releases page](https://rickub.com/bots-garden/mini-me/releases) and put it on your `PATH`. | |
| 32 | + | |
| 33 | +Then pick a configuration file. Two are shipped: `agent.dmr.yaml` for Docker Model Runner and `agent.llamacpp.yaml` for llama.cpp. Copy one to `agent.yaml` in your project and adjust the `model:` and `system:` keys to taste. | |
| 34 | + | |
| 35 | +## Use | |
| 36 | + | |
| 37 | +The base command is `mm`. Run it **from the directory of the project you want to work on**: that is where commands run and where files are read and written. | |
| 38 | + | |
| 39 | +### 1. Terminal, line by line | |
| 40 | + | |
| 41 | +```bash | |
| 42 | +mm # reads ./agent.yaml | |
| 43 | +mm agent.dmr.yaml # or name the file | |
| 44 | +``` | |
| 45 | + | |
| 46 | +Type a question at the `>` prompt. Each command the model runs is shown with its output, and every turn ends with a recap (`⚙ 2 command(s)`). Useful commands: `/help`, `/new`, `/compact`, `/quit`. | |
| 47 | + | |
| 48 | +### 2. Terminal, one window | |
| 49 | + | |
| 50 | +```bash | |
| 51 | +mm -tui | |
| 52 | +``` | |
| 53 | + | |
| 54 | +The conversation above, a box to type in below. **Enter** sends, **Ctrl-J** adds a line, **Esc** cancels a turn, **F1** lists the keys. With `permissions: ask` in the YAML, the agent asks before running a command or writing a file. | |
| 55 | + | |
| 56 | +### 3. Inside an editor (ACP) | |
| 57 | + | |
| 58 | +```bash | |
| 59 | +mm -acp | |
| 60 | +``` | |
| 61 | + | |
| 62 | +Speaks the Agent Client Protocol on stdin/stdout. You never run this one by hand: the editor launches it. See the next section. | |
| 63 | + | |
| 64 | +## Use with the Zed editor | |
| 65 | + | |
| 66 | +1. Install `mm` on your `PATH` (see above). | |
| 67 | +2. In Zed, open `settings.json` and register the agent: | |
| 68 | + | |
| 69 | + ```json | |
| 70 | + { | |
| 71 | + "agent_servers": { | |
| 72 | + "mini-me": { | |
| 73 | + "type": "custom", | |
| 74 | + "command": "mm", | |
| 75 | + "args": ["-acp"], | |
| 76 | + "env": { "AGENT_CONFIG": "/absolute/path/to/agent.yaml" } | |
| 77 | + } | |
| 78 | + } | |
| 79 | + } | |
| 80 | + ``` | |
| 81 | + | |
| 82 | + `AGENT_CONFIG` must be an **absolute** path: Zed starts `mm` in the project directory, and a relative path would not be found. `"type": "custom"` is required. | |
| 83 | + | |
| 84 | +3. Open the Agent panel, pick **mini-me**, and talk to it. Every `bash`, `read_file`, `write_file` and `edit_file` call shows up as a tool call, and Zed asks you to allow it before it runs. File changes appear as real diffs. | |
| 85 | +4. Handy things: **Escape** cancels a turn; `/new` and `/compact` are in the slash-command menu; `@file` attaches a file for the model to read; with `sessions.enabled: true` in the YAML, **Reload Agent** brings the whole conversation back. | |
| 86 | +5. When something looks wrong, open `dev: open acp logs`: the banner, warnings and the agent's `[acp] …` trail go there. | |
| 87 | + | |
| 88 | +### Other ACP editors | |
| 89 | + | |
| 90 | +Anything that speaks ACP can host `mm -acp` the same way: **JetBrains IDEs** (IntelliJ IDEA, PyCharm, WebStorm…), **Neovim** (CodeCompanion, agentic.nvim, avante.nvim), **Emacs** (agent-shell), **Visual Studio Code** through third-party ACP extensions, Sublime Text and Qt Creator through their ACP plugins. The registration details differ; the agent side does not. | |
| 91 | + | |
| 92 | +## Learn more | |
| 93 | + | |
| 94 | +- [Documentation](docs/README.md) — tutorials, how-to guides, reference and explanations, in English and French. | |
| 95 | +- The `skills/` directory: Markdown procedures the agent can load with its `read_skill` tool. | |
| @@ -1,4 +1,95 @@ | |||
| 1 | # Mini Me | 1 | # Mini Me |
| 2 | 2 | ||
| 3 | -## ACP with Mini Me and Zed | 3 | +> No, no. We don't gnaw on our kitty. |
| 4 | 4 | ||
| 5 | +**Mini Me** (`mm`) is a minimalist coding agent for **local LLMs**, written in Go, that runs in your terminal or inside an editor such as **Zed**. | ||
| 6 | + | ||
| 7 | +## What is mini-me? | ||
| 8 | + | ||
| 9 | +Mini Me started as the preparation of a *Tools-in-Action* talk for **Devoxx Belgium 2026**, where I explain how an **agent loop** works with a single tool: `bash`. The model asks for a command, the agent runs it, the output goes back to the model, and so on until the model answers. | ||
| 10 | + | ||
| 11 | +From there it grew into the coding agent I wanted for local models (Docker Model Runner, llama.cpp): small enough to be read in an afternoon, and careful about what a small model does wrong: | ||
| 12 | + | ||
| 13 | +- **hallucinations** — the agent shows the real output of every command before the model comments on it, and the prompt forbids stating anything a command did not return; | ||
| 14 | +- **silent failures** — a failing command, a refused edit or a rejected permission goes back to the model as text, so it reads why and tries something else; | ||
| 15 | +- **endless loops** — the same action with the same result, repeated, is detected and the model is told to change approach; a watchdog catches a connection that went silent. | ||
| 16 | + | ||
| 17 | +What makes Mini Me a bit special is that it was designed from the start to be used **from an editor through the [Agent Client Protocol (ACP)](https://agentclientprotocol.com)**: the same loop runs in a terminal REPL, in a full-screen terminal window, and inside Zed. | ||
| 18 | + | ||
| 19 | +## Install | ||
| 20 | + | ||
| 21 | +You need Go 1.25+ and a model server that can emit tool calls: Docker Model Runner (default) or Llamacpp. | ||
| 22 | + | ||
| 23 | +```bash | ||
| 24 | +git clone https://git.rickub.com/bots-garden/mini-me.git | ||
| 25 | +cd mini-me | ||
| 26 | +./install.sh # go build, then copies mm to /usr/local/bin | ||
| 27 | +mm -h | ||
| 28 | +``` | ||
| 29 | +> `install.sh` works only on macOS and Linux | ||
| 30 | + | ||
| 31 | +Or download a binary from the [releases page](https://rickub.com/bots-garden/mini-me/releases) and put it on your `PATH`. | ||
| 32 | + | ||
| 33 | +Then pick a configuration file. Two are shipped: `agent.dmr.yaml` for Docker Model Runner and `agent.llamacpp.yaml` for llama.cpp. Copy one to `agent.yaml` in your project and adjust the `model:` and `system:` keys to taste. | ||
| 34 | + | ||
| 35 | +## Use | ||
| 36 | + | ||
| 37 | +The base command is `mm`. Run it **from the directory of the project you want to work on**: that is where commands run and where files are read and written. | ||
| 38 | + | ||
| 39 | +### 1. Terminal, line by line | ||
| 40 | + | ||
| 41 | +```bash | ||
| 42 | +mm # reads ./agent.yaml | ||
| 43 | +mm agent.dmr.yaml # or name the file | ||
| 44 | +``` | ||
| 45 | + | ||
| 46 | +Type a question at the `>` prompt. Each command the model runs is shown with its output, and every turn ends with a recap (`⚙ 2 command(s)`). Useful commands: `/help`, `/new`, `/compact`, `/quit`. | ||
| 47 | + | ||
| 48 | +### 2. Terminal, one window | ||
| 49 | + | ||
| 50 | +```bash | ||
| 51 | +mm -tui | ||
| 52 | +``` | ||
| 53 | + | ||
| 54 | +The conversation above, a box to type in below. **Enter** sends, **Ctrl-J** adds a line, **Esc** cancels a turn, **F1** lists the keys. With `permissions: ask` in the YAML, the agent asks before running a command or writing a file. | ||
| 55 | + | ||
| 56 | +### 3. Inside an editor (ACP) | ||
| 57 | + | ||
| 58 | +```bash | ||
| 59 | +mm -acp | ||
| 60 | +``` | ||
| 61 | + | ||
| 62 | +Speaks the Agent Client Protocol on stdin/stdout. You never run this one by hand: the editor launches it. See the next section. | ||
| 63 | + | ||
| 64 | +## Use with the Zed editor | ||
| 65 | + | ||
| 66 | +1. Install `mm` on your `PATH` (see above). | ||
| 67 | +2. In Zed, open `settings.json` and register the agent: | ||
| 68 | + | ||
| 69 | + ```json | ||
| 70 | + { | ||
| 71 | + "agent_servers": { | ||
| 72 | + "mini-me": { | ||
| 73 | + "type": "custom", | ||
| 74 | + "command": "mm", | ||
| 75 | + "args": ["-acp"], | ||
| 76 | + "env": { "AGENT_CONFIG": "/absolute/path/to/agent.yaml" } | ||
| 77 | + } | ||
| 78 | + } | ||
| 79 | + } | ||
| 80 | + ``` | ||
| 81 | + | ||
| 82 | + `AGENT_CONFIG` must be an **absolute** path: Zed starts `mm` in the project directory, and a relative path would not be found. `"type": "custom"` is required. | ||
| 83 | + | ||
| 84 | +3. Open the Agent panel, pick **mini-me**, and talk to it. Every `bash`, `read_file`, `write_file` and `edit_file` call shows up as a tool call, and Zed asks you to allow it before it runs. File changes appear as real diffs. | ||
| 85 | +4. Handy things: **Escape** cancels a turn; `/new` and `/compact` are in the slash-command menu; `@file` attaches a file for the model to read; with `sessions.enabled: true` in the YAML, **Reload Agent** brings the whole conversation back. | ||
| 86 | +5. When something looks wrong, open `dev: open acp logs`: the banner, warnings and the agent's `[acp] …` trail go there. | ||
| 87 | + | ||
| 88 | +### Other ACP editors | ||
| 89 | + | ||
| 90 | +Anything that speaks ACP can host `mm -acp` the same way: **JetBrains IDEs** (IntelliJ IDEA, PyCharm, WebStorm…), **Neovim** (CodeCompanion, agentic.nvim, avante.nvim), **Emacs** (agent-shell), **Visual Studio Code** through third-party ACP extensions, Sublime Text and Qt Creator through their ACP plugins. The registration details differ; the agent side does not. | ||
| 91 | + | ||
| 92 | +## Learn more | ||
| 93 | + | ||
| 94 | +- [Documentation](docs/README.md) — tutorials, how-to guides, reference and explanations, in English and French. | ||
| 95 | +- The `skills/` directory: Markdown procedures the agent can load with its `read_skill` tool. | ||
modified
agent.dmr.yaml +30 -101 | @@ -1,85 +1,44 @@ | ||
| 1 | 1 | # Agent settings. |
| 2 | 2 | # |
| 3 | -# Every key is optional: what you leave out keeps its built-in default | |
| 4 | -# (see internal/config/config.go). Nothing here requires recompiling — | |
| 5 | -# `go run .` picks the file up at startup. | |
| 6 | -# | |
| 7 | -# Use another file with: AGENT_CONFIG=./fast.yaml go run . | |
| 8 | - | |
| 9 | -# Which server is behind the URL. Left out, it means `dmr`: every agent.yaml | |
| 10 | -# written before this key existed keeps working unchanged. `llamacpp` speaks | |
| 11 | -# the same OpenAI-compatible protocol, but with its own port, its own way of | |
| 12 | -# refusing tool calls (no --jinja) and its own words in error messages — the | |
| 13 | -# provider is what turns those into one useful line. See agent.llamacpp.yaml. | |
| 14 | -# AGENT_PROVIDER overrides this key for one run. | |
| 15 | 3 | provider: dmr |
| 16 | 4 | |
| 17 | -# The chat model, in the provider's own naming (DMR: `ai/…`, `hf.co/…`). It | |
| 18 | -# MUST be able to emit tool_calls, otherwise the agent never runs a single | |
| 19 | -# command. AGENT_MODEL overrides it. | |
| 20 | 5 | model: huggingface.co/jetbrains/mellum2-12b-a2.5b-instruct-gguf-q4_k_m:Q4_K_M |
| 6 | +baseUrl: http://localhost:12434/engines/v1 | |
| 7 | +fallback: http://host.docker.internal:12434/engines/v1 | |
| 8 | + | |
| 21 | 9 | |
| 22 | -# Endpoint. `baseUrl` is tried first; if it does not answer, `fallback` is used | |
| 23 | -# — handy from a container or a sandbox, where the host is reachable as | |
| 24 | -# host.docker.internal. Both are LEFT OUT here on purpose: each provider has its | |
| 25 | -# defaults, and an explicit value is always respected — so with DMR's URL | |
| 26 | -# written in this file, `AGENT_PROVIDER=llamacpp ./bob agent.yaml` still talked | |
| 27 | -# to port 12434. Leaving them out keeps the file provider-neutral. These are the | |
| 28 | -# values the `dmr` provider fills in: | |
| 29 | -# baseUrl: http://localhost:12434/engines/v1 | |
| 30 | -# fallback: http://host.docker.internal:12434/engines/v1 | |
| 31 | -# AGENT_BASE_URL — or, for dmr, the older DMR_BASE_URL — overrides both without | |
| 32 | -# probing. `fallback: ""` (an explicit empty string) disables the fallback. | |
| 33 | - | |
| 34 | -# The NAME of the environment variable holding the API key — never the key | |
| 35 | -# itself, so this file can be committed and shown on screen. DMR ignores the | |
| 36 | -# key, so nothing is needed here; llama-server checks one only when started | |
| 37 | -# with --api-key (default variable: LLAMA_API_KEY). | |
| 38 | -# apiKeyEnv: LLAMA_API_KEY | |
| 39 | - | |
| 40 | -# How many tokens the server actually serves. The model does not know this: | |
| 41 | -# it is what the operator started the server with (`llama-server -c 32768`, | |
| 42 | -# `docker model configure`). Shown at start-up next to its origin, because a | |
| 43 | -# number without an origin never gets corrected. 0 = ask the server | |
| 44 | -# (llama-server tells its n_ctx on /props), else "unknown". | |
| 10 | +# How many tokens the server actually serves. | |
| 11 | +# 0 = ask the server | |
| 45 | 12 | contextWindow: 0 |
| 46 | 13 | |
| 47 | -# Max number of characters a tool returns to the model. Context safeguard: | |
| 48 | -# beyond that, the output is truncated (beginning + end kept). | |
| 14 | +# Max number of characters a tool returns to the model. | |
| 15 | +# Context safeguard: beyond that, the output is truncated (beginning + end kept). | |
| 49 | 16 | maxOutput: 16000 |
| 50 | 17 | |
| 51 | -# Max number of model ↔ tools round trips for a single question. Raise it for | |
| 52 | -# tasks that need long chains of commands. | |
| 18 | +# Max number of model <-> tools round trips for a single question. | |
| 19 | +# Raise it for tasks that need long chains of commands. | |
| 53 | 20 | maxTurns: 40 |
| 54 | 21 | |
| 55 | -# The built-in file tools: read_file, write_file, edit_file. This is the switch | |
| 56 | -# this part exists for. `true`: the model edits files through tools it can SEE | |
| 57 | -# in its tool list. `false`: the agent is part 09 again — bash and read_skill — | |
| 58 | -# and edits files through the `edit` CLI if it is on the PATH. Same binary, | |
| 59 | -# same prompts, two set-ups: measured side by side, that is the comparison | |
| 60 | -# (in part 07, `read_skill` as a tool was loaded 3/3 times where a catalogue | |
| 61 | -# in the prompt plus `cat` managed 1/11). Paths are relative to the current | |
| 62 | -# directory and are not confined to it. | |
| 22 | +# The `bash` tool. | |
| 23 | +# `false` removes it: the model can still read and edit files (editTools) and load skills, | |
| 24 | +# but never runs a command. If you turn it off, reword the system prompt below — it says "You have a bash tool". | |
| 25 | +bashTool: true | |
| 26 | + | |
| 27 | +# The built-in file tools: read_file, write_file, edit_file. | |
| 63 | 28 | editTools: true |
| 64 | 29 | |
| 65 | -# Where the `read_skill` tool looks for its markdown procedures. The path is | |
| 66 | -# relative to the CURRENT DIRECTORY, so run the agent from the directory that | |
| 67 | -# holds skills/ — not from src/. When the directory has no *.md file, the tool | |
| 68 | -# is not declared at all. | |
| 30 | +# Where the `read_skill` tool looks for its markdown procedures. | |
| 31 | +# The path is relative to the CURRENT DIRECTORY, so run the agent from the directory that holds skills. | |
| 69 | 32 | skillsDir: skills |
| 70 | 33 | |
| 71 | -# Persistent sessions, per project. When enabled, every conversation is saved | |
| 72 | -# as one JSON file under `dir` (relative to the directory the agent is started | |
| 73 | -# from; the editor's project in ACP mode), rewritten after each turn. In the | |
| 74 | -# terminal, `/sessions` lists them and `/resume` continues the last one; in an | |
| 75 | -# editor, "Reload Agent" (ACP session/load) replays one. `/new` always starts | |
| 76 | -# a fresh session and file. Add `.mm/` to the project's .gitignore. | |
| 34 | +# Persistent sessions, per project. | |
| 35 | +# When enabled, every conversation is saved as one JSON file under `dir` | |
| 77 | 36 | sessions: |
| 78 | 37 | enabled: false |
| 79 | 38 | dir: .mm/sessions |
| 80 | 39 | |
| 81 | -# How many lines of a command's output are echoed to the terminal. "Show me | |
| 82 | -# that file" means show it: running `cat` is not displaying it. 0 disables it. | |
| 40 | +# How many lines of a command's output are echoed to the terminal. | |
| 41 | +# 0 disables it. | |
| 83 | 42 | previewLines: 20 |
| 84 | 43 | |
| 85 | 44 | # The system prompt: what the agent is, and what it is allowed to do. |
| @@ -155,64 +114,34 @@ system: | | ||
| 155 | 114 | # we want precise and reproducible answers, not creativity. |
| 156 | 115 | sampling: |
| 157 | 116 | temperature: 0.0 |
| 158 | - # Une seule commande par tour. Sans ça, le modèle peut en demander | |
| 159 | - # plusieurs d'un coup : Genkit les exécute EN PARALLÈLE, dans le même | |
| 160 | - # répertoire, et on ne sait plus quelle sortie appartient à quelle | |
| 161 | - # commande. Pour le détecteur de boucle, c'est pire : deux appels du même | |
| 162 | - # tour s'enregistrent dans un ordre indéterminé, et « la même action deux | |
| 163 | - # fois de suite » perd son sens. Mettre `true` pour retrouver le | |
| 164 | - # comportement par défaut de l'API. | |
| 165 | 117 | parallel_tool_calls: false |
| 166 | 118 | top_p: 0.9 |
| 167 | 119 | max_tokens: 4096 |
| 168 | 120 | |
| 169 | 121 | watchdogTimeout: 30s |
| 170 | 122 | |
| 171 | -# Context compression, carried over from part 08 (its CONTEXT_WINDOW.md has the | |
| 172 | -# reasoning). OFF by default: with `enabled: false` the agent behaves exactly | |
| 173 | -# as before. Why it exists: nothing in the agent ever shortens the history — | |
| 174 | -# measured with the fake engine of part 03, the messages sent to the model grew | |
| 175 | -# 2 → 5 → 7 → 9 over four requests, and every `bash` output (up to maxOutput | |
| 176 | -# characters) and every skill read stays until /quit. On a local model the | |
| 177 | -# window is fixed at load time and the prefill runs on the laptop, so a long | |
| 178 | -# session first shows up as a false "[watchdog: connection hang]", then as an | |
| 179 | -# error from the server. | |
| 180 | -# | |
| 181 | -# The window it measures against is the top-level `contextWindow` above — ONE | |
| 182 | -# key for the banner and for the trigger, so the two cannot disagree. When | |
| 183 | -# that key is 0, the value the provider's probe learned from the server is | |
| 184 | -# used (llama-server tells its n_ctx on /props; DMR tells nothing). When both | |
| 185 | -# are unknown, only `maxMessages` can trigger, and the agent says so at | |
| 186 | -# start-up. | |
| 123 | +# Context compression | |
| 187 | 124 | context: |
| 188 | 125 | enabled: false |
| 189 | 126 | |
| 190 | - # Compress when the history reaches this share of the window. 75 leaves a | |
| 191 | - # quarter for the next question, the outputs of its turns and the answer — | |
| 192 | - # a single `bash` output can be maxOutput characters, about 4-5k tokens. | |
| 127 | + # Compress when the history reaches this share of the window. | |
| 193 | 128 | threshold: 75 |
| 194 | 129 | |
| 195 | - # Fallback on the message count, for when the window is unknown or the | |
| 196 | - # estimate is off. One command costs 2 messages (call + response), so 80 is | |
| 197 | - # roughly 30 commands of history. | |
| 130 | + # Fallback on the message count, for when the window is unknown. | |
| 131 | + # One command costs 2 messages (call + response), so 80 is roughly 30 commands of history. | |
| 198 | 132 | maxMessages: 80 |
| 199 | 133 | |
| 200 | - # Question turns kept raw at the end. The recent turns are where the model | |
| 201 | - # works; summarising them makes it re-run what it just did. Three covers | |
| 202 | - # "do X", "now fix it", "and test it" — the usual shape of a demo. | |
| 134 | + # How many recent questions (with their commands and answers) are kept as-is instead of being summarised. | |
| 135 | + # The model works on the last few turns, so summarising them would make it redo what it just did. | |
| 203 | 136 | keepLastTurns: 3 |
| 204 | 137 | |
| 205 | - # max_tokens of the summary request. Seven sections of one line per item | |
| 206 | - # fit in far less; the cap stops a runaway model from filling the window it | |
| 207 | - # was asked to empty. | |
| 138 | + # max_tokens of the summary request. | |
| 208 | 139 | summaryMaxTokens: 1200 |
| 209 | 140 | |
| 210 | - # Replaces the built-in summary prompt (08-context-compression/CONTEXT_WINDOW.md | |
| 211 | - # § 4.1) when set. Empty = built-in. | |
| 141 | + # Replaces the built-in summary prompt when set. | |
| 142 | + # Empty = built-in. | |
| 212 | 143 | prompt: "" |
| 213 | 144 | |
| 214 | - # One line after each compression: what was replaced, what was kept, | |
| 215 | - # tokens before → after. Worth showing on a screen, noise in a log. | |
| 216 | 145 | showStats: true |
| 217 | 146 | |
| 218 | 147 | displayCommands: true |
| @@ -1,85 +1,44 @@ | |||
| 1 | # Agent settings. | 1 | # Agent settings. |
| 2 | # | 2 | # |
| 3 | -# Every key is optional: what you leave out keeps its built-in default | ||
| 4 | -# (see internal/config/config.go). Nothing here requires recompiling — | ||
| 5 | -# `go run .` picks the file up at startup. | ||
| 6 | -# | ||
| 7 | -# Use another file with: AGENT_CONFIG=./fast.yaml go run . | ||
| 8 | - | ||
| 9 | -# Which server is behind the URL. Left out, it means `dmr`: every agent.yaml | ||
| 10 | -# written before this key existed keeps working unchanged. `llamacpp` speaks | ||
| 11 | -# the same OpenAI-compatible protocol, but with its own port, its own way of | ||
| 12 | -# refusing tool calls (no --jinja) and its own words in error messages — the | ||
| 13 | -# provider is what turns those into one useful line. See agent.llamacpp.yaml. | ||
| 14 | -# AGENT_PROVIDER overrides this key for one run. | ||
| 15 | provider: dmr | 3 | provider: dmr |
| 16 | 4 | ||
| 17 | -# The chat model, in the provider's own naming (DMR: `ai/…`, `hf.co/…`). It | ||
| 18 | -# MUST be able to emit tool_calls, otherwise the agent never runs a single | ||
| 19 | -# command. AGENT_MODEL overrides it. | ||
| 20 | model: huggingface.co/jetbrains/mellum2-12b-a2.5b-instruct-gguf-q4_k_m:Q4_K_M | 5 | model: huggingface.co/jetbrains/mellum2-12b-a2.5b-instruct-gguf-q4_k_m:Q4_K_M |
| 6 | +baseUrl: http://localhost:12434/engines/v1 | ||
| 7 | +fallback: http://host.docker.internal:12434/engines/v1 | ||
| 8 | + | ||
| 21 | 9 | ||
| 22 | -# Endpoint. `baseUrl` is tried first; if it does not answer, `fallback` is used | 10 | +# How many tokens the server actually serves. |
| 23 | -# — handy from a container or a sandbox, where the host is reachable as | 11 | +# 0 = ask the server |
| 24 | -# host.docker.internal. Both are LEFT OUT here on purpose: each provider has its | ||
| 25 | -# defaults, and an explicit value is always respected — so with DMR's URL | ||
| 26 | -# written in this file, `AGENT_PROVIDER=llamacpp ./bob agent.yaml` still talked | ||
| 27 | -# to port 12434. Leaving them out keeps the file provider-neutral. These are the | ||
| 28 | -# values the `dmr` provider fills in: | ||
| 29 | -# baseUrl: http://localhost:12434/engines/v1 | ||
| 30 | -# fallback: http://host.docker.internal:12434/engines/v1 | ||
| 31 | -# AGENT_BASE_URL — or, for dmr, the older DMR_BASE_URL — overrides both without | ||
| 32 | -# probing. `fallback: ""` (an explicit empty string) disables the fallback. | ||
| 33 | - | ||
| 34 | -# The NAME of the environment variable holding the API key — never the key | ||
| 35 | -# itself, so this file can be committed and shown on screen. DMR ignores the | ||
| 36 | -# key, so nothing is needed here; llama-server checks one only when started | ||
| 37 | -# with --api-key (default variable: LLAMA_API_KEY). | ||
| 38 | -# apiKeyEnv: LLAMA_API_KEY | ||
| 39 | - | ||
| 40 | -# How many tokens the server actually serves. The model does not know this: | ||
| 41 | -# it is what the operator started the server with (`llama-server -c 32768`, | ||
| 42 | -# `docker model configure`). Shown at start-up next to its origin, because a | ||
| 43 | -# number without an origin never gets corrected. 0 = ask the server | ||
| 44 | -# (llama-server tells its n_ctx on /props), else "unknown". | ||
| 45 | contextWindow: 0 | 12 | contextWindow: 0 |
| 46 | 13 | ||
| 47 | -# Max number of characters a tool returns to the model. Context safeguard: | 14 | +# Max number of characters a tool returns to the model. |
| 48 | -# beyond that, the output is truncated (beginning + end kept). | 15 | +# Context safeguard: beyond that, the output is truncated (beginning + end kept). |
| 49 | maxOutput: 16000 | 16 | maxOutput: 16000 |
| 50 | 17 | ||
| 51 | -# Max number of model ↔ tools round trips for a single question. Raise it for | 18 | +# Max number of model <-> tools round trips for a single question. |
| 52 | -# tasks that need long chains of commands. | 19 | +# Raise it for tasks that need long chains of commands. |
| 53 | maxTurns: 40 | 20 | maxTurns: 40 |
| 54 | 21 | ||
| 55 | -# The built-in file tools: read_file, write_file, edit_file. This is the switch | 22 | +# The `bash` tool. |
| 56 | -# this part exists for. `true`: the model edits files through tools it can SEE | 23 | +# `false` removes it: the model can still read and edit files (editTools) and load skills, |
| 57 | -# in its tool list. `false`: the agent is part 09 again — bash and read_skill — | 24 | +# but never runs a command. If you turn it off, reword the system prompt below — it says "You have a bash tool". |
| 58 | -# and edits files through the `edit` CLI if it is on the PATH. Same binary, | 25 | +bashTool: true |
| 59 | -# same prompts, two set-ups: measured side by side, that is the comparison | 26 | + |
| 60 | -# (in part 07, `read_skill` as a tool was loaded 3/3 times where a catalogue | 27 | +# The built-in file tools: read_file, write_file, edit_file. |
| 61 | -# in the prompt plus `cat` managed 1/11). Paths are relative to the current | ||
| 62 | -# directory and are not confined to it. | ||
| 63 | editTools: true | 28 | editTools: true |
| 64 | 29 | ||
| 65 | -# Where the `read_skill` tool looks for its markdown procedures. The path is | 30 | +# Where the `read_skill` tool looks for its markdown procedures. |
| 66 | -# relative to the CURRENT DIRECTORY, so run the agent from the directory that | 31 | +# The path is relative to the CURRENT DIRECTORY, so run the agent from the directory that holds skills. |
| 67 | -# holds skills/ — not from src/. When the directory has no *.md file, the tool | ||
| 68 | -# is not declared at all. | ||
| 69 | skillsDir: skills | 32 | skillsDir: skills |
| 70 | 33 | ||
| 71 | -# Persistent sessions, per project. When enabled, every conversation is saved | 34 | +# Persistent sessions, per project. |
| 72 | -# as one JSON file under `dir` (relative to the directory the agent is started | 35 | +# When enabled, every conversation is saved as one JSON file under `dir` |
| 73 | -# from; the editor's project in ACP mode), rewritten after each turn. In the | ||
| 74 | -# terminal, `/sessions` lists them and `/resume` continues the last one; in an | ||
| 75 | -# editor, "Reload Agent" (ACP session/load) replays one. `/new` always starts | ||
| 76 | -# a fresh session and file. Add `.mm/` to the project's .gitignore. | ||
| 77 | sessions: | 36 | sessions: |
| 78 | enabled: false | 37 | enabled: false |
| 79 | dir: .mm/sessions | 38 | dir: .mm/sessions |
| 80 | 39 | ||
| 81 | -# How many lines of a command's output are echoed to the terminal. "Show me | 40 | +# How many lines of a command's output are echoed to the terminal. |
| 82 | -# that file" means show it: running `cat` is not displaying it. 0 disables it. | 41 | +# 0 disables it. |
| 83 | previewLines: 20 | 42 | previewLines: 20 |
| 84 | 43 | ||
| 85 | # The system prompt: what the agent is, and what it is allowed to do. | 44 | # The system prompt: what the agent is, and what it is allowed to do. |
| @@ -155,64 +114,34 @@ system: | | |||
| 155 | # we want precise and reproducible answers, not creativity. | 114 | # we want precise and reproducible answers, not creativity. |
| 156 | sampling: | 115 | sampling: |
| 157 | temperature: 0.0 | 116 | temperature: 0.0 |
| 158 | - # Une seule commande par tour. Sans ça, le modèle peut en demander | ||
| 159 | - # plusieurs d'un coup : Genkit les exécute EN PARALLÈLE, dans le même | ||
| 160 | - # répertoire, et on ne sait plus quelle sortie appartient à quelle | ||
| 161 | - # commande. Pour le détecteur de boucle, c'est pire : deux appels du même | ||
| 162 | - # tour s'enregistrent dans un ordre indéterminé, et « la même action deux | ||
| 163 | - # fois de suite » perd son sens. Mettre `true` pour retrouver le | ||
| 164 | - # comportement par défaut de l'API. | ||
| 165 | parallel_tool_calls: false | 117 | parallel_tool_calls: false |
| 166 | top_p: 0.9 | 118 | top_p: 0.9 |
| 167 | max_tokens: 4096 | 119 | max_tokens: 4096 |
| 168 | 120 | ||
| 169 | watchdogTimeout: 30s | 121 | watchdogTimeout: 30s |
| 170 | 122 | ||
| 171 | -# Context compression, carried over from part 08 (its CONTEXT_WINDOW.md has the | 123 | +# Context compression |
| 172 | -# reasoning). OFF by default: with `enabled: false` the agent behaves exactly | ||
| 173 | -# as before. Why it exists: nothing in the agent ever shortens the history — | ||
| 174 | -# measured with the fake engine of part 03, the messages sent to the model grew | ||
| 175 | -# 2 → 5 → 7 → 9 over four requests, and every `bash` output (up to maxOutput | ||
| 176 | -# characters) and every skill read stays until /quit. On a local model the | ||
| 177 | -# window is fixed at load time and the prefill runs on the laptop, so a long | ||
| 178 | -# session first shows up as a false "[watchdog: connection hang]", then as an | ||
| 179 | -# error from the server. | ||
| 180 | -# | ||
| 181 | -# The window it measures against is the top-level `contextWindow` above — ONE | ||
| 182 | -# key for the banner and for the trigger, so the two cannot disagree. When | ||
| 183 | -# that key is 0, the value the provider's probe learned from the server is | ||
| 184 | -# used (llama-server tells its n_ctx on /props; DMR tells nothing). When both | ||
| 185 | -# are unknown, only `maxMessages` can trigger, and the agent says so at | ||
| 186 | -# start-up. | ||
| 187 | context: | 124 | context: |
| 188 | enabled: false | 125 | enabled: false |
| 189 | 126 | ||
| 190 | - # Compress when the history reaches this share of the window. 75 leaves a | 127 | + # Compress when the history reaches this share of the window. |
| 191 | - # quarter for the next question, the outputs of its turns and the answer — | ||
| 192 | - # a single `bash` output can be maxOutput characters, about 4-5k tokens. | ||
| 193 | threshold: 75 | 128 | threshold: 75 |
| 194 | 129 | ||
| 195 | - # Fallback on the message count, for when the window is unknown or the | 130 | + # Fallback on the message count, for when the window is unknown. |
| 196 | - # estimate is off. One command costs 2 messages (call + response), so 80 is | 131 | + # One command costs 2 messages (call + response), so 80 is roughly 30 commands of history. |
| 197 | - # roughly 30 commands of history. | ||
| 198 | maxMessages: 80 | 132 | maxMessages: 80 |
| 199 | 133 | ||
| 200 | - # Question turns kept raw at the end. The recent turns are where the model | 134 | + # How many recent questions (with their commands and answers) are kept as-is instead of being summarised. |
| 201 | - # works; summarising them makes it re-run what it just did. Three covers | 135 | + # The model works on the last few turns, so summarising them would make it redo what it just did. |
| 202 | - # "do X", "now fix it", "and test it" — the usual shape of a demo. | ||
| 203 | keepLastTurns: 3 | 136 | keepLastTurns: 3 |
| 204 | 137 | ||
| 205 | - # max_tokens of the summary request. Seven sections of one line per item | 138 | + # max_tokens of the summary request. |
| 206 | - # fit in far less; the cap stops a runaway model from filling the window it | ||
| 207 | - # was asked to empty. | ||
| 208 | summaryMaxTokens: 1200 | 139 | summaryMaxTokens: 1200 |
| 209 | 140 | ||
| 210 | - # Replaces the built-in summary prompt (08-context-compression/CONTEXT_WINDOW.md | 141 | + # Replaces the built-in summary prompt when set. |
| 211 | - # § 4.1) when set. Empty = built-in. | 142 | + # Empty = built-in. |
| 212 | prompt: "" | 143 | prompt: "" |
| 213 | 144 | ||
| 214 | - # One line after each compression: what was replaced, what was kept, | ||
| 215 | - # tokens before → after. Worth showing on a screen, noise in a log. | ||
| 216 | showStats: true | 145 | showStats: true |
| 217 | 146 | ||
| 218 | displayCommands: true | 147 | displayCommands: true |
modified
agent.llamacpp.yaml +44 -73 | @@ -1,91 +1,59 @@ | ||
| 1 | -# Same agent, served by llama.cpp's `llama-server` instead of Docker Model Runner. | |
| 1 | +# Agent settings — same agent, served by llama.cpp's `llama-server` instead of Docker Model Runner. | |
| 2 | 2 | # |
| 3 | -# llama-server -hf poolside/Laguna-XS-2.1-GGUF:Q4_K_M \ | |
| 4 | -# -a poolside/Laguna-XS-2.1-GGUF:Q4_K_M --jinja -c 32768 --port 8080 | |
| 5 | -# ./bob agent.llamacpp.yaml # from demo/, so that skills/ is found | |
| 3 | +# llama serve -hf JetBrains/Mellum2-12B-A2.5B-Instruct-GGUF-Q4_K_M:Q4_K_M | |
| 6 | 4 | # |
| 7 | -# `-hf <user>/<repo>:<quant>` downloads the GGUF from Hugging Face (the quant tag | |
| 8 | -# is optional and defaults to Q4_K_M; here the repo has exactly one such file, | |
| 9 | -# Laguna-XS-2.1-Q4_K_M.gguf, 20.3 GB). `-a` gives the served model the SAME name | |
| 10 | -# as the `model:` key below: llama-server routes requests on the `model` field, | |
| 11 | -# and without an alias it exposes the file's name on /v1/models, not the repo's. | |
| 12 | -# `--jinja` is not optional: without it llama-server refuses the `tools` | |
| 13 | -# parameter, and this agent is nothing but tool calls. The agent recognises that | |
| 14 | -# refusal and says so in one line instead of printing the server's stack. | |
| 15 | -# `-c` is the context the server SERVES (the model accepts up to 262,144); it is | |
| 16 | -# what shows up under `ctx:` at start-up, read from GET /props. | |
| 17 | -# Laguna XS 2.1 is a 33B MoE with 3B active parameters, built for coding; its | |
| 18 | -# card says the chat template does tool calling under --jinja, and that it | |
| 19 | -# needs a recent llama.cpp (the card points at PR #25165 — check it is merged | |
| 20 | -# in your build, or build that branch). | |
| 21 | - | |
| 22 | 5 | provider: llamacpp |
| 23 | 6 | |
| 24 | -# Must match what GET /v1/models returns — the `-a` alias above. llama-server | |
| 25 | -# routes on this field; a name it does not serve is an error, not a fallback. | |
| 26 | -#model: poolside/Laguna-XS-2.1-GGUF:Q4_K_M | |
| 27 | -#model: unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q4_K_M | |
| 28 | 7 | model: jetbrains/mellum2-12b-a2.5b-instruct-gguf-q4_k_m:Q4_K_M |
| 29 | 8 | |
| 30 | 9 | # llama-server's default is 127.0.0.1:8080; the OpenAI routes sit under /v1. |
| 31 | -# Leave it out to get exactly this value. | |
| 32 | 10 | baseUrl: http://127.0.0.1:8080/v1 |
| 33 | 11 | |
| 34 | -# No fallback here: llama-server is wherever you started it. | |
| 12 | +# No fallback: llama-server is wherever you started it. | |
| 35 | 13 | fallback: "" |
| 36 | 14 | |
| 37 | 15 | # Only when llama-server was started with --api-key; unset otherwise. |
| 38 | 16 | # apiKeyEnv: LLAMA_API_KEY |
| 39 | 17 | |
| 40 | -# The built-in file tools: read_file, write_file, edit_file. This is the switch | |
| 41 | -# this part exists for. `true`: the model edits files through tools it can SEE | |
| 42 | -# in its tool list. `false`: the agent is part 09 again — bash and read_skill — | |
| 43 | -# and edits files through the `edit` CLI if it is on the PATH. Same binary, | |
| 44 | -# same prompts, two set-ups: measured side by side, that is the comparison | |
| 45 | -# (in part 07, `read_skill` as a tool was loaded 3/3 times where a catalogue | |
| 46 | -# in the prompt plus `cat` managed 1/11). Paths are relative to the current | |
| 47 | -# directory and are not confined to it. | |
| 18 | +# The `bash` tool. | |
| 19 | +# `false` removes it: the model can still read and edit files (editTools) and load skills, | |
| 20 | +# but never runs a command. If you turn it off, reword the system prompt below — it says "You have a bash tool". | |
| 21 | +bashTool: true | |
| 22 | + | |
| 23 | +# The built-in file tools: read_file, write_file, edit_file. | |
| 48 | 24 | editTools: true |
| 49 | 25 | |
| 50 | -# 0 = read the served size from /props. Set it when the server hides /props | |
| 51 | -# (a reverse proxy) or when you know better. | |
| 26 | +# How many tokens the server actually serves. | |
| 27 | +# 0 = ask the server (llama-server tells it on /props) | |
| 52 | 28 | contextWindow: 0 |
| 53 | 29 | |
| 30 | +# Max number of characters a tool returns to the model. | |
| 31 | +# Context safeguard: beyond that, the output is truncated (beginning + end kept). | |
| 54 | 32 | maxOutput: 16000 |
| 33 | + | |
| 34 | +# Max number of model <-> tools round trips for a single question. | |
| 35 | +# Raise it for tasks that need long chains of commands. | |
| 55 | 36 | maxTurns: 40 |
| 37 | + | |
| 38 | +# Where the `read_skill` tool looks for its markdown procedures. | |
| 39 | +# The path is relative to the CURRENT DIRECTORY, so run the agent from the directory that holds skills. | |
| 56 | 40 | skillsDir: skills |
| 57 | 41 | |
| 58 | -# Persistent sessions, per project — OFF by default. | |
| 59 | -# | |
| 60 | -# How to turn them on: | |
| 61 | -# | |
| 62 | -# 1. Set `enabled: true` below (keep `dir` unless you want another place). | |
| 63 | -# 2. Restart the agent from the project directory: | |
| 64 | -# mm agent.llamacpp.yaml | |
| 65 | -# or, from Zed, point AGENT_CONFIG at this file (absolute path) and use | |
| 66 | -# "Reload Agent" / open a new thread so the new process reads the key. | |
| 67 | -# 3. Check the banner: it now shows a `sessions:` line with the directory | |
| 68 | -# and the number of earlier sessions found there. | |
| 69 | -# 4. Add `.mm/` to the project's .gitignore (already done in this repository). | |
| 70 | -# | |
| 71 | -# What it does once on: every conversation is saved as one JSON file | |
| 72 | -# `<dir>/<id>.json`, rewritten after each turn, `/compact` and `/new`. `dir` | |
| 73 | -# is relative to the directory the agent is started from — the editor's | |
| 74 | -# project in ACP mode — so each project keeps its own sessions; an absolute | |
| 75 | -# path works too. A start always opens a NEW session: in the terminal, | |
| 76 | -# `/sessions` lists the stored ones and `/resume` continues the latest | |
| 77 | -# (`/resume <id>` a given one); in an editor, "Reload Agent" (ACP | |
| 78 | -# session/load) replays the whole thread, tool calls included, without | |
| 79 | -# running anything again. `/new` always starts a fresh session and file. | |
| 80 | -# Write failures are logged, never fatal; with `enabled: false` nothing is | |
| 81 | -# written and `/sessions` says so. | |
| 42 | +# Persistent sessions, per project. | |
| 43 | +# When enabled, every conversation is saved as one JSON file under `dir`. | |
| 44 | +# `/sessions` lists them, `/resume` continues one; in an editor, "Reload Agent" replays the thread. | |
| 82 | 45 | sessions: |
| 83 | 46 | enabled: true |
| 84 | 47 | dir: .mm/sessions |
| 85 | 48 | |
| 49 | +# How many lines of a command's output are echoed to the terminal. | |
| 50 | +# 0 disables it. | |
| 86 | 51 | previewLines: 20 |
| 52 | + | |
| 87 | 53 | displayCommands: true |
| 88 | 54 | |
| 55 | +# The system prompt: what the agent is, and what it is allowed to do. | |
| 56 | +# This is the knob to play with — it is the shortest path to changing behaviour. | |
| 89 | 57 | system: | |
| 90 | 58 | Your name is Bob. |
| 91 | 59 | You are a coding agent working in a terminal. |
| @@ -153,32 +121,35 @@ system: | | ||
| 153 | 121 | rule you cannot guess. Stop every job you started before you finish, and say |
| 154 | 122 | which ones you left running. |
| 155 | 123 | |
| 156 | -# Same sampling as the DMR file. llama-server honours `parallel_tool_calls` | |
| 157 | -# (off by default on its side too) and `max_tokens`. | |
| 124 | +# Generation settings (OpenAI API keys). Low temperature for a coding agent: | |
| 125 | +# we want precise and reproducible answers, not creativity. | |
| 158 | 126 | sampling: |
| 159 | 127 | temperature: 0.0 |
| 160 | 128 | parallel_tool_calls: false |
| 161 | 129 | top_p: 0.9 |
| 162 | 130 | max_tokens: 4096 |
| 163 | 131 | |
| 164 | -# llama-server processes the whole prompt before the first token; on a laptop a | |
| 165 | -# 32k context can take a while. Same watchdog as DMR, raise it if it fires. | |
| 132 | +# llama-server processes the whole prompt before the first token; | |
| 133 | +# on a laptop a 32k context can take a while. Raise it if it fires. | |
| 166 | 134 | watchdogTimeout: 30s |
| 167 | 135 | |
| 168 | -# Context compression (from part 08). ON here, because this is the one set-up | |
| 169 | -# where the agent knows the window without being told: `contextWindow: 0` | |
| 170 | -# above means "read n_ctx from /props", and that number is what `threshold` | |
| 171 | -# applies to — so the banner's `ctx: 32768 (/props)` and the trigger agree by | |
| 172 | -# construction. With a 32k window and a 33B MoE, the whole history is | |
| 173 | -# re-read at every turn; compressing at 75 % keeps the prefill — and the | |
| 174 | -# watchdog — inside the 30 s above. Set `enabled: false` to get the exact | |
| 175 | -# part-07 behaviour back; `/compact` still works. | |
| 136 | +# Context compression. | |
| 137 | +# ON here: the agent knows the window (read from /props), so `threshold` has a number to apply to. | |
| 176 | 138 | context: |
| 177 | 139 | enabled: true |
| 140 | + | |
| 141 | + # Compress when the history reaches this share of the window. | |
| 178 | 142 | threshold: 75 |
| 179 | - # Kept as a net for a reverse proxy that hides /props: then the window is | |
| 180 | - # unknown and only this can trigger. | |
| 143 | + | |
| 144 | + # Fallback on the message count, for when the window is unknown. | |
| 145 | + # One command costs 2 messages (call + response), so 80 is roughly 30 commands of history. | |
| 181 | 146 | maxMessages: 80 |
| 147 | + | |
| 148 | + # How many recent questions (with their commands and answers) are kept as-is instead of being summarised. | |
| 149 | + # The model works on the last few turns, so summarising them would make it redo what it just did. | |
| 182 | 150 | keepLastTurns: 3 |
| 151 | + | |
| 152 | + # max_tokens of the summary request. | |
| 183 | 153 | summaryMaxTokens: 1200 |
| 154 | + | |
| 184 | 155 | showStats: true |
| @@ -1,91 +1,59 @@ | |||
| 1 | -# Same agent, served by llama.cpp's `llama-server` instead of Docker Model Runner. | 1 | +# Agent settings — same agent, served by llama.cpp's `llama-server` instead of Docker Model Runner. |
| 2 | # | 2 | # |
| 3 | -# llama-server -hf poolside/Laguna-XS-2.1-GGUF:Q4_K_M \ | 3 | +# llama serve -hf JetBrains/Mellum2-12B-A2.5B-Instruct-GGUF-Q4_K_M:Q4_K_M |
| 4 | -# -a poolside/Laguna-XS-2.1-GGUF:Q4_K_M --jinja -c 32768 --port 8080 | ||
| 5 | -# ./bob agent.llamacpp.yaml # from demo/, so that skills/ is found | ||
| 6 | # | 4 | # |
| 7 | -# `-hf <user>/<repo>:<quant>` downloads the GGUF from Hugging Face (the quant tag | ||
| 8 | -# is optional and defaults to Q4_K_M; here the repo has exactly one such file, | ||
| 9 | -# Laguna-XS-2.1-Q4_K_M.gguf, 20.3 GB). `-a` gives the served model the SAME name | ||
| 10 | -# as the `model:` key below: llama-server routes requests on the `model` field, | ||
| 11 | -# and without an alias it exposes the file's name on /v1/models, not the repo's. | ||
| 12 | -# `--jinja` is not optional: without it llama-server refuses the `tools` | ||
| 13 | -# parameter, and this agent is nothing but tool calls. The agent recognises that | ||
| 14 | -# refusal and says so in one line instead of printing the server's stack. | ||
| 15 | -# `-c` is the context the server SERVES (the model accepts up to 262,144); it is | ||
| 16 | -# what shows up under `ctx:` at start-up, read from GET /props. | ||
| 17 | -# Laguna XS 2.1 is a 33B MoE with 3B active parameters, built for coding; its | ||
| 18 | -# card says the chat template does tool calling under --jinja, and that it | ||
| 19 | -# needs a recent llama.cpp (the card points at PR #25165 — check it is merged | ||
| 20 | -# in your build, or build that branch). | ||
| 21 | - | ||
| 22 | provider: llamacpp | 5 | provider: llamacpp |
| 23 | 6 | ||
| 24 | -# Must match what GET /v1/models returns — the `-a` alias above. llama-server | ||
| 25 | -# routes on this field; a name it does not serve is an error, not a fallback. | ||
| 26 | -#model: poolside/Laguna-XS-2.1-GGUF:Q4_K_M | ||
| 27 | -#model: unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q4_K_M | ||
| 28 | model: jetbrains/mellum2-12b-a2.5b-instruct-gguf-q4_k_m:Q4_K_M | 7 | model: jetbrains/mellum2-12b-a2.5b-instruct-gguf-q4_k_m:Q4_K_M |
| 29 | 8 | ||
| 30 | # llama-server's default is 127.0.0.1:8080; the OpenAI routes sit under /v1. | 9 | # llama-server's default is 127.0.0.1:8080; the OpenAI routes sit under /v1. |
| 31 | -# Leave it out to get exactly this value. | ||
| 32 | baseUrl: http://127.0.0.1:8080/v1 | 10 | baseUrl: http://127.0.0.1:8080/v1 |
| 33 | 11 | ||
| 34 | -# No fallback here: llama-server is wherever you started it. | 12 | +# No fallback: llama-server is wherever you started it. |
| 35 | fallback: "" | 13 | fallback: "" |
| 36 | 14 | ||
| 37 | # Only when llama-server was started with --api-key; unset otherwise. | 15 | # Only when llama-server was started with --api-key; unset otherwise. |
| 38 | # apiKeyEnv: LLAMA_API_KEY | 16 | # apiKeyEnv: LLAMA_API_KEY |
| 39 | 17 | ||
| 40 | -# The built-in file tools: read_file, write_file, edit_file. This is the switch | 18 | +# The `bash` tool. |
| 41 | -# this part exists for. `true`: the model edits files through tools it can SEE | 19 | +# `false` removes it: the model can still read and edit files (editTools) and load skills, |
| 42 | -# in its tool list. `false`: the agent is part 09 again — bash and read_skill — | 20 | +# but never runs a command. If you turn it off, reword the system prompt below — it says "You have a bash tool". |
| 43 | -# and edits files through the `edit` CLI if it is on the PATH. Same binary, | 21 | +bashTool: true |
| 44 | -# same prompts, two set-ups: measured side by side, that is the comparison | 22 | + |
| 45 | -# (in part 07, `read_skill` as a tool was loaded 3/3 times where a catalogue | 23 | +# The built-in file tools: read_file, write_file, edit_file. |
| 46 | -# in the prompt plus `cat` managed 1/11). Paths are relative to the current | ||
| 47 | -# directory and are not confined to it. | ||
| 48 | editTools: true | 24 | editTools: true |
| 49 | 25 | ||
| 50 | -# 0 = read the served size from /props. Set it when the server hides /props | 26 | +# How many tokens the server actually serves. |
| 51 | -# (a reverse proxy) or when you know better. | 27 | +# 0 = ask the server (llama-server tells it on /props) |
| 52 | contextWindow: 0 | 28 | contextWindow: 0 |
| 53 | 29 | ||
| 30 | +# Max number of characters a tool returns to the model. | ||
| 31 | +# Context safeguard: beyond that, the output is truncated (beginning + end kept). | ||
| 54 | maxOutput: 16000 | 32 | maxOutput: 16000 |
| 33 | + | ||
| 34 | +# Max number of model <-> tools round trips for a single question. | ||
| 35 | +# Raise it for tasks that need long chains of commands. | ||
| 55 | maxTurns: 40 | 36 | maxTurns: 40 |
| 37 | + | ||
| 38 | +# Where the `read_skill` tool looks for its markdown procedures. | ||
| 39 | +# The path is relative to the CURRENT DIRECTORY, so run the agent from the directory that holds skills. | ||
| 56 | skillsDir: skills | 40 | skillsDir: skills |
| 57 | 41 | ||
| 58 | -# Persistent sessions, per project — OFF by default. | 42 | +# Persistent sessions, per project. |
| 59 | -# | 43 | +# When enabled, every conversation is saved as one JSON file under `dir`. |
| 60 | -# How to turn them on: | 44 | +# `/sessions` lists them, `/resume` continues one; in an editor, "Reload Agent" replays the thread. |
| 61 | -# | ||
| 62 | -# 1. Set `enabled: true` below (keep `dir` unless you want another place). | ||
| 63 | -# 2. Restart the agent from the project directory: | ||
| 64 | -# mm agent.llamacpp.yaml | ||
| 65 | -# or, from Zed, point AGENT_CONFIG at this file (absolute path) and use | ||
| 66 | -# "Reload Agent" / open a new thread so the new process reads the key. | ||
| 67 | -# 3. Check the banner: it now shows a `sessions:` line with the directory | ||
| 68 | -# and the number of earlier sessions found there. | ||
| 69 | -# 4. Add `.mm/` to the project's .gitignore (already done in this repository). | ||
| 70 | -# | ||
| 71 | -# What it does once on: every conversation is saved as one JSON file | ||
| 72 | -# `<dir>/<id>.json`, rewritten after each turn, `/compact` and `/new`. `dir` | ||
| 73 | -# is relative to the directory the agent is started from — the editor's | ||
| 74 | -# project in ACP mode — so each project keeps its own sessions; an absolute | ||
| 75 | -# path works too. A start always opens a NEW session: in the terminal, | ||
| 76 | -# `/sessions` lists the stored ones and `/resume` continues the latest | ||
| 77 | -# (`/resume <id>` a given one); in an editor, "Reload Agent" (ACP | ||
| 78 | -# session/load) replays the whole thread, tool calls included, without | ||
| 79 | -# running anything again. `/new` always starts a fresh session and file. | ||
| 80 | -# Write failures are logged, never fatal; with `enabled: false` nothing is | ||
| 81 | -# written and `/sessions` says so. | ||
| 82 | sessions: | 45 | sessions: |
| 83 | enabled: true | 46 | enabled: true |
| 84 | dir: .mm/sessions | 47 | dir: .mm/sessions |
| 85 | 48 | ||
| 49 | +# How many lines of a command's output are echoed to the terminal. | ||
| 50 | +# 0 disables it. | ||
| 86 | previewLines: 20 | 51 | previewLines: 20 |
| 52 | + | ||
| 87 | displayCommands: true | 53 | displayCommands: true |
| 88 | 54 | ||
| 55 | +# The system prompt: what the agent is, and what it is allowed to do. | ||
| 56 | +# This is the knob to play with — it is the shortest path to changing behaviour. | ||
| 89 | system: | | 57 | system: | |
| 90 | Your name is Bob. | 58 | Your name is Bob. |
| 91 | You are a coding agent working in a terminal. | 59 | You are a coding agent working in a terminal. |
| @@ -153,32 +121,35 @@ system: | | |||
| 153 | rule you cannot guess. Stop every job you started before you finish, and say | 121 | rule you cannot guess. Stop every job you started before you finish, and say |
| 154 | which ones you left running. | 122 | which ones you left running. |
| 155 | 123 | ||
| 156 | -# Same sampling as the DMR file. llama-server honours `parallel_tool_calls` | 124 | +# Generation settings (OpenAI API keys). Low temperature for a coding agent: |
| 157 | -# (off by default on its side too) and `max_tokens`. | 125 | +# we want precise and reproducible answers, not creativity. |
| 158 | sampling: | 126 | sampling: |
| 159 | temperature: 0.0 | 127 | temperature: 0.0 |
| 160 | parallel_tool_calls: false | 128 | parallel_tool_calls: false |
| 161 | top_p: 0.9 | 129 | top_p: 0.9 |
| 162 | max_tokens: 4096 | 130 | max_tokens: 4096 |
| 163 | 131 | ||
| 164 | -# llama-server processes the whole prompt before the first token; on a laptop a | 132 | +# llama-server processes the whole prompt before the first token; |
| 165 | -# 32k context can take a while. Same watchdog as DMR, raise it if it fires. | 133 | +# on a laptop a 32k context can take a while. Raise it if it fires. |
| 166 | watchdogTimeout: 30s | 134 | watchdogTimeout: 30s |
| 167 | 135 | ||
| 168 | -# Context compression (from part 08). ON here, because this is the one set-up | 136 | +# Context compression. |
| 169 | -# where the agent knows the window without being told: `contextWindow: 0` | 137 | +# ON here: the agent knows the window (read from /props), so `threshold` has a number to apply to. |
| 170 | -# above means "read n_ctx from /props", and that number is what `threshold` | ||
| 171 | -# applies to — so the banner's `ctx: 32768 (/props)` and the trigger agree by | ||
| 172 | -# construction. With a 32k window and a 33B MoE, the whole history is | ||
| 173 | -# re-read at every turn; compressing at 75 % keeps the prefill — and the | ||
| 174 | -# watchdog — inside the 30 s above. Set `enabled: false` to get the exact | ||
| 175 | -# part-07 behaviour back; `/compact` still works. | ||
| 176 | context: | 138 | context: |
| 177 | enabled: true | 139 | enabled: true |
| 140 | + | ||
| 141 | + # Compress when the history reaches this share of the window. | ||
| 178 | threshold: 75 | 142 | threshold: 75 |
| 179 | - # Kept as a net for a reverse proxy that hides /props: then the window is | 143 | + |
| 180 | - # unknown and only this can trigger. | 144 | + # Fallback on the message count, for when the window is unknown. |
| 145 | + # One command costs 2 messages (call + response), so 80 is roughly 30 commands of history. | ||
| 181 | maxMessages: 80 | 146 | maxMessages: 80 |
| 147 | + | ||
| 148 | + # How many recent questions (with their commands and answers) are kept as-is instead of being summarised. | ||
| 149 | + # The model works on the last few turns, so summarising them would make it redo what it just did. | ||
| 182 | keepLastTurns: 3 | 150 | keepLastTurns: 3 |
| 151 | + | ||
| 152 | + # max_tokens of the summary request. | ||
| 183 | summaryMaxTokens: 1200 | 153 | summaryMaxTokens: 1200 |
| 154 | + | ||
| 184 | showStats: true | 155 | showStats: true |
modified
docs/en/reference/cli.md +1 -1 | @@ -43,7 +43,7 @@ Printed on stdout in terminal mode, on stderr in ACP mode. Dimmed when stdout is | ||
| 43 | 43 | |
| 44 | 44 | | Line | Content | |
| 45 | 45 | |------|---------| |
| 46 | -| `[warning: …]` | Zero or more probe warnings: server unreachable, model not in the server's list, no chat template, compression that can never trigger. | | |
| 46 | +| `[warning: …]` | Zero or more probe warnings: server unreachable, model not in the server's list, no chat template, compression that can never trigger, no tool declared at all. | | |
| 47 | 47 | | `config: … \| provider: … \| model: … \| ctx: … \| tools: … \| skills: N` | Configuration path, provider name, model identifier, context window with its origin (`config`, `/props`) or `unknown`, declared tool names, number of skills found. | |
| 48 | 48 | | `Agent ready. Commands: …` | Terminal mode only. | |
| 49 | 49 | | `Shortcut: Ctrl+C …` | Terminal mode only. | |
| @@ -43,7 +43,7 @@ Printed on stdout in terminal mode, on stderr in ACP mode. Dimmed when stdout is | |||
| 43 | 43 | ||
| 44 | | Line | Content | | 44 | | Line | Content | |
| 45 | |------|---------| | 45 | |------|---------| |
| 46 | -| `[warning: …]` | Zero or more probe warnings: server unreachable, model not in the server's list, no chat template, compression that can never trigger. | | 46 | +| `[warning: …]` | Zero or more probe warnings: server unreachable, model not in the server's list, no chat template, compression that can never trigger, no tool declared at all. | |
| 47 | | `config: … \| provider: … \| model: … \| ctx: … \| tools: … \| skills: N` | Configuration path, provider name, model identifier, context window with its origin (`config`, `/props`) or `unknown`, declared tool names, number of skills found. | | 47 | | `config: … \| provider: … \| model: … \| ctx: … \| tools: … \| skills: N` | Configuration path, provider name, model identifier, context window with its origin (`config`, `/props`) or `unknown`, declared tool names, number of skills found. | |
| 48 | | `Agent ready. Commands: …` | Terminal mode only. | | 48 | | `Agent ready. Commands: …` | Terminal mode only. | |
| 49 | | `Shortcut: Ctrl+C …` | Terminal mode only. | | 49 | | `Shortcut: Ctrl+C …` | Terminal mode only. | |
modified
docs/en/reference/configuration.md +1 -0 | @@ -18,6 +18,7 @@ | ||
| 18 | 18 | | `displayCommands` | bool | `false` | Print the numbered list of commands and file operations after each answer. | |
| 19 | 19 | | `system` | string | built-in prompt | The system prompt, first message of every history. | |
| 20 | 20 | | `skillsDir` | string | `skills` | Directory holding the skills, as `<name>.md` files or `<name>/SKILL.md` directories. A relative path is resolved from the directory `mm` is started in (terminal mode, or ACP mode without a config file), or next to the config file (ACP mode with one); the banner warns with the absolute path when nothing is found there. | |
| 21 | +| `bashTool` | bool | `true` | Declare `bash`. `false` removes it: the model can still read and edit files (`editTools`) and load skills, but never runs a command. Reword a system prompt that still announces a bash tool. | | |
| 21 | 22 | | `editTools` | bool | `true` | Declare `read_file`, `write_file`, `edit_file`. `false` leaves `bash` and `read_skill` only. | |
| 22 | 23 | | `sampling` | map | `temperature: 0.0`, `top_p: 0.9`, `max_tokens: 4096` | Generation settings, OpenAI API key names, passed to the server. The shipped `agent.yaml` adds `parallel_tool_calls: false`. | |
| 23 | 24 | | `watchdogTimeout` | duration | `20s` | Silence tolerated between two streamed tokens before the connection is declared hung. Doubled for summary requests. | |
| @@ -18,6 +18,7 @@ | |||
| 18 | | `displayCommands` | bool | `false` | Print the numbered list of commands and file operations after each answer. | | 18 | | `displayCommands` | bool | `false` | Print the numbered list of commands and file operations after each answer. | |
| 19 | | `system` | string | built-in prompt | The system prompt, first message of every history. | | 19 | | `system` | string | built-in prompt | The system prompt, first message of every history. | |
| 20 | | `skillsDir` | string | `skills` | Directory holding the skills, as `<name>.md` files or `<name>/SKILL.md` directories. A relative path is resolved from the directory `mm` is started in (terminal mode, or ACP mode without a config file), or next to the config file (ACP mode with one); the banner warns with the absolute path when nothing is found there. | | 20 | | `skillsDir` | string | `skills` | Directory holding the skills, as `<name>.md` files or `<name>/SKILL.md` directories. A relative path is resolved from the directory `mm` is started in (terminal mode, or ACP mode without a config file), or next to the config file (ACP mode with one); the banner warns with the absolute path when nothing is found there. | |
| 21 | +| `bashTool` | bool | `true` | Declare `bash`. `false` removes it: the model can still read and edit files (`editTools`) and load skills, but never runs a command. Reword a system prompt that still announces a bash tool. | | ||
| 21 | | `editTools` | bool | `true` | Declare `read_file`, `write_file`, `edit_file`. `false` leaves `bash` and `read_skill` only. | | 22 | | `editTools` | bool | `true` | Declare `read_file`, `write_file`, `edit_file`. `false` leaves `bash` and `read_skill` only. | |
| 22 | | `sampling` | map | `temperature: 0.0`, `top_p: 0.9`, `max_tokens: 4096` | Generation settings, OpenAI API key names, passed to the server. The shipped `agent.yaml` adds `parallel_tool_calls: false`. | | 23 | | `sampling` | map | `temperature: 0.0`, `top_p: 0.9`, `max_tokens: 4096` | Generation settings, OpenAI API key names, passed to the server. The shipped `agent.yaml` adds `parallel_tool_calls: false`. | |
| 23 | | `watchdogTimeout` | duration | `20s` | Silence tolerated between two streamed tokens before the connection is declared hung. Doubled for summary requests. | | 24 | | `watchdogTimeout` | duration | `20s` | Silence tolerated between two streamed tokens before the connection is declared hung. Doubled for summary requests. | |
modified
docs/en/reference/tools.md +1 -1 | @@ -4,7 +4,7 @@ | ||
| 4 | 4 | |
| 5 | 5 | ## `bash` |
| 6 | 6 | |
| 7 | -Always declared. | |
| 7 | +Declared when `bashTool: true` (the default). | |
| 8 | 8 | |
| 9 | 9 | | Field | Type | Description | |
| 10 | 10 | |-------|------|-------------| |
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | ## `bash` | 5 | ## `bash` |
| 6 | 6 | ||
| 7 | -Always declared. | 7 | +Declared when `bashTool: true` (the default). |
| 8 | 8 | ||
| 9 | | Field | Type | Description | | 9 | | Field | Type | Description | |
| 10 | |-------|------|-------------| | 10 | |-------|------|-------------| |
modified
docs/fr/reference/cli.md +1 -1 | @@ -43,7 +43,7 @@ Affichée sur stdout en mode terminal, sur stderr en mode ACP. Grisée quand std | ||
| 43 | 43 | |
| 44 | 44 | | Ligne | Contenu | |
| 45 | 45 | |-------|---------| |
| 46 | -| `[warning: …]` | Zéro ou plusieurs avertissements de la sonde : serveur injoignable, modèle absent de la liste du serveur, pas de template de chat, compression qui ne peut jamais se déclencher. | | |
| 46 | +| `[warning: …]` | Zéro ou plusieurs avertissements de la sonde : serveur injoignable, modèle absent de la liste du serveur, pas de template de chat, compression qui ne peut jamais se déclencher, aucun outil déclaré. | | |
| 47 | 47 | | `config: … \| provider: … \| model: … \| ctx: … \| tools: … \| skills: N` | Chemin de la configuration, nom du fournisseur, identifiant du modèle, fenêtre de contexte avec son origine (`config`, `/props`) ou `unknown`, noms des outils déclarés, nombre de skills trouvés. | |
| 48 | 48 | | `Agent ready. Commands: …` | Mode terminal seulement. | |
| 49 | 49 | | `Shortcut: Ctrl+C …` | Mode terminal seulement. | |
| @@ -43,7 +43,7 @@ Affichée sur stdout en mode terminal, sur stderr en mode ACP. Grisée quand std | |||
| 43 | 43 | ||
| 44 | | Ligne | Contenu | | 44 | | Ligne | Contenu | |
| 45 | |-------|---------| | 45 | |-------|---------| |
| 46 | -| `[warning: …]` | Zéro ou plusieurs avertissements de la sonde : serveur injoignable, modèle absent de la liste du serveur, pas de template de chat, compression qui ne peut jamais se déclencher. | | 46 | +| `[warning: …]` | Zéro ou plusieurs avertissements de la sonde : serveur injoignable, modèle absent de la liste du serveur, pas de template de chat, compression qui ne peut jamais se déclencher, aucun outil déclaré. | |
| 47 | | `config: … \| provider: … \| model: … \| ctx: … \| tools: … \| skills: N` | Chemin de la configuration, nom du fournisseur, identifiant du modèle, fenêtre de contexte avec son origine (`config`, `/props`) ou `unknown`, noms des outils déclarés, nombre de skills trouvés. | | 47 | | `config: … \| provider: … \| model: … \| ctx: … \| tools: … \| skills: N` | Chemin de la configuration, nom du fournisseur, identifiant du modèle, fenêtre de contexte avec son origine (`config`, `/props`) ou `unknown`, noms des outils déclarés, nombre de skills trouvés. | |
| 48 | | `Agent ready. Commands: …` | Mode terminal seulement. | | 48 | | `Agent ready. Commands: …` | Mode terminal seulement. | |
| 49 | | `Shortcut: Ctrl+C …` | Mode terminal seulement. | | 49 | | `Shortcut: Ctrl+C …` | Mode terminal seulement. | |
modified
docs/fr/reference/configuration.md +1 -0 | @@ -18,6 +18,7 @@ | ||
| 18 | 18 | | `displayCommands` | booléen | `false` | Affiche la liste numérotée des commandes et opérations sur fichiers après chaque réponse. | |
| 19 | 19 | | `system` | chaîne | prompt intégré | Le prompt système, premier message de chaque historique. | |
| 20 | 20 | | `skillsDir` | chaîne | `skills` | Répertoire des skills, sous forme de fichiers `<nom>.md` ou de répertoires `<nom>/SKILL.md`. Un chemin relatif est résolu depuis le répertoire de lancement de `mm` (mode terminal, ou mode ACP sans fichier de configuration), ou à côté du fichier de configuration (mode ACP avec fichier) ; la bannière avertit avec le chemin absolu quand rien n'y est trouvé. | |
| 21 | +| `bashTool` | booléen | `true` | Déclare `bash`. `false` le retire : le modèle peut encore lire et modifier des fichiers (`editTools`) et charger des skills, mais n'exécute jamais de commande. Reformulez un prompt système qui annonce encore un outil bash. | | |
| 21 | 22 | | `editTools` | booléen | `true` | Déclare `read_file`, `write_file`, `edit_file`. `false` ne laisse que `bash` et `read_skill`. | |
| 22 | 23 | | `sampling` | dictionnaire | `temperature: 0.0`, `top_p: 0.9`, `max_tokens: 4096` | Paramètres de génération, noms de l'API OpenAI, transmis au serveur. L'`agent.yaml` livré ajoute `parallel_tool_calls: false`. | |
| 23 | 24 | | `watchdogTimeout` | durée | `20s` | Silence toléré entre deux tokens diffusés avant de déclarer la connexion bloquée. Doublé pour les requêtes de résumé. | |
| @@ -18,6 +18,7 @@ | |||
| 18 | | `displayCommands` | booléen | `false` | Affiche la liste numérotée des commandes et opérations sur fichiers après chaque réponse. | | 18 | | `displayCommands` | booléen | `false` | Affiche la liste numérotée des commandes et opérations sur fichiers après chaque réponse. | |
| 19 | | `system` | chaîne | prompt intégré | Le prompt système, premier message de chaque historique. | | 19 | | `system` | chaîne | prompt intégré | Le prompt système, premier message de chaque historique. | |
| 20 | | `skillsDir` | chaîne | `skills` | Répertoire des skills, sous forme de fichiers `<nom>.md` ou de répertoires `<nom>/SKILL.md`. Un chemin relatif est résolu depuis le répertoire de lancement de `mm` (mode terminal, ou mode ACP sans fichier de configuration), ou à côté du fichier de configuration (mode ACP avec fichier) ; la bannière avertit avec le chemin absolu quand rien n'y est trouvé. | | 20 | | `skillsDir` | chaîne | `skills` | Répertoire des skills, sous forme de fichiers `<nom>.md` ou de répertoires `<nom>/SKILL.md`. Un chemin relatif est résolu depuis le répertoire de lancement de `mm` (mode terminal, ou mode ACP sans fichier de configuration), ou à côté du fichier de configuration (mode ACP avec fichier) ; la bannière avertit avec le chemin absolu quand rien n'y est trouvé. | |
| 21 | +| `bashTool` | booléen | `true` | Déclare `bash`. `false` le retire : le modèle peut encore lire et modifier des fichiers (`editTools`) et charger des skills, mais n'exécute jamais de commande. Reformulez un prompt système qui annonce encore un outil bash. | | ||
| 21 | | `editTools` | booléen | `true` | Déclare `read_file`, `write_file`, `edit_file`. `false` ne laisse que `bash` et `read_skill`. | | 22 | | `editTools` | booléen | `true` | Déclare `read_file`, `write_file`, `edit_file`. `false` ne laisse que `bash` et `read_skill`. | |
| 22 | | `sampling` | dictionnaire | `temperature: 0.0`, `top_p: 0.9`, `max_tokens: 4096` | Paramètres de génération, noms de l'API OpenAI, transmis au serveur. L'`agent.yaml` livré ajoute `parallel_tool_calls: false`. | | 23 | | `sampling` | dictionnaire | `temperature: 0.0`, `top_p: 0.9`, `max_tokens: 4096` | Paramètres de génération, noms de l'API OpenAI, transmis au serveur. L'`agent.yaml` livré ajoute `parallel_tool_calls: false`. | |
| 23 | | `watchdogTimeout` | durée | `20s` | Silence toléré entre deux tokens diffusés avant de déclarer la connexion bloquée. Doublé pour les requêtes de résumé. | | 24 | | `watchdogTimeout` | durée | `20s` | Silence toléré entre deux tokens diffusés avant de déclarer la connexion bloquée. Doublé pour les requêtes de résumé. | |
modified
docs/fr/reference/tools.md +1 -1 | @@ -4,7 +4,7 @@ | ||
| 4 | 4 | |
| 5 | 5 | ## `bash` |
| 6 | 6 | |
| 7 | -Toujours déclaré. | |
| 7 | +Déclaré quand `bashTool: true` (la valeur par défaut). | |
| 8 | 8 | |
| 9 | 9 | | Champ | Type | Description | |
| 10 | 10 | |-------|------|-------------| |
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | ## `bash` | 5 | ## `bash` |
| 6 | 6 | ||
| 7 | -Toujours déclaré. | 7 | +Déclaré quand `bashTool: true` (la valeur par défaut). |
| 8 | 8 | ||
| 9 | | Champ | Type | Description | | 9 | | Champ | Type | Description | |
| 10 | |-------|------|-------------| | 10 | |-------|------|-------------| |
modified
internal/config/config.go +9 -0 | @@ -73,6 +73,14 @@ type Config struct { | ||
| 73 | 73 | // tool. When it holds no *.md file, the tool is not declared at all. |
| 74 | 74 | SkillsDir string `yaml:"skillsDir"` |
| 75 | 75 | |
| 76 | + // BashTool: declare the `bash` tool. On by default — it is the one tool | |
| 77 | + // the talk is built on. Off, the model can only read and edit files (with | |
| 78 | + // EditTools) and load skills: an agent that explains and patches code but | |
| 79 | + // never runs a command, for a machine where a shell in the model's hands is | |
| 80 | + // not wanted. The system prompt is the operator's: one that still says | |
| 81 | + // "you have a bash tool" should be reworded with it. | |
| 82 | + BashTool bool `yaml:"bashTool"` | |
| 83 | + | |
| 76 | 84 | // EditTools: declare the built-in file tools (read_file, write_file, |
| 77 | 85 | // edit_file). Off, the agent has bash and read_skill only, exactly like |
| 78 | 86 | // part 09 — and edits files through the `edit` CLI if it is on the PATH. |
| @@ -184,6 +192,7 @@ var Cfg = Config{ | ||
| 184 | 192 | // needs. `displayCommands: true` in the YAML turns it on. |
| 185 | 193 | DisplayCommands: false, |
| 186 | 194 | SkillsDir: "skills", |
| 195 | + BashTool: true, | |
| 187 | 196 | EditTools: true, |
| 188 | 197 | System: `You are a coding agent working in a terminal. |
| 189 | 198 | You have a "bash" tool to run shell commands. |
| @@ -73,6 +73,14 @@ type Config struct { | |||
| 73 | // tool. When it holds no *.md file, the tool is not declared at all. | 73 | // tool. When it holds no *.md file, the tool is not declared at all. |
| 74 | SkillsDir string `yaml:"skillsDir"` | 74 | SkillsDir string `yaml:"skillsDir"` |
| 75 | 75 | ||
| 76 | + // BashTool: declare the `bash` tool. On by default — it is the one tool | ||
| 77 | + // the talk is built on. Off, the model can only read and edit files (with | ||
| 78 | + // EditTools) and load skills: an agent that explains and patches code but | ||
| 79 | + // never runs a command, for a machine where a shell in the model's hands is | ||
| 80 | + // not wanted. The system prompt is the operator's: one that still says | ||
| 81 | + // "you have a bash tool" should be reworded with it. | ||
| 82 | + BashTool bool `yaml:"bashTool"` | ||
| 83 | + | ||
| 76 | // EditTools: declare the built-in file tools (read_file, write_file, | 84 | // EditTools: declare the built-in file tools (read_file, write_file, |
| 77 | // edit_file). Off, the agent has bash and read_skill only, exactly like | 85 | // edit_file). Off, the agent has bash and read_skill only, exactly like |
| 78 | // part 09 — and edits files through the `edit` CLI if it is on the PATH. | 86 | // part 09 — and edits files through the `edit` CLI if it is on the PATH. |
| @@ -184,6 +192,7 @@ var Cfg = Config{ | |||
| 184 | // needs. `displayCommands: true` in the YAML turns it on. | 192 | // needs. `displayCommands: true` in the YAML turns it on. |
| 185 | DisplayCommands: false, | 193 | DisplayCommands: false, |
| 186 | SkillsDir: "skills", | 194 | SkillsDir: "skills", |
| 195 | + BashTool: true, | ||
| 187 | EditTools: true, | 196 | EditTools: true, |
| 188 | System: `You are a coding agent working in a terminal. | 197 | System: `You are a coding agent working in a terminal. |
| 189 | You have a "bash" tool to run shell commands. | 198 | You have a "bash" tool to run shell commands. |
modified
internal/config/config_test.go +36 -0 | @@ -72,3 +72,39 @@ func TestTUIAndPermissionsConfig(t *testing.T) { | ||
| 72 | 72 | t.Errorf("Load(bad.yaml) = %v, want a permissions validation error", err) |
| 73 | 73 | } |
| 74 | 74 | } |
| 75 | + | |
| 76 | +// bash is declared by default, so every YAML written before `bashTool` | |
| 77 | +// existed keeps its tool; `bashTool: false` is read; a YAML that says nothing | |
| 78 | +// about it leaves the default alone even when it sets other keys. | |
| 79 | +func TestBashToolConfig(t *testing.T) { | |
| 80 | + defaults := Cfg | |
| 81 | + t.Cleanup(func() { Cfg = defaults }) | |
| 82 | + | |
| 83 | + if !Cfg.BashTool { | |
| 84 | + t.Fatal("defaults: bashTool off, want on") | |
| 85 | + } | |
| 86 | + | |
| 87 | + dir := t.TempDir() | |
| 88 | + off := filepath.Join(dir, "off.yaml") | |
| 89 | + if err := os.WriteFile(off, []byte("bashTool: false\n"), 0o644); err != nil { | |
| 90 | + t.Fatal(err) | |
| 91 | + } | |
| 92 | + if _, err := Load(off); err != nil { | |
| 93 | + t.Fatal(err) | |
| 94 | + } | |
| 95 | + if Cfg.BashTool { | |
| 96 | + t.Error("after off.yaml: bashTool still on") | |
| 97 | + } | |
| 98 | + | |
| 99 | + Cfg = defaults | |
| 100 | + silent := filepath.Join(dir, "silent.yaml") | |
| 101 | + if err := os.WriteFile(silent, []byte("maxTurns: 3\n"), 0o644); err != nil { | |
| 102 | + t.Fatal(err) | |
| 103 | + } | |
| 104 | + if _, err := Load(silent); err != nil { | |
| 105 | + t.Fatal(err) | |
| 106 | + } | |
| 107 | + if !Cfg.BashTool { | |
| 108 | + t.Error("after silent.yaml: bashTool off, want the default kept") | |
| 109 | + } | |
| 110 | +} | |
| @@ -72,3 +72,39 @@ func TestTUIAndPermissionsConfig(t *testing.T) { | |||
| 72 | t.Errorf("Load(bad.yaml) = %v, want a permissions validation error", err) | 72 | t.Errorf("Load(bad.yaml) = %v, want a permissions validation error", err) |
| 73 | } | 73 | } |
| 74 | } | 74 | } |
| 75 | + | ||
| 76 | +// bash is declared by default, so every YAML written before `bashTool` | ||
| 77 | +// existed keeps its tool; `bashTool: false` is read; a YAML that says nothing | ||
| 78 | +// about it leaves the default alone even when it sets other keys. | ||
| 79 | +func TestBashToolConfig(t *testing.T) { | ||
| 80 | + defaults := Cfg | ||
| 81 | + t.Cleanup(func() { Cfg = defaults }) | ||
| 82 | + | ||
| 83 | + if !Cfg.BashTool { | ||
| 84 | + t.Fatal("defaults: bashTool off, want on") | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + dir := t.TempDir() | ||
| 88 | + off := filepath.Join(dir, "off.yaml") | ||
| 89 | + if err := os.WriteFile(off, []byte("bashTool: false\n"), 0o644); err != nil { | ||
| 90 | + t.Fatal(err) | ||
| 91 | + } | ||
| 92 | + if _, err := Load(off); err != nil { | ||
| 93 | + t.Fatal(err) | ||
| 94 | + } | ||
| 95 | + if Cfg.BashTool { | ||
| 96 | + t.Error("after off.yaml: bashTool still on") | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + Cfg = defaults | ||
| 100 | + silent := filepath.Join(dir, "silent.yaml") | ||
| 101 | + if err := os.WriteFile(silent, []byte("maxTurns: 3\n"), 0o644); err != nil { | ||
| 102 | + t.Fatal(err) | ||
| 103 | + } | ||
| 104 | + if _, err := Load(silent); err != nil { | ||
| 105 | + t.Fatal(err) | ||
| 106 | + } | ||
| 107 | + if !Cfg.BashTool { | ||
| 108 | + t.Error("after silent.yaml: bashTool off, want the default kept") | ||
| 109 | + } | ||
| 110 | +} | ||
modified
main.go +20 -5 | @@ -158,16 +158,21 @@ func runWindow(ctx context.Context, e *engine.Engine, agentTools []ai.ToolRef, d | ||
| 158 | 158 | agent.Run(ctx, e, config.Cfg.System, agentTools, d) |
| 159 | 159 | } |
| 160 | 160 | |
| 161 | -// assembleTools declares the tools every front end gets: `bash` always; | |
| 162 | -// `read_skill` only when the skills directory holds something to read (its | |
| 161 | +// assembleTools declares the tools every front end gets: `bash` unless | |
| 162 | +// `bashTool: false` turned it off; `read_skill` only when the skills | |
| 163 | +// directory holds something to read (its | |
| 163 | 164 | // description carries the catalogue, so declaring it empty would advertise a |
| 164 | 165 | // tool that can do nothing); the file tools behind `editTools` — the same |
| 165 | 166 | // binary must run as part 09 did (bash + the `edit` CLI) and as this part |
| 166 | 167 | // (built-in tools), so the two can be compared on the same prompts. It |
| 167 | 168 | // returns the tools, their names for the banner, and the skill count. |
| 168 | 169 | func assembleTools(e *engine.Engine, d *detector.LoopDetector) ([]ai.ToolRef, []string, int) { |
| 169 | - agentTools := []ai.ToolRef{tools.Bash(e.G, d)} | |
| 170 | - toolNames := []string{"bash"} | |
| 170 | + var agentTools []ai.ToolRef | |
| 171 | + var toolNames []string | |
| 172 | + if config.Cfg.BashTool { | |
| 173 | + agentTools = append(agentTools, tools.Bash(e.G, d)) | |
| 174 | + toolNames = append(toolNames, "bash") | |
| 175 | + } | |
| 171 | 176 | skillCount := len(skills.List(config.Cfg.SkillsDir)) |
| 172 | 177 | if skillTool := tools.ReadSkill(e.G, config.Cfg.SkillsDir, d); skillTool != nil { |
| 173 | 178 | agentTools = append(agentTools, skillTool) |
| @@ -211,11 +216,21 @@ func printBanner(ctx context.Context, e *engine.Engine, path string, toolNames [ | ||
| 211 | 216 | info.Warnings = append(info.Warnings, |
| 212 | 217 | fmt.Sprintf("no skills found in %s (expected <name>.md or <name>/SKILL.md there)", config.Cfg.SkillsDir)) |
| 213 | 218 | } |
| 219 | + // No tool at all is a legal set-up — a chat with the model — but rarely the | |
| 220 | + // intended one; say so where the "tools:" column would otherwise be empty. | |
| 221 | + if len(toolNames) == 0 { | |
| 222 | + info.Warnings = append(info.Warnings, | |
| 223 | + "no tool is declared (bashTool and editTools are both off, and there are no skills): the model can only answer, not act") | |
| 224 | + } | |
| 214 | 225 | for _, w := range info.Warnings { |
| 215 | 226 | say("%s[warning: %s]%s", dim, w, off) |
| 216 | 227 | } |
| 228 | + toolCol := strings.Join(toolNames, ", ") | |
| 229 | + if toolCol == "" { | |
| 230 | + toolCol = "none" | |
| 231 | + } | |
| 217 | 232 | say("%sconfig: %s | provider: %s | model: %s | ctx: %s | tools: %s | skills: %d%s", |
| 218 | - dim, path, e.Backend.Provider, config.Cfg.Model, ctxCol, strings.Join(toolNames, ", "), skillCount, off) | |
| 233 | + dim, path, e.Backend.Provider, config.Cfg.Model, ctxCol, toolCol, skillCount, off) | |
| 219 | 234 | } |
| 220 | 235 | |
| 221 | 236 | // windowMode decides whether the window front end runs, and where the banner |
| @@ -158,16 +158,21 @@ func runWindow(ctx context.Context, e *engine.Engine, agentTools []ai.ToolRef, d | |||
| 158 | agent.Run(ctx, e, config.Cfg.System, agentTools, d) | 158 | agent.Run(ctx, e, config.Cfg.System, agentTools, d) |
| 159 | } | 159 | } |
| 160 | 160 | ||
| 161 | -// assembleTools declares the tools every front end gets: `bash` always; | 161 | +// assembleTools declares the tools every front end gets: `bash` unless |
| 162 | -// `read_skill` only when the skills directory holds something to read (its | 162 | +// `bashTool: false` turned it off; `read_skill` only when the skills |
| 163 | +// directory holds something to read (its | ||
| 163 | // description carries the catalogue, so declaring it empty would advertise a | 164 | // description carries the catalogue, so declaring it empty would advertise a |
| 164 | // tool that can do nothing); the file tools behind `editTools` — the same | 165 | // tool that can do nothing); the file tools behind `editTools` — the same |
| 165 | // binary must run as part 09 did (bash + the `edit` CLI) and as this part | 166 | // binary must run as part 09 did (bash + the `edit` CLI) and as this part |
| 166 | // (built-in tools), so the two can be compared on the same prompts. It | 167 | // (built-in tools), so the two can be compared on the same prompts. It |
| 167 | // returns the tools, their names for the banner, and the skill count. | 168 | // returns the tools, their names for the banner, and the skill count. |
| 168 | func assembleTools(e *engine.Engine, d *detector.LoopDetector) ([]ai.ToolRef, []string, int) { | 169 | func assembleTools(e *engine.Engine, d *detector.LoopDetector) ([]ai.ToolRef, []string, int) { |
| 169 | - agentTools := []ai.ToolRef{tools.Bash(e.G, d)} | 170 | + var agentTools []ai.ToolRef |
| 170 | - toolNames := []string{"bash"} | 171 | + var toolNames []string |
| 172 | + if config.Cfg.BashTool { | ||
| 173 | + agentTools = append(agentTools, tools.Bash(e.G, d)) | ||
| 174 | + toolNames = append(toolNames, "bash") | ||
| 175 | + } | ||
| 171 | skillCount := len(skills.List(config.Cfg.SkillsDir)) | 176 | skillCount := len(skills.List(config.Cfg.SkillsDir)) |
| 172 | if skillTool := tools.ReadSkill(e.G, config.Cfg.SkillsDir, d); skillTool != nil { | 177 | if skillTool := tools.ReadSkill(e.G, config.Cfg.SkillsDir, d); skillTool != nil { |
| 173 | agentTools = append(agentTools, skillTool) | 178 | agentTools = append(agentTools, skillTool) |
| @@ -211,11 +216,21 @@ func printBanner(ctx context.Context, e *engine.Engine, path string, toolNames [ | |||
| 211 | info.Warnings = append(info.Warnings, | 216 | info.Warnings = append(info.Warnings, |
| 212 | fmt.Sprintf("no skills found in %s (expected <name>.md or <name>/SKILL.md there)", config.Cfg.SkillsDir)) | 217 | fmt.Sprintf("no skills found in %s (expected <name>.md or <name>/SKILL.md there)", config.Cfg.SkillsDir)) |
| 213 | } | 218 | } |
| 219 | + // No tool at all is a legal set-up — a chat with the model — but rarely the | ||
| 220 | + // intended one; say so where the "tools:" column would otherwise be empty. | ||
| 221 | + if len(toolNames) == 0 { | ||
| 222 | + info.Warnings = append(info.Warnings, | ||
| 223 | + "no tool is declared (bashTool and editTools are both off, and there are no skills): the model can only answer, not act") | ||
| 224 | + } | ||
| 214 | for _, w := range info.Warnings { | 225 | for _, w := range info.Warnings { |
| 215 | say("%s[warning: %s]%s", dim, w, off) | 226 | say("%s[warning: %s]%s", dim, w, off) |
| 216 | } | 227 | } |
| 228 | + toolCol := strings.Join(toolNames, ", ") | ||
| 229 | + if toolCol == "" { | ||
| 230 | + toolCol = "none" | ||
| 231 | + } | ||
| 217 | say("%sconfig: %s | provider: %s | model: %s | ctx: %s | tools: %s | skills: %d%s", | 232 | say("%sconfig: %s | provider: %s | model: %s | ctx: %s | tools: %s | skills: %d%s", |
| 218 | - dim, path, e.Backend.Provider, config.Cfg.Model, ctxCol, strings.Join(toolNames, ", "), skillCount, off) | 233 | + dim, path, e.Backend.Provider, config.Cfg.Model, ctxCol, toolCol, skillCount, off) |
| 219 | } | 234 | } |
| 220 | 235 | ||
| 221 | // windowMode decides whether the window front end runs, and where the banner | 236 | // windowMode decides whether the window front end runs, and where the banner |