bots-garden/mini-mepublic Fork
⑂ main
Commits
⬇ Clone ▾
git clone https://git.rickub.com/bots-garden/mini-me.git
git clone ssh://git@rickub.com/bots-garden/mini-me.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

Talk to another Mini Me #21

Merged@k33g wants to merge feature/talk-to-another-mm into main
Merged 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
1717 # From a Docker sandbox, the daemon runs on the host.
1818 fallback: http://host.docker.internal:17434/v1
1919
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+
2029 # Only when the daemon was started with LLMMAN_API_KEYS (mandatory when it
2130 # listens beyond loopback); unset otherwise. The key never goes in this file.
2231 # 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/v118 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 it29 # 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_KEY31 # 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 @@
11 <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="&lt;b&gt;main (mm)&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;wiring: config, engine, tools, then one front end&lt;/font&gt;" 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="&lt;b&gt;internal/agent&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;terminal REPL: /quit /abort /compact /new&lt;/font&gt;" 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="&lt;b&gt;internal/acp&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;editor front end: Agent Client Protocol on stdio&lt;/font&gt;" 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="&lt;b&gt;internal/session&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;what a conversation starts from; the /new reset&lt;/font&gt;" 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="&lt;b&gt;internal/compact&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;history compression into a model-written summary&lt;/font&gt;" 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="&lt;b&gt;internal/engine&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;LLM connection + generation, one Provider per server&lt;/font&gt;" 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="&lt;b&gt;internal/tools&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;built-in tools: bash, read_skill, file tools, lsp, fetch_content&lt;/font&gt;" 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="&lt;b&gt;internal/detector&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;repeated-action (loop) detection&lt;/font&gt;" 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="&lt;b&gt;internal/config&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;settings with YAML overrides (agent.yaml)&lt;/font&gt;" 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="&lt;b&gt;internal/spinner&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;terminal spinner and styling&lt;/font&gt;" 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="&lt;b&gt;internal/ui&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;human output destination + shared event sink&lt;/font&gt;" 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="&lt;b&gt;internal/fileedit&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;exact-replacement file editing&lt;/font&gt;" 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="&lt;b&gt;internal/skills&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;markdown skills catalogue (./skills)&lt;/font&gt;" 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="&lt;b&gt;github.com/firebase/genkit&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;ai.Message, tools, model calls&lt;/font&gt;" 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="&lt;b&gt;github.com/coder/acp-go-sdk&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;JSON-RPC transport for ACP&lt;/font&gt;" 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="&lt;b&gt;github.com/openai/openai-go&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;OpenAI-compatible HTTP client&lt;/font&gt;" 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="&lt;b&gt;gopkg.in/yaml.v3&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;YAML parsing&lt;/font&gt;" 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="&lt;b&gt;internal/version&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;which mm this is: build stamp, else the commit&lt;/font&gt;" 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>
22 <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="&lt;b&gt;internal/mention&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;the @path notation of a question&lt;/font&gt;" 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="&lt;b&gt;internal/store&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;per-project session files (.mm/sessions/&lt;id&gt;.json)&lt;/font&gt;" 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="&lt;b&gt;internal/tui&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;window front end: one screen on tcell (mm -tui)&lt;/font&gt;" 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="&lt;b&gt;internal/command&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;the one slash-command table, per front end&lt;/font&gt;" 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="&lt;b&gt;github.com/gdamore/tcell/v2&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;terminal screen, keys, mouse&lt;/font&gt;" 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="&lt;b&gt;internal/clipboard&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;system clipboard: native tool, then OSC 52&lt;/font&gt;" 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="&lt;b&gt;internal/lsp&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;language-server client: definition, references, hover, symbols, diagnostics, rename&lt;/font&gt;" 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="&lt;b&gt;go.lsp.dev/protocol + jsonrpc2&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;LSP types and JSON-RPC 2.0 over stdio&lt;/font&gt;" 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="&lt;b&gt;mm — package dependencies&lt;/b&gt;&lt;br/&gt;Arrow: &quot;imports&quot;. Yellow: entry point. Blue: front ends. Green: shared logic. Grey: leaf packages. Dashed purple: third-party modules.&lt;br/&gt;Regenerate the edge list from: go list -f '{{.ImportPath}}: {{join .Imports &quot; &quot;}}' ./..." 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="&lt;b&gt;internal/webmd&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;web page → Markdown: fetch (robots.txt, charset → UTF-8), clean, convert&lt;/font&gt;" 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="&lt;b&gt;golang.org/x/net/html (+ x/text)&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;HTML5 parser, charset lookup&lt;/font&gt;" 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="&lt;b&gt;internal/mention&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;the @path notation of a question&lt;/font&gt;" 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="&lt;b&gt;internal/store&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;per-project session files (.mm/sessions/&lt;id&gt;.json)&lt;/font&gt;" 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="&lt;b&gt;internal/tui&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;window front end: one screen on tcell (mm -tui)&lt;/font&gt;" 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="&lt;b&gt;internal/command&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;the one slash-command table, per front end&lt;/font&gt;" 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="&lt;b&gt;github.com/gdamore/tcell/v2&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;terminal screen, keys, mouse&lt;/font&gt;" 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="&lt;b&gt;internal/clipboard&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;system clipboard: native tool, then OSC 52&lt;/font&gt;" 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="&lt;b&gt;internal/lsp&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;language-server client: definition, references, hover, symbols, diagnostics, rename&lt;/font&gt;" 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="&lt;b&gt;go.lsp.dev/protocol + jsonrpc2&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;LSP types and JSON-RPC 2.0 over stdio&lt;/font&gt;" 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="&lt;b&gt;mm — package dependencies&lt;/b&gt;&lt;br/&gt;Arrow: &quot;imports&quot;. Yellow: entry point. Blue: front ends. Green: shared logic. Grey: leaf packages. Dashed purple: third-party modules.&lt;br/&gt;Regenerate the edge list from: go list -f '{{.ImportPath}}: {{join .Imports &quot; &quot;}}' ./..." 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="&lt;b&gt;internal/webmd&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;web page → Markdown: fetch (robots.txt, charset → UTF-8), clean, convert&lt;/font&gt;" 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="&lt;b&gt;golang.org/x/net/html (+ x/text)&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;HTML5 parser, charset lookup&lt;/font&gt;" 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="&lt;b&gt;internal/ask&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;/ask another mm agent: child mm -acp, ACP client side&lt;/font&gt;" 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="&lt;b&gt;main (mm)&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;wiring: config, engine, tools, then one front end&lt;/font&gt;" 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="&lt;b&gt;internal/agent&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;terminal REPL: /quit /abort /compact /new&lt;/font&gt;" 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="&lt;b&gt;internal/acp&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;editor front end: Agent Client Protocol on stdio&lt;/font&gt;" 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="&lt;b&gt;internal/session&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;what a conversation starts from; the /new reset&lt;/font&gt;" 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="&lt;b&gt;internal/compact&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;history compression into a model-written summary&lt;/font&gt;" 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="&lt;b&gt;internal/engine&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;LLM connection + generation, one Provider per server&lt;/font&gt;" 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="&lt;b&gt;internal/tools&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;built-in tools: bash, read_skill, file tools, lsp, fetch_content&lt;/font&gt;" 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="&lt;b&gt;internal/detector&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;repeated-action (loop) detection&lt;/font&gt;" 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="&lt;b&gt;internal/config&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;settings with YAML overrides (agent.yaml)&lt;/font&gt;" 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="&lt;b&gt;internal/spinner&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;terminal spinner and styling&lt;/font&gt;" 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="&lt;b&gt;internal/ui&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;human output destination + shared event sink&lt;/font&gt;" 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="&lt;b&gt;internal/fileedit&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;exact-replacement file editing&lt;/font&gt;" 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="&lt;b&gt;internal/skills&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;markdown skills catalogue (./skills)&lt;/font&gt;" 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="&lt;b&gt;github.com/firebase/genkit&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;ai.Message, tools, model calls&lt;/font&gt;" 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="&lt;b&gt;github.com/coder/acp-go-sdk&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;JSON-RPC transport for ACP&lt;/font&gt;" 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="&lt;b&gt;github.com/openai/openai-go&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;OpenAI-compatible HTTP client&lt;/font&gt;" 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="&lt;b&gt;gopkg.in/yaml.v3&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;YAML parsing&lt;/font&gt;" 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="&lt;b&gt;internal/version&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;which mm this is: build stamp, else the commit&lt;/font&gt;" 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="&lt;b&gt;main (mm)&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;wiring: config, engine, tools, then one front end&lt;/font&gt;" 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="&lt;b&gt;internal/agent&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;terminal REPL: /quit /abort /compact /new&lt;/font&gt;" 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="&lt;b&gt;internal/acp&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;editor front end: Agent Client Protocol on stdio&lt;/font&gt;" 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="&lt;b&gt;internal/session&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;what a conversation starts from; the /new reset&lt;/font&gt;" 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="&lt;b&gt;internal/compact&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;history compression into a model-written summary&lt;/font&gt;" 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="&lt;b&gt;internal/engine&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;LLM connection + generation, one Provider per server&lt;/font&gt;" 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="&lt;b&gt;internal/tools&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;built-in tools: bash, read_skill, file tools, lsp, fetch_content&lt;/font&gt;" 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="&lt;b&gt;internal/detector&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;repeated-action (loop) detection&lt;/font&gt;" 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="&lt;b&gt;internal/config&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;settings with YAML overrides (agent.yaml)&lt;/font&gt;" 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="&lt;b&gt;internal/spinner&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;terminal spinner and styling&lt;/font&gt;" 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="&lt;b&gt;internal/ui&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;human output destination + shared event sink&lt;/font&gt;" 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="&lt;b&gt;internal/fileedit&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;exact-replacement file editing&lt;/font&gt;" 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="&lt;b&gt;internal/skills&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;markdown skills catalogue (./skills)&lt;/font&gt;" 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="&lt;b&gt;github.com/firebase/genkit&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;ai.Message, tools, model calls&lt;/font&gt;" 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="&lt;b&gt;github.com/coder/acp-go-sdk&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;JSON-RPC transport for ACP&lt;/font&gt;" 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="&lt;b&gt;github.com/openai/openai-go&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;OpenAI-compatible HTTP client&lt;/font&gt;" 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="&lt;b&gt;gopkg.in/yaml.v3&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;YAML parsing&lt;/font&gt;" 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="&lt;b&gt;internal/version&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;which mm this is: build stamp, else the commit&lt;/font&gt;" 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="&lt;b&gt;internal/mention&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;the @path notation of a question&lt;/font&gt;" 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="&lt;b&gt;internal/store&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;per-project session files (.mm/sessions/&lt;id&gt;.json)&lt;/font&gt;" 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="&lt;b&gt;internal/tui&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;window front end: one screen on tcell (mm -tui)&lt;/font&gt;" 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="&lt;b&gt;internal/command&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;the one slash-command table, per front end&lt;/font&gt;" 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="&lt;b&gt;github.com/gdamore/tcell/v2&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;terminal screen, keys, mouse&lt;/font&gt;" 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="&lt;b&gt;internal/clipboard&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;system clipboard: native tool, then OSC 52&lt;/font&gt;" 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="&lt;b&gt;internal/lsp&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;language-server client: definition, references, hover, symbols, diagnostics, rename&lt;/font&gt;" 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="&lt;b&gt;go.lsp.dev/protocol + jsonrpc2&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;LSP types and JSON-RPC 2.0 over stdio&lt;/font&gt;" 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="&lt;b&gt;mm — package dependencies&lt;/b&gt;&lt;br/&gt;Arrow: &quot;imports&quot;. Yellow: entry point. Blue: front ends. Green: shared logic. Grey: leaf packages. Dashed purple: third-party modules.&lt;br/&gt;Regenerate the edge list from: go list -f '{{.ImportPath}}: {{join .Imports &quot; &quot;}}' ./..." 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="&lt;b&gt;internal/webmd&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;web page → Markdown: fetch (robots.txt, charset → UTF-8), clean, convert&lt;/font&gt;" 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="&lt;b&gt;golang.org/x/net/html (+ x/text)&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;HTML5 parser, charset lookup&lt;/font&gt;" 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="&lt;b&gt;internal/mention&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;the @path notation of a question&lt;/font&gt;" 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="&lt;b&gt;internal/store&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;per-project session files (.mm/sessions/&lt;id&gt;.json)&lt;/font&gt;" 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="&lt;b&gt;internal/tui&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;window front end: one screen on tcell (mm -tui)&lt;/font&gt;" 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="&lt;b&gt;internal/command&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;the one slash-command table, per front end&lt;/font&gt;" 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="&lt;b&gt;github.com/gdamore/tcell/v2&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;terminal screen, keys, mouse&lt;/font&gt;" 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="&lt;b&gt;internal/clipboard&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;system clipboard: native tool, then OSC 52&lt;/font&gt;" 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="&lt;b&gt;internal/lsp&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;language-server client: definition, references, hover, symbols, diagnostics, rename&lt;/font&gt;" 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="&lt;b&gt;go.lsp.dev/protocol + jsonrpc2&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;LSP types and JSON-RPC 2.0 over stdio&lt;/font&gt;" 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="&lt;b&gt;mm — package dependencies&lt;/b&gt;&lt;br/&gt;Arrow: &quot;imports&quot;. Yellow: entry point. Blue: front ends. Green: shared logic. Grey: leaf packages. Dashed purple: third-party modules.&lt;br/&gt;Regenerate the edge list from: go list -f '{{.ImportPath}}: {{join .Imports &quot; &quot;}}' ./..." 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="&lt;b&gt;internal/webmd&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;web page → Markdown: fetch (robots.txt, charset → UTF-8), clean, convert&lt;/font&gt;" 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="&lt;b&gt;golang.org/x/net/html (+ x/text)&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;HTML5 parser, charset lookup&lt;/font&gt;" 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="&lt;b&gt;internal/ask&lt;/b&gt;&lt;br/&gt;&lt;font style=&quot;font-size:10px&quot;&gt;/ask another mm agent: child mm -acp, ACP client side&lt;/font&gt;" 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)
1313 - [Run mini-me on llama.cpp instead of Docker Model Runner](how-to/run-with-llama-cpp.md)
1414 - [Run mini-me on llmman](how-to/run-with-llmman.md) — `provider: llmman`, the demo config in `demo-llmman/`
1515 - [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>`
1617 - [Use mini-me from an editor (ACP)](how-to/use-from-an-editor.md)
1718 - [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
1819 - [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)
5657 - [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
5758 - [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
5859 - [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
5961 - Package diagram (draw.io source): [`docs/diagrams/packages.drawio`](../diagrams/packages.drawio)
6062
6163 ### 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 editor18 - [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 permission57 - [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 limits58 - [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 alive59 - [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) Explanation63 ### 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
1515 | `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`. |
1616 | `skills` | Discovery of markdown procedures and rendering of the `read_skill` catalogue. |
1717 | `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). |
1819 | `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. |
1920 | `store` | Per-project session files (`<dir>/<id>.json`, whole rewrite, atomic): create, save, load, list, latest. Used by both front ends when `sessions.enabled`. |
2021 | `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.
5555 | `AGENT_MODEL` | Overrides the `model` key. |
5656 | `AGENT_BASE_URL` | Overrides `baseUrl` and disables the fallback probe. |
5757 | `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. |
5860 | 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`. |
5961
6062 ## Start-up output
@@ -63,7 +65,7 @@ Printed on stdout in terminal mode, on stderr in ACP mode. Dimmed when stdout is
6365
6466 | Line | Content |
6567 |------|---------|
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`). |
6769 | `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. |
6870 | `Agent ready. Commands: …` | Terminal mode only. |
6971 | `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 output62 ## 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 @@
3131 | `lsp` | map | see below | Language servers behind the `lsp` tool. No server listed = no tool. |
3232 | `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`. |
3333 | `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. |
3435
3536 ## `context` keys
3637
@@ -117,6 +118,9 @@ Reported as `[config error: <path>: <message>]`, exit code 1.
117118 | `lsp.servers[N].extensions: "go" must start with a dot, like ".go"` | an extension without its dot |
118119 | `lsp.servers[N]: extension ".ts" is already served by "typescript"` | two servers on one extension |
119120 | `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>: ""` |
120124 | `unknown provider "x" (known: dmr, llamacpp, llmman, ollama)` | Reported by the engine, as `[engine error: …]`. |
121125
122126 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` keys36 ## `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:
1313 - [Faire tourner mini-me sur llama.cpp plutôt que Docker Model Runner](how-to/run-with-llama-cpp.md)
1414 - [Faire tourner mini-me sur llmman](how-to/run-with-llmman.md) — `provider: llmman`, la config de démo dans `demo-llmman/`
1515 - [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>`
1617 - [Utiliser mini-me depuis un éditeur (ACP)](how-to/use-from-an-editor.md)
1718 - [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
1819 - [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:
5657 - [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
5758 - [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
5859 - [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
5961 - Diagramme des paquets (source draw.io) : [`docs/diagrams/packages.drawio`](../diagrams/packages.drawio)
6062
6163 ### 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 éditeur18 - [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 à permission57 - [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 connues58 - [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 vie59 - [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.