bots-garden/mini-mepublic Fork
⑂ de01f04
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

🛟 Updated.

k33g committed 2026-09-21T06:50:36+02:00 Browse files
de01f04 parent: 0394624
modified .tickets/epics.yaml +9 -0
@@ -3,3 +3,12 @@ epics:
33 - title: Improve TUI
44 state: open
55 color: "#5319e7"
6+ - title: CLI
7+ state: open
8+ color: "#0e8a16"
9+ - title: Builtin tools
10+ state: open
11+ color: "#1d76db"
12+ - title: Ideas
13+ state: open
14+ color: "#bfdadc"
@@ -3,3 +3,12 @@ epics:
3 - title: Improve TUI3 - title: Improve TUI
4 state: open4 state: open
5 color: "#5319e7"5 color: "#5319e7"
6+ - title: CLI
7+ state: open
8+ color: "#0e8a16"
9+ - title: Builtin tools
10+ state: open
11+ color: "#1d76db"
12+ - title: Ideas
13+ state: open
14+ color: "#bfdadc"
modified .tickets/issues/0029-thinking-mode.yaml +129 -4
@@ -1,14 +1,139 @@
11 # Managed by IssueSpec. Hand edits are welcome; keep the schema valid.
22 id: 29
33 title: thinking mode
4-state: open
4+state: closed
55 author:
66 name: k33g
77 email: ph.charriere@gmail.com
88 createdAt: 2026-09-21T03:43:16.681Z
9-updatedAt: 2026-09-21T03:43:16.681Z
9+updatedAt: 2026-09-21T04:30:00.000Z
1010 labels: []
1111 milestone: v0.0.3
12-body: ""
13-tasks: []
12+body: |
13+ Some models have a thinking mode. Mini Me showed nothing at all while they thought; it should show the reasoning, in a different colour and in italic. Two configurations to test with in `./tests` (gemma-4-e2b, qwen3.5-2b), both confirmed thinking through the Docker Model Runner CLI.
14+
15+ ## What a thinking model actually sends (Claude, 2026-09-21)
16+
17+ The reasoning travels in a field of its own, never inside `content`:
18+
19+ ```
20+ data: {"choices":[{"delta":{"reasoning_content":"Here's a thinking process: ..."}}]}
21+ data: {"choices":[{"delta":{"content":"Quicksort is usually faster because ..."}}]}
22+ ```
23+
24+ `reasoning_content` is what Docker Model Runner, llama.cpp, vLLM and DeepSeek send; a few servers say `reasoning`. Measured on both test models against the real DMR: qwen, 300 reasoning deltas and 0 content; gemma **with tools in the request**, 247 reasoning deltas, 0 content, 0 tool calls. Nothing has to be enabled — the models that think, think.
25+
26+ ## Genkit's reasoning mode is a dead end here
27+
28+ `ai` does have `PartReasoning` / `NewReasoningPart` / `IsReasoning`, but `plugins/compat_oai` never builds one: it maps only `Usage.CompletionTokensDetails.ReasoningTokens` — a token COUNT — and its streaming loop reads `Delta.Content` and `Delta.ToolCalls`. `openai-go` v1.8.2 has no field for it on the delta either. The text has to be read off the wire.
29+
30+ ## The bug this uncovered
31+
32+ The plugin calls its chunk handler only `if len(modelChunk.Content) > 0`. `beat()` — the watchdog's heartbeat — lives in that handler. A chunk carrying nothing but reasoning beats nothing.
33+
34+ **So a model reasoning for longer than `watchdogTimeout` was killed as "stalled" while its server was streaming perfectly happily.** Measured: 38 seconds of uninterrupted reasoning on qwen3.5-2b at `max_tokens: 2000`. The default timeout is 20 s; the configurations in `./tests` say 30 s.
35+
36+ Proved before/after with a fake engine that reasons for 12 s under a 5 s watchdog:
37+
38+ | Binary | watchdog fired | thinking shown | answer produced |
39+ |---|---|---|---|
40+ | before | **yes** | no | **no** |
41+ | after | no | yes | yes |
42+
43+ This is why `showThinking: false` turns off the display **and nothing else**: the reasoning is always read, because reading it is what keeps the turn alive. Verified — with the display off, the same 12 s run still completes.
44+
45+ ## Implementation
46+
47+ `internal/engine/reasoning.go` — an `option.WithMiddleware` on the openai-go client, passed through `compat_oai.OpenAICompatible.Opts`, which is the one hook the plugin leaves open. It wraps the SSE body in `sseTap`, a **pass-through `Read`** that scans lines as they are read: no goroutine and no pipe, so the reasoning of a chunk is reported while that chunk is handed to the plugin and can never arrive after the answer it preceded. Every byte goes through untouched — the plugin sees exactly the stream it would have seen. The listener rides on the **request context** (`WithReasoning`), not a package variable, so two turns at once cannot cross their reasoning.
48+
49+ `internal/engine/thoughts.go` — the terminal block: `💭` printed once, then each fragment dim and italic (`\033[2;3m`), plain text when the output is not a styled terminal. Closed by the answer's first token or the end of the turn, whichever comes first, idempotent.
50+
51+ `Generate` registers a listener that beats **and** displays; `Summarize` registers one that only beats — a summary is written behind the user's back and must not spill its reasoning into the middle of a conversation.
52+
53+ `ui.Sink` gained `Reasoning(text)`:
54+
55+ | Front end | What it does |
56+ |---|---|
57+ | Line mode | The `💭` block above, before the `🤖` answer. |
58+ | Window (`mm -tui`) | Its own `‣ thinking` entry (`KindThinking`), dim italic purple — an aside, visibly not the answer. |
59+ | Editor (ACP) | `agent_thought_chunk`. The protocol has the concept, so this is not a rendering trick: Zed folds it under its own "Thinking" disclosure. |
60+
61+ New setting `showThinking`, default `true`, added to the five shipped YAML files. Reasoning never enters the history and never replays on `/resume` — the plugin drops it, so that is free.
62+
63+ ## Not handled, on purpose
64+
65+ Models that wrap their reasoning in `<think>` tags inside `content`. Those arrive as ordinary answer text and are shown as the answer; the two shapes need different treatment and neither test model does it. Written up as a known limit in `docs/*/explanation/thinking-models.md` rather than left to be rediscovered.
66+
67+ ## Verified
68+
69+ Unit: `internal/engine/reasoning_test.go` (9 line shapes including a half-received line and `[DONE]`; byte-for-byte pass-through; a fragment split across 7-byte reads; the endless-line cap; two concurrent turns that must not hear each other, plus one request with no listener; a non-streamed response left alone) and `internal/tui/thinking_test.go` (one entry per block and not one per fragment; thought and answer stay separate; the aside's label and style; `showThinking: false` draws nothing).
70+
71+ End to end: both `./tests` models against the real DMR — `💭` block then `🤖` answer, dim+italic escapes confirmed under `TERM=xterm-256color`; window mode on a pseudo-terminal — `‣ thinking` label and 146 italic+purple SGR runs, then the answer; the fake engine for the watchdog table above and for `showThinking: false`.
72+
73+ Quality gate: run 32 — 10 warnings, 20 smells, the project's baseline. Runs 30/31 had one more (`linesOf` reaching 6 returns with the new entry kind), cleared by giving it a single exit rather than by touching a threshold. `go test ./...`, `gofmt`, `go vet` green; all five shipped YAML files still load.
74+
75+ Docs EN+FR: new `explanation/thinking-models.md`, linked from both `docs/*/README.md`; `reference/configuration.md` (`showThinking`), `reference/cli.md` (window-mode row), `explanation/architecture.md` (`engine` row).
76+
77+ Branch `thinking`.
78+tasks:
79+ - id: 1
80+ title: "Read the reasoning off the wire: openai-go middleware, pass-through SSE tap, both field spellings"
81+ state: closed
82+ priority: high
83+ author:
84+ name: k33g
85+ email: ph.charriere@gmail.com
86+ createdAt: 2026-09-21T04:00:00.000Z
87+ updatedAt: 2026-09-21T04:30:00.000Z
88+ closedAt: 2026-09-21T04:30:00.000Z
89+ - id: 2
90+ title: "Beat the watchdog while the model reasons, so a thinking turn is not killed as stalled"
91+ state: closed
92+ priority: high
93+ author:
94+ name: k33g
95+ email: ph.charriere@gmail.com
96+ createdAt: 2026-09-21T04:00:00.000Z
97+ updatedAt: 2026-09-21T04:30:00.000Z
98+ closedAt: 2026-09-21T04:30:00.000Z
99+ - id: 3
100+ title: "Line mode: a 💭 block in dim italic before the answer"
101+ state: closed
102+ priority: medium
103+ author:
104+ name: k33g
105+ email: ph.charriere@gmail.com
106+ createdAt: 2026-09-21T04:00:00.000Z
107+ updatedAt: 2026-09-21T04:30:00.000Z
108+ closedAt: 2026-09-21T04:30:00.000Z
109+ - id: 4
110+ title: "Window mode: its own thinking entry, a different colour and italic"
111+ state: closed
112+ priority: medium
113+ author:
114+ name: k33g
115+ email: ph.charriere@gmail.com
116+ createdAt: 2026-09-21T04:00:00.000Z
117+ updatedAt: 2026-09-21T04:30:00.000Z
118+ closedAt: 2026-09-21T04:30:00.000Z
119+ - id: 5
120+ title: "ACP: agent_thought_chunk, so the editor folds it its own way"
121+ state: closed
122+ priority: none
123+ author:
124+ name: k33g
125+ email: ph.charriere@gmail.com
126+ createdAt: 2026-09-21T04:00:00.000Z
127+ updatedAt: 2026-09-21T04:30:00.000Z
128+ closedAt: 2026-09-21T04:30:00.000Z
129+ - id: 6
130+ title: "showThinking setting (display only), tests and EN+FR docs"
131+ state: closed
132+ priority: none
133+ author:
134+ name: k33g
135+ email: ph.charriere@gmail.com
136+ createdAt: 2026-09-21T04:00:00.000Z
137+ updatedAt: 2026-09-21T04:30:00.000Z
138+ closedAt: 2026-09-21T04:30:00.000Z
14139 comments: []
@@ -1,14 +1,139 @@
1 # Managed by IssueSpec. Hand edits are welcome; keep the schema valid.1 # Managed by IssueSpec. Hand edits are welcome; keep the schema valid.
2 id: 292 id: 29
3 title: thinking mode3 title: thinking mode
4-state: open4+state: closed
5 author:5 author:
6 name: k33g6 name: k33g
7 email: ph.charriere@gmail.com7 email: ph.charriere@gmail.com
8 createdAt: 2026-09-21T03:43:16.681Z8 createdAt: 2026-09-21T03:43:16.681Z
9-updatedAt: 2026-09-21T03:43:16.681Z9+updatedAt: 2026-09-21T04:30:00.000Z
10 labels: []10 labels: []
11 milestone: v0.0.311 milestone: v0.0.3
12-body: ""12+body: |
13-tasks: []13+ Some models have a thinking mode. Mini Me showed nothing at all while they thought; it should show the reasoning, in a different colour and in italic. Two configurations to test with in `./tests` (gemma-4-e2b, qwen3.5-2b), both confirmed thinking through the Docker Model Runner CLI.
14+
15+ ## What a thinking model actually sends (Claude, 2026-09-21)
16+
17+ The reasoning travels in a field of its own, never inside `content`:
18+
19+ ```
20+ data: {"choices":[{"delta":{"reasoning_content":"Here's a thinking process: ..."}}]}
21+ data: {"choices":[{"delta":{"content":"Quicksort is usually faster because ..."}}]}
22+ ```
23+
24+ `reasoning_content` is what Docker Model Runner, llama.cpp, vLLM and DeepSeek send; a few servers say `reasoning`. Measured on both test models against the real DMR: qwen, 300 reasoning deltas and 0 content; gemma **with tools in the request**, 247 reasoning deltas, 0 content, 0 tool calls. Nothing has to be enabled — the models that think, think.
25+
26+ ## Genkit's reasoning mode is a dead end here
27+
28+ `ai` does have `PartReasoning` / `NewReasoningPart` / `IsReasoning`, but `plugins/compat_oai` never builds one: it maps only `Usage.CompletionTokensDetails.ReasoningTokens` — a token COUNT — and its streaming loop reads `Delta.Content` and `Delta.ToolCalls`. `openai-go` v1.8.2 has no field for it on the delta either. The text has to be read off the wire.
29+
30+ ## The bug this uncovered
31+
32+ The plugin calls its chunk handler only `if len(modelChunk.Content) > 0`. `beat()` — the watchdog's heartbeat — lives in that handler. A chunk carrying nothing but reasoning beats nothing.
33+
34+ **So a model reasoning for longer than `watchdogTimeout` was killed as "stalled" while its server was streaming perfectly happily.** Measured: 38 seconds of uninterrupted reasoning on qwen3.5-2b at `max_tokens: 2000`. The default timeout is 20 s; the configurations in `./tests` say 30 s.
35+
36+ Proved before/after with a fake engine that reasons for 12 s under a 5 s watchdog:
37+
38+ | Binary | watchdog fired | thinking shown | answer produced |
39+ |---|---|---|---|
40+ | before | **yes** | no | **no** |
41+ | after | no | yes | yes |
42+
43+ This is why `showThinking: false` turns off the display **and nothing else**: the reasoning is always read, because reading it is what keeps the turn alive. Verified — with the display off, the same 12 s run still completes.
44+
45+ ## Implementation
46+
47+ `internal/engine/reasoning.go` — an `option.WithMiddleware` on the openai-go client, passed through `compat_oai.OpenAICompatible.Opts`, which is the one hook the plugin leaves open. It wraps the SSE body in `sseTap`, a **pass-through `Read`** that scans lines as they are read: no goroutine and no pipe, so the reasoning of a chunk is reported while that chunk is handed to the plugin and can never arrive after the answer it preceded. Every byte goes through untouched — the plugin sees exactly the stream it would have seen. The listener rides on the **request context** (`WithReasoning`), not a package variable, so two turns at once cannot cross their reasoning.
48+
49+ `internal/engine/thoughts.go` — the terminal block: `💭` printed once, then each fragment dim and italic (`\033[2;3m`), plain text when the output is not a styled terminal. Closed by the answer's first token or the end of the turn, whichever comes first, idempotent.
50+
51+ `Generate` registers a listener that beats **and** displays; `Summarize` registers one that only beats — a summary is written behind the user's back and must not spill its reasoning into the middle of a conversation.
52+
53+ `ui.Sink` gained `Reasoning(text)`:
54+
55+ | Front end | What it does |
56+ |---|---|
57+ | Line mode | The `💭` block above, before the `🤖` answer. |
58+ | Window (`mm -tui`) | Its own `‣ thinking` entry (`KindThinking`), dim italic purple — an aside, visibly not the answer. |
59+ | Editor (ACP) | `agent_thought_chunk`. The protocol has the concept, so this is not a rendering trick: Zed folds it under its own "Thinking" disclosure. |
60+
61+ New setting `showThinking`, default `true`, added to the five shipped YAML files. Reasoning never enters the history and never replays on `/resume` — the plugin drops it, so that is free.
62+
63+ ## Not handled, on purpose
64+
65+ Models that wrap their reasoning in `<think>` tags inside `content`. Those arrive as ordinary answer text and are shown as the answer; the two shapes need different treatment and neither test model does it. Written up as a known limit in `docs/*/explanation/thinking-models.md` rather than left to be rediscovered.
66+
67+ ## Verified
68+
69+ Unit: `internal/engine/reasoning_test.go` (9 line shapes including a half-received line and `[DONE]`; byte-for-byte pass-through; a fragment split across 7-byte reads; the endless-line cap; two concurrent turns that must not hear each other, plus one request with no listener; a non-streamed response left alone) and `internal/tui/thinking_test.go` (one entry per block and not one per fragment; thought and answer stay separate; the aside's label and style; `showThinking: false` draws nothing).
70+
71+ End to end: both `./tests` models against the real DMR — `💭` block then `🤖` answer, dim+italic escapes confirmed under `TERM=xterm-256color`; window mode on a pseudo-terminal — `‣ thinking` label and 146 italic+purple SGR runs, then the answer; the fake engine for the watchdog table above and for `showThinking: false`.
72+
73+ Quality gate: run 32 — 10 warnings, 20 smells, the project's baseline. Runs 30/31 had one more (`linesOf` reaching 6 returns with the new entry kind), cleared by giving it a single exit rather than by touching a threshold. `go test ./...`, `gofmt`, `go vet` green; all five shipped YAML files still load.
74+
75+ Docs EN+FR: new `explanation/thinking-models.md`, linked from both `docs/*/README.md`; `reference/configuration.md` (`showThinking`), `reference/cli.md` (window-mode row), `explanation/architecture.md` (`engine` row).
76+
77+ Branch `thinking`.
78+tasks:
79+ - id: 1
80+ title: "Read the reasoning off the wire: openai-go middleware, pass-through SSE tap, both field spellings"
81+ state: closed
82+ priority: high
83+ author:
84+ name: k33g
85+ email: ph.charriere@gmail.com
86+ createdAt: 2026-09-21T04:00:00.000Z
87+ updatedAt: 2026-09-21T04:30:00.000Z
88+ closedAt: 2026-09-21T04:30:00.000Z
89+ - id: 2
90+ title: "Beat the watchdog while the model reasons, so a thinking turn is not killed as stalled"
91+ state: closed
92+ priority: high
93+ author:
94+ name: k33g
95+ email: ph.charriere@gmail.com
96+ createdAt: 2026-09-21T04:00:00.000Z
97+ updatedAt: 2026-09-21T04:30:00.000Z
98+ closedAt: 2026-09-21T04:30:00.000Z
99+ - id: 3
100+ title: "Line mode: a 💭 block in dim italic before the answer"
101+ state: closed
102+ priority: medium
103+ author:
104+ name: k33g
105+ email: ph.charriere@gmail.com
106+ createdAt: 2026-09-21T04:00:00.000Z
107+ updatedAt: 2026-09-21T04:30:00.000Z
108+ closedAt: 2026-09-21T04:30:00.000Z
109+ - id: 4
110+ title: "Window mode: its own thinking entry, a different colour and italic"
111+ state: closed
112+ priority: medium
113+ author:
114+ name: k33g
115+ email: ph.charriere@gmail.com
116+ createdAt: 2026-09-21T04:00:00.000Z
117+ updatedAt: 2026-09-21T04:30:00.000Z
118+ closedAt: 2026-09-21T04:30:00.000Z
119+ - id: 5
120+ title: "ACP: agent_thought_chunk, so the editor folds it its own way"
121+ state: closed
122+ priority: none
123+ author:
124+ name: k33g
125+ email: ph.charriere@gmail.com
126+ createdAt: 2026-09-21T04:00:00.000Z
127+ updatedAt: 2026-09-21T04:30:00.000Z
128+ closedAt: 2026-09-21T04:30:00.000Z
129+ - id: 6
130+ title: "showThinking setting (display only), tests and EN+FR docs"
131+ state: closed
132+ priority: none
133+ author:
134+ name: k33g
135+ email: ph.charriere@gmail.com
136+ createdAt: 2026-09-21T04:00:00.000Z
137+ updatedAt: 2026-09-21T04:30:00.000Z
138+ closedAt: 2026-09-21T04:30:00.000Z
14 comments: []139 comments: []
added .tickets/issues/0032-add-prompt-flag.yaml +14 -0
new file mode 100644
@@ -0,0 +1,14 @@
1+# Managed by IssueSpec. Hand edits are welcome; keep the schema valid.
2+id: 32
3+title: Add --prompt flag
4+state: open
5+author:
6+ name: k33g
7+ email: ph.charriere@gmail.com
8+createdAt: 2026-09-21T04:47:19.089Z
9+updatedAt: 2026-09-21T04:47:19.089Z
10+labels: []
11+epic: CLI
12+body: ""
13+tasks: []
14+comments: []
new file mode 100644
@@ -0,0 +1,14 @@
1+# Managed by IssueSpec. Hand edits are welcome; keep the schema valid.
2+id: 32
3+title: Add --prompt flag
4+state: open
5+author:
6+ name: k33g
7+ email: ph.charriere@gmail.com
8+createdAt: 2026-09-21T04:47:19.089Z
9+updatedAt: 2026-09-21T04:47:19.089Z
10+labels: []
11+epic: CLI
12+body: ""
13+tasks: []
14+comments: []
added .tickets/issues/0033-invoke-another-mini-me.yaml +15 -0
new file mode 100644
@@ -0,0 +1,15 @@
1+# Managed by IssueSpec. Hand edits are welcome; keep the schema valid.
2+id: 33
3+title: Invoke (another) Mini Me
4+state: open
5+author:
6+ name: k33g
7+ email: ph.charriere@gmail.com
8+createdAt: 2026-09-21T04:48:03.596Z
9+updatedAt: 2026-09-21T04:48:35.612Z
10+labels: []
11+epic: Builtin tools
12+body: |
13+ Should I use ACP ? I think yes
14+tasks: []
15+comments: []
new file mode 100644
@@ -0,0 +1,15 @@
1+# Managed by IssueSpec. Hand edits are welcome; keep the schema valid.
2+id: 33
3+title: Invoke (another) Mini Me
4+state: open
5+author:
6+ name: k33g
7+ email: ph.charriere@gmail.com
8+createdAt: 2026-09-21T04:48:03.596Z
9+updatedAt: 2026-09-21T04:48:35.612Z
10+labels: []
11+epic: Builtin tools
12+body: |
13+ Should I use ACP ? I think yes
14+tasks: []
15+comments: []
added .tickets/issues/0034-anatolian-mini-me-s-orchestrator.yaml +14 -0
new file mode 100644
@@ -0,0 +1,14 @@
1+# Managed by IssueSpec. Hand edits are welcome; keep the schema valid.
2+id: 34
3+title: Anatolian -> Mini Me(s) Orchestrator?
4+state: open
5+author:
6+ name: k33g
7+ email: ph.charriere@gmail.com
8+createdAt: 2026-09-21T04:49:57.129Z
9+updatedAt: 2026-09-21T04:49:57.129Z
10+labels: []
11+epic: Ideas
12+body: ""
13+tasks: []
14+comments: []
new file mode 100644
@@ -0,0 +1,14 @@
1+# Managed by IssueSpec. Hand edits are welcome; keep the schema valid.
2+id: 34
3+title: Anatolian -> Mini Me(s) Orchestrator?
4+state: open
5+author:
6+ name: k33g
7+ email: ph.charriere@gmail.com
8+createdAt: 2026-09-21T04:49:57.129Z
9+updatedAt: 2026-09-21T04:49:57.129Z
10+labels: []
11+epic: Ideas
12+body: ""
13+tasks: []
14+comments: []