Talk to another Mini Me #21
Merged@k33g wants to merge
feature/talk-to-another-mm into mainMerged as
8654e3f485a0.50 files changed
added
.tickets/issues/0066-talk-to-another-mm.yaml +391 -0 | new file mode 100644 | ||
| @@ -0,0 +1,391 @@ | ||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | |
| 2 | +id: 66 | |
| 3 | +title: talk to another mm | |
| 4 | +state: open | |
| 5 | +author: | |
| 6 | + name: k33g | |
| 7 | + email: ph.charriere@gmail.com | |
| 8 | +createdAt: 2026-10-02T18:48:36.275Z | |
| 9 | +updatedAt: 2026-10-02T20:30:00.000Z | |
| 10 | +labels: | |
| 11 | + - kind::feature | |
| 12 | +related: | |
| 13 | + - 57 | |
| 14 | + - 59 | |
| 15 | +body: | | |
| 16 | + Goal: from the current session, send a prompt to ANOTHER mm (another config: | |
| 17 | + other model, provider, system prompt, tools, skills) and bring its answer | |
| 18 | + back into the current conversation, so the current model can use it. | |
| 19 | + | |
| 20 | + /ask bob <prompt> | |
| 21 | + /ask riker <prompt> | |
| 22 | + | |
| 23 | + Config (user's proposal): | |
| 24 | + | |
| 25 | + mm-configs: | |
| 26 | + bob: path/to/agent.bob.yaml | |
| 27 | + riker: path/to/agent.riker.yaml | |
| 28 | + | |
| 29 | + Feasibility: yes. Checked 2026-10-02 (no code written). | |
| 30 | + | |
| 31 | + Recommended transport: ACP over stdio, mm talking to a child `mm -acp`. | |
| 32 | + - The child is started with os.Executable() (same mm build, not whatever is | |
| 33 | + on PATH) and `-config <path>`, cwd = the current working directory. | |
| 34 | + - The parent is an ACP CLIENT: github.com/coder/acp-go-sdk (already a | |
| 35 | + dependency, v0.13.5) has NewClientSideConnection + the Client interface. | |
| 36 | + - mm's ACP server only ever calls two client methods (grep internal/acp): | |
| 37 | + SessionUpdate (message/thought/tool-call/usage chunks) and | |
| 38 | + RequestPermission. The rest of the Client interface (fs, terminal) can be | |
| 39 | + stubs returning "not supported", and the parent advertises no fs/terminal | |
| 40 | + capability. | |
| 41 | + - Flow: initialize -> session/new {cwd} -> session/prompt {text} -> collect | |
| 42 | + agent_message_chunk text until the prompt response (stopReason) -> | |
| 43 | + insert the answer in the current history. | |
| 44 | + - Proof it works as a protocol: tests/tool-order/session.py and the sidekick | |
| 45 | + web client already drive `mm -acp` from outside. | |
| 46 | + | |
| 47 | + Why ACP rather than the alternatives: | |
| 48 | + - `mm -p` subprocess (one-shot): simpler, but stdout mixes the answer with | |
| 49 | + thoughts, tool echoes, ANSI and the final command summary (fragile | |
| 50 | + parsing); line mode never asks permission, so bob would run bash/write | |
| 51 | + with NO gate; no cancel except kill; no usage; no follow-up turns. | |
| 52 | + - In-process second Engine: no process, but config.Cfg is a global read | |
| 53 | + everywhere (engine, tools, compact, store...). Two configs in one process | |
| 54 | + means de-globalising the config first: a large refactor for this feature. | |
| 55 | + The subprocess also isolates bob's tools, MCP servers and skills. | |
| 56 | + - ACP gives, for free: structured events (tool calls can be shown as | |
| 57 | + "bob: bash ls"), permission requests that can be FORWARDED to the user | |
| 58 | + through the current front end's existing sink.Allow (line mode: no gate, | |
| 59 | + window: inline prompt, editor: the editor's dialog), session/cancel for | |
| 60 | + /abort and Ctrl+C, usage, and a session id -> multi-turn with bob | |
| 61 | + (keep the child alive, reuse the session: "/ask bob and what about X?"). | |
| 62 | + | |
| 63 | + Proposed behaviour (to confirm): | |
| 64 | + - One child per name, started lazily on the first /ask, kept alive for the | |
| 65 | + parent session (start-up cost), killed on /quit and exit; a fresh ACP | |
| 66 | + session (session/new) per /ask if 3 is confirmed. | |
| 67 | + - The /ask exchange enters the CURRENT history as one user message, e.g. | |
| 68 | + [bob (ollama/qwen3.5:2b) was asked: <prompt>] | |
| 69 | + <bob's answer> | |
| 70 | + so the current model sees it on the next question and the session file | |
| 71 | + persists and replays it on /resume. Must keep the history valid for | |
| 72 | + the next model call (user/model alternation: merge into the next user | |
| 73 | + message, or a user+model pair "[noted]" — to decide in the code). | |
| 74 | + - Bob's answer is shown while it streams, prefixed/styled as bob's | |
| 75 | + (decided: displayed, kept for the next question). | |
| 76 | + - Bob's thoughts: not shown (or dim), never inserted in the history. | |
| 77 | + - Long answers: capped (maxOutput-like) before insertion — the current | |
| 78 | + model's context pays for them. | |
| 79 | + | |
| 80 | + Risks / things to decide: | |
| 81 | + - Ticket 57: a permission forwarded to the user and answered slowly will | |
| 82 | + trip the CHILD's watchdog exactly like today in an editor. | |
| 83 | + - Ticket 59 (yolo): which permission policy for the child — forward to the | |
| 84 | + user (default), or auto per config entry? | |
| 85 | + - Two agents on the same working tree: the parent waits while bob works | |
| 86 | + (/ask is blocking), so no concurrent edits; but bob may change files the | |
| 87 | + parent model "remembers" — say so in the inserted message? | |
| 88 | + - Recursion: /ask is a USER command, the model cannot call it, so no | |
| 89 | + automatic loop. If it ever becomes a tool ("ask_agent"), cap the depth | |
| 90 | + (env var MM_ASK_DEPTH) and refuse cycles. | |
| 91 | + - Paths in mm-configs: relative to the PARENT config file's directory (the | |
| 92 | + ACP mode already resolves skillsDir that way; a path relative to the cwd | |
| 93 | + breaks when the config is elsewhere). The child's own relative paths | |
| 94 | + (skillsDir) resolve against ITS config dir in -acp mode. | |
| 95 | + - Start-up cost: Genkit init + probe + MCP servers per child (~1 s+); | |
| 96 | + hence keep the child alive. | |
| 97 | + - Child stderr: capture it and show it only on failure (start error, | |
| 98 | + unknown config, provider down -> one line: "bob: nothing answers at …"). | |
| 99 | + | |
| 100 | + Demo ready (2026-10-02): demo-llmman/agent.llmman.yaml declares | |
| 101 | + mmConfigs: | |
| 102 | + jan: agent.jan-nano.yaml # hf.co/Menlo/Jan-nano-gguf:Q4_K_M | |
| 103 | + lucy: agent.lucy.yaml # hf.co/Menlo/Lucy-gguf:Q4_K_M | |
| 104 | + (the key is ignored until implemented: config loading is not strict). Both | |
| 105 | + configs answered a bash-tool prompt live through llmman. | |
| 106 | + | |
| 107 | + Decisions (user, 2026-10-02): | |
| 108 | + 1. Key in camelCase: `mmConfigs:` (name -> path of a config file). | |
| 109 | + 2. Bob's answer is DISPLAYED, and stays in the current history for the | |
| 110 | + NEXT question; the current model does not answer right away. | |
| 111 | + 3. Two commands: | |
| 112 | + - /ask <name> <prompt>: bob receives the typed prompt only; | |
| 113 | + - /ask-with-context <name> <prompt>: bob also receives the context of | |
| 114 | + the current conversation. | |
| 115 | + Interpretation to confirm: every /ask starts a FRESH bob session (no | |
| 116 | + memory between two /ask); continuity comes from /ask-with-context. | |
| 117 | + 4. Both commands work in the three front ends (Terminal, Window, Editor). | |
| 118 | + 5. Context: only with /ask-with-context (see 3). | |
| 119 | + | |
| 120 | + Editor visibility (checked): yes. The ACP facade already sends | |
| 121 | + `available_commands_update` built from command.For(Editor) | |
| 122 | + (internal/acp/acp.go availableCommands), and skills are already advertised | |
| 123 | + with `input.unstructured.hint`. Today the built-in commands are sent | |
| 124 | + WITHOUT an input hint: /ask and /ask-with-context need one, e.g. | |
| 125 | + "bob|riker <prompt>", built from mmConfigs. The editor completes the | |
| 126 | + command name, not the argument; alternative to discuss: one command per | |
| 127 | + agent (/ask-bob, /ask-with-context-bob) so the picker lists the agents. | |
| 128 | + The CHILD's own available_commands_update is ignored by the parent. | |
| 129 | + | |
| 130 | + Decided too (user, 2026-10-02): | |
| 131 | + a. Context for /ask-with-context = a text transcript of the current | |
| 132 | + history: user questions and assistant answers, each tool call as one | |
| 133 | + line (name + short args), no tool outputs, no reasoning, the compaction | |
| 134 | + summary if any; capped (chars), newest turns kept first. Sent as: | |
| 135 | + [Conversation so far, from <parent agent name>] | |
| 136 | + ... | |
| 137 | + [Question] | |
| 138 | + <prompt> | |
| 139 | + b. Every /ask (and /ask-with-context) starts a FRESH bob session | |
| 140 | + (session/new); no memory on bob's side between two calls. | |
| 141 | + c. Editors: ONE COMMAND PER AGENT, generated from mmConfigs — | |
| 142 | + /ask-bob, /ask-with-context-bob, /ask-riker, … each with an input hint | |
| 143 | + "<prompt>". Consequences: | |
| 144 | + - agent names become command names: validate them in config | |
| 145 | + (lowercase letters, digits, '-'; error otherwise); | |
| 146 | + - a skill or built-in with the same name: the existing guard in | |
| 147 | + availableCommands (commandNamed) keeps the command and drops the skill, | |
| 148 | + with the banner warning — extend it to the generated names; | |
| 149 | + - terminal and window (confirmed by the user, 2026-10-02): accept the | |
| 150 | + per-agent forms there too (same table, no drift), and keep | |
| 151 | + `/ask <name> <prompt>` as the generic form; the window's "/" picker | |
| 152 | + lists the per-agent commands. | |
| 153 | + | |
| 154 | + Proposed behaviour (to confirm): | |
| 155 | + - One child per name, started lazily on the first /ask, kept alive for the | |
| 156 | + parent session (start-up cost), killed on /quit and exit; a fresh ACP | |
| 157 | + session (session/new) per /ask if 3 is confirmed. | |
| 158 | + - The /ask exchange enters the CURRENT history as one user message, e.g. | |
| 159 | + [bob (ollama/qwen3.5:2b) was asked: <prompt>] | |
| 160 | + <bob's answer> | |
| 161 | + so the current model sees it on the next question and the session file | |
| 162 | + persists and replays it on /resume. Must keep the history valid for | |
| 163 | + the next model call (user/model alternation: merge into the next user | |
| 164 | + message, or a user+model pair "[noted]" — to decide in the code). | |
| 165 | + - Bob's answer is shown while it streams, prefixed/styled as bob's | |
| 166 | + (decided: displayed, kept for the next question). | |
| 167 | + - Bob's thoughts: not shown (or dim), never inserted in the history. | |
| 168 | + - Long answers: capped (maxOutput-like) before insertion — the current | |
| 169 | + model's context pays for them. | |
| 170 | + | |
| 171 | + Risks / things to decide: | |
| 172 | + - Ticket 57: a permission forwarded to the user and answered slowly will | |
| 173 | + trip the CHILD's watchdog exactly like today in an editor. | |
| 174 | + - Ticket 59 (yolo): which permission policy for the child — forward to the | |
| 175 | + user (default), or auto per config entry? | |
| 176 | + - Two agents on the same working tree: the parent waits while bob works | |
| 177 | + (/ask is blocking), so no concurrent edits; but bob may change files the | |
| 178 | + parent model "remembers" — say so in the inserted message? | |
| 179 | + - Recursion: /ask is a USER command, the model cannot call it, so no | |
| 180 | + automatic loop. If it ever becomes a tool ("ask_agent"), cap the depth | |
| 181 | + (env var MM_ASK_DEPTH) and refuse cycles. | |
| 182 | + - Paths in mm-configs: relative to the PARENT config file's directory (the | |
| 183 | + ACP mode already resolves skillsDir that way; a path relative to the cwd | |
| 184 | + breaks when the config is elsewhere). The child's own relative paths | |
| 185 | + (skillsDir) resolve against ITS config dir in -acp mode. | |
| 186 | + - Start-up cost: Genkit init + probe + MCP servers per child (~1 s+); | |
| 187 | + hence keep the child alive. | |
| 188 | + - Child stderr: capture it and show it only on failure (start error, | |
| 189 | + unknown config, provider down -> one line: "bob: nothing answers at …"). | |
| 190 | + | |
| 191 | + Decisions (user, 2026-10-02): | |
| 192 | + 1. Key in camelCase: `mmConfigs:` (name -> path of a config file). | |
| 193 | + 2. Bob's answer is DISPLAYED, and stays in the current history for the | |
| 194 | + NEXT question; the current model does not answer right away. | |
| 195 | + 3. Two commands: | |
| 196 | + - /ask <name> <prompt>: bob receives the typed prompt only; | |
| 197 | + - /ask-with-context <name> <prompt>: bob also receives the context of | |
| 198 | + the current conversation. | |
| 199 | + Interpretation to confirm: every /ask starts a FRESH bob session (no | |
| 200 | + memory between two /ask); continuity comes from /ask-with-context. | |
| 201 | + 4. Both commands work in the three front ends (Terminal, Window, Editor). | |
| 202 | + 5. Context: only with /ask-with-context (see 3). | |
| 203 | + | |
| 204 | + Editor visibility (checked): yes. The ACP facade already sends | |
| 205 | + `available_commands_update` built from command.For(Editor) | |
| 206 | + (internal/acp/acp.go availableCommands), and skills are already advertised | |
| 207 | + with `input.unstructured.hint`. Today the built-in commands are sent | |
| 208 | + WITHOUT an input hint: /ask and /ask-with-context need one, e.g. | |
| 209 | + "bob|riker <prompt>", built from mmConfigs. The editor completes the | |
| 210 | + command name, not the argument; alternative to discuss: one command per | |
| 211 | + agent (/ask-bob, /ask-with-context-bob) so the picker lists the agents. | |
| 212 | + The CHILD's own available_commands_update is ignored by the parent. | |
| 213 | + | |
| 214 | + Still open: | |
| 215 | + a. What "context" is for /ask-with-context. Proposal: a text transcript of | |
| 216 | + the current history — user questions and assistant answers, each tool | |
| 217 | + call as one line (name + short args), no tool outputs, no reasoning, | |
| 218 | + the compaction summary if any — capped (chars) to protect bob's | |
| 219 | + window, newest turns kept first. Sent as the first part of the prompt: | |
| 220 | + [Conversation so far, from <parent agent name>] | |
| 221 | + ... | |
| 222 | + [Question] | |
| 223 | + <prompt> | |
| 224 | + b. Fresh bob session per /ask (interpretation of 3) — confirm. | |
| 225 | + c. Editor: one /ask with a hint, or one command per agent? | |
| 226 | +tasks: | |
| 227 | + - id: 1 | |
| 228 | + title: "Answer the open questions (key name, immediate answer, multi-turn, fronts, context passed)" | |
| 229 | + state: closed | |
| 230 | + priority: none | |
| 231 | + author: | |
| 232 | + name: k33g | |
| 233 | + email: ph.charriere@gmail.com | |
| 234 | + createdAt: 2026-10-02T19:30:00.000Z | |
| 235 | + updatedAt: 2026-10-02T20:00:00.000Z | |
| 236 | + - id: 2 | |
| 237 | + title: "Confirm the remaining points a/b/c (context format, fresh session per /ask, editor hint vs one command per agent)" | |
| 238 | + state: closed | |
| 239 | + priority: none | |
| 240 | + author: | |
| 241 | + name: k33g | |
| 242 | + email: ph.charriere@gmail.com | |
| 243 | + createdAt: 2026-10-02T19:30:00.000Z | |
| 244 | + updatedAt: 2026-10-02T20:00:00.000Z | |
| 245 | + - id: 3 | |
| 246 | + title: "config: mmConfigs map (validation: name = [a-z0-9-]+ since it becomes a command name, file exists; paths relative to the config file dir)" | |
| 247 | + state: closed | |
| 248 | + priority: none | |
| 249 | + author: | |
| 250 | + name: k33g | |
| 251 | + email: ph.charriere@gmail.com | |
| 252 | + createdAt: 2026-10-02T19:30:00.000Z | |
| 253 | + updatedAt: 2026-10-02T20:00:00.000Z | |
| 254 | + - id: 4 | |
| 255 | + title: "internal/askagent (name TBD): ACP client over a child `mm -acp -config <path>` (os.Executable), lazy start, one child per name, shutdown" | |
| 256 | + state: closed | |
| 257 | + priority: none | |
| 258 | + author: | |
| 259 | + name: k33g | |
| 260 | + email: ph.charriere@gmail.com | |
| 261 | + createdAt: 2026-10-02T19:30:00.000Z | |
| 262 | + updatedAt: 2026-10-02T20:00:00.000Z | |
| 263 | + - id: 5 | |
| 264 | + title: "Client side: SessionUpdate -> stream text/tool-call lines to the current sink; RequestPermission -> current sink.Allow; fs/terminal stubs" | |
| 265 | + state: closed | |
| 266 | + priority: none | |
| 267 | + author: | |
| 268 | + name: k33g | |
| 269 | + email: ph.charriere@gmail.com | |
| 270 | + createdAt: 2026-10-02T19:30:00.000Z | |
| 271 | + updatedAt: 2026-10-02T20:00:00.000Z | |
| 272 | + - id: 6 | |
| 273 | + title: "command table: /ask <name> <prompt>, /ask-with-context <name> <prompt>, and per-agent /ask-<name>, /ask-with-context-<name> generated from mmConfigs (all fronts); ACP advertises the per-agent ones with hint <prompt>; window picker; name clash guard" | |
| 274 | + state: closed | |
| 275 | + priority: none | |
| 276 | + author: | |
| 277 | + name: k33g | |
| 278 | + email: ph.charriere@gmail.com | |
| 279 | + createdAt: 2026-10-02T19:30:00.000Z | |
| 280 | + updatedAt: 2026-10-02T20:00:00.000Z | |
| 281 | + - id: 7 | |
| 282 | + title: "Context builder for /ask-with-context: transcript of the current history (text + one line per tool call), capped" | |
| 283 | + state: closed | |
| 284 | + priority: none | |
| 285 | + author: | |
| 286 | + name: k33g | |
| 287 | + email: ph.charriere@gmail.com | |
| 288 | + createdAt: 2026-10-02T19:30:00.000Z | |
| 289 | + updatedAt: 2026-10-02T20:00:00.000Z | |
| 290 | + - id: 8 | |
| 291 | + title: "Insert the exchange in the current history (format, cap, valid alternation), persisted by the store and replayed on /resume" | |
| 292 | + state: closed | |
| 293 | + priority: none | |
| 294 | + author: | |
| 295 | + name: k33g | |
| 296 | + email: ph.charriere@gmail.com | |
| 297 | + createdAt: 2026-10-02T19:30:00.000Z | |
| 298 | + updatedAt: 2026-10-02T20:00:00.000Z | |
| 299 | + - id: 9 | |
| 300 | + title: "/abort, Ctrl+C and editor cancel -> session/cancel on the child; child crash -> one-line error, history untouched" | |
| 301 | + state: closed | |
| 302 | + priority: none | |
| 303 | + author: | |
| 304 | + name: k33g | |
| 305 | + email: ph.charriere@gmail.com | |
| 306 | + createdAt: 2026-10-02T19:30:00.000Z | |
| 307 | + updatedAt: 2026-10-02T20:00:00.000Z | |
| 308 | + - id: 10 | |
| 309 | + title: "Tests: fake child agent (acp-go-sdk agent side) for the client; end-to-end with two real configs (demo-ollama + demo-llmman)" | |
| 310 | + state: closed | |
| 311 | + priority: none | |
| 312 | + author: | |
| 313 | + name: k33g | |
| 314 | + email: ph.charriere@gmail.com | |
| 315 | + createdAt: 2026-10-02T19:30:00.000Z | |
| 316 | + updatedAt: 2026-10-02T20:00:00.000Z | |
| 317 | + - id: 11 | |
| 318 | + title: "Docs EN+FR: how-to ask another agent, reference (config key, commands), explanation (why ACP, why a subprocess)" | |
| 319 | + state: closed | |
| 320 | + priority: none | |
| 321 | + author: | |
| 322 | + name: k33g | |
| 323 | + email: ph.charriere@gmail.com | |
| 324 | + createdAt: 2026-10-02T19:30:00.000Z | |
| 325 | + updatedAt: 2026-10-02T20:00:00.000Z | |
| 326 | + - id: 12 | |
| 327 | + title: "Quality gate (qlty) where available — not run: qlty absent from this sandbox" | |
| 328 | + state: open | |
| 329 | + priority: none | |
| 330 | + author: | |
| 331 | + name: k33g | |
| 332 | + email: ph.charriere@gmail.com | |
| 333 | + createdAt: 2026-10-02T20:30:00.000Z | |
| 334 | + updatedAt: 2026-10-02T20:30:00.000Z | |
| 335 | + - id: 13 | |
| 336 | + title: "Live check of the window mode (/ask-jan in mm -tui) — unit-tested only" | |
| 337 | + state: open | |
| 338 | + priority: none | |
| 339 | + author: | |
| 340 | + name: k33g | |
| 341 | + email: ph.charriere@gmail.com | |
| 342 | + createdAt: 2026-10-02T20:30:00.000Z | |
| 343 | + updatedAt: 2026-10-02T20:30:00.000Z | |
| 344 | + - id: 14 | |
| 345 | + title: "Review, commit, merge" | |
| 346 | + state: open | |
| 347 | + priority: none | |
| 348 | + author: | |
| 349 | + name: k33g | |
| 350 | + email: ph.charriere@gmail.com | |
| 351 | + createdAt: 2026-10-02T20:30:00.000Z | |
| 352 | + updatedAt: 2026-10-02T20:30:00.000Z | |
| 353 | +comments: | |
| 354 | + - id: 1 | |
| 355 | + author: | |
| 356 | + name: claude | |
| 357 | + email: noreply@anthropic.com | |
| 358 | + createdAt: 2026-10-02T20:30:00.000Z | |
| 359 | + body: | | |
| 360 | + **Implemented** on branch `feature/talk-to-another-mm` (uncommitted, 2026-10-02). | |
| 361 | + | |
| 362 | + - config: `mmConfigs` (internal/config/agents.go) — names [a-z0-9-], not | |
| 363 | + starting with `with-context`; relative paths resolved against the config | |
| 364 | + file's dir at Load; missing file = start-up warning. `AGENT_SESSIONS=off` | |
| 365 | + env override (applyEnv). | |
| 366 | + - command: /ask family generated by SetAgents (internal/command/ask.go); | |
| 367 | + `Hint` field; ParseAsk returns (req, isAsk, err). | |
| 368 | + - internal/ask (new): Transcript/Prompt (context.go), Exchange as a | |
| 369 | + user+model pair + Describe (exchange.go), ChildEnv/Depth/CanAsk | |
| 370 | + (env.go, MaxDepth 2), ACP client (client.go), Pool with one lazy child | |
| 371 | + per agent + fresh session per ask (pool.go), Pool.Run + Runner interface | |
| 372 | + + Label + Missing (run.go). | |
| 373 | + - Front ends: agent/ask.go (Ctrl+C, /abort, /quit), tui/ask.go (job | |
| 374 | + "asking <agent>"), acp/ask.go (per-agent commands with `<prompt>` hint); | |
| 375 | + main.go wiring; store.FailAsk. | |
| 376 | + - Traps found: the SDK dispatches the child's requests concurrently with | |
| 377 | + its notifications — a permission request can overtake its tool_call | |
| 378 | + (client shows the call first, once); the SDK's default slog would print | |
| 379 | + "connection closed" on the user's terminal (silenced); the parent's | |
| 380 | + AGENT_* env would override the child's config (stripped). | |
| 381 | + - Tests: config, command, ask (fake ACP agent = the test binary re-run, | |
| 382 | + no network), agent, tui, acp. `go test ./...` green 3x; ask tests 15x. | |
| 383 | + `-race` not available (no C compiler in the sandbox). | |
| 384 | + - Live (host llmman): line mode `/ask-jan` → answer kept, the current | |
| 385 | + model quoted it next turn; ACP `/ask-with-context-lucy` → per-agent | |
| 386 | + commands announced, Lucy knew the name given earlier, her bash | |
| 387 | + permission went to the client titled "lucy · bash: …"; no child | |
| 388 | + session files, no leftover processes. | |
| 389 | + - Docs EN+FR: how-to/ask-another-agent.md, explanation/asking-another-agent.md, | |
| 390 | + reference (slash-commands, configuration, cli), architecture table, | |
| 391 | + READMEs, docs/diagrams/packages.drawio (internal/ask + edges). | |
| new file mode 100644 | |||
| @@ -0,0 +1,391 @@ | |||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | ||
| 2 | +id: 66 | ||
| 3 | +title: talk to another mm | ||
| 4 | +state: open | ||
| 5 | +author: | ||
| 6 | + name: k33g | ||
| 7 | + email: ph.charriere@gmail.com | ||
| 8 | +createdAt: 2026-10-02T18:48:36.275Z | ||
| 9 | +updatedAt: 2026-10-02T20:30:00.000Z | ||
| 10 | +labels: | ||
| 11 | + - kind::feature | ||
| 12 | +related: | ||
| 13 | + - 57 | ||
| 14 | + - 59 | ||
| 15 | +body: | | ||
| 16 | + Goal: from the current session, send a prompt to ANOTHER mm (another config: | ||
| 17 | + other model, provider, system prompt, tools, skills) and bring its answer | ||
| 18 | + back into the current conversation, so the current model can use it. | ||
| 19 | + | ||
| 20 | + /ask bob <prompt> | ||
| 21 | + /ask riker <prompt> | ||
| 22 | + | ||
| 23 | + Config (user's proposal): | ||
| 24 | + | ||
| 25 | + mm-configs: | ||
| 26 | + bob: path/to/agent.bob.yaml | ||
| 27 | + riker: path/to/agent.riker.yaml | ||
| 28 | + | ||
| 29 | + Feasibility: yes. Checked 2026-10-02 (no code written). | ||
| 30 | + | ||
| 31 | + Recommended transport: ACP over stdio, mm talking to a child `mm -acp`. | ||
| 32 | + - The child is started with os.Executable() (same mm build, not whatever is | ||
| 33 | + on PATH) and `-config <path>`, cwd = the current working directory. | ||
| 34 | + - The parent is an ACP CLIENT: github.com/coder/acp-go-sdk (already a | ||
| 35 | + dependency, v0.13.5) has NewClientSideConnection + the Client interface. | ||
| 36 | + - mm's ACP server only ever calls two client methods (grep internal/acp): | ||
| 37 | + SessionUpdate (message/thought/tool-call/usage chunks) and | ||
| 38 | + RequestPermission. The rest of the Client interface (fs, terminal) can be | ||
| 39 | + stubs returning "not supported", and the parent advertises no fs/terminal | ||
| 40 | + capability. | ||
| 41 | + - Flow: initialize -> session/new {cwd} -> session/prompt {text} -> collect | ||
| 42 | + agent_message_chunk text until the prompt response (stopReason) -> | ||
| 43 | + insert the answer in the current history. | ||
| 44 | + - Proof it works as a protocol: tests/tool-order/session.py and the sidekick | ||
| 45 | + web client already drive `mm -acp` from outside. | ||
| 46 | + | ||
| 47 | + Why ACP rather than the alternatives: | ||
| 48 | + - `mm -p` subprocess (one-shot): simpler, but stdout mixes the answer with | ||
| 49 | + thoughts, tool echoes, ANSI and the final command summary (fragile | ||
| 50 | + parsing); line mode never asks permission, so bob would run bash/write | ||
| 51 | + with NO gate; no cancel except kill; no usage; no follow-up turns. | ||
| 52 | + - In-process second Engine: no process, but config.Cfg is a global read | ||
| 53 | + everywhere (engine, tools, compact, store...). Two configs in one process | ||
| 54 | + means de-globalising the config first: a large refactor for this feature. | ||
| 55 | + The subprocess also isolates bob's tools, MCP servers and skills. | ||
| 56 | + - ACP gives, for free: structured events (tool calls can be shown as | ||
| 57 | + "bob: bash ls"), permission requests that can be FORWARDED to the user | ||
| 58 | + through the current front end's existing sink.Allow (line mode: no gate, | ||
| 59 | + window: inline prompt, editor: the editor's dialog), session/cancel for | ||
| 60 | + /abort and Ctrl+C, usage, and a session id -> multi-turn with bob | ||
| 61 | + (keep the child alive, reuse the session: "/ask bob and what about X?"). | ||
| 62 | + | ||
| 63 | + Proposed behaviour (to confirm): | ||
| 64 | + - One child per name, started lazily on the first /ask, kept alive for the | ||
| 65 | + parent session (start-up cost), killed on /quit and exit; a fresh ACP | ||
| 66 | + session (session/new) per /ask if 3 is confirmed. | ||
| 67 | + - The /ask exchange enters the CURRENT history as one user message, e.g. | ||
| 68 | + [bob (ollama/qwen3.5:2b) was asked: <prompt>] | ||
| 69 | + <bob's answer> | ||
| 70 | + so the current model sees it on the next question and the session file | ||
| 71 | + persists and replays it on /resume. Must keep the history valid for | ||
| 72 | + the next model call (user/model alternation: merge into the next user | ||
| 73 | + message, or a user+model pair "[noted]" — to decide in the code). | ||
| 74 | + - Bob's answer is shown while it streams, prefixed/styled as bob's | ||
| 75 | + (decided: displayed, kept for the next question). | ||
| 76 | + - Bob's thoughts: not shown (or dim), never inserted in the history. | ||
| 77 | + - Long answers: capped (maxOutput-like) before insertion — the current | ||
| 78 | + model's context pays for them. | ||
| 79 | + | ||
| 80 | + Risks / things to decide: | ||
| 81 | + - Ticket 57: a permission forwarded to the user and answered slowly will | ||
| 82 | + trip the CHILD's watchdog exactly like today in an editor. | ||
| 83 | + - Ticket 59 (yolo): which permission policy for the child — forward to the | ||
| 84 | + user (default), or auto per config entry? | ||
| 85 | + - Two agents on the same working tree: the parent waits while bob works | ||
| 86 | + (/ask is blocking), so no concurrent edits; but bob may change files the | ||
| 87 | + parent model "remembers" — say so in the inserted message? | ||
| 88 | + - Recursion: /ask is a USER command, the model cannot call it, so no | ||
| 89 | + automatic loop. If it ever becomes a tool ("ask_agent"), cap the depth | ||
| 90 | + (env var MM_ASK_DEPTH) and refuse cycles. | ||
| 91 | + - Paths in mm-configs: relative to the PARENT config file's directory (the | ||
| 92 | + ACP mode already resolves skillsDir that way; a path relative to the cwd | ||
| 93 | + breaks when the config is elsewhere). The child's own relative paths | ||
| 94 | + (skillsDir) resolve against ITS config dir in -acp mode. | ||
| 95 | + - Start-up cost: Genkit init + probe + MCP servers per child (~1 s+); | ||
| 96 | + hence keep the child alive. | ||
| 97 | + - Child stderr: capture it and show it only on failure (start error, | ||
| 98 | + unknown config, provider down -> one line: "bob: nothing answers at …"). | ||
| 99 | + | ||
| 100 | + Demo ready (2026-10-02): demo-llmman/agent.llmman.yaml declares | ||
| 101 | + mmConfigs: | ||
| 102 | + jan: agent.jan-nano.yaml # hf.co/Menlo/Jan-nano-gguf:Q4_K_M | ||
| 103 | + lucy: agent.lucy.yaml # hf.co/Menlo/Lucy-gguf:Q4_K_M | ||
| 104 | + (the key is ignored until implemented: config loading is not strict). Both | ||
| 105 | + configs answered a bash-tool prompt live through llmman. | ||
| 106 | + | ||
| 107 | + Decisions (user, 2026-10-02): | ||
| 108 | + 1. Key in camelCase: `mmConfigs:` (name -> path of a config file). | ||
| 109 | + 2. Bob's answer is DISPLAYED, and stays in the current history for the | ||
| 110 | + NEXT question; the current model does not answer right away. | ||
| 111 | + 3. Two commands: | ||
| 112 | + - /ask <name> <prompt>: bob receives the typed prompt only; | ||
| 113 | + - /ask-with-context <name> <prompt>: bob also receives the context of | ||
| 114 | + the current conversation. | ||
| 115 | + Interpretation to confirm: every /ask starts a FRESH bob session (no | ||
| 116 | + memory between two /ask); continuity comes from /ask-with-context. | ||
| 117 | + 4. Both commands work in the three front ends (Terminal, Window, Editor). | ||
| 118 | + 5. Context: only with /ask-with-context (see 3). | ||
| 119 | + | ||
| 120 | + Editor visibility (checked): yes. The ACP facade already sends | ||
| 121 | + `available_commands_update` built from command.For(Editor) | ||
| 122 | + (internal/acp/acp.go availableCommands), and skills are already advertised | ||
| 123 | + with `input.unstructured.hint`. Today the built-in commands are sent | ||
| 124 | + WITHOUT an input hint: /ask and /ask-with-context need one, e.g. | ||
| 125 | + "bob|riker <prompt>", built from mmConfigs. The editor completes the | ||
| 126 | + command name, not the argument; alternative to discuss: one command per | ||
| 127 | + agent (/ask-bob, /ask-with-context-bob) so the picker lists the agents. | ||
| 128 | + The CHILD's own available_commands_update is ignored by the parent. | ||
| 129 | + | ||
| 130 | + Decided too (user, 2026-10-02): | ||
| 131 | + a. Context for /ask-with-context = a text transcript of the current | ||
| 132 | + history: user questions and assistant answers, each tool call as one | ||
| 133 | + line (name + short args), no tool outputs, no reasoning, the compaction | ||
| 134 | + summary if any; capped (chars), newest turns kept first. Sent as: | ||
| 135 | + [Conversation so far, from <parent agent name>] | ||
| 136 | + ... | ||
| 137 | + [Question] | ||
| 138 | + <prompt> | ||
| 139 | + b. Every /ask (and /ask-with-context) starts a FRESH bob session | ||
| 140 | + (session/new); no memory on bob's side between two calls. | ||
| 141 | + c. Editors: ONE COMMAND PER AGENT, generated from mmConfigs — | ||
| 142 | + /ask-bob, /ask-with-context-bob, /ask-riker, … each with an input hint | ||
| 143 | + "<prompt>". Consequences: | ||
| 144 | + - agent names become command names: validate them in config | ||
| 145 | + (lowercase letters, digits, '-'; error otherwise); | ||
| 146 | + - a skill or built-in with the same name: the existing guard in | ||
| 147 | + availableCommands (commandNamed) keeps the command and drops the skill, | ||
| 148 | + with the banner warning — extend it to the generated names; | ||
| 149 | + - terminal and window (confirmed by the user, 2026-10-02): accept the | ||
| 150 | + per-agent forms there too (same table, no drift), and keep | ||
| 151 | + `/ask <name> <prompt>` as the generic form; the window's "/" picker | ||
| 152 | + lists the per-agent commands. | ||
| 153 | + | ||
| 154 | + Proposed behaviour (to confirm): | ||
| 155 | + - One child per name, started lazily on the first /ask, kept alive for the | ||
| 156 | + parent session (start-up cost), killed on /quit and exit; a fresh ACP | ||
| 157 | + session (session/new) per /ask if 3 is confirmed. | ||
| 158 | + - The /ask exchange enters the CURRENT history as one user message, e.g. | ||
| 159 | + [bob (ollama/qwen3.5:2b) was asked: <prompt>] | ||
| 160 | + <bob's answer> | ||
| 161 | + so the current model sees it on the next question and the session file | ||
| 162 | + persists and replays it on /resume. Must keep the history valid for | ||
| 163 | + the next model call (user/model alternation: merge into the next user | ||
| 164 | + message, or a user+model pair "[noted]" — to decide in the code). | ||
| 165 | + - Bob's answer is shown while it streams, prefixed/styled as bob's | ||
| 166 | + (decided: displayed, kept for the next question). | ||
| 167 | + - Bob's thoughts: not shown (or dim), never inserted in the history. | ||
| 168 | + - Long answers: capped (maxOutput-like) before insertion — the current | ||
| 169 | + model's context pays for them. | ||
| 170 | + | ||
| 171 | + Risks / things to decide: | ||
| 172 | + - Ticket 57: a permission forwarded to the user and answered slowly will | ||
| 173 | + trip the CHILD's watchdog exactly like today in an editor. | ||
| 174 | + - Ticket 59 (yolo): which permission policy for the child — forward to the | ||
| 175 | + user (default), or auto per config entry? | ||
| 176 | + - Two agents on the same working tree: the parent waits while bob works | ||
| 177 | + (/ask is blocking), so no concurrent edits; but bob may change files the | ||
| 178 | + parent model "remembers" — say so in the inserted message? | ||
| 179 | + - Recursion: /ask is a USER command, the model cannot call it, so no | ||
| 180 | + automatic loop. If it ever becomes a tool ("ask_agent"), cap the depth | ||
| 181 | + (env var MM_ASK_DEPTH) and refuse cycles. | ||
| 182 | + - Paths in mm-configs: relative to the PARENT config file's directory (the | ||
| 183 | + ACP mode already resolves skillsDir that way; a path relative to the cwd | ||
| 184 | + breaks when the config is elsewhere). The child's own relative paths | ||
| 185 | + (skillsDir) resolve against ITS config dir in -acp mode. | ||
| 186 | + - Start-up cost: Genkit init + probe + MCP servers per child (~1 s+); | ||
| 187 | + hence keep the child alive. | ||
| 188 | + - Child stderr: capture it and show it only on failure (start error, | ||
| 189 | + unknown config, provider down -> one line: "bob: nothing answers at …"). | ||
| 190 | + | ||
| 191 | + Decisions (user, 2026-10-02): | ||
| 192 | + 1. Key in camelCase: `mmConfigs:` (name -> path of a config file). | ||
| 193 | + 2. Bob's answer is DISPLAYED, and stays in the current history for the | ||
| 194 | + NEXT question; the current model does not answer right away. | ||
| 195 | + 3. Two commands: | ||
| 196 | + - /ask <name> <prompt>: bob receives the typed prompt only; | ||
| 197 | + - /ask-with-context <name> <prompt>: bob also receives the context of | ||
| 198 | + the current conversation. | ||
| 199 | + Interpretation to confirm: every /ask starts a FRESH bob session (no | ||
| 200 | + memory between two /ask); continuity comes from /ask-with-context. | ||
| 201 | + 4. Both commands work in the three front ends (Terminal, Window, Editor). | ||
| 202 | + 5. Context: only with /ask-with-context (see 3). | ||
| 203 | + | ||
| 204 | + Editor visibility (checked): yes. The ACP facade already sends | ||
| 205 | + `available_commands_update` built from command.For(Editor) | ||
| 206 | + (internal/acp/acp.go availableCommands), and skills are already advertised | ||
| 207 | + with `input.unstructured.hint`. Today the built-in commands are sent | ||
| 208 | + WITHOUT an input hint: /ask and /ask-with-context need one, e.g. | ||
| 209 | + "bob|riker <prompt>", built from mmConfigs. The editor completes the | ||
| 210 | + command name, not the argument; alternative to discuss: one command per | ||
| 211 | + agent (/ask-bob, /ask-with-context-bob) so the picker lists the agents. | ||
| 212 | + The CHILD's own available_commands_update is ignored by the parent. | ||
| 213 | + | ||
| 214 | + Still open: | ||
| 215 | + a. What "context" is for /ask-with-context. Proposal: a text transcript of | ||
| 216 | + the current history — user questions and assistant answers, each tool | ||
| 217 | + call as one line (name + short args), no tool outputs, no reasoning, | ||
| 218 | + the compaction summary if any — capped (chars) to protect bob's | ||
| 219 | + window, newest turns kept first. Sent as the first part of the prompt: | ||
| 220 | + [Conversation so far, from <parent agent name>] | ||
| 221 | + ... | ||
| 222 | + [Question] | ||
| 223 | + <prompt> | ||
| 224 | + b. Fresh bob session per /ask (interpretation of 3) — confirm. | ||
| 225 | + c. Editor: one /ask with a hint, or one command per agent? | ||
| 226 | +tasks: | ||
| 227 | + - id: 1 | ||
| 228 | + title: "Answer the open questions (key name, immediate answer, multi-turn, fronts, context passed)" | ||
| 229 | + state: closed | ||
| 230 | + priority: none | ||
| 231 | + author: | ||
| 232 | + name: k33g | ||
| 233 | + email: ph.charriere@gmail.com | ||
| 234 | + createdAt: 2026-10-02T19:30:00.000Z | ||
| 235 | + updatedAt: 2026-10-02T20:00:00.000Z | ||
| 236 | + - id: 2 | ||
| 237 | + title: "Confirm the remaining points a/b/c (context format, fresh session per /ask, editor hint vs one command per agent)" | ||
| 238 | + state: closed | ||
| 239 | + priority: none | ||
| 240 | + author: | ||
| 241 | + name: k33g | ||
| 242 | + email: ph.charriere@gmail.com | ||
| 243 | + createdAt: 2026-10-02T19:30:00.000Z | ||
| 244 | + updatedAt: 2026-10-02T20:00:00.000Z | ||
| 245 | + - id: 3 | ||
| 246 | + title: "config: mmConfigs map (validation: name = [a-z0-9-]+ since it becomes a command name, file exists; paths relative to the config file dir)" | ||
| 247 | + state: closed | ||
| 248 | + priority: none | ||
| 249 | + author: | ||
| 250 | + name: k33g | ||
| 251 | + email: ph.charriere@gmail.com | ||
| 252 | + createdAt: 2026-10-02T19:30:00.000Z | ||
| 253 | + updatedAt: 2026-10-02T20:00:00.000Z | ||
| 254 | + - id: 4 | ||
| 255 | + title: "internal/askagent (name TBD): ACP client over a child `mm -acp -config <path>` (os.Executable), lazy start, one child per name, shutdown" | ||
| 256 | + state: closed | ||
| 257 | + priority: none | ||
| 258 | + author: | ||
| 259 | + name: k33g | ||
| 260 | + email: ph.charriere@gmail.com | ||
| 261 | + createdAt: 2026-10-02T19:30:00.000Z | ||
| 262 | + updatedAt: 2026-10-02T20:00:00.000Z | ||
| 263 | + - id: 5 | ||
| 264 | + title: "Client side: SessionUpdate -> stream text/tool-call lines to the current sink; RequestPermission -> current sink.Allow; fs/terminal stubs" | ||
| 265 | + state: closed | ||
| 266 | + priority: none | ||
| 267 | + author: | ||
| 268 | + name: k33g | ||
| 269 | + email: ph.charriere@gmail.com | ||
| 270 | + createdAt: 2026-10-02T19:30:00.000Z | ||
| 271 | + updatedAt: 2026-10-02T20:00:00.000Z | ||
| 272 | + - id: 6 | ||
| 273 | + title: "command table: /ask <name> <prompt>, /ask-with-context <name> <prompt>, and per-agent /ask-<name>, /ask-with-context-<name> generated from mmConfigs (all fronts); ACP advertises the per-agent ones with hint <prompt>; window picker; name clash guard" | ||
| 274 | + state: closed | ||
| 275 | + priority: none | ||
| 276 | + author: | ||
| 277 | + name: k33g | ||
| 278 | + email: ph.charriere@gmail.com | ||
| 279 | + createdAt: 2026-10-02T19:30:00.000Z | ||
| 280 | + updatedAt: 2026-10-02T20:00:00.000Z | ||
| 281 | + - id: 7 | ||
| 282 | + title: "Context builder for /ask-with-context: transcript of the current history (text + one line per tool call), capped" | ||
| 283 | + state: closed | ||
| 284 | + priority: none | ||
| 285 | + author: | ||
| 286 | + name: k33g | ||
| 287 | + email: ph.charriere@gmail.com | ||
| 288 | + createdAt: 2026-10-02T19:30:00.000Z | ||
| 289 | + updatedAt: 2026-10-02T20:00:00.000Z | ||
| 290 | + - id: 8 | ||
| 291 | + title: "Insert the exchange in the current history (format, cap, valid alternation), persisted by the store and replayed on /resume" | ||
| 292 | + state: closed | ||
| 293 | + priority: none | ||
| 294 | + author: | ||
| 295 | + name: k33g | ||
| 296 | + email: ph.charriere@gmail.com | ||
| 297 | + createdAt: 2026-10-02T19:30:00.000Z | ||
| 298 | + updatedAt: 2026-10-02T20:00:00.000Z | ||
| 299 | + - id: 9 | ||
| 300 | + title: "/abort, Ctrl+C and editor cancel -> session/cancel on the child; child crash -> one-line error, history untouched" | ||
| 301 | + state: closed | ||
| 302 | + priority: none | ||
| 303 | + author: | ||
| 304 | + name: k33g | ||
| 305 | + email: ph.charriere@gmail.com | ||
| 306 | + createdAt: 2026-10-02T19:30:00.000Z | ||
| 307 | + updatedAt: 2026-10-02T20:00:00.000Z | ||
| 308 | + - id: 10 | ||
| 309 | + title: "Tests: fake child agent (acp-go-sdk agent side) for the client; end-to-end with two real configs (demo-ollama + demo-llmman)" | ||
| 310 | + state: closed | ||
| 311 | + priority: none | ||
| 312 | + author: | ||
| 313 | + name: k33g | ||
| 314 | + email: ph.charriere@gmail.com | ||
| 315 | + createdAt: 2026-10-02T19:30:00.000Z | ||
| 316 | + updatedAt: 2026-10-02T20:00:00.000Z | ||
| 317 | + - id: 11 | ||
| 318 | + title: "Docs EN+FR: how-to ask another agent, reference (config key, commands), explanation (why ACP, why a subprocess)" | ||
| 319 | + state: closed | ||
| 320 | + priority: none | ||
| 321 | + author: | ||
| 322 | + name: k33g | ||
| 323 | + email: ph.charriere@gmail.com | ||
| 324 | + createdAt: 2026-10-02T19:30:00.000Z | ||
| 325 | + updatedAt: 2026-10-02T20:00:00.000Z | ||
| 326 | + - id: 12 | ||
| 327 | + title: "Quality gate (qlty) where available — not run: qlty absent from this sandbox" | ||
| 328 | + state: open | ||
| 329 | + priority: none | ||
| 330 | + author: | ||
| 331 | + name: k33g | ||
| 332 | + email: ph.charriere@gmail.com | ||
| 333 | + createdAt: 2026-10-02T20:30:00.000Z | ||
| 334 | + updatedAt: 2026-10-02T20:30:00.000Z | ||
| 335 | + - id: 13 | ||
| 336 | + title: "Live check of the window mode (/ask-jan in mm -tui) — unit-tested only" | ||
| 337 | + state: open | ||
| 338 | + priority: none | ||
| 339 | + author: | ||
| 340 | + name: k33g | ||
| 341 | + email: ph.charriere@gmail.com | ||
| 342 | + createdAt: 2026-10-02T20:30:00.000Z | ||
| 343 | + updatedAt: 2026-10-02T20:30:00.000Z | ||
| 344 | + - id: 14 | ||
| 345 | + title: "Review, commit, merge" | ||
| 346 | + state: open | ||
| 347 | + priority: none | ||
| 348 | + author: | ||
| 349 | + name: k33g | ||
| 350 | + email: ph.charriere@gmail.com | ||
| 351 | + createdAt: 2026-10-02T20:30:00.000Z | ||
| 352 | + updatedAt: 2026-10-02T20:30:00.000Z | ||
| 353 | +comments: | ||
| 354 | + - id: 1 | ||
| 355 | + author: | ||
| 356 | + name: claude | ||
| 357 | + email: noreply@anthropic.com | ||
| 358 | + createdAt: 2026-10-02T20:30:00.000Z | ||
| 359 | + body: | | ||
| 360 | + **Implemented** on branch `feature/talk-to-another-mm` (uncommitted, 2026-10-02). | ||
| 361 | + | ||
| 362 | + - config: `mmConfigs` (internal/config/agents.go) — names [a-z0-9-], not | ||
| 363 | + starting with `with-context`; relative paths resolved against the config | ||
| 364 | + file's dir at Load; missing file = start-up warning. `AGENT_SESSIONS=off` | ||
| 365 | + env override (applyEnv). | ||
| 366 | + - command: /ask family generated by SetAgents (internal/command/ask.go); | ||
| 367 | + `Hint` field; ParseAsk returns (req, isAsk, err). | ||
| 368 | + - internal/ask (new): Transcript/Prompt (context.go), Exchange as a | ||
| 369 | + user+model pair + Describe (exchange.go), ChildEnv/Depth/CanAsk | ||
| 370 | + (env.go, MaxDepth 2), ACP client (client.go), Pool with one lazy child | ||
| 371 | + per agent + fresh session per ask (pool.go), Pool.Run + Runner interface | ||
| 372 | + + Label + Missing (run.go). | ||
| 373 | + - Front ends: agent/ask.go (Ctrl+C, /abort, /quit), tui/ask.go (job | ||
| 374 | + "asking <agent>"), acp/ask.go (per-agent commands with `<prompt>` hint); | ||
| 375 | + main.go wiring; store.FailAsk. | ||
| 376 | + - Traps found: the SDK dispatches the child's requests concurrently with | ||
| 377 | + its notifications — a permission request can overtake its tool_call | ||
| 378 | + (client shows the call first, once); the SDK's default slog would print | ||
| 379 | + "connection closed" on the user's terminal (silenced); the parent's | ||
| 380 | + AGENT_* env would override the child's config (stripped). | ||
| 381 | + - Tests: config, command, ask (fake ACP agent = the test binary re-run, | ||
| 382 | + no network), agent, tui, acp. `go test ./...` green 3x; ask tests 15x. | ||
| 383 | + `-race` not available (no C compiler in the sandbox). | ||
| 384 | + - Live (host llmman): line mode `/ask-jan` → answer kept, the current | ||
| 385 | + model quoted it next turn; ACP `/ask-with-context-lucy` → per-agent | ||
| 386 | + commands announced, Lucy knew the name given earlier, her bash | ||
| 387 | + permission went to the client titled "lucy · bash: …"; no child | ||
| 388 | + session files, no leftover processes. | ||
| 389 | + - Docs EN+FR: how-to/ask-another-agent.md, explanation/asking-another-agent.md, | ||
| 390 | + reference (slash-commands, configuration, cli), architecture table, | ||
| 391 | + READMEs, docs/diagrams/packages.drawio (internal/ask + edges). | ||
added
demo-llmman/agent.jan-nano.yaml +199 -0 | new file mode 100644 | ||
| @@ -0,0 +1,199 @@ | ||
| 1 | +# Agent settings — Jan: mini-me on hf.co/Menlo/Jan-nano-gguf:Q4_K_M, served by llmman (https://github.com/llmmanorg/llmman) | |
| 2 | +# through its OpenAI-compatible endpoint (/v1/chat/completions). | |
| 3 | +# | |
| 4 | +# llmman pull hf.co/Menlo/Jan-nano-gguf:Q4_K_M # optional: the first question pulls it otherwise | |
| 5 | +# llmman serve # 127.0.0.1:17434 | |
| 6 | +# | |
| 7 | +# llmman loads the model on the first request (in its own llama-server) and | |
| 8 | +# unloads it after five idle minutes. | |
| 9 | +provider: llmman | |
| 10 | + | |
| 11 | +# The model id exactly as `llmman list` / GET /v1/models spells it. | |
| 12 | +model: hf.co/Menlo/Jan-nano-gguf:Q4_K_M | |
| 13 | + | |
| 14 | +# `llmman serve`'s default bind; the OpenAI routes sit under /v1. | |
| 15 | +baseUrl: http://127.0.0.1:17434/v1 | |
| 16 | + | |
| 17 | +# From a Docker sandbox, the daemon runs on the host. | |
| 18 | +fallback: http://host.docker.internal:17434/v1 | |
| 19 | + | |
| 20 | +# Only when the daemon was started with LLMMAN_API_KEYS (mandatory when it | |
| 21 | +# listens beyond loopback); unset otherwise. The key never goes in this file. | |
| 22 | +# apiKeyEnv: LLMMAN_API_KEY | |
| 23 | + | |
| 24 | +# The `bash` tool. | |
| 25 | +# `false` removes it: the model can still read and edit files (editTools) and load skills, | |
| 26 | +# but never runs a command. If you turn it off, reword the system prompt below — it says "You have a bash tool". | |
| 27 | +bashTool: true | |
| 28 | + | |
| 29 | +# The built-in file tools: read_file, write_file, edit_file. | |
| 30 | +editTools: true | |
| 31 | + | |
| 32 | +# The built-in plan tool for managing multi-step plans. | |
| 33 | +# Set to false to disable plan creation and tracking via the `plan` tool. | |
| 34 | +planTool: true | |
| 35 | + | |
| 36 | +# Language servers for the `lsp` tool (definition, references, hover, symbols, diagnostics, rename). | |
| 37 | +# The file extension picks the server. A server missing from the PATH is skipped with a warning. | |
| 38 | +# No server listed = no `lsp` tool. Examples (uncomment what you have installed): | |
| 39 | +# lsp: | |
| 40 | +# servers: | |
| 41 | +# - name: gopls | |
| 42 | +# command: gopls | |
| 43 | +# extensions: [".go"] | |
| 44 | +# - name: typescript | |
| 45 | +# command: typescript-language-server | |
| 46 | +# args: ["--stdio"] | |
| 47 | +# extensions: [".ts", ".tsx", ".js", ".jsx"] | |
| 48 | +# - name: python | |
| 49 | +# command: pyright-langserver | |
| 50 | +# args: ["--stdio"] | |
| 51 | +# extensions: [".py"] | |
| 52 | +# - name: rust | |
| 53 | +# command: rust-analyzer | |
| 54 | +# extensions: [".rs"] | |
| 55 | + | |
| 56 | +# How many tokens the server actually serves. | |
| 57 | +# 0 = ask llmman: /api/ps tells it once the model is loaded, so the window is | |
| 58 | +# learned before the second question. Set it here if you want it from the start | |
| 59 | +# — and to the same value as LLMMAN_CONTEXT_LENGTH if you set that variable. | |
| 60 | +contextWindow: 0 | |
| 61 | + | |
| 62 | +# Max number of characters a tool returns to the model. | |
| 63 | +# Context safeguard: beyond that, the output is truncated (beginning + end kept). | |
| 64 | +maxOutput: 16000 | |
| 65 | + | |
| 66 | +# Max number of model <-> tools round trips for a single question. | |
| 67 | +# Raise it for tasks that need long chains of commands. | |
| 68 | +maxTurns: 40 | |
| 69 | + | |
| 70 | +# Where the `read_skill` tool looks for its markdown procedures. | |
| 71 | +# The path is relative to the CURRENT DIRECTORY: run the agent from demo-llmman/. | |
| 72 | +skillsDir: ../skills | |
| 73 | + | |
| 74 | +# Persistent sessions, per project. | |
| 75 | +# When enabled, every conversation is saved as one JSON file under `dir`. | |
| 76 | +# `/sessions` lists them, `/resume` continues one; in an editor, "Reload Agent" replays the thread. | |
| 77 | +sessions: | |
| 78 | + enabled: true | |
| 79 | + dir: .mm/sessions | |
| 80 | + | |
| 81 | +# How many lines of a command's output are echoed to the terminal. | |
| 82 | +# 0 disables it. | |
| 83 | +previewLines: 20 | |
| 84 | + | |
| 85 | +displayCommands: true | |
| 86 | +# Show what a thinking model reasons about before it answers (dim italic in the | |
| 87 | +# terminal, its own style in the window, a thought chunk in an editor). | |
| 88 | +# The reasoning is always READ — it is what keeps the watchdog alive while the | |
| 89 | +# model thinks — this only says whether to show it. | |
| 90 | +showThinking: true | |
| 91 | + | |
| 92 | + | |
| 93 | +# The system prompt: what the agent is, and what it is allowed to do. | |
| 94 | +# This is the knob to play with — it is the shortest path to changing behaviour. | |
| 95 | +system: | | |
| 96 | + Your name is Jan. | |
| 97 | + You are a coding agent working in a terminal. | |
| 98 | + You have a "bash" tool to run shell commands. | |
| 99 | + Use it to explore files, run tests, inspect the repository, etc. | |
| 100 | + Chain several commands if needed, then answer clearly in English. | |
| 101 | + | |
| 102 | + A request often mixes things you answer from yourself ("say hello") with | |
| 103 | + things only a command can answer ("list the files"). Handle every part, in | |
| 104 | + the order asked, and run a command for each part that needs one. | |
| 105 | + Never state the contents of a file, the output of a command, or the state of | |
| 106 | + the repository unless a command in THIS answer returned it. What you did not | |
| 107 | + read, you do not know: run the command instead of recalling it. | |
| 108 | + | |
| 109 | + SKILLS | |
| 110 | + You have a second tool, `read_skill`. Its description lists the procedures | |
| 111 | + available for this project — one per kind of task. | |
| 112 | + | |
| 113 | + Any request to DO something to a Go project is a skill, not a shell command | |
| 114 | + you invent. Match the request against that list, call `read_skill` FIRST, | |
| 115 | + before any bash command, and then follow what it says step by step. | |
| 116 | + | |
| 117 | + FILE EDITING | |
| 118 | + You have three tools for files: `read_file`, `edit_file` and `write_file`. | |
| 119 | + They are how a file gets read and changed here: each change is exact, | |
| 120 | + checked before it is written, and comes back as a diff with line numbers. | |
| 121 | + bash is for running things — building, testing, listing, searching. | |
| 122 | + | |
| 123 | + - Read before you write: call `read_file` on the file (numbered=true when | |
| 124 | + you need line numbers). You cannot target text you have not seen; never | |
| 125 | + rely on what you think you remember about a file. | |
| 126 | + - To change an existing file, call `edit_file` with one or more {old, new} | |
| 127 | + pairs. `old` is copied from the file character for character — same | |
| 128 | + spaces, same indentation, same line breaks — and appears exactly once: | |
| 129 | + add the surrounding lines until it is unique. Several pairs are applied | |
| 130 | + together, against the original file. An empty `new` deletes the text. | |
| 131 | + - Call `write_file` only to create a file, or to rewrite one entirely and | |
| 132 | + on purpose. On an existing file it replaces everything, including what | |
| 133 | + you did not intend to touch. | |
| 134 | + - Read the diff the tool returns: it says exactly what changed and on which | |
| 135 | + line. If `edit_file` refuses — text not found, ambiguous, overlapping | |
| 136 | + edits — read the file again and fix `old`. Do not fall back to | |
| 137 | + `write_file` to force the change through. | |
| 138 | + - After editing code, run the narrowest check with bash: the formatter, the | |
| 139 | + compiler, or the test covering that file. | |
| 140 | + | |
| 141 | + RULES | |
| 142 | + - Keep everything the file already does, unless the user asked to remove it. | |
| 143 | + - Touch only the files the request is about. Do not add tests, files or | |
| 144 | + features that were not asked for. | |
| 145 | + - Never run a git command unless the user says git, commit or push. | |
| 146 | + - Never move, rename or delete a file unless the user asked for it. | |
| 147 | + - Then answer in English, in a few lines. | |
| 148 | + - If you don't know how to use a <cli>, run `<cli> --help` (or `<cli> help`) | |
| 149 | + to understand the options, then run the command. | |
| 150 | + | |
| 151 | + BACKGROUND JOBS | |
| 152 | + Never let a command block the answer. Anything that serves, watches or runs | |
| 153 | + long goes to the background, with BOTH streams redirected and its pid kept: | |
| 154 | + | |
| 155 | + nohup <command> > /tmp/<job>.log 2>&1 & echo $! > /tmp/<job>.pid | |
| 156 | + | |
| 157 | + Redirecting only stdout still blocks until the process exits. Read the | |
| 158 | + `bg-jobs` skill before you wait on, inspect or stop such a job — each has a | |
| 159 | + rule you cannot guess. Stop every job you started before you finish, and say | |
| 160 | + which ones you left running. | |
| 161 | + | |
| 162 | +# Generation settings (OpenAI API keys). Low temperature for a coding agent: | |
| 163 | +# we want precise and reproducible answers, not creativity. | |
| 164 | +sampling: | |
| 165 | + temperature: 0.0 | |
| 166 | + parallel_tool_calls: false | |
| 167 | + top_p: 0.9 | |
| 168 | + max_tokens: 4096 | |
| 169 | + | |
| 170 | +# Silence allowed between two tokens once the answer has started. | |
| 171 | +watchdogTimeout: 30s | |
| 172 | + | |
| 173 | +# Silence allowed while the model reads the prompt, before the first token: | |
| 174 | +# on a laptop a 25k-token prompt can take minutes. llama-server reports its | |
| 175 | +# progress meanwhile, through llmman ("Reading the prompt 12.0k/25.3k tokens"), and each report | |
| 176 | +# restarts this clock, so it only has to cover one batch of the prompt. | |
| 177 | +firstTokenTimeout: 3m | |
| 178 | + | |
| 179 | +# Context compression. | |
| 180 | +# ON here: the agent learns the window from /api/ps, so `threshold` has a number to apply to. | |
| 181 | +context: | |
| 182 | + enabled: true | |
| 183 | + | |
| 184 | + # Compress when the history reaches this share of the window. | |
| 185 | + threshold: 75 | |
| 186 | + | |
| 187 | + # Fallback on the message count, for when the window is unknown. | |
| 188 | + # One command costs 2 messages (call + response), so 80 is roughly 30 commands of history. | |
| 189 | + maxMessages: 80 | |
| 190 | + | |
| 191 | + # How many recent questions (with their commands and answers) are kept as-is instead of being summarised. | |
| 192 | + # The model works on the last few turns, so summarising them would make it redo what it just did. | |
| 193 | + keepLastTurns: 3 | |
| 194 | + | |
| 195 | + # max_tokens of the summary request. Reasoning is turned off for it on this provider | |
| 196 | + # (a thinking model would otherwise spend the budget thinking and write nothing). | |
| 197 | + summaryMaxTokens: 1200 | |
| 198 | + | |
| 199 | + showStats: true | |
| new file mode 100644 | |||
| @@ -0,0 +1,199 @@ | |||
| 1 | +# Agent settings — Jan: mini-me on hf.co/Menlo/Jan-nano-gguf:Q4_K_M, served by llmman (https://github.com/llmmanorg/llmman) | ||
| 2 | +# through its OpenAI-compatible endpoint (/v1/chat/completions). | ||
| 3 | +# | ||
| 4 | +# llmman pull hf.co/Menlo/Jan-nano-gguf:Q4_K_M # optional: the first question pulls it otherwise | ||
| 5 | +# llmman serve # 127.0.0.1:17434 | ||
| 6 | +# | ||
| 7 | +# llmman loads the model on the first request (in its own llama-server) and | ||
| 8 | +# unloads it after five idle minutes. | ||
| 9 | +provider: llmman | ||
| 10 | + | ||
| 11 | +# The model id exactly as `llmman list` / GET /v1/models spells it. | ||
| 12 | +model: hf.co/Menlo/Jan-nano-gguf:Q4_K_M | ||
| 13 | + | ||
| 14 | +# `llmman serve`'s default bind; the OpenAI routes sit under /v1. | ||
| 15 | +baseUrl: http://127.0.0.1:17434/v1 | ||
| 16 | + | ||
| 17 | +# From a Docker sandbox, the daemon runs on the host. | ||
| 18 | +fallback: http://host.docker.internal:17434/v1 | ||
| 19 | + | ||
| 20 | +# Only when the daemon was started with LLMMAN_API_KEYS (mandatory when it | ||
| 21 | +# listens beyond loopback); unset otherwise. The key never goes in this file. | ||
| 22 | +# apiKeyEnv: LLMMAN_API_KEY | ||
| 23 | + | ||
| 24 | +# The `bash` tool. | ||
| 25 | +# `false` removes it: the model can still read and edit files (editTools) and load skills, | ||
| 26 | +# but never runs a command. If you turn it off, reword the system prompt below — it says "You have a bash tool". | ||
| 27 | +bashTool: true | ||
| 28 | + | ||
| 29 | +# The built-in file tools: read_file, write_file, edit_file. | ||
| 30 | +editTools: true | ||
| 31 | + | ||
| 32 | +# The built-in plan tool for managing multi-step plans. | ||
| 33 | +# Set to false to disable plan creation and tracking via the `plan` tool. | ||
| 34 | +planTool: true | ||
| 35 | + | ||
| 36 | +# Language servers for the `lsp` tool (definition, references, hover, symbols, diagnostics, rename). | ||
| 37 | +# The file extension picks the server. A server missing from the PATH is skipped with a warning. | ||
| 38 | +# No server listed = no `lsp` tool. Examples (uncomment what you have installed): | ||
| 39 | +# lsp: | ||
| 40 | +# servers: | ||
| 41 | +# - name: gopls | ||
| 42 | +# command: gopls | ||
| 43 | +# extensions: [".go"] | ||
| 44 | +# - name: typescript | ||
| 45 | +# command: typescript-language-server | ||
| 46 | +# args: ["--stdio"] | ||
| 47 | +# extensions: [".ts", ".tsx", ".js", ".jsx"] | ||
| 48 | +# - name: python | ||
| 49 | +# command: pyright-langserver | ||
| 50 | +# args: ["--stdio"] | ||
| 51 | +# extensions: [".py"] | ||
| 52 | +# - name: rust | ||
| 53 | +# command: rust-analyzer | ||
| 54 | +# extensions: [".rs"] | ||
| 55 | + | ||
| 56 | +# How many tokens the server actually serves. | ||
| 57 | +# 0 = ask llmman: /api/ps tells it once the model is loaded, so the window is | ||
| 58 | +# learned before the second question. Set it here if you want it from the start | ||
| 59 | +# — and to the same value as LLMMAN_CONTEXT_LENGTH if you set that variable. | ||
| 60 | +contextWindow: 0 | ||
| 61 | + | ||
| 62 | +# Max number of characters a tool returns to the model. | ||
| 63 | +# Context safeguard: beyond that, the output is truncated (beginning + end kept). | ||
| 64 | +maxOutput: 16000 | ||
| 65 | + | ||
| 66 | +# Max number of model <-> tools round trips for a single question. | ||
| 67 | +# Raise it for tasks that need long chains of commands. | ||
| 68 | +maxTurns: 40 | ||
| 69 | + | ||
| 70 | +# Where the `read_skill` tool looks for its markdown procedures. | ||
| 71 | +# The path is relative to the CURRENT DIRECTORY: run the agent from demo-llmman/. | ||
| 72 | +skillsDir: ../skills | ||
| 73 | + | ||
| 74 | +# Persistent sessions, per project. | ||
| 75 | +# When enabled, every conversation is saved as one JSON file under `dir`. | ||
| 76 | +# `/sessions` lists them, `/resume` continues one; in an editor, "Reload Agent" replays the thread. | ||
| 77 | +sessions: | ||
| 78 | + enabled: true | ||
| 79 | + dir: .mm/sessions | ||
| 80 | + | ||
| 81 | +# How many lines of a command's output are echoed to the terminal. | ||
| 82 | +# 0 disables it. | ||
| 83 | +previewLines: 20 | ||
| 84 | + | ||
| 85 | +displayCommands: true | ||
| 86 | +# Show what a thinking model reasons about before it answers (dim italic in the | ||
| 87 | +# terminal, its own style in the window, a thought chunk in an editor). | ||
| 88 | +# The reasoning is always READ — it is what keeps the watchdog alive while the | ||
| 89 | +# model thinks — this only says whether to show it. | ||
| 90 | +showThinking: true | ||
| 91 | + | ||
| 92 | + | ||
| 93 | +# The system prompt: what the agent is, and what it is allowed to do. | ||
| 94 | +# This is the knob to play with — it is the shortest path to changing behaviour. | ||
| 95 | +system: | | ||
| 96 | + Your name is Jan. | ||
| 97 | + You are a coding agent working in a terminal. | ||
| 98 | + You have a "bash" tool to run shell commands. | ||
| 99 | + Use it to explore files, run tests, inspect the repository, etc. | ||
| 100 | + Chain several commands if needed, then answer clearly in English. | ||
| 101 | + | ||
| 102 | + A request often mixes things you answer from yourself ("say hello") with | ||
| 103 | + things only a command can answer ("list the files"). Handle every part, in | ||
| 104 | + the order asked, and run a command for each part that needs one. | ||
| 105 | + Never state the contents of a file, the output of a command, or the state of | ||
| 106 | + the repository unless a command in THIS answer returned it. What you did not | ||
| 107 | + read, you do not know: run the command instead of recalling it. | ||
| 108 | + | ||
| 109 | + SKILLS | ||
| 110 | + You have a second tool, `read_skill`. Its description lists the procedures | ||
| 111 | + available for this project — one per kind of task. | ||
| 112 | + | ||
| 113 | + Any request to DO something to a Go project is a skill, not a shell command | ||
| 114 | + you invent. Match the request against that list, call `read_skill` FIRST, | ||
| 115 | + before any bash command, and then follow what it says step by step. | ||
| 116 | + | ||
| 117 | + FILE EDITING | ||
| 118 | + You have three tools for files: `read_file`, `edit_file` and `write_file`. | ||
| 119 | + They are how a file gets read and changed here: each change is exact, | ||
| 120 | + checked before it is written, and comes back as a diff with line numbers. | ||
| 121 | + bash is for running things — building, testing, listing, searching. | ||
| 122 | + | ||
| 123 | + - Read before you write: call `read_file` on the file (numbered=true when | ||
| 124 | + you need line numbers). You cannot target text you have not seen; never | ||
| 125 | + rely on what you think you remember about a file. | ||
| 126 | + - To change an existing file, call `edit_file` with one or more {old, new} | ||
| 127 | + pairs. `old` is copied from the file character for character — same | ||
| 128 | + spaces, same indentation, same line breaks — and appears exactly once: | ||
| 129 | + add the surrounding lines until it is unique. Several pairs are applied | ||
| 130 | + together, against the original file. An empty `new` deletes the text. | ||
| 131 | + - Call `write_file` only to create a file, or to rewrite one entirely and | ||
| 132 | + on purpose. On an existing file it replaces everything, including what | ||
| 133 | + you did not intend to touch. | ||
| 134 | + - Read the diff the tool returns: it says exactly what changed and on which | ||
| 135 | + line. If `edit_file` refuses — text not found, ambiguous, overlapping | ||
| 136 | + edits — read the file again and fix `old`. Do not fall back to | ||
| 137 | + `write_file` to force the change through. | ||
| 138 | + - After editing code, run the narrowest check with bash: the formatter, the | ||
| 139 | + compiler, or the test covering that file. | ||
| 140 | + | ||
| 141 | + RULES | ||
| 142 | + - Keep everything the file already does, unless the user asked to remove it. | ||
| 143 | + - Touch only the files the request is about. Do not add tests, files or | ||
| 144 | + features that were not asked for. | ||
| 145 | + - Never run a git command unless the user says git, commit or push. | ||
| 146 | + - Never move, rename or delete a file unless the user asked for it. | ||
| 147 | + - Then answer in English, in a few lines. | ||
| 148 | + - If you don't know how to use a <cli>, run `<cli> --help` (or `<cli> help`) | ||
| 149 | + to understand the options, then run the command. | ||
| 150 | + | ||
| 151 | + BACKGROUND JOBS | ||
| 152 | + Never let a command block the answer. Anything that serves, watches or runs | ||
| 153 | + long goes to the background, with BOTH streams redirected and its pid kept: | ||
| 154 | + | ||
| 155 | + nohup <command> > /tmp/<job>.log 2>&1 & echo $! > /tmp/<job>.pid | ||
| 156 | + | ||
| 157 | + Redirecting only stdout still blocks until the process exits. Read the | ||
| 158 | + `bg-jobs` skill before you wait on, inspect or stop such a job — each has a | ||
| 159 | + rule you cannot guess. Stop every job you started before you finish, and say | ||
| 160 | + which ones you left running. | ||
| 161 | + | ||
| 162 | +# Generation settings (OpenAI API keys). Low temperature for a coding agent: | ||
| 163 | +# we want precise and reproducible answers, not creativity. | ||
| 164 | +sampling: | ||
| 165 | + temperature: 0.0 | ||
| 166 | + parallel_tool_calls: false | ||
| 167 | + top_p: 0.9 | ||
| 168 | + max_tokens: 4096 | ||
| 169 | + | ||
| 170 | +# Silence allowed between two tokens once the answer has started. | ||
| 171 | +watchdogTimeout: 30s | ||
| 172 | + | ||
| 173 | +# Silence allowed while the model reads the prompt, before the first token: | ||
| 174 | +# on a laptop a 25k-token prompt can take minutes. llama-server reports its | ||
| 175 | +# progress meanwhile, through llmman ("Reading the prompt 12.0k/25.3k tokens"), and each report | ||
| 176 | +# restarts this clock, so it only has to cover one batch of the prompt. | ||
| 177 | +firstTokenTimeout: 3m | ||
| 178 | + | ||
| 179 | +# Context compression. | ||
| 180 | +# ON here: the agent learns the window from /api/ps, so `threshold` has a number to apply to. | ||
| 181 | +context: | ||
| 182 | + enabled: true | ||
| 183 | + | ||
| 184 | + # Compress when the history reaches this share of the window. | ||
| 185 | + threshold: 75 | ||
| 186 | + | ||
| 187 | + # Fallback on the message count, for when the window is unknown. | ||
| 188 | + # One command costs 2 messages (call + response), so 80 is roughly 30 commands of history. | ||
| 189 | + maxMessages: 80 | ||
| 190 | + | ||
| 191 | + # How many recent questions (with their commands and answers) are kept as-is instead of being summarised. | ||
| 192 | + # The model works on the last few turns, so summarising them would make it redo what it just did. | ||
| 193 | + keepLastTurns: 3 | ||
| 194 | + | ||
| 195 | + # max_tokens of the summary request. Reasoning is turned off for it on this provider | ||
| 196 | + # (a thinking model would otherwise spend the budget thinking and write nothing). | ||
| 197 | + summaryMaxTokens: 1200 | ||
| 198 | + | ||
| 199 | + showStats: true | ||
modified
demo-llmman/agent.llmman.yaml +9 -0 | @@ -17,6 +17,15 @@ baseUrl: http://127.0.0.1:17434/v1 | ||
| 17 | 17 | # From a Docker sandbox, the daemon runs on the host. |
| 18 | 18 | fallback: http://host.docker.internal:17434/v1 |
| 19 | 19 | |
| 20 | +# Other mm agents this one can ask. Each name becomes /ask-<name> and | |
| 21 | +# /ask-with-context-<name> (also `/ask <name> <prompt>` in the terminal and the | |
| 22 | +# window); the answer is shown and kept in the history for the next question. | |
| 23 | +# Each agent runs as a child `mm -acp` started on its first /ask. Paths are | |
| 24 | +# relative to THIS file's directory. Names: lowercase letters, digits and '-'. | |
| 25 | +mmConfigs: | |
| 26 | + jan: agent.jan-nano.yaml | |
| 27 | + lucy: agent.lucy.yaml | |
| 28 | + | |
| 20 | 29 | # Only when the daemon was started with LLMMAN_API_KEYS (mandatory when it |
| 21 | 30 | # listens beyond loopback); unset otherwise. The key never goes in this file. |
| 22 | 31 | # apiKeyEnv: LLMMAN_API_KEY |
| @@ -17,6 +17,15 @@ baseUrl: http://127.0.0.1:17434/v1 | |||
| 17 | # From a Docker sandbox, the daemon runs on the host. | 17 | # From a Docker sandbox, the daemon runs on the host. |
| 18 | fallback: http://host.docker.internal:17434/v1 | 18 | fallback: http://host.docker.internal:17434/v1 |
| 19 | 19 | ||
| 20 | +# Other mm agents this one can ask. Each name becomes /ask-<name> and | ||
| 21 | +# /ask-with-context-<name> (also `/ask <name> <prompt>` in the terminal and the | ||
| 22 | +# window); the answer is shown and kept in the history for the next question. | ||
| 23 | +# Each agent runs as a child `mm -acp` started on its first /ask. Paths are | ||
| 24 | +# relative to THIS file's directory. Names: lowercase letters, digits and '-'. | ||
| 25 | +mmConfigs: | ||
| 26 | + jan: agent.jan-nano.yaml | ||
| 27 | + lucy: agent.lucy.yaml | ||
| 28 | + | ||
| 20 | # Only when the daemon was started with LLMMAN_API_KEYS (mandatory when it | 29 | # Only when the daemon was started with LLMMAN_API_KEYS (mandatory when it |
| 21 | # listens beyond loopback); unset otherwise. The key never goes in this file. | 30 | # listens beyond loopback); unset otherwise. The key never goes in this file. |
| 22 | # apiKeyEnv: LLMMAN_API_KEY | 31 | # apiKeyEnv: LLMMAN_API_KEY |
added
demo-llmman/agent.lucy.yaml +199 -0 Large diff collapsed to keep the page fast — load it when you need it.
modified
docs/diagrams/packages.drawio +1 -1 | @@ -1,3 +1,3 @@ | ||
| 1 | 1 | <mxfile host="app.diagrams.net" modified="2026-09-15T00:00:00.000Z" agent="mm docs" version="24.0.0" type="device"><diagram id="packages" name="packages"><mxGraphModel dx="1400" dy="900" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1400" pageHeight="900"><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="main" value="<b>main (mm)</b><br/><font style="font-size:10px">wiring: config, engine, tools, then one front end</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="1"><mxGeometry x="420" y="40" width="200" height="60" as="geometry"/></mxCell><mxCell id="agent" value="<b>internal/agent</b><br/><font style="font-size:10px">terminal REPL: /quit /abort /compact /new</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1"><mxGeometry x="160" y="180" width="200" height="60" as="geometry"/></mxCell><mxCell id="acp" value="<b>internal/acp</b><br/><font style="font-size:10px">editor front end: Agent Client Protocol on stdio</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1"><mxGeometry x="700" y="180" width="200" height="60" as="geometry"/></mxCell><mxCell id="session" value="<b>internal/session</b><br/><font style="font-size:10px">what a conversation starts from; the /new reset</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="320" width="200" height="60" as="geometry"/></mxCell><mxCell id="compact" value="<b>internal/compact</b><br/><font style="font-size:10px">history compression into a model-written summary</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="40" y="320" width="200" height="60" as="geometry"/></mxCell><mxCell id="engine" value="<b>internal/engine</b><br/><font style="font-size:10px">LLM connection + generation, one Provider per server</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="700" y="320" width="200" height="60" as="geometry"/></mxCell><mxCell id="tools" value="<b>internal/tools</b><br/><font style="font-size:10px">built-in tools: bash, read_skill, file tools, lsp, fetch_content</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="460" width="200" height="60" as="geometry"/></mxCell><mxCell id="detector" value="<b>internal/detector</b><br/><font style="font-size:10px">repeated-action (loop) detection</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="40" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="config" value="<b>internal/config</b><br/><font style="font-size:10px">settings with YAML overrides (agent.yaml)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="260" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="spinner" value="<b>internal/spinner</b><br/><font style="font-size:10px">terminal spinner and styling</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="480" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="ui" value="<b>internal/ui</b><br/><font style="font-size:10px">human output destination + shared event sink</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="700" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="fileedit" value="<b>internal/fileedit</b><br/><font style="font-size:10px">exact-replacement file editing</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="920" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="skills" value="<b>internal/skills</b><br/><font style="font-size:10px">markdown skills catalogue (./skills)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1140" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="genkit" value="<b>github.com/firebase/genkit</b><br/><font style="font-size:10px">ai.Message, tools, model calls</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="160" y="760" width="200" height="60" as="geometry"/></mxCell><mxCell id="acpsdk" value="<b>github.com/coder/acp-go-sdk</b><br/><font style="font-size:10px">JSON-RPC transport for ACP</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="700" y="760" width="200" height="60" as="geometry"/></mxCell><mxCell id="openai" value="<b>github.com/openai/openai-go</b><br/><font style="font-size:10px">OpenAI-compatible HTTP client</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="920" y="760" width="200" height="60" as="geometry"/></mxCell><mxCell id="yaml" value="<b>gopkg.in/yaml.v3</b><br/><font style="font-size:10px">YAML parsing</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="400" y="760" width="200" height="60" as="geometry"/></mxCell><mxCell id="e0" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="agent"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="version" value="<b>internal/version</b><br/><font style="font-size:10px">which mm this is: build stamp, else the commit</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1380" y="600" width="200" height="60" as="geometry"/></mxCell> |
| 2 | 2 | <mxCell id="e61" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="version"><mxGeometry relative="1" as="geometry"/></mxCell> |
| 3 | - <mxCell id="e1" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="acp"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e2" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e3" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="detector"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e4" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="engine"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e5" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="skills"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e6" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="spinner"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e7" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="tools"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e8" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e9" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="engine"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e10" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="session"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e11" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e12" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="acp" target="acpsdk"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e13" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="acp" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e14" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="compact"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e15" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e16" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="detector"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e17" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="engine"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e18" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="session"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e19" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="spinner"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e20" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="agent" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e21" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="compact" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e22" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="compact" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e23" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="config" target="yaml"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e24" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="engine" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e25" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="engine" target="spinner"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e26" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="engine" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e27" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="engine" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e28" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="engine" target="openai"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e29" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e30" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="detector"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e31" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="fileedit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e32" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="skills"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e33" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="spinner"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e34" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e35" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="tools" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e36" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="session" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="mention" value="<b>internal/mention</b><br/><font style="font-size:10px">the @path notation of a question</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="400" width="200" height="60" as="geometry"/></mxCell><mxCell id="e37" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="mention"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e38" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="mention"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="store" value="<b>internal/store</b><br/><font style="font-size:10px">per-project session files (.mm/sessions/<id>.json)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="480" width="200" height="60" as="geometry"/></mxCell><mxCell id="e39" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="store"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e40" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="store"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="tui" value="<b>internal/tui</b><br/><font style="font-size:10px">window front end: one screen on tcell (mm -tui)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1"><mxGeometry x="430" y="160" width="200" height="60" as="geometry"/></mxCell><mxCell id="command" value="<b>internal/command</b><br/><font style="font-size:10px">the one slash-command table, per front end</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="560" width="200" height="60" as="geometry"/></mxCell><mxCell id="tcell" value="<b>github.com/gdamore/tcell/v2</b><br/><font style="font-size:10px">terminal screen, keys, mouse</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="920" y="840" width="200" height="60" as="geometry"/></mxCell><mxCell id="e41" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="tui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e42" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e43" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="engine"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e44" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="compact"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e45" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="store"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e46" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="mention"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e47" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="session"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e48" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="detector"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e49" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e50" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="command"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e51" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="tcell"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e52" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="command"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e53" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="command"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e54" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="command" target="session"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="clipboard" value="<b>internal/clipboard</b><br/><font style="font-size:10px">system clipboard: native tool, then OSC 52</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1140" y="680" width="200" height="60" as="geometry"/></mxCell><mxCell id="e55" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="clipboard"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e56" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="clipboard"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="lsp" value="<b>internal/lsp</b><br/><font style="font-size:10px">language-server client: definition, references, hover, symbols, diagnostics, rename</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1160" y="600" width="220" height="60" as="geometry"/></mxCell><mxCell id="lspdev" value="<b>go.lsp.dev/protocol + jsonrpc2</b><br/><font style="font-size:10px">LSP types and JSON-RPC 2.0 over stdio</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#ffffff;strokeColor=#999999;" vertex="1" parent="1"><mxGeometry x="1160" y="700" width="220" height="60" as="geometry"/></mxCell><mxCell id="e57" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="lsp"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e58" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="lsp"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e59" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="lsp" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e60" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="lsp" target="lspdev"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="legend" value="<b>mm — package dependencies</b><br/>Arrow: "imports". Yellow: entry point. Blue: front ends. Green: shared logic. Grey: leaf packages. Dashed purple: third-party modules.<br/>Regenerate the edge list from: go list -f '{{.ImportPath}}: {{join .Imports " "}}' ./..." style="text;html=1;whiteSpace=wrap;align=left;verticalAlign=top;fillColor=#ffffff;strokeColor=#999999;" vertex="1" parent="1"><mxGeometry x="900" y="40" width="440" height="90" as="geometry"/></mxCell><mxCell id="webmd" value="<b>internal/webmd</b><br/><font style="font-size:10px">web page → Markdown: fetch (robots.txt, charset → UTF-8), clean, convert</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1400" y="460" width="220" height="60" as="geometry"/></mxCell><mxCell id="xnet" value="<b>golang.org/x/net/html (+ x/text)</b><br/><font style="font-size:10px">HTML5 parser, charset lookup</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="1400" y="760" width="220" height="60" as="geometry"/></mxCell><mxCell id="e_main_webmd" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="webmd"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_tools_webmd" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="webmd"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_webmd_xnet" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="webmd" target="xnet"><mxGeometry relative="1" as="geometry"/></mxCell></root></mxGraphModel></diagram></mxfile> | |
| \ No newline at end of file | ||
| 3 | + <mxCell id="e1" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="acp"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e2" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e3" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="detector"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e4" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="engine"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e5" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="skills"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e6" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="spinner"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e7" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="tools"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e8" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e9" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="engine"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e10" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="session"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e11" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e12" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="acp" target="acpsdk"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e13" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="acp" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e14" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="compact"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e15" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e16" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="detector"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e17" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="engine"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e18" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="session"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e19" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="spinner"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e20" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="agent" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e21" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="compact" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e22" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="compact" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e23" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="config" target="yaml"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e24" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="engine" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e25" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="engine" target="spinner"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e26" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="engine" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e27" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="engine" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e28" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="engine" target="openai"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e29" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e30" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="detector"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e31" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="fileedit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e32" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="skills"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e33" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="spinner"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e34" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e35" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="tools" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e36" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="session" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="mention" value="<b>internal/mention</b><br/><font style="font-size:10px">the @path notation of a question</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="400" width="200" height="60" as="geometry"/></mxCell><mxCell id="e37" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="mention"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e38" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="mention"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="store" value="<b>internal/store</b><br/><font style="font-size:10px">per-project session files (.mm/sessions/<id>.json)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="480" width="200" height="60" as="geometry"/></mxCell><mxCell id="e39" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="store"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e40" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="store"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="tui" value="<b>internal/tui</b><br/><font style="font-size:10px">window front end: one screen on tcell (mm -tui)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1"><mxGeometry x="430" y="160" width="200" height="60" as="geometry"/></mxCell><mxCell id="command" value="<b>internal/command</b><br/><font style="font-size:10px">the one slash-command table, per front end</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="560" width="200" height="60" as="geometry"/></mxCell><mxCell id="tcell" value="<b>github.com/gdamore/tcell/v2</b><br/><font style="font-size:10px">terminal screen, keys, mouse</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="920" y="840" width="200" height="60" as="geometry"/></mxCell><mxCell id="e41" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="tui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e42" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e43" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="engine"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e44" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="compact"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e45" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="store"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e46" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="mention"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e47" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="session"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e48" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="detector"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e49" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e50" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="command"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e51" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="tcell"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e52" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="command"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e53" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="command"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e54" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="command" target="session"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="clipboard" value="<b>internal/clipboard</b><br/><font style="font-size:10px">system clipboard: native tool, then OSC 52</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1140" y="680" width="200" height="60" as="geometry"/></mxCell><mxCell id="e55" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="clipboard"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e56" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="clipboard"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="lsp" value="<b>internal/lsp</b><br/><font style="font-size:10px">language-server client: definition, references, hover, symbols, diagnostics, rename</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1160" y="600" width="220" height="60" as="geometry"/></mxCell><mxCell id="lspdev" value="<b>go.lsp.dev/protocol + jsonrpc2</b><br/><font style="font-size:10px">LSP types and JSON-RPC 2.0 over stdio</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#ffffff;strokeColor=#999999;" vertex="1" parent="1"><mxGeometry x="1160" y="700" width="220" height="60" as="geometry"/></mxCell><mxCell id="e57" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="lsp"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e58" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="lsp"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e59" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="lsp" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e60" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="lsp" target="lspdev"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="legend" value="<b>mm — package dependencies</b><br/>Arrow: "imports". Yellow: entry point. Blue: front ends. Green: shared logic. Grey: leaf packages. Dashed purple: third-party modules.<br/>Regenerate the edge list from: go list -f '{{.ImportPath}}: {{join .Imports " "}}' ./..." style="text;html=1;whiteSpace=wrap;align=left;verticalAlign=top;fillColor=#ffffff;strokeColor=#999999;" vertex="1" parent="1"><mxGeometry x="900" y="40" width="440" height="90" as="geometry"/></mxCell><mxCell id="webmd" value="<b>internal/webmd</b><br/><font style="font-size:10px">web page → Markdown: fetch (robots.txt, charset → UTF-8), clean, convert</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1400" y="460" width="220" height="60" as="geometry"/></mxCell><mxCell id="xnet" value="<b>golang.org/x/net/html (+ x/text)</b><br/><font style="font-size:10px">HTML5 parser, charset lookup</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="1400" y="760" width="220" height="60" as="geometry"/></mxCell><mxCell id="e_main_webmd" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="webmd"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_tools_webmd" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="webmd"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_webmd_xnet" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="webmd" target="xnet"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="ask" value="<b>internal/ask</b><br/><font style="font-size:10px">/ask another mm agent: child mm -acp, ACP client side</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="640" width="200" height="60" as="geometry"/></mxCell><mxCell id="e_main_ask" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="ask"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_agent_ask" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="ask"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_acp_ask" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="ask"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_tui_ask" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="ask"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_ask_command" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="ask" target="command"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_ask_config" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="ask" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_ask_compact" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="ask" target="compact"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_ask_ui" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="ask" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_ask_acpsdk" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="ask" target="acpsdk"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_ask_genkit" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="ask" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_ask_yaml" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="ask" target="yaml"><mxGeometry relative="1" as="geometry"/></mxCell></root></mxGraphModel></diagram></mxfile> | |
| \ No newline at end of file | ||
| @@ -1,3 +1,3 @@ | |||
| 1 | <mxfile host="app.diagrams.net" modified="2026-09-15T00:00:00.000Z" agent="mm docs" version="24.0.0" type="device"><diagram id="packages" name="packages"><mxGraphModel dx="1400" dy="900" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1400" pageHeight="900"><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="main" value="<b>main (mm)</b><br/><font style="font-size:10px">wiring: config, engine, tools, then one front end</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="1"><mxGeometry x="420" y="40" width="200" height="60" as="geometry"/></mxCell><mxCell id="agent" value="<b>internal/agent</b><br/><font style="font-size:10px">terminal REPL: /quit /abort /compact /new</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1"><mxGeometry x="160" y="180" width="200" height="60" as="geometry"/></mxCell><mxCell id="acp" value="<b>internal/acp</b><br/><font style="font-size:10px">editor front end: Agent Client Protocol on stdio</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1"><mxGeometry x="700" y="180" width="200" height="60" as="geometry"/></mxCell><mxCell id="session" value="<b>internal/session</b><br/><font style="font-size:10px">what a conversation starts from; the /new reset</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="320" width="200" height="60" as="geometry"/></mxCell><mxCell id="compact" value="<b>internal/compact</b><br/><font style="font-size:10px">history compression into a model-written summary</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="40" y="320" width="200" height="60" as="geometry"/></mxCell><mxCell id="engine" value="<b>internal/engine</b><br/><font style="font-size:10px">LLM connection + generation, one Provider per server</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="700" y="320" width="200" height="60" as="geometry"/></mxCell><mxCell id="tools" value="<b>internal/tools</b><br/><font style="font-size:10px">built-in tools: bash, read_skill, file tools, lsp, fetch_content</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="460" width="200" height="60" as="geometry"/></mxCell><mxCell id="detector" value="<b>internal/detector</b><br/><font style="font-size:10px">repeated-action (loop) detection</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="40" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="config" value="<b>internal/config</b><br/><font style="font-size:10px">settings with YAML overrides (agent.yaml)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="260" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="spinner" value="<b>internal/spinner</b><br/><font style="font-size:10px">terminal spinner and styling</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="480" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="ui" value="<b>internal/ui</b><br/><font style="font-size:10px">human output destination + shared event sink</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="700" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="fileedit" value="<b>internal/fileedit</b><br/><font style="font-size:10px">exact-replacement file editing</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="920" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="skills" value="<b>internal/skills</b><br/><font style="font-size:10px">markdown skills catalogue (./skills)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1140" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="genkit" value="<b>github.com/firebase/genkit</b><br/><font style="font-size:10px">ai.Message, tools, model calls</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="160" y="760" width="200" height="60" as="geometry"/></mxCell><mxCell id="acpsdk" value="<b>github.com/coder/acp-go-sdk</b><br/><font style="font-size:10px">JSON-RPC transport for ACP</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="700" y="760" width="200" height="60" as="geometry"/></mxCell><mxCell id="openai" value="<b>github.com/openai/openai-go</b><br/><font style="font-size:10px">OpenAI-compatible HTTP client</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="920" y="760" width="200" height="60" as="geometry"/></mxCell><mxCell id="yaml" value="<b>gopkg.in/yaml.v3</b><br/><font style="font-size:10px">YAML parsing</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="400" y="760" width="200" height="60" as="geometry"/></mxCell><mxCell id="e0" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="agent"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="version" value="<b>internal/version</b><br/><font style="font-size:10px">which mm this is: build stamp, else the commit</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1380" y="600" width="200" height="60" as="geometry"/></mxCell> | 1 | <mxfile host="app.diagrams.net" modified="2026-09-15T00:00:00.000Z" agent="mm docs" version="24.0.0" type="device"><diagram id="packages" name="packages"><mxGraphModel dx="1400" dy="900" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1400" pageHeight="900"><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="main" value="<b>main (mm)</b><br/><font style="font-size:10px">wiring: config, engine, tools, then one front end</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="1"><mxGeometry x="420" y="40" width="200" height="60" as="geometry"/></mxCell><mxCell id="agent" value="<b>internal/agent</b><br/><font style="font-size:10px">terminal REPL: /quit /abort /compact /new</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1"><mxGeometry x="160" y="180" width="200" height="60" as="geometry"/></mxCell><mxCell id="acp" value="<b>internal/acp</b><br/><font style="font-size:10px">editor front end: Agent Client Protocol on stdio</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1"><mxGeometry x="700" y="180" width="200" height="60" as="geometry"/></mxCell><mxCell id="session" value="<b>internal/session</b><br/><font style="font-size:10px">what a conversation starts from; the /new reset</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="320" width="200" height="60" as="geometry"/></mxCell><mxCell id="compact" value="<b>internal/compact</b><br/><font style="font-size:10px">history compression into a model-written summary</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="40" y="320" width="200" height="60" as="geometry"/></mxCell><mxCell id="engine" value="<b>internal/engine</b><br/><font style="font-size:10px">LLM connection + generation, one Provider per server</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="700" y="320" width="200" height="60" as="geometry"/></mxCell><mxCell id="tools" value="<b>internal/tools</b><br/><font style="font-size:10px">built-in tools: bash, read_skill, file tools, lsp, fetch_content</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="460" width="200" height="60" as="geometry"/></mxCell><mxCell id="detector" value="<b>internal/detector</b><br/><font style="font-size:10px">repeated-action (loop) detection</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="40" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="config" value="<b>internal/config</b><br/><font style="font-size:10px">settings with YAML overrides (agent.yaml)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="260" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="spinner" value="<b>internal/spinner</b><br/><font style="font-size:10px">terminal spinner and styling</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="480" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="ui" value="<b>internal/ui</b><br/><font style="font-size:10px">human output destination + shared event sink</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="700" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="fileedit" value="<b>internal/fileedit</b><br/><font style="font-size:10px">exact-replacement file editing</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="920" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="skills" value="<b>internal/skills</b><br/><font style="font-size:10px">markdown skills catalogue (./skills)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1140" y="600" width="200" height="60" as="geometry"/></mxCell><mxCell id="genkit" value="<b>github.com/firebase/genkit</b><br/><font style="font-size:10px">ai.Message, tools, model calls</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="160" y="760" width="200" height="60" as="geometry"/></mxCell><mxCell id="acpsdk" value="<b>github.com/coder/acp-go-sdk</b><br/><font style="font-size:10px">JSON-RPC transport for ACP</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="700" y="760" width="200" height="60" as="geometry"/></mxCell><mxCell id="openai" value="<b>github.com/openai/openai-go</b><br/><font style="font-size:10px">OpenAI-compatible HTTP client</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="920" y="760" width="200" height="60" as="geometry"/></mxCell><mxCell id="yaml" value="<b>gopkg.in/yaml.v3</b><br/><font style="font-size:10px">YAML parsing</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="400" y="760" width="200" height="60" as="geometry"/></mxCell><mxCell id="e0" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="agent"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="version" value="<b>internal/version</b><br/><font style="font-size:10px">which mm this is: build stamp, else the commit</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1380" y="600" width="200" height="60" as="geometry"/></mxCell> |
| 2 | <mxCell id="e61" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="version"><mxGeometry relative="1" as="geometry"/></mxCell> | 2 | <mxCell id="e61" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="version"><mxGeometry relative="1" as="geometry"/></mxCell> |
| 3 | - <mxCell id="e1" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="acp"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e2" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e3" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="detector"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e4" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="engine"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e5" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="skills"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e6" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="spinner"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e7" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="tools"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e8" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e9" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="engine"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e10" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="session"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e11" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e12" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="acp" target="acpsdk"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e13" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="acp" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e14" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="compact"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e15" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e16" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="detector"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e17" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="engine"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e18" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="session"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e19" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="spinner"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e20" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="agent" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e21" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="compact" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e22" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="compact" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e23" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="config" target="yaml"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e24" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="engine" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e25" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="engine" target="spinner"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e26" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="engine" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e27" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="engine" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e28" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="engine" target="openai"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e29" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e30" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="detector"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e31" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="fileedit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e32" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="skills"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e33" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="spinner"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e34" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e35" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="tools" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e36" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="session" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="mention" value="<b>internal/mention</b><br/><font style="font-size:10px">the @path notation of a question</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="400" width="200" height="60" as="geometry"/></mxCell><mxCell id="e37" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="mention"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e38" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="mention"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="store" value="<b>internal/store</b><br/><font style="font-size:10px">per-project session files (.mm/sessions/<id>.json)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="480" width="200" height="60" as="geometry"/></mxCell><mxCell id="e39" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="store"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e40" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="store"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="tui" value="<b>internal/tui</b><br/><font style="font-size:10px">window front end: one screen on tcell (mm -tui)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1"><mxGeometry x="430" y="160" width="200" height="60" as="geometry"/></mxCell><mxCell id="command" value="<b>internal/command</b><br/><font style="font-size:10px">the one slash-command table, per front end</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="560" width="200" height="60" as="geometry"/></mxCell><mxCell id="tcell" value="<b>github.com/gdamore/tcell/v2</b><br/><font style="font-size:10px">terminal screen, keys, mouse</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="920" y="840" width="200" height="60" as="geometry"/></mxCell><mxCell id="e41" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="tui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e42" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e43" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="engine"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e44" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="compact"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e45" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="store"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e46" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="mention"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e47" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="session"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e48" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="detector"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e49" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e50" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="command"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e51" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="tcell"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e52" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="command"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e53" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="command"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e54" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="command" target="session"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="clipboard" value="<b>internal/clipboard</b><br/><font style="font-size:10px">system clipboard: native tool, then OSC 52</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1140" y="680" width="200" height="60" as="geometry"/></mxCell><mxCell id="e55" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="clipboard"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e56" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="clipboard"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="lsp" value="<b>internal/lsp</b><br/><font style="font-size:10px">language-server client: definition, references, hover, symbols, diagnostics, rename</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1160" y="600" width="220" height="60" as="geometry"/></mxCell><mxCell id="lspdev" value="<b>go.lsp.dev/protocol + jsonrpc2</b><br/><font style="font-size:10px">LSP types and JSON-RPC 2.0 over stdio</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#ffffff;strokeColor=#999999;" vertex="1" parent="1"><mxGeometry x="1160" y="700" width="220" height="60" as="geometry"/></mxCell><mxCell id="e57" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="lsp"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e58" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="lsp"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e59" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="lsp" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e60" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="lsp" target="lspdev"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="legend" value="<b>mm — package dependencies</b><br/>Arrow: "imports". Yellow: entry point. Blue: front ends. Green: shared logic. Grey: leaf packages. Dashed purple: third-party modules.<br/>Regenerate the edge list from: go list -f '{{.ImportPath}}: {{join .Imports " "}}' ./..." style="text;html=1;whiteSpace=wrap;align=left;verticalAlign=top;fillColor=#ffffff;strokeColor=#999999;" vertex="1" parent="1"><mxGeometry x="900" y="40" width="440" height="90" as="geometry"/></mxCell><mxCell id="webmd" value="<b>internal/webmd</b><br/><font style="font-size:10px">web page → Markdown: fetch (robots.txt, charset → UTF-8), clean, convert</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1400" y="460" width="220" height="60" as="geometry"/></mxCell><mxCell id="xnet" value="<b>golang.org/x/net/html (+ x/text)</b><br/><font style="font-size:10px">HTML5 parser, charset lookup</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="1400" y="760" width="220" height="60" as="geometry"/></mxCell><mxCell id="e_main_webmd" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="webmd"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_tools_webmd" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="webmd"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_webmd_xnet" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="webmd" target="xnet"><mxGeometry relative="1" as="geometry"/></mxCell></root></mxGraphModel></diagram></mxfile> | ||
| \ No newline at end of file | \ No newline at end of file | ||
| 3 | + <mxCell id="e1" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="acp"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e2" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e3" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="detector"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e4" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="engine"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e5" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="skills"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e6" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="spinner"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e7" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="tools"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e8" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e9" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="engine"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e10" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="session"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e11" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e12" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="acp" target="acpsdk"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e13" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="acp" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e14" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="compact"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e15" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e16" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="detector"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e17" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="engine"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e18" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="session"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e19" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="spinner"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e20" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="agent" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e21" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="compact" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e22" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="compact" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e23" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="config" target="yaml"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e24" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="engine" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e25" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="engine" target="spinner"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e26" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="engine" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e27" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="engine" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e28" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="engine" target="openai"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e29" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e30" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="detector"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e31" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="fileedit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e32" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="skills"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e33" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="spinner"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e34" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e35" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="tools" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e36" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="session" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="mention" value="<b>internal/mention</b><br/><font style="font-size:10px">the @path notation of a question</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="400" width="200" height="60" as="geometry"/></mxCell><mxCell id="e37" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="mention"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e38" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="mention"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="store" value="<b>internal/store</b><br/><font style="font-size:10px">per-project session files (.mm/sessions/<id>.json)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="480" width="200" height="60" as="geometry"/></mxCell><mxCell id="e39" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="store"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e40" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="store"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="tui" value="<b>internal/tui</b><br/><font style="font-size:10px">window front end: one screen on tcell (mm -tui)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1"><mxGeometry x="430" y="160" width="200" height="60" as="geometry"/></mxCell><mxCell id="command" value="<b>internal/command</b><br/><font style="font-size:10px">the one slash-command table, per front end</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="560" width="200" height="60" as="geometry"/></mxCell><mxCell id="tcell" value="<b>github.com/gdamore/tcell/v2</b><br/><font style="font-size:10px">terminal screen, keys, mouse</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="920" y="840" width="200" height="60" as="geometry"/></mxCell><mxCell id="e41" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="tui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e42" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e43" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="engine"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e44" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="compact"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e45" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="store"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e46" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="mention"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e47" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="session"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e48" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="detector"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e49" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e50" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="command"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e51" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="tcell"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e52" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="command"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e53" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="command"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e54" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="command" target="session"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="clipboard" value="<b>internal/clipboard</b><br/><font style="font-size:10px">system clipboard: native tool, then OSC 52</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1140" y="680" width="200" height="60" as="geometry"/></mxCell><mxCell id="e55" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="clipboard"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e56" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="clipboard"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="lsp" value="<b>internal/lsp</b><br/><font style="font-size:10px">language-server client: definition, references, hover, symbols, diagnostics, rename</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1160" y="600" width="220" height="60" as="geometry"/></mxCell><mxCell id="lspdev" value="<b>go.lsp.dev/protocol + jsonrpc2</b><br/><font style="font-size:10px">LSP types and JSON-RPC 2.0 over stdio</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#ffffff;strokeColor=#999999;" vertex="1" parent="1"><mxGeometry x="1160" y="700" width="220" height="60" as="geometry"/></mxCell><mxCell id="e57" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="lsp"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e58" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="lsp"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e59" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="lsp" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e60" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="lsp" target="lspdev"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="legend" value="<b>mm — package dependencies</b><br/>Arrow: "imports". Yellow: entry point. Blue: front ends. Green: shared logic. Grey: leaf packages. Dashed purple: third-party modules.<br/>Regenerate the edge list from: go list -f '{{.ImportPath}}: {{join .Imports " "}}' ./..." style="text;html=1;whiteSpace=wrap;align=left;verticalAlign=top;fillColor=#ffffff;strokeColor=#999999;" vertex="1" parent="1"><mxGeometry x="900" y="40" width="440" height="90" as="geometry"/></mxCell><mxCell id="webmd" value="<b>internal/webmd</b><br/><font style="font-size:10px">web page → Markdown: fetch (robots.txt, charset → UTF-8), clean, convert</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" vertex="1" parent="1"><mxGeometry x="1400" y="460" width="220" height="60" as="geometry"/></mxCell><mxCell id="xnet" value="<b>golang.org/x/net/html (+ x/text)</b><br/><font style="font-size:10px">HTML5 parser, charset lookup</font>" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1"><mxGeometry x="1400" y="760" width="220" height="60" as="geometry"/></mxCell><mxCell id="e_main_webmd" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="webmd"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_tools_webmd" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tools" target="webmd"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_webmd_xnet" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="webmd" target="xnet"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="ask" value="<b>internal/ask</b><br/><font style="font-size:10px">/ask another mm agent: child mm -acp, ACP client side</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1"><mxGeometry x="430" y="640" width="200" height="60" as="geometry"/></mxCell><mxCell id="e_main_ask" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="main" target="ask"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_agent_ask" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="agent" target="ask"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_acp_ask" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="acp" target="ask"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_tui_ask" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="tui" target="ask"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_ask_command" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="ask" target="command"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_ask_config" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="ask" target="config"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_ask_compact" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="ask" target="compact"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_ask_ui" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="ask" target="ui"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_ask_acpsdk" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="ask" target="acpsdk"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_ask_genkit" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="ask" target="genkit"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="e_ask_yaml" style="endArrow=block;html=1;rounded=0;edgeStyle=orthogonalEdgeStyle;dashed=1;" edge="1" parent="1" source="ask" target="yaml"><mxGeometry relative="1" as="geometry"/></mxCell></root></mxGraphModel></diagram></mxfile> | ||
| \ No newline at end of file | \ No newline at end of file | ||
modified
docs/en/README.md +2 -0 | @@ -13,6 +13,7 @@ This documentation is organised along the four [Diátaxis](https://diataxis.fr) | ||
| 13 | 13 | - [Run mini-me on llama.cpp instead of Docker Model Runner](how-to/run-with-llama-cpp.md) |
| 14 | 14 | - [Run mini-me on llmman](how-to/run-with-llmman.md) — `provider: llmman`, the demo config in `demo-llmman/` |
| 15 | 15 | - [Run mini-me on Ollama](how-to/run-with-ollama.md) — `provider: ollama`, the demo config in `demo-ollama/` |
| 16 | +- [Ask another mm agent from your session](how-to/ask-another-agent.md) — `mmConfigs`, `/ask-<name>`, `/ask-with-context-<name>` | |
| 16 | 17 | - [Use mini-me from an editor (ACP)](how-to/use-from-an-editor.md) |
| 17 | 18 | - [Run mini-me in a Docker Sandbox](how-to/run-in-a-docker-sandbox.md) — the template and the two kits under `docker-sbx/`, window mode and editor |
| 18 | 19 | - [Run mini-me with the v3 sandbox kits](how-to/run-with-sandbox-kits-v3.md) — the same two kits for the kit specification v3, under `docker-sbx-v3/` |
| @@ -56,6 +57,7 @@ This documentation is organised along the four [Diátaxis](https://diataxis.fr) | ||
| 56 | 57 | - [The `lsp` tool](explanation/lsp-tool.md) — one tool with an `action` field, a `symbol` field instead of columns, files synced from disk, rename gated by permission |
| 57 | 58 | - [The `fetch_content` tool](explanation/fetch-content-tool.md) — why a built-in tool beside MCP `fetch`, a home-made converter, robots.txt, errors as numbered text, known limits |
| 58 | 59 | - [Thinking models](explanation/thinking-models.md) — where a model's reasoning comes from, why `mm` reads it below Genkit, and why reading it keeps the watchdog alive |
| 60 | +- [Asking another agent](explanation/asking-another-agent.md) — why another mm agent is a child process spoken to over ACP, the history pair, fresh sessions, the depth limit | |
| 59 | 61 | - Package diagram (draw.io source): [`docs/diagrams/packages.drawio`](../diagrams/packages.drawio) |
| 60 | 62 | |
| 61 | 63 | ### MCP (Model Context Protocol) Explanation |
| @@ -13,6 +13,7 @@ This documentation is organised along the four [Diátaxis](https://diataxis.fr) | |||
| 13 | - [Run mini-me on llama.cpp instead of Docker Model Runner](how-to/run-with-llama-cpp.md) | 13 | - [Run mini-me on llama.cpp instead of Docker Model Runner](how-to/run-with-llama-cpp.md) |
| 14 | - [Run mini-me on llmman](how-to/run-with-llmman.md) — `provider: llmman`, the demo config in `demo-llmman/` | 14 | - [Run mini-me on llmman](how-to/run-with-llmman.md) — `provider: llmman`, the demo config in `demo-llmman/` |
| 15 | - [Run mini-me on Ollama](how-to/run-with-ollama.md) — `provider: ollama`, the demo config in `demo-ollama/` | 15 | - [Run mini-me on Ollama](how-to/run-with-ollama.md) — `provider: ollama`, the demo config in `demo-ollama/` |
| 16 | +- [Ask another mm agent from your session](how-to/ask-another-agent.md) — `mmConfigs`, `/ask-<name>`, `/ask-with-context-<name>` | ||
| 16 | - [Use mini-me from an editor (ACP)](how-to/use-from-an-editor.md) | 17 | - [Use mini-me from an editor (ACP)](how-to/use-from-an-editor.md) |
| 17 | - [Run mini-me in a Docker Sandbox](how-to/run-in-a-docker-sandbox.md) — the template and the two kits under `docker-sbx/`, window mode and editor | 18 | - [Run mini-me in a Docker Sandbox](how-to/run-in-a-docker-sandbox.md) — the template and the two kits under `docker-sbx/`, window mode and editor |
| 18 | - [Run mini-me with the v3 sandbox kits](how-to/run-with-sandbox-kits-v3.md) — the same two kits for the kit specification v3, under `docker-sbx-v3/` | 19 | - [Run mini-me with the v3 sandbox kits](how-to/run-with-sandbox-kits-v3.md) — the same two kits for the kit specification v3, under `docker-sbx-v3/` |
| @@ -56,6 +57,7 @@ This documentation is organised along the four [Diátaxis](https://diataxis.fr) | |||
| 56 | - [The `lsp` tool](explanation/lsp-tool.md) — one tool with an `action` field, a `symbol` field instead of columns, files synced from disk, rename gated by permission | 57 | - [The `lsp` tool](explanation/lsp-tool.md) — one tool with an `action` field, a `symbol` field instead of columns, files synced from disk, rename gated by permission |
| 57 | - [The `fetch_content` tool](explanation/fetch-content-tool.md) — why a built-in tool beside MCP `fetch`, a home-made converter, robots.txt, errors as numbered text, known limits | 58 | - [The `fetch_content` tool](explanation/fetch-content-tool.md) — why a built-in tool beside MCP `fetch`, a home-made converter, robots.txt, errors as numbered text, known limits |
| 58 | - [Thinking models](explanation/thinking-models.md) — where a model's reasoning comes from, why `mm` reads it below Genkit, and why reading it keeps the watchdog alive | 59 | - [Thinking models](explanation/thinking-models.md) — where a model's reasoning comes from, why `mm` reads it below Genkit, and why reading it keeps the watchdog alive |
| 60 | +- [Asking another agent](explanation/asking-another-agent.md) — why another mm agent is a child process spoken to over ACP, the history pair, fresh sessions, the depth limit | ||
| 59 | - Package diagram (draw.io source): [`docs/diagrams/packages.drawio`](../diagrams/packages.drawio) | 61 | - Package diagram (draw.io source): [`docs/diagrams/packages.drawio`](../diagrams/packages.drawio) |
| 60 | 62 | ||
| 61 | ### MCP (Model Context Protocol) Explanation | 63 | ### MCP (Model Context Protocol) Explanation |
modified
docs/en/explanation/architecture.md +1 -0 | @@ -15,6 +15,7 @@ The entry point, `main.go`, only does wiring: it loads the settings, opens the e | ||
| 15 | 15 | | `lsp` | The language-server client behind the `lsp` tool: starts the configured servers on first use (config `lsp.servers`), syncs files from disk, asks for definition, references, hover, symbols, diagnostics and rename, and shapes the answers as `path:line:column <line>` text. Built on `go.lsp.dev/protocol` and `go.lsp.dev/jsonrpc2`. | |
| 16 | 16 | | `skills` | Discovery of markdown procedures and rendering of the `read_skill` catalogue. | |
| 17 | 17 | | `command` | The one table of slash commands: name, description, which front ends offer it, whether it takes an argument. The REPL dispatches on it, the ACP façade advertises its Editor slice, the window's `/` picker lists it. | |
| 18 | +| `ask` | `/ask-<name>`: starts another mm agent (config `mmConfigs`) as a child `mm -acp`, talks to it as an ACP client, streams its answer and tool calls through the asking front end's `ui.Sink`, and returns the exchange for the history. See [Asking another agent](asking-another-agent.md). | | |
| 18 | 19 | | `clipboard` | Puts text on the system clipboard: a native tool on `PATH` first (`pbcopy`, `wl-copy`, `xclip`, `xsel`), OSC 52 as the fallback, and it reports which. Used by `/copy` in the REPL and the window. | |
| 19 | 20 | | `store` | Per-project session files (`<dir>/<id>.json`, whole rewrite, atomic): create, save, load, list, latest. Used by both front ends when `sessions.enabled`. | |
| 20 | 21 | | `mention` | The `@path` notation of a question: existing paths become `[attached file: …]` lines, the same shape as an editor's `resource_link`. Used by both front ends. | |
| @@ -15,6 +15,7 @@ The entry point, `main.go`, only does wiring: it loads the settings, opens the e | |||
| 15 | | `lsp` | The language-server client behind the `lsp` tool: starts the configured servers on first use (config `lsp.servers`), syncs files from disk, asks for definition, references, hover, symbols, diagnostics and rename, and shapes the answers as `path:line:column <line>` text. Built on `go.lsp.dev/protocol` and `go.lsp.dev/jsonrpc2`. | | 15 | | `lsp` | The language-server client behind the `lsp` tool: starts the configured servers on first use (config `lsp.servers`), syncs files from disk, asks for definition, references, hover, symbols, diagnostics and rename, and shapes the answers as `path:line:column <line>` text. Built on `go.lsp.dev/protocol` and `go.lsp.dev/jsonrpc2`. | |
| 16 | | `skills` | Discovery of markdown procedures and rendering of the `read_skill` catalogue. | | 16 | | `skills` | Discovery of markdown procedures and rendering of the `read_skill` catalogue. | |
| 17 | | `command` | The one table of slash commands: name, description, which front ends offer it, whether it takes an argument. The REPL dispatches on it, the ACP façade advertises its Editor slice, the window's `/` picker lists it. | | 17 | | `command` | The one table of slash commands: name, description, which front ends offer it, whether it takes an argument. The REPL dispatches on it, the ACP façade advertises its Editor slice, the window's `/` picker lists it. | |
| 18 | +| `ask` | `/ask-<name>`: starts another mm agent (config `mmConfigs`) as a child `mm -acp`, talks to it as an ACP client, streams its answer and tool calls through the asking front end's `ui.Sink`, and returns the exchange for the history. See [Asking another agent](asking-another-agent.md). | | ||
| 18 | | `clipboard` | Puts text on the system clipboard: a native tool on `PATH` first (`pbcopy`, `wl-copy`, `xclip`, `xsel`), OSC 52 as the fallback, and it reports which. Used by `/copy` in the REPL and the window. | | 19 | | `clipboard` | Puts text on the system clipboard: a native tool on `PATH` first (`pbcopy`, `wl-copy`, `xclip`, `xsel`), OSC 52 as the fallback, and it reports which. Used by `/copy` in the REPL and the window. | |
| 19 | | `store` | Per-project session files (`<dir>/<id>.json`, whole rewrite, atomic): create, save, load, list, latest. Used by both front ends when `sessions.enabled`. | | 20 | | `store` | Per-project session files (`<dir>/<id>.json`, whole rewrite, atomic): create, save, load, list, latest. Used by both front ends when `sessions.enabled`. | |
| 20 | | `mention` | The `@path` notation of a question: existing paths become `[attached file: …]` lines, the same shape as an editor's `resource_link`. Used by both front ends. | | 21 | | `mention` | The `@path` notation of a question: existing paths become `[attached file: …]` lines, the same shape as an editor's `resource_link`. Used by both front ends. | |
added
docs/en/explanation/asking-another-agent.md +38 -0 | new file mode 100644 | ||
| @@ -0,0 +1,38 @@ | ||
| 1 | +# Asking another agent — explanation | |
| 2 | + | |
| 3 | +## What is this about? | |
| 4 | + | |
| 5 | +`/ask-bob <prompt>` lets the agent you are talking to consult another mm agent: another model, another system prompt, other tools and skills. Bob's answer is shown, then kept in your conversation so your own model can use it on your next question. This page explains how that is built and why. | |
| 6 | + | |
| 7 | +## Why it is designed this way | |
| 8 | + | |
| 9 | +**Another agent is another process.** Bob runs as a child `mm -acp -config <bob's file>`, the same binary as the agent that asks. Running him inside the same process was the obvious alternative and the wrong one: mm's configuration is one global, read by the engine, the tools, the compaction and the sessions alike, and bob's differs in all of them. A second engine in the same process would first have meant pulling that global apart everywhere. A child keeps each agent whole, with its own tools, skills and MCP servers. | |
| 10 | + | |
| 11 | +**The two talk ACP.** mm already speaks the Agent Client Protocol as a server, for editors. Asking bob makes the asking agent an ACP *client* of its child. The protocol gives, for free, what scraping a terminal never would: bob's answer as text, his tool calls as structured events, his permission requests as requests that can be answered, and a standard way to cancel. mm's ACP server only ever calls two client methods — session updates and permission requests — so the client side is small; file and terminal requests are refused as absent. | |
| 12 | + | |
| 13 | +**Bob's permissions are yours to give.** When bob wants to run a command, the request is forwarded to the front end you are using: in an editor its own dialog, in the window the `permissions` setting and its inline prompt, in the line mode nothing — exactly as for your own agent's tools there. Bob's tools are named `bob/<tool>`, so allowing your agent's `bash` "for this session" does not allow bob's. | |
| 14 | + | |
| 15 | +**The answer is a pair of messages.** It enters your history as a user message (`[asked bob (provider/model): …]`) followed by an assistant message (`[bob's answer]` and the text). One message would have been simpler, but your next question is a user message too, and several chat templates (Gemma's, Mistral's) refuse two user messages in a row. The marks say who said what, so your model does not mistake bob's answer for its own. The answer is capped like a tool's output, at `maxOutput` characters: it lands in your model's window. | |
| 16 | + | |
| 17 | +**Each ask is a fresh conversation for bob.** Bob keeps no memory between two asks. When he needs to know what you were doing, `/ask-with-context-bob` sends a transcript: questions, answers, one line per tool call — never the tool outputs, which would cost his window what they already cost yours, never your system prompt or reasoning. Continuity is explicit, chosen per question, rather than a second conversation drifting on its own. | |
| 18 | + | |
| 19 | +**One child per agent, started once.** Starting mm costs its engine set-up, its server probe and its MCP servers. The first ask starts bob; later asks reuse him, each with a new session. He stops when your agent exits. | |
| 20 | + | |
| 21 | +**The child is fenced off from what is meant for its parent.** Variables like `AGENT_MODEL`, `AGENT_PROVIDER` or `AGENT_BASE_URL` override a configuration; inherited by bob, they would silently turn him into a copy of you. They are removed from his environment. Bob also runs with `AGENT_SESSIONS=off`: he works in your project, and his session files would otherwise appear in your `/sessions` and `/resume`. | |
| 22 | + | |
| 23 | +**Chains stop.** `/ask` is a command a person types; no model can call it, so no agent loops on its own. A person could still type `/ask` into an agent that was itself asked, so each child is told its depth (`MM_ASK_DEPTH`): your agent may ask bob, bob may ask riker, riker may not ask anyone. | |
| 24 | + | |
| 25 | +## Rejected alternatives | |
| 26 | + | |
| 27 | +- **`mm -p "<prompt>"` as a subprocess.** One process per question and nothing to keep alive — but its output is a terminal screen (answer, reasoning, tool echoes, colours, the command recap) that would have to be parsed; it never asks permission, so bob would run commands unchecked; it cannot be cancelled except by killing it. | |
| 28 | +- **A second engine in the same process.** Fast, no process — at the price of turning the global configuration into a per-agent one throughout the code base. | |
| 29 | +- **Letting the current model answer at once with bob's input.** The user chose otherwise: bob's answer is shown and kept, and you decide what to ask next. | |
| 30 | +- **Keeping bob's own conversation across asks.** Two conversations evolving in parallel, one of them invisible, are harder to reason about than an explicit transcript sent when asked. | |
| 31 | +- **A generic `/ask <name>` only, in editors too.** An editor's `/` menu completes a command name, never its argument; one command per agent is the only way for the menu to list the agents. | |
| 32 | + | |
| 33 | +## How it relates to the rest | |
| 34 | + | |
| 35 | +- The child is an ordinary [ACP agent](../reference/acp.md); the parent reuses the protocol from the other side. | |
| 36 | +- Its tool calls and permissions go through the same event interface the three front ends implement for your own agent's tools. | |
| 37 | +- The exchange is part of the history like any other turn: it is saved with the [session](persistent-sessions.md), replayed by `/resume`, and summarised by [context compression](context-compression.md) when it gets old. | |
| 38 | +- Using it: [how to ask another agent](../how-to/ask-another-agent.md). Every command and message: [slash commands reference](../reference/slash-commands.md#asking-another-agent). | |
| new file mode 100644 | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | +# Asking another agent — explanation | ||
| 2 | + | ||
| 3 | +## What is this about? | ||
| 4 | + | ||
| 5 | +`/ask-bob <prompt>` lets the agent you are talking to consult another mm agent: another model, another system prompt, other tools and skills. Bob's answer is shown, then kept in your conversation so your own model can use it on your next question. This page explains how that is built and why. | ||
| 6 | + | ||
| 7 | +## Why it is designed this way | ||
| 8 | + | ||
| 9 | +**Another agent is another process.** Bob runs as a child `mm -acp -config <bob's file>`, the same binary as the agent that asks. Running him inside the same process was the obvious alternative and the wrong one: mm's configuration is one global, read by the engine, the tools, the compaction and the sessions alike, and bob's differs in all of them. A second engine in the same process would first have meant pulling that global apart everywhere. A child keeps each agent whole, with its own tools, skills and MCP servers. | ||
| 10 | + | ||
| 11 | +**The two talk ACP.** mm already speaks the Agent Client Protocol as a server, for editors. Asking bob makes the asking agent an ACP *client* of its child. The protocol gives, for free, what scraping a terminal never would: bob's answer as text, his tool calls as structured events, his permission requests as requests that can be answered, and a standard way to cancel. mm's ACP server only ever calls two client methods — session updates and permission requests — so the client side is small; file and terminal requests are refused as absent. | ||
| 12 | + | ||
| 13 | +**Bob's permissions are yours to give.** When bob wants to run a command, the request is forwarded to the front end you are using: in an editor its own dialog, in the window the `permissions` setting and its inline prompt, in the line mode nothing — exactly as for your own agent's tools there. Bob's tools are named `bob/<tool>`, so allowing your agent's `bash` "for this session" does not allow bob's. | ||
| 14 | + | ||
| 15 | +**The answer is a pair of messages.** It enters your history as a user message (`[asked bob (provider/model): …]`) followed by an assistant message (`[bob's answer]` and the text). One message would have been simpler, but your next question is a user message too, and several chat templates (Gemma's, Mistral's) refuse two user messages in a row. The marks say who said what, so your model does not mistake bob's answer for its own. The answer is capped like a tool's output, at `maxOutput` characters: it lands in your model's window. | ||
| 16 | + | ||
| 17 | +**Each ask is a fresh conversation for bob.** Bob keeps no memory between two asks. When he needs to know what you were doing, `/ask-with-context-bob` sends a transcript: questions, answers, one line per tool call — never the tool outputs, which would cost his window what they already cost yours, never your system prompt or reasoning. Continuity is explicit, chosen per question, rather than a second conversation drifting on its own. | ||
| 18 | + | ||
| 19 | +**One child per agent, started once.** Starting mm costs its engine set-up, its server probe and its MCP servers. The first ask starts bob; later asks reuse him, each with a new session. He stops when your agent exits. | ||
| 20 | + | ||
| 21 | +**The child is fenced off from what is meant for its parent.** Variables like `AGENT_MODEL`, `AGENT_PROVIDER` or `AGENT_BASE_URL` override a configuration; inherited by bob, they would silently turn him into a copy of you. They are removed from his environment. Bob also runs with `AGENT_SESSIONS=off`: he works in your project, and his session files would otherwise appear in your `/sessions` and `/resume`. | ||
| 22 | + | ||
| 23 | +**Chains stop.** `/ask` is a command a person types; no model can call it, so no agent loops on its own. A person could still type `/ask` into an agent that was itself asked, so each child is told its depth (`MM_ASK_DEPTH`): your agent may ask bob, bob may ask riker, riker may not ask anyone. | ||
| 24 | + | ||
| 25 | +## Rejected alternatives | ||
| 26 | + | ||
| 27 | +- **`mm -p "<prompt>"` as a subprocess.** One process per question and nothing to keep alive — but its output is a terminal screen (answer, reasoning, tool echoes, colours, the command recap) that would have to be parsed; it never asks permission, so bob would run commands unchecked; it cannot be cancelled except by killing it. | ||
| 28 | +- **A second engine in the same process.** Fast, no process — at the price of turning the global configuration into a per-agent one throughout the code base. | ||
| 29 | +- **Letting the current model answer at once with bob's input.** The user chose otherwise: bob's answer is shown and kept, and you decide what to ask next. | ||
| 30 | +- **Keeping bob's own conversation across asks.** Two conversations evolving in parallel, one of them invisible, are harder to reason about than an explicit transcript sent when asked. | ||
| 31 | +- **A generic `/ask <name>` only, in editors too.** An editor's `/` menu completes a command name, never its argument; one command per agent is the only way for the menu to list the agents. | ||
| 32 | + | ||
| 33 | +## How it relates to the rest | ||
| 34 | + | ||
| 35 | +- The child is an ordinary [ACP agent](../reference/acp.md); the parent reuses the protocol from the other side. | ||
| 36 | +- Its tool calls and permissions go through the same event interface the three front ends implement for your own agent's tools. | ||
| 37 | +- The exchange is part of the history like any other turn: it is saved with the [session](persistent-sessions.md), replayed by `/resume`, and summarised by [context compression](context-compression.md) when it gets old. | ||
| 38 | +- Using it: [how to ask another agent](../how-to/ask-another-agent.md). Every command and message: [slash commands reference](../reference/slash-commands.md#asking-another-agent). | ||
added
docs/en/how-to/ask-another-agent.md +61 -0 | new file mode 100644 | ||
| @@ -0,0 +1,61 @@ | ||
| 1 | +# How to ask another mm agent from your session | |
| 2 | + | |
| 3 | +This guide shows how to put a question to another mm agent — another model, another system prompt, other tools — and keep its answer in your conversation. It assumes you already run `mm` with a working provider. | |
| 4 | + | |
| 5 | +## Steps | |
| 6 | + | |
| 7 | +1. Write (or reuse) one configuration file per agent you want to ask. Each is an ordinary mm configuration; `demo-llmman/` ships two, for llmman: | |
| 8 | + | |
| 9 | + - `demo-llmman/agent.jan-nano.yaml` — `hf.co/Menlo/Jan-nano-gguf:Q4_K_M`, named Jan; | |
| 10 | + - `demo-llmman/agent.lucy.yaml` — `hf.co/Menlo/Lucy-gguf:Q4_K_M`, named Lucy. | |
| 11 | + | |
| 12 | +2. Declare them in YOUR configuration, under `mmConfigs`. Paths are relative to the directory of the file you write them in: | |
| 13 | + | |
| 14 | + ```yaml | |
| 15 | + mmConfigs: | |
| 16 | + jan: agent.jan-nano.yaml | |
| 17 | + lucy: agent.lucy.yaml | |
| 18 | + ``` | |
| 19 | + | |
| 20 | + `demo-llmman/agent.llmman.yaml` already does this. | |
| 21 | + | |
| 22 | +3. Start `mm` with that configuration: | |
| 23 | + | |
| 24 | + ```bash | |
| 25 | + cd demo-llmman | |
| 26 | + ../mm agent.llmman.yaml | |
| 27 | + ``` | |
| 28 | + | |
| 29 | + An agent whose file is missing shows up as a `[warning: mmConfigs: …]` line; the others work. | |
| 30 | + | |
| 31 | +4. Ask a question: | |
| 32 | + | |
| 33 | + ```text | |
| 34 | + > /ask-jan In one short sentence: what is the capital of France? | |
| 35 | + 🗣 asking jan (llmman/hf.co/Menlo/Jan-nano-gguf:Q4_K_M)… | |
| 36 | + The capital of France is Paris. | |
| 37 | + 🗣 jan's answer is in the history for your next question | |
| 38 | + ``` | |
| 39 | + | |
| 40 | + The first ask of an agent starts it, so it takes a little longer; the next ones reuse it. | |
| 41 | + | |
| 42 | +5. Use the answer: your next question goes to your own model, which now sees Jan's answer. | |
| 43 | + | |
| 44 | + ```text | |
| 45 | + > What did Jan answer? | |
| 46 | + 🤖 Jan answered that the capital of France is Paris. | |
| 47 | + ``` | |
| 48 | + | |
| 49 | +## Variants | |
| 50 | + | |
| 51 | +- **Let the other agent see the conversation.** Use `/ask-with-context-lucy <prompt>`: Lucy receives a transcript of the conversation (questions, answers, one line per tool call — never tool outputs) before your question. | |
| 52 | +- **In the terminal or the window, by name.** `/ask lucy <prompt>` and `/ask-with-context lucy <prompt>` are the same commands with the agent as an argument. | |
| 53 | +- **In an editor (ACP).** The menu lists `/ask-jan`, `/ask-with-context-jan`, `/ask-lucy`, `/ask-with-context-lucy`; when Lucy wants to run a command, the editor's own permission dialog asks you. | |
| 54 | +- **Stop a long answer.** `/abort`, `Esc` or `Ctrl+C` — nothing is kept from a cancelled ask. | |
| 55 | +- **Agents on other providers.** Any mm configuration works: an Ollama agent next to an llmman one is just another `mmConfigs` entry. | |
| 56 | + | |
| 57 | +## See also | |
| 58 | + | |
| 59 | +- Every form, message and error: [slash commands reference](../reference/slash-commands.md#asking-another-agent) | |
| 60 | +- The `mmConfigs` key: [configuration reference](../reference/configuration.md) | |
| 61 | +- Why another agent is a separate process spoken to over ACP: [asking another agent — explanation](../explanation/asking-another-agent.md) | |
| new file mode 100644 | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | +# How to ask another mm agent from your session | ||
| 2 | + | ||
| 3 | +This guide shows how to put a question to another mm agent — another model, another system prompt, other tools — and keep its answer in your conversation. It assumes you already run `mm` with a working provider. | ||
| 4 | + | ||
| 5 | +## Steps | ||
| 6 | + | ||
| 7 | +1. Write (or reuse) one configuration file per agent you want to ask. Each is an ordinary mm configuration; `demo-llmman/` ships two, for llmman: | ||
| 8 | + | ||
| 9 | + - `demo-llmman/agent.jan-nano.yaml` — `hf.co/Menlo/Jan-nano-gguf:Q4_K_M`, named Jan; | ||
| 10 | + - `demo-llmman/agent.lucy.yaml` — `hf.co/Menlo/Lucy-gguf:Q4_K_M`, named Lucy. | ||
| 11 | + | ||
| 12 | +2. Declare them in YOUR configuration, under `mmConfigs`. Paths are relative to the directory of the file you write them in: | ||
| 13 | + | ||
| 14 | + ```yaml | ||
| 15 | + mmConfigs: | ||
| 16 | + jan: agent.jan-nano.yaml | ||
| 17 | + lucy: agent.lucy.yaml | ||
| 18 | + ``` | ||
| 19 | + | ||
| 20 | + `demo-llmman/agent.llmman.yaml` already does this. | ||
| 21 | + | ||
| 22 | +3. Start `mm` with that configuration: | ||
| 23 | + | ||
| 24 | + ```bash | ||
| 25 | + cd demo-llmman | ||
| 26 | + ../mm agent.llmman.yaml | ||
| 27 | + ``` | ||
| 28 | + | ||
| 29 | + An agent whose file is missing shows up as a `[warning: mmConfigs: …]` line; the others work. | ||
| 30 | + | ||
| 31 | +4. Ask a question: | ||
| 32 | + | ||
| 33 | + ```text | ||
| 34 | + > /ask-jan In one short sentence: what is the capital of France? | ||
| 35 | + 🗣 asking jan (llmman/hf.co/Menlo/Jan-nano-gguf:Q4_K_M)… | ||
| 36 | + The capital of France is Paris. | ||
| 37 | + 🗣 jan's answer is in the history for your next question | ||
| 38 | + ``` | ||
| 39 | + | ||
| 40 | + The first ask of an agent starts it, so it takes a little longer; the next ones reuse it. | ||
| 41 | + | ||
| 42 | +5. Use the answer: your next question goes to your own model, which now sees Jan's answer. | ||
| 43 | + | ||
| 44 | + ```text | ||
| 45 | + > What did Jan answer? | ||
| 46 | + 🤖 Jan answered that the capital of France is Paris. | ||
| 47 | + ``` | ||
| 48 | + | ||
| 49 | +## Variants | ||
| 50 | + | ||
| 51 | +- **Let the other agent see the conversation.** Use `/ask-with-context-lucy <prompt>`: Lucy receives a transcript of the conversation (questions, answers, one line per tool call — never tool outputs) before your question. | ||
| 52 | +- **In the terminal or the window, by name.** `/ask lucy <prompt>` and `/ask-with-context lucy <prompt>` are the same commands with the agent as an argument. | ||
| 53 | +- **In an editor (ACP).** The menu lists `/ask-jan`, `/ask-with-context-jan`, `/ask-lucy`, `/ask-with-context-lucy`; when Lucy wants to run a command, the editor's own permission dialog asks you. | ||
| 54 | +- **Stop a long answer.** `/abort`, `Esc` or `Ctrl+C` — nothing is kept from a cancelled ask. | ||
| 55 | +- **Agents on other providers.** Any mm configuration works: an Ollama agent next to an llmman one is just another `mmConfigs` entry. | ||
| 56 | + | ||
| 57 | +## See also | ||
| 58 | + | ||
| 59 | +- Every form, message and error: [slash commands reference](../reference/slash-commands.md#asking-another-agent) | ||
| 60 | +- The `mmConfigs` key: [configuration reference](../reference/configuration.md) | ||
| 61 | +- Why another agent is a separate process spoken to over ACP: [asking another agent — explanation](../explanation/asking-another-agent.md) | ||
modified
docs/en/reference/cli.md +3 -1 | @@ -55,6 +55,8 @@ The first existing source wins. | ||
| 55 | 55 | | `AGENT_MODEL` | Overrides the `model` key. | |
| 56 | 56 | | `AGENT_BASE_URL` | Overrides `baseUrl` and disables the fallback probe. | |
| 57 | 57 | | `DMR_BASE_URL` | Same as `AGENT_BASE_URL`, for the `dmr` provider only; `AGENT_BASE_URL` wins when both are set. | |
| 58 | +| `AGENT_SESSIONS` | `off` turns session persistence off for this run, whatever `sessions.enabled` says. Set by `/ask` on the agents it starts. | | |
| 59 | +| `MM_ASK_DEPTH` | How many `/ask` levels deep this agent runs (unset = 0). Set by `/ask`; at 2, `/ask` is refused. | | |
| 58 | 60 | | Variable named by `apiKeyEnv` | Holds the API key. Default name: none for `dmr`, `LLAMA_API_KEY` for `llamacpp`, `LLMMAN_API_KEY` for `llmman`, `OLLAMA_API_KEY` for `ollama`. | |
| 59 | 61 | |
| 60 | 62 | ## Start-up output |
| @@ -63,7 +65,7 @@ Printed on stdout in terminal mode, on stderr in ACP mode. Dimmed when stdout is | ||
| 63 | 65 | |
| 64 | 66 | | Line | Content | |
| 65 | 67 | |------|---------| |
| 66 | -| `[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. | | |
| 68 | +| `[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, an `mmConfigs` file not found (`mmConfigs: <name> → <path> not found — /ask-<name> will fail`). | | |
| 67 | 69 | | `mm <version> \| config: … \| provider: … \| model: … \| ctx: … \| tools: … \| skills: N` | Version (see above), configuration path, provider name, model identifier, context window with its origin (`config`, `/props`) or `unknown`, declared tool names, number of skills found. | |
| 68 | 70 | | `Agent ready. Commands: …` | Terminal mode only. | |
| 69 | 71 | | `Shortcut: Ctrl+C …` | Terminal mode only. | |
| @@ -55,6 +55,8 @@ The first existing source wins. | |||
| 55 | | `AGENT_MODEL` | Overrides the `model` key. | | 55 | | `AGENT_MODEL` | Overrides the `model` key. | |
| 56 | | `AGENT_BASE_URL` | Overrides `baseUrl` and disables the fallback probe. | | 56 | | `AGENT_BASE_URL` | Overrides `baseUrl` and disables the fallback probe. | |
| 57 | | `DMR_BASE_URL` | Same as `AGENT_BASE_URL`, for the `dmr` provider only; `AGENT_BASE_URL` wins when both are set. | | 57 | | `DMR_BASE_URL` | Same as `AGENT_BASE_URL`, for the `dmr` provider only; `AGENT_BASE_URL` wins when both are set. | |
| 58 | +| `AGENT_SESSIONS` | `off` turns session persistence off for this run, whatever `sessions.enabled` says. Set by `/ask` on the agents it starts. | | ||
| 59 | +| `MM_ASK_DEPTH` | How many `/ask` levels deep this agent runs (unset = 0). Set by `/ask`; at 2, `/ask` is refused. | | ||
| 58 | | Variable named by `apiKeyEnv` | Holds the API key. Default name: none for `dmr`, `LLAMA_API_KEY` for `llamacpp`, `LLMMAN_API_KEY` for `llmman`, `OLLAMA_API_KEY` for `ollama`. | | 60 | | Variable named by `apiKeyEnv` | Holds the API key. Default name: none for `dmr`, `LLAMA_API_KEY` for `llamacpp`, `LLMMAN_API_KEY` for `llmman`, `OLLAMA_API_KEY` for `ollama`. | |
| 59 | 61 | ||
| 60 | ## Start-up output | 62 | ## Start-up output |
| @@ -63,7 +65,7 @@ Printed on stdout in terminal mode, on stderr in ACP mode. Dimmed when stdout is | |||
| 63 | 65 | ||
| 64 | | Line | Content | | 66 | | Line | Content | |
| 65 | |------|---------| | 67 | |------|---------| |
| 66 | -| `[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. | | 68 | +| `[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, an `mmConfigs` file not found (`mmConfigs: <name> → <path> not found — /ask-<name> will fail`). | |
| 67 | | `mm <version> \| config: … \| provider: … \| model: … \| ctx: … \| tools: … \| skills: N` | Version (see above), configuration path, provider name, model identifier, context window with its origin (`config`, `/props`) or `unknown`, declared tool names, number of skills found. | | 69 | | `mm <version> \| config: … \| provider: … \| model: … \| ctx: … \| tools: … \| skills: N` | Version (see above), configuration path, provider name, model identifier, context window with its origin (`config`, `/props`) or `unknown`, declared tool names, number of skills found. | |
| 68 | | `Agent ready. Commands: …` | Terminal mode only. | | 70 | | `Agent ready. Commands: …` | Terminal mode only. | |
| 69 | | `Shortcut: Ctrl+C …` | Terminal mode only. | | 71 | | `Shortcut: Ctrl+C …` | Terminal mode only. | |
modified
docs/en/reference/configuration.md +4 -0 | @@ -31,6 +31,7 @@ | ||
| 31 | 31 | | `lsp` | map | see below | Language servers behind the `lsp` tool. No server listed = no tool. | |
| 32 | 32 | | `tui` | bool | `false` | Start in the single-window mode without the `-tui` flag. Falls back to the line mode when stdin or stdout is not a terminal; ignored with `-acp`. | |
| 33 | 33 | | `permissions` | string | `auto` | `ask`: in the window mode, `bash`, `write_file` and `edit_file` wait for the user's answer (`a`/`s`/`r`) before running. `auto`: tools run at once. The line mode and the ACP façade do not read it (ACP has its own dialog). Must be `auto` or `ask`. | |
| 34 | +| `mmConfigs` | map name → path | none | Other mm agents this one can ask. Each name generates `/ask-<name>` and `/ask-with-context-<name>` (see [slash commands](slash-commands.md#asking-another-agent)). A relative path is relative to the directory of the file that declares it. Names: lowercase letters, digits and `-`, starting with a letter or a digit, not starting with `with-context`. A path whose file does not exist is a start-up warning, not an error. | | |
| 34 | 35 | |
| 35 | 36 | ## `context` keys |
| 36 | 37 | |
| @@ -117,6 +118,9 @@ Reported as `[config error: <path>: <message>]`, exit code 1. | ||
| 117 | 118 | | `lsp.servers[N].extensions: "go" must start with a dot, like ".go"` | an extension without its dot | |
| 118 | 119 | | `lsp.servers[N]: extension ".ts" is already served by "typescript"` | two servers on one extension | |
| 119 | 120 | | `permissions must be "auto" or "ask"` | any other value | |
| 121 | +| `mmConfigs: "Bob" is not a valid name (…)` | a name with uppercase letters, spaces or other characters, or starting with `-` | | |
| 122 | +| `mmConfigs: "with-context-x" is not a valid name (it would read as /ask-with-context-…)` | a name starting with `with-context` | | |
| 123 | +| `mmConfigs.<name>: the path must not be empty` | `<name>: ""` | | |
| 120 | 124 | | `unknown provider "x" (known: dmr, llamacpp, llmman, ollama)` | Reported by the engine, as `[engine error: …]`. | |
| 121 | 125 | |
| 122 | 126 | A start-up warning, not an error, is printed when `context.enabled` is `true`, the window is unknown and `context.maxMessages` is `0`. |
| @@ -31,6 +31,7 @@ | |||
| 31 | | `lsp` | map | see below | Language servers behind the `lsp` tool. No server listed = no tool. | | 31 | | `lsp` | map | see below | Language servers behind the `lsp` tool. No server listed = no tool. | |
| 32 | | `tui` | bool | `false` | Start in the single-window mode without the `-tui` flag. Falls back to the line mode when stdin or stdout is not a terminal; ignored with `-acp`. | | 32 | | `tui` | bool | `false` | Start in the single-window mode without the `-tui` flag. Falls back to the line mode when stdin or stdout is not a terminal; ignored with `-acp`. | |
| 33 | | `permissions` | string | `auto` | `ask`: in the window mode, `bash`, `write_file` and `edit_file` wait for the user's answer (`a`/`s`/`r`) before running. `auto`: tools run at once. The line mode and the ACP façade do not read it (ACP has its own dialog). Must be `auto` or `ask`. | | 33 | | `permissions` | string | `auto` | `ask`: in the window mode, `bash`, `write_file` and `edit_file` wait for the user's answer (`a`/`s`/`r`) before running. `auto`: tools run at once. The line mode and the ACP façade do not read it (ACP has its own dialog). Must be `auto` or `ask`. | |
| 34 | +| `mmConfigs` | map name → path | none | Other mm agents this one can ask. Each name generates `/ask-<name>` and `/ask-with-context-<name>` (see [slash commands](slash-commands.md#asking-another-agent)). A relative path is relative to the directory of the file that declares it. Names: lowercase letters, digits and `-`, starting with a letter or a digit, not starting with `with-context`. A path whose file does not exist is a start-up warning, not an error. | | ||
| 34 | 35 | ||
| 35 | ## `context` keys | 36 | ## `context` keys |
| 36 | 37 | ||
| @@ -117,6 +118,9 @@ Reported as `[config error: <path>: <message>]`, exit code 1. | |||
| 117 | | `lsp.servers[N].extensions: "go" must start with a dot, like ".go"` | an extension without its dot | | 118 | | `lsp.servers[N].extensions: "go" must start with a dot, like ".go"` | an extension without its dot | |
| 118 | | `lsp.servers[N]: extension ".ts" is already served by "typescript"` | two servers on one extension | | 119 | | `lsp.servers[N]: extension ".ts" is already served by "typescript"` | two servers on one extension | |
| 119 | | `permissions must be "auto" or "ask"` | any other value | | 120 | | `permissions must be "auto" or "ask"` | any other value | |
| 121 | +| `mmConfigs: "Bob" is not a valid name (…)` | a name with uppercase letters, spaces or other characters, or starting with `-` | | ||
| 122 | +| `mmConfigs: "with-context-x" is not a valid name (it would read as /ask-with-context-…)` | a name starting with `with-context` | | ||
| 123 | +| `mmConfigs.<name>: the path must not be empty` | `<name>: ""` | | ||
| 120 | | `unknown provider "x" (known: dmr, llamacpp, llmman, ollama)` | Reported by the engine, as `[engine error: …]`. | | 124 | | `unknown provider "x" (known: dmr, llamacpp, llmman, ollama)` | Reported by the engine, as `[engine error: …]`. | |
| 121 | 125 | ||
| 122 | A start-up warning, not an error, is printed when `context.enabled` is `true`, the window is unknown and `context.maxMessages` is `0`. | 126 | A start-up warning, not an error, is printed when `context.enabled` is `true`, the window is unknown and `context.maxMessages` is `0`. |
modified
docs/en/reference/slash-commands.md +45 -0 Large diff collapsed to keep the page fast — load it when you need it.
modified
docs/fr/README.md +2 -0 | @@ -13,6 +13,7 @@ Cette documentation est organisée selon les quatre quadrants [Diátaxis](https: | ||
| 13 | 13 | - [Faire tourner mini-me sur llama.cpp plutôt que Docker Model Runner](how-to/run-with-llama-cpp.md) |
| 14 | 14 | - [Faire tourner mini-me sur llmman](how-to/run-with-llmman.md) — `provider: llmman`, la config de démo dans `demo-llmman/` |
| 15 | 15 | - [Faire tourner mini-me sur Ollama](how-to/run-with-ollama.md) — `provider: ollama`, la config de démo dans `demo-ollama/` |
| 16 | +- [Interroger un autre agent mm depuis votre session](how-to/ask-another-agent.md) — `mmConfigs`, `/ask-<nom>`, `/ask-with-context-<nom>` | |
| 16 | 17 | - [Utiliser mini-me depuis un éditeur (ACP)](how-to/use-from-an-editor.md) |
| 17 | 18 | - [Faire tourner mini-me dans un Docker Sandbox](how-to/run-in-a-docker-sandbox.md) — le template et les deux kits sous `docker-sbx/`, mode fenêtre et éditeur |
| 18 | 19 | - [Faire tourner mini-me avec les kits sandbox v3](how-to/run-with-sandbox-kits-v3.md) — les deux mêmes kits pour la spécification de kit v3, sous `docker-sbx-v3/` |
| @@ -56,6 +57,7 @@ Cette documentation est organisée selon les quatre quadrants [Diátaxis](https: | ||
| 56 | 57 | - [L'outil `lsp`](explanation/lsp-tool.md) — un seul outil avec un champ `action`, un champ `symbol` plutôt que des colonnes, des fichiers synchronisés depuis le disque, un renommage soumis à permission |
| 57 | 58 | - [L'outil `fetch_content`](explanation/fetch-content-tool.md) — pourquoi un outil intégré à côté du `fetch` MCP, un convertisseur maison, le robots.txt, des erreurs numérotées en texte, les limites connues |
| 58 | 59 | - [Modèles à raisonnement](explanation/thinking-models.md) — d'où vient le raisonnement d'un modèle, pourquoi `mm` le lit sous Genkit, et pourquoi le lire garde le chien de garde en vie |
| 60 | +- [Interroger un autre agent](explanation/asking-another-agent.md) — pourquoi un autre agent mm est un processus enfant interrogé en ACP, la paire de messages, les sessions neuves, la limite de profondeur | |
| 59 | 61 | - Diagramme des paquets (source draw.io) : [`docs/diagrams/packages.drawio`](../diagrams/packages.drawio) |
| 60 | 62 | |
| 61 | 63 | ### Explication MCP (Model Context Protocol) |
| @@ -13,6 +13,7 @@ Cette documentation est organisée selon les quatre quadrants [Diátaxis](https: | |||
| 13 | - [Faire tourner mini-me sur llama.cpp plutôt que Docker Model Runner](how-to/run-with-llama-cpp.md) | 13 | - [Faire tourner mini-me sur llama.cpp plutôt que Docker Model Runner](how-to/run-with-llama-cpp.md) |
| 14 | - [Faire tourner mini-me sur llmman](how-to/run-with-llmman.md) — `provider: llmman`, la config de démo dans `demo-llmman/` | 14 | - [Faire tourner mini-me sur llmman](how-to/run-with-llmman.md) — `provider: llmman`, la config de démo dans `demo-llmman/` |
| 15 | - [Faire tourner mini-me sur Ollama](how-to/run-with-ollama.md) — `provider: ollama`, la config de démo dans `demo-ollama/` | 15 | - [Faire tourner mini-me sur Ollama](how-to/run-with-ollama.md) — `provider: ollama`, la config de démo dans `demo-ollama/` |
| 16 | +- [Interroger un autre agent mm depuis votre session](how-to/ask-another-agent.md) — `mmConfigs`, `/ask-<nom>`, `/ask-with-context-<nom>` | ||
| 16 | - [Utiliser mini-me depuis un éditeur (ACP)](how-to/use-from-an-editor.md) | 17 | - [Utiliser mini-me depuis un éditeur (ACP)](how-to/use-from-an-editor.md) |
| 17 | - [Faire tourner mini-me dans un Docker Sandbox](how-to/run-in-a-docker-sandbox.md) — le template et les deux kits sous `docker-sbx/`, mode fenêtre et éditeur | 18 | - [Faire tourner mini-me dans un Docker Sandbox](how-to/run-in-a-docker-sandbox.md) — le template et les deux kits sous `docker-sbx/`, mode fenêtre et éditeur |
| 18 | - [Faire tourner mini-me avec les kits sandbox v3](how-to/run-with-sandbox-kits-v3.md) — les deux mêmes kits pour la spécification de kit v3, sous `docker-sbx-v3/` | 19 | - [Faire tourner mini-me avec les kits sandbox v3](how-to/run-with-sandbox-kits-v3.md) — les deux mêmes kits pour la spécification de kit v3, sous `docker-sbx-v3/` |
| @@ -56,6 +57,7 @@ Cette documentation est organisée selon les quatre quadrants [Diátaxis](https: | |||
| 56 | - [L'outil `lsp`](explanation/lsp-tool.md) — un seul outil avec un champ `action`, un champ `symbol` plutôt que des colonnes, des fichiers synchronisés depuis le disque, un renommage soumis à permission | 57 | - [L'outil `lsp`](explanation/lsp-tool.md) — un seul outil avec un champ `action`, un champ `symbol` plutôt que des colonnes, des fichiers synchronisés depuis le disque, un renommage soumis à permission |
| 57 | - [L'outil `fetch_content`](explanation/fetch-content-tool.md) — pourquoi un outil intégré à côté du `fetch` MCP, un convertisseur maison, le robots.txt, des erreurs numérotées en texte, les limites connues | 58 | - [L'outil `fetch_content`](explanation/fetch-content-tool.md) — pourquoi un outil intégré à côté du `fetch` MCP, un convertisseur maison, le robots.txt, des erreurs numérotées en texte, les limites connues |
| 58 | - [Modèles à raisonnement](explanation/thinking-models.md) — d'où vient le raisonnement d'un modèle, pourquoi `mm` le lit sous Genkit, et pourquoi le lire garde le chien de garde en vie | 59 | - [Modèles à raisonnement](explanation/thinking-models.md) — d'où vient le raisonnement d'un modèle, pourquoi `mm` le lit sous Genkit, et pourquoi le lire garde le chien de garde en vie |
| 60 | +- [Interroger un autre agent](explanation/asking-another-agent.md) — pourquoi un autre agent mm est un processus enfant interrogé en ACP, la paire de messages, les sessions neuves, la limite de profondeur | ||
| 59 | - Diagramme des paquets (source draw.io) : [`docs/diagrams/packages.drawio`](../diagrams/packages.drawio) | 61 | - Diagramme des paquets (source draw.io) : [`docs/diagrams/packages.drawio`](../diagrams/packages.drawio) |
| 60 | 62 | ||
| 61 | ### Explication MCP (Model Context Protocol) | 63 | ### Explication MCP (Model Context Protocol) |
modified
docs/fr/explanation/architecture.md +1 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
docs/fr/explanation/asking-another-agent.md +38 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
docs/fr/how-to/ask-another-agent.md +61 -0 Large diff collapsed to keep the page fast — load it when you need it.
modified
docs/fr/reference/cli.md +3 -1 Large diff collapsed to keep the page fast — load it when you need it.
modified
docs/fr/reference/configuration.md +4 -0 Large diff collapsed to keep the page fast — load it when you need it.
modified
docs/fr/reference/slash-commands.md +45 -0 Large diff collapsed to keep the page fast — load it when you need it.
modified
internal/acp/acp.go +22 -3 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/acp/ask.go +42 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/acp/ask_test.go +89 -0 Large diff collapsed to keep the page fast — load it when you need it.
modified
internal/agent/agent.go +20 -1 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/agent/ask.go +125 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/agent/ask_test.go +83 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/ask/client.go +235 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/ask/context.go +101 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/ask/context_test.go +66 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/ask/doc.go +22 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/ask/env.go +55 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/ask/env_test.go +42 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/ask/exchange.go +63 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/ask/exchange_test.go +56 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/ask/pool.go +253 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/ask/pool_test.go +268 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/ask/run.go +79 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/ask/run_test.go +83 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/command/ask.go +106 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/command/ask_test.go +99 -0 Large diff collapsed to keep the page fast — load it when you need it.
modified
internal/command/command.go +11 -4 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/config/agents.go +68 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/config/agents_test.go +96 -0 Large diff collapsed to keep the page fast — load it when you need it.
modified
internal/config/config.go +16 -0 Large diff collapsed to keep the page fast — load it when you need it.
modified
internal/store/store.go +2 -1 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/tui/ask.go +51 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
internal/tui/ask_test.go +95 -0 Large diff collapsed to keep the page fast — load it when you need it.
modified
internal/tui/commands.go +8 -0 Large diff collapsed to keep the page fast — load it when you need it.
modified
internal/tui/turn.go +12 -3 Large diff collapsed to keep the page fast — load it when you need it.
modified
internal/tui/view.go +1 -1 Large diff collapsed to keep the page fast — load it when you need it.
modified
main.go +15 -5 Large diff collapsed to keep the page fast — load it when you need it.