1st steps for a sdk #48
Merged@k33g wants to merge
feature/mini-me-as-a-sdk into mainMerged as
b044d58138c1.65 files changed
modified
.tickets/issues/0071-mini-me-as-a-sdk.yaml +10 -2 | @@ -6,7 +6,7 @@ author: | ||
| 6 | 6 | name: k33g |
| 7 | 7 | email: ph.charriere@gmail.com |
| 8 | 8 | createdAt: 2026-10-02T05:32:33.878Z |
| 9 | -updatedAt: 2026-10-22T17:07:15.939Z | |
| 9 | +updatedAt: 2026-10-26T05:00:00.000Z | |
| 10 | 10 | labels: |
| 11 | 11 | - priority::urgent |
| 12 | 12 | - kind::feature |
| @@ -103,4 +103,12 @@ body: | | ||
| 103 | 103 | - **Stability promise**: `v0` and experimental until step 4 is done? |
| 104 | 104 | - **Priority**: is headless/stream-json wanted first, as a quick win? |
| 105 | 105 | tasks: [] |
| 106 | -comments: [] | |
| 106 | +comments: | |
| 107 | + - id: 1 | |
| 108 | + author: | |
| 109 | + name: k33g | |
| 110 | + email: ph.charriere@gmail.com | |
| 111 | + createdAt: 2026-10-26T05:00:00.000Z | |
| 112 | + body: | | |
| 113 | + Update 2026-10-26: a smaller first version is proposed in ticket 139 — a public package with ONE method (`minime.Load` + `agent.Complete(ctx, prompt, minime.OnEvent(fn))`, streamed or not, cancelled by ctx, no STDIO), built as a thin façade over today's engine, the way ACP already uses it (`ui.SetActive(sink)` + `engine.Generate`). It would come BEFORE step 1 of this plan, with the same rules (own types, no Genkit in the API) so this plan can then remove the globals behind it without changing the API. | |
| 114 | + Facts that changed since 2026-10-14: `config.Cfg` is now read in 92 files; the wiring in `main.go` has grown (autoPull, skill and tool routing, struggle signals, decision models), which is one more reason to move it out of `package main` first. `ui.Sink.Allow(ctx, call)` is where an SDK tool-confirmation callback would plug in. | |
| @@ -6,7 +6,7 @@ author: | |||
| 6 | name: k33g | 6 | name: k33g |
| 7 | email: ph.charriere@gmail.com | 7 | email: ph.charriere@gmail.com |
| 8 | createdAt: 2026-10-02T05:32:33.878Z | 8 | createdAt: 2026-10-02T05:32:33.878Z |
| 9 | -updatedAt: 2026-10-22T17:07:15.939Z | 9 | +updatedAt: 2026-10-26T05:00:00.000Z |
| 10 | labels: | 10 | labels: |
| 11 | - priority::urgent | 11 | - priority::urgent |
| 12 | - kind::feature | 12 | - kind::feature |
| @@ -103,4 +103,12 @@ body: | | |||
| 103 | - **Stability promise**: `v0` and experimental until step 4 is done? | 103 | - **Stability promise**: `v0` and experimental until step 4 is done? |
| 104 | - **Priority**: is headless/stream-json wanted first, as a quick win? | 104 | - **Priority**: is headless/stream-json wanted first, as a quick win? |
| 105 | tasks: [] | 105 | tasks: [] |
| 106 | -comments: [] | 106 | +comments: |
| 107 | + - id: 1 | ||
| 108 | + author: | ||
| 109 | + name: k33g | ||
| 110 | + email: ph.charriere@gmail.com | ||
| 111 | + createdAt: 2026-10-26T05:00:00.000Z | ||
| 112 | + body: | | ||
| 113 | + Update 2026-10-26: a smaller first version is proposed in ticket 139 — a public package with ONE method (`minime.Load` + `agent.Complete(ctx, prompt, minime.OnEvent(fn))`, streamed or not, cancelled by ctx, no STDIO), built as a thin façade over today's engine, the way ACP already uses it (`ui.SetActive(sink)` + `engine.Generate`). It would come BEFORE step 1 of this plan, with the same rules (own types, no Genkit in the API) so this plan can then remove the globals behind it without changing the API. | ||
| 114 | + Facts that changed since 2026-10-14: `config.Cfg` is now read in 92 files; the wiring in `main.go` has grown (autoPull, skill and tool routing, struggle signals, decision models), which is one more reason to move it out of `package main` first. `ui.Sink.Allow(ctx, call)` is where an SDK tool-confirmation callback would plug in. | ||
modified
.tickets/issues/0105-mini-me-as-a-sdk-2.yaml +9 -2 | @@ -6,7 +6,7 @@ author: | ||
| 6 | 6 | name: k33g |
| 7 | 7 | email: ph.charriere@gmail.com |
| 8 | 8 | createdAt: 2026-10-20T07:51:18.410Z |
| 9 | -updatedAt: 2026-10-22T17:07:42.565Z | |
| 9 | +updatedAt: 2026-10-26T05:00:00.000Z | |
| 10 | 10 | labels: [] |
| 11 | 11 | epic: SDK |
| 12 | 12 | related: |
| @@ -14,4 +14,11 @@ related: | ||
| 14 | 14 | body: | |
| 15 | 15 | Check how it's possible to create a specific package that "exposes" the various methods |
| 16 | 16 | tasks: [] |
| 17 | -comments: [] | |
| 17 | +comments: | |
| 18 | + - id: 1 | |
| 19 | + author: | |
| 20 | + name: k33g | |
| 21 | + email: ph.charriere@gmail.com | |
| 22 | + createdAt: 2026-10-26T05:00:00.000Z | |
| 23 | + body: | | |
| 24 | + Answer 2026-10-26: yes, it is possible — Go forbids importing `internal/` from another module, so the package that exposes the methods must sit outside `internal/` (proposed: `minime/`) and the module needs an importable path (`rickub.com/bots-garden/mini-me`, go-get on Rickub to verify). The agent wiring now in `package main` would move to an internal package used by both the binary and this public package. The full proposal, with its first single method (`Complete`), is in ticket 139. | |
| @@ -6,7 +6,7 @@ author: | |||
| 6 | name: k33g | 6 | name: k33g |
| 7 | email: ph.charriere@gmail.com | 7 | email: ph.charriere@gmail.com |
| 8 | createdAt: 2026-10-20T07:51:18.410Z | 8 | createdAt: 2026-10-20T07:51:18.410Z |
| 9 | -updatedAt: 2026-10-22T17:07:42.565Z | 9 | +updatedAt: 2026-10-26T05:00:00.000Z |
| 10 | labels: [] | 10 | labels: [] |
| 11 | epic: SDK | 11 | epic: SDK |
| 12 | related: | 12 | related: |
| @@ -14,4 +14,11 @@ related: | |||
| 14 | body: | | 14 | body: | |
| 15 | Check how it's possible to create a specific package that "exposes" the various methods | 15 | Check how it's possible to create a specific package that "exposes" the various methods |
| 16 | tasks: [] | 16 | tasks: [] |
| 17 | -comments: [] | 17 | +comments: |
| 18 | + - id: 1 | ||
| 19 | + author: | ||
| 20 | + name: k33g | ||
| 21 | + email: ph.charriere@gmail.com | ||
| 22 | + createdAt: 2026-10-26T05:00:00.000Z | ||
| 23 | + body: | | ||
| 24 | + Answer 2026-10-26: yes, it is possible — Go forbids importing `internal/` from another module, so the package that exposes the methods must sit outside `internal/` (proposed: `minime/`) and the module needs an importable path (`rickub.com/bots-garden/mini-me`, go-get on Rickub to verify). The agent wiring now in `package main` would move to an internal package used by both the binary and this public package. The full proposal, with its first single method (`Complete`), is in ticket 139. | ||
modified
.tickets/issues/0139-some-api-ideas.yaml +134 -3 | @@ -6,13 +6,144 @@ author: | ||
| 6 | 6 | name: k33g |
| 7 | 7 | email: ph.charriere@gmail.com |
| 8 | 8 | createdAt: 2026-10-23T09:54:13.140Z |
| 9 | -updatedAt: 2026-10-23T09:57:11.218Z | |
| 10 | -labels: [] | |
| 9 | +updatedAt: 2026-10-26T05:00:00.000Z | |
| 10 | +labels: | |
| 11 | + - kind::feature | |
| 11 | 12 | epic: SDK |
| 13 | +related: | |
| 14 | + - 71 | |
| 15 | + - 105 | |
| 16 | + - 113 | |
| 12 | 17 | body: | |
| 13 | 18 | il faudrait par exemple créer une lib avec une méthode par exemple `prompt` |
| 14 | 19 | (equivalent de mm -p "hello this is my question" |
| 15 | 20 | |
| 16 | 21 | et on aurait la possibilité via le pattern go de passer les éléments necessaires (les reglages du fichiers de config, des tools, ...) |
| 17 | -tasks: [] | |
| 22 | + | |
| 23 | + --- | |
| 24 | + | |
| 25 | + ## Request (2026-10-26) | |
| 26 | + A first version of mini-me as a library with ONE method: from an agent configuration, run a completion, get the result streamed or not, and be able to cancel it. No STDIO: a Go program must not need the `mm` binary installed. In the end, mini-me should be usable both as an agent (the binary) and as a library. Nothing implemented yet: this section records the analysis and the proposals. | |
| 27 | + | |
| 28 | + ## Proposed API (the same in every proposal below) | |
| 29 | + ```go | |
| 30 | + agent, err := minime.Load(ctx, "agent.yaml") // builds the agent, runs nothing | |
| 31 | + | |
| 32 | + // not streamed | |
| 33 | + res, err := agent.Complete(ctx, "Explain this repository") | |
| 34 | + fmt.Println(res.Text) | |
| 35 | + | |
| 36 | + // streamed: an option on the same method | |
| 37 | + res, err := agent.Complete(ctx, "Explain this repository", | |
| 38 | + minime.OnEvent(func(ev minime.Event) { // Text, Thinking, ToolCall, ToolResult, Notice, Struggle | |
| 39 | + if ev.Kind == minime.Text { fmt.Print(ev.Text) } | |
| 40 | + })) | |
| 41 | + | |
| 42 | + // cancel: the Go idiom, the context | |
| 43 | + ctx, cancel := context.WithCancel(ctx) | |
| 44 | + ``` | |
| 45 | + - One method, `Complete`. `Load` only builds the agent once (reading the YAML, starting MCP / LSP servers on every call would be wasteful). | |
| 46 | + - The public types belong to `minime`, never to Genkit or `internal/` (same rule as ticket 71: Genkit changes, ticket 76). | |
| 47 | + | |
| 48 | + ## What blocks an import today (checked 2026-10-26) | |
| 49 | + | Obstacle | Where | Consequence | | |
| 50 | + |---|---|---| | |
| 51 | + | The module is named `mm` | `go.mod` | not go-gettable; would become `rickub.com/bots-garden/mini-me` — whether Rickub serves the `?go-get=1` meta tag is NOT verified | | |
| 52 | + | Every package is under `internal/` | 35 packages | another module cannot import them: a public package is needed | | |
| 53 | + | The agent's wiring is in `package main` | `main.go`: `assembleTools`, `autoPull`, skill/tool routing, hooks, struggle signals | the SDK cannot reuse it; copying it would make SDK and binary drift | | |
| 54 | + | Global state | `config.Cfg` (read in 92 files), `ui.Out`, `ui.SetActive`, `skills.Declare`, the spinner | one agent per process, one completion at a time | | |
| 55 | + | Tool confirmation is a terminal question | `yolo: false` | a library has no terminal: another rule is needed | | |
| 56 | + | |
| 57 | + ## What already fits | |
| 58 | + The editor mode (ACP) already does what the SDK needs, without STDIO for the answer itself (`internal/acp/acp.go` ~413–451): `ui.SetActive(sink)` then `engine.Generate(ctx, messages, tools)`. | |
| 59 | + - `engine.New(ctx)` opens the provider of the config (llmman, Ollama, DMR…). | |
| 60 | + - `engine.Generate` runs the whole turn (model calls, tools, hooks, watchdog, loop detector, routing) and returns the final answer plus the whole conversation. | |
| 61 | + - `ui.Sink` receives the stream: `Text`, `Reasoning`, `ToolStart` / `ToolRunning` / `ToolEnd`, and `Allow(ctx, call) bool` — the place for a tool-confirmation callback. | |
| 62 | + - Cancelling the `ctx` already stops the turn, a running `bash` included (Esc in Zed works that way). | |
| 63 | + | |
| 64 | + ## Proposals | |
| 65 | + - **A — Thin façade over the existing engine (fastest).** A public package `minime/` calls the engine as ACP does. The wiring of `main.go` moves to an internal package (`internal/assemble`), called by both `main` and the SDK. Documented limits: one agent loaded per process (`config.Load` fills the global `config.Cfg`), completions serialised under a lock (`ui.SetActive` is ONE sink for the process). Moderate cost, low risk. | |
| 66 | + - **B — Per-instance core (the real library).** Replace the global state with an `Agent` value carrying its config, sink and engine; terminal, window and editor become its clients. Several agents and completions in parallel. High cost (at least the 92 files reading `config.Cfg`), regression risk everywhere. This is step 1 of ticket 71. | |
| 67 | + - **C — A first, then B behind the same API (recommended).** v1 = the façade; since its API exposes nothing internal, the globals can then be removed package by package without changing one SDK signature, and the "one agent per process" rule disappears without breaking users. | |
| 68 | + - **D — `mm serve`, HTTP + SSE (later, for other languages).** The same `Complete` behind an HTTP server for Python / JavaScript. Needs `mm` running somewhere (a container is enough), so it does not meet "no binary" for Go — a complement, not v1. See ticket 113. | |
| 69 | + | |
| 70 | + Sketch of the façade (proposal A / C): | |
| 71 | + ```go | |
| 72 | + func Load(ctx context.Context, path string) (*Agent, error) { | |
| 73 | + config.Load(path) // fills config.Cfg (global) | |
| 74 | + e, _ := engine.New(ctx) | |
| 75 | + tools := assemble.Tools(e, …) // today in main.go → moved under internal/ | |
| 76 | + return &Agent{e: e, tools: tools, history: []*ai.Message{system}}, nil | |
| 77 | + } | |
| 78 | + | |
| 79 | + func (a *Agent) Complete(ctx context.Context, prompt string, opts ...Option) (Result, error) { | |
| 80 | + mu.Lock(); defer mu.Unlock() // one turn at a time: the sink is global | |
| 81 | + ui.SetActive(eventSink{onEvent}) // turns Text/ToolStart/… into minime.Event | |
| 82 | + defer ui.SetActive(nil) | |
| 83 | + resp, full, err := a.e.Generate(ctx, append(a.history, user(prompt)), a.tools) | |
| 84 | + a.history = full | |
| 85 | + return Result{Text: resp.Text()}, err | |
| 86 | + } | |
| 87 | + ``` | |
| 88 | + Not streamed = the same path: without `OnEvent` the sink drops the chunks and `Complete` returns the final text. | |
| 89 | + | |
| 90 | + ## Traps to handle | |
| 91 | + - The engine still writes some lines straight to `ui.Out` (the spinner, "stream cut — retrying this model call once"…): they must go to the sink or be off in SDK mode — a library never writes to its host's stdout. | |
| 92 | + - Renaming the module rewrites every import AND the `-X mm/internal/version.stamped` ldflag in `02-build-releases.sh`. | |
| 93 | + - `ask_user` and the `/ask` hand-offs (`mmConfigs` starts child `mm -acp` processes) need the binary: out of v1, or refused with a clear error. | |
| 94 | + | |
| 95 | + ## Decisions for the user | |
| 96 | + 1. Proposal: A, B, C (recommended) or D first. | |
| 97 | + 2. Does `Complete` keep the conversation between calls (like the REPL, recommended), or is each call isolated (like `mm -p`)? | |
| 98 | + 3. Tool confirmation with `yolo: false`: a callback (`OnApprove`), refusing when there is none (recommended), or the YAML only (`yolo: false` refuses whatever would ask)? | |
| 99 | + 4. Module path and package name: `rickub.com/bots-garden/mini-me` + `minime/`? | |
| 100 | +tasks: | |
| 101 | + - id: 1 | |
| 102 | + title: Decide the proposal, the history and the tool-confirmation rule | |
| 103 | + state: open | |
| 104 | + priority: none | |
| 105 | + author: | |
| 106 | + name: k33g | |
| 107 | + email: ph.charriere@gmail.com | |
| 108 | + createdAt: 2026-10-26T05:00:00.000Z | |
| 109 | + - id: 2 | |
| 110 | + title: Make the module importable (module path, go-get on Rickub) | |
| 111 | + state: open | |
| 112 | + priority: none | |
| 113 | + author: | |
| 114 | + name: k33g | |
| 115 | + email: ph.charriere@gmail.com | |
| 116 | + createdAt: 2026-10-26T05:00:00.000Z | |
| 117 | + - id: 3 | |
| 118 | + title: Move the agent wiring out of package main into an internal package | |
| 119 | + state: open | |
| 120 | + priority: none | |
| 121 | + author: | |
| 122 | + name: k33g | |
| 123 | + email: ph.charriere@gmail.com | |
| 124 | + createdAt: 2026-10-26T05:00:00.000Z | |
| 125 | + - id: 4 | |
| 126 | + title: Public package with Load + Complete (stream option, ctx cancel), unit tests and Example tests | |
| 127 | + state: open | |
| 128 | + priority: none | |
| 129 | + author: | |
| 130 | + name: k33g | |
| 131 | + email: ph.charriere@gmail.com | |
| 132 | + createdAt: 2026-10-26T05:00:00.000Z | |
| 133 | + - id: 5 | |
| 134 | + title: Engine output that bypasses the sink goes to events (no write to the host's stdout) | |
| 135 | + state: open | |
| 136 | + priority: none | |
| 137 | + author: | |
| 138 | + name: k33g | |
| 139 | + email: ph.charriere@gmail.com | |
| 140 | + createdAt: 2026-10-26T05:00:00.000Z | |
| 141 | + - id: 6 | |
| 142 | + title: Docs EN + FR (how-to embed mini-me in a Go program, API reference) | |
| 143 | + state: open | |
| 144 | + priority: none | |
| 145 | + author: | |
| 146 | + name: k33g | |
| 147 | + email: ph.charriere@gmail.com | |
| 148 | + createdAt: 2026-10-26T05:00:00.000Z | |
| 18 | 149 | comments: [] |
| @@ -6,13 +6,144 @@ author: | |||
| 6 | name: k33g | 6 | name: k33g |
| 7 | email: ph.charriere@gmail.com | 7 | email: ph.charriere@gmail.com |
| 8 | createdAt: 2026-10-23T09:54:13.140Z | 8 | createdAt: 2026-10-23T09:54:13.140Z |
| 9 | -updatedAt: 2026-10-23T09:57:11.218Z | 9 | +updatedAt: 2026-10-26T05:00:00.000Z |
| 10 | -labels: [] | 10 | +labels: |
| 11 | + - kind::feature | ||
| 11 | epic: SDK | 12 | epic: SDK |
| 13 | +related: | ||
| 14 | + - 71 | ||
| 15 | + - 105 | ||
| 16 | + - 113 | ||
| 12 | body: | | 17 | body: | |
| 13 | il faudrait par exemple créer une lib avec une méthode par exemple `prompt` | 18 | il faudrait par exemple créer une lib avec une méthode par exemple `prompt` |
| 14 | (equivalent de mm -p "hello this is my question" | 19 | (equivalent de mm -p "hello this is my question" |
| 15 | 20 | ||
| 16 | et on aurait la possibilité via le pattern go de passer les éléments necessaires (les reglages du fichiers de config, des tools, ...) | 21 | et on aurait la possibilité via le pattern go de passer les éléments necessaires (les reglages du fichiers de config, des tools, ...) |
| 17 | -tasks: [] | 22 | + |
| 23 | + --- | ||
| 24 | + | ||
| 25 | + ## Request (2026-10-26) | ||
| 26 | + A first version of mini-me as a library with ONE method: from an agent configuration, run a completion, get the result streamed or not, and be able to cancel it. No STDIO: a Go program must not need the `mm` binary installed. In the end, mini-me should be usable both as an agent (the binary) and as a library. Nothing implemented yet: this section records the analysis and the proposals. | ||
| 27 | + | ||
| 28 | + ## Proposed API (the same in every proposal below) | ||
| 29 | + ```go | ||
| 30 | + agent, err := minime.Load(ctx, "agent.yaml") // builds the agent, runs nothing | ||
| 31 | + | ||
| 32 | + // not streamed | ||
| 33 | + res, err := agent.Complete(ctx, "Explain this repository") | ||
| 34 | + fmt.Println(res.Text) | ||
| 35 | + | ||
| 36 | + // streamed: an option on the same method | ||
| 37 | + res, err := agent.Complete(ctx, "Explain this repository", | ||
| 38 | + minime.OnEvent(func(ev minime.Event) { // Text, Thinking, ToolCall, ToolResult, Notice, Struggle | ||
| 39 | + if ev.Kind == minime.Text { fmt.Print(ev.Text) } | ||
| 40 | + })) | ||
| 41 | + | ||
| 42 | + // cancel: the Go idiom, the context | ||
| 43 | + ctx, cancel := context.WithCancel(ctx) | ||
| 44 | + ``` | ||
| 45 | + - One method, `Complete`. `Load` only builds the agent once (reading the YAML, starting MCP / LSP servers on every call would be wasteful). | ||
| 46 | + - The public types belong to `minime`, never to Genkit or `internal/` (same rule as ticket 71: Genkit changes, ticket 76). | ||
| 47 | + | ||
| 48 | + ## What blocks an import today (checked 2026-10-26) | ||
| 49 | + | Obstacle | Where | Consequence | | ||
| 50 | + |---|---|---| | ||
| 51 | + | The module is named `mm` | `go.mod` | not go-gettable; would become `rickub.com/bots-garden/mini-me` — whether Rickub serves the `?go-get=1` meta tag is NOT verified | | ||
| 52 | + | Every package is under `internal/` | 35 packages | another module cannot import them: a public package is needed | | ||
| 53 | + | The agent's wiring is in `package main` | `main.go`: `assembleTools`, `autoPull`, skill/tool routing, hooks, struggle signals | the SDK cannot reuse it; copying it would make SDK and binary drift | | ||
| 54 | + | Global state | `config.Cfg` (read in 92 files), `ui.Out`, `ui.SetActive`, `skills.Declare`, the spinner | one agent per process, one completion at a time | | ||
| 55 | + | Tool confirmation is a terminal question | `yolo: false` | a library has no terminal: another rule is needed | | ||
| 56 | + | ||
| 57 | + ## What already fits | ||
| 58 | + The editor mode (ACP) already does what the SDK needs, without STDIO for the answer itself (`internal/acp/acp.go` ~413–451): `ui.SetActive(sink)` then `engine.Generate(ctx, messages, tools)`. | ||
| 59 | + - `engine.New(ctx)` opens the provider of the config (llmman, Ollama, DMR…). | ||
| 60 | + - `engine.Generate` runs the whole turn (model calls, tools, hooks, watchdog, loop detector, routing) and returns the final answer plus the whole conversation. | ||
| 61 | + - `ui.Sink` receives the stream: `Text`, `Reasoning`, `ToolStart` / `ToolRunning` / `ToolEnd`, and `Allow(ctx, call) bool` — the place for a tool-confirmation callback. | ||
| 62 | + - Cancelling the `ctx` already stops the turn, a running `bash` included (Esc in Zed works that way). | ||
| 63 | + | ||
| 64 | + ## Proposals | ||
| 65 | + - **A — Thin façade over the existing engine (fastest).** A public package `minime/` calls the engine as ACP does. The wiring of `main.go` moves to an internal package (`internal/assemble`), called by both `main` and the SDK. Documented limits: one agent loaded per process (`config.Load` fills the global `config.Cfg`), completions serialised under a lock (`ui.SetActive` is ONE sink for the process). Moderate cost, low risk. | ||
| 66 | + - **B — Per-instance core (the real library).** Replace the global state with an `Agent` value carrying its config, sink and engine; terminal, window and editor become its clients. Several agents and completions in parallel. High cost (at least the 92 files reading `config.Cfg`), regression risk everywhere. This is step 1 of ticket 71. | ||
| 67 | + - **C — A first, then B behind the same API (recommended).** v1 = the façade; since its API exposes nothing internal, the globals can then be removed package by package without changing one SDK signature, and the "one agent per process" rule disappears without breaking users. | ||
| 68 | + - **D — `mm serve`, HTTP + SSE (later, for other languages).** The same `Complete` behind an HTTP server for Python / JavaScript. Needs `mm` running somewhere (a container is enough), so it does not meet "no binary" for Go — a complement, not v1. See ticket 113. | ||
| 69 | + | ||
| 70 | + Sketch of the façade (proposal A / C): | ||
| 71 | + ```go | ||
| 72 | + func Load(ctx context.Context, path string) (*Agent, error) { | ||
| 73 | + config.Load(path) // fills config.Cfg (global) | ||
| 74 | + e, _ := engine.New(ctx) | ||
| 75 | + tools := assemble.Tools(e, …) // today in main.go → moved under internal/ | ||
| 76 | + return &Agent{e: e, tools: tools, history: []*ai.Message{system}}, nil | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + func (a *Agent) Complete(ctx context.Context, prompt string, opts ...Option) (Result, error) { | ||
| 80 | + mu.Lock(); defer mu.Unlock() // one turn at a time: the sink is global | ||
| 81 | + ui.SetActive(eventSink{onEvent}) // turns Text/ToolStart/… into minime.Event | ||
| 82 | + defer ui.SetActive(nil) | ||
| 83 | + resp, full, err := a.e.Generate(ctx, append(a.history, user(prompt)), a.tools) | ||
| 84 | + a.history = full | ||
| 85 | + return Result{Text: resp.Text()}, err | ||
| 86 | + } | ||
| 87 | + ``` | ||
| 88 | + Not streamed = the same path: without `OnEvent` the sink drops the chunks and `Complete` returns the final text. | ||
| 89 | + | ||
| 90 | + ## Traps to handle | ||
| 91 | + - The engine still writes some lines straight to `ui.Out` (the spinner, "stream cut — retrying this model call once"…): they must go to the sink or be off in SDK mode — a library never writes to its host's stdout. | ||
| 92 | + - Renaming the module rewrites every import AND the `-X mm/internal/version.stamped` ldflag in `02-build-releases.sh`. | ||
| 93 | + - `ask_user` and the `/ask` hand-offs (`mmConfigs` starts child `mm -acp` processes) need the binary: out of v1, or refused with a clear error. | ||
| 94 | + | ||
| 95 | + ## Decisions for the user | ||
| 96 | + 1. Proposal: A, B, C (recommended) or D first. | ||
| 97 | + 2. Does `Complete` keep the conversation between calls (like the REPL, recommended), or is each call isolated (like `mm -p`)? | ||
| 98 | + 3. Tool confirmation with `yolo: false`: a callback (`OnApprove`), refusing when there is none (recommended), or the YAML only (`yolo: false` refuses whatever would ask)? | ||
| 99 | + 4. Module path and package name: `rickub.com/bots-garden/mini-me` + `minime/`? | ||
| 100 | +tasks: | ||
| 101 | + - id: 1 | ||
| 102 | + title: Decide the proposal, the history and the tool-confirmation rule | ||
| 103 | + state: open | ||
| 104 | + priority: none | ||
| 105 | + author: | ||
| 106 | + name: k33g | ||
| 107 | + email: ph.charriere@gmail.com | ||
| 108 | + createdAt: 2026-10-26T05:00:00.000Z | ||
| 109 | + - id: 2 | ||
| 110 | + title: Make the module importable (module path, go-get on Rickub) | ||
| 111 | + state: open | ||
| 112 | + priority: none | ||
| 113 | + author: | ||
| 114 | + name: k33g | ||
| 115 | + email: ph.charriere@gmail.com | ||
| 116 | + createdAt: 2026-10-26T05:00:00.000Z | ||
| 117 | + - id: 3 | ||
| 118 | + title: Move the agent wiring out of package main into an internal package | ||
| 119 | + state: open | ||
| 120 | + priority: none | ||
| 121 | + author: | ||
| 122 | + name: k33g | ||
| 123 | + email: ph.charriere@gmail.com | ||
| 124 | + createdAt: 2026-10-26T05:00:00.000Z | ||
| 125 | + - id: 4 | ||
| 126 | + title: Public package with Load + Complete (stream option, ctx cancel), unit tests and Example tests | ||
| 127 | + state: open | ||
| 128 | + priority: none | ||
| 129 | + author: | ||
| 130 | + name: k33g | ||
| 131 | + email: ph.charriere@gmail.com | ||
| 132 | + createdAt: 2026-10-26T05:00:00.000Z | ||
| 133 | + - id: 5 | ||
| 134 | + title: Engine output that bypasses the sink goes to events (no write to the host's stdout) | ||
| 135 | + state: open | ||
| 136 | + priority: none | ||
| 137 | + author: | ||
| 138 | + name: k33g | ||
| 139 | + email: ph.charriere@gmail.com | ||
| 140 | + createdAt: 2026-10-26T05:00:00.000Z | ||
| 141 | + - id: 6 | ||
| 142 | + title: Docs EN + FR (how-to embed mini-me in a Go program, API reference) | ||
| 143 | + state: open | ||
| 144 | + priority: none | ||
| 145 | + author: | ||
| 146 | + name: k33g | ||
| 147 | + email: ph.charriere@gmail.com | ||
| 148 | + createdAt: 2026-10-26T05:00:00.000Z | ||
| 18 | comments: [] | 149 | comments: [] |
added
.tickets/issues/0167-node-js-client-over-acp-a-first-sdk-prototype-fo.yaml +136 -0 | new file mode 100644 | ||
| @@ -0,0 +1,136 @@ | ||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | |
| 2 | +id: 167 | |
| 3 | +title: "Node.js client over ACP: a first SDK prototype for other languages" | |
| 4 | +state: closed | |
| 5 | +author: | |
| 6 | + name: k33g | |
| 7 | + email: ph.charriere@gmail.com | |
| 8 | +createdAt: 2026-10-26T06:00:00.000Z | |
| 9 | +updatedAt: 2026-10-26T11:00:00.000Z | |
| 10 | +labels: | |
| 11 | + - kind::feature | |
| 12 | +milestone: v1.0.2 | |
| 13 | +epic: SDK | |
| 14 | +related: | |
| 15 | + - 139 | |
| 16 | + - 71 | |
| 17 | + - 113 | |
| 18 | + - 168 | |
| 19 | + - 169 | |
| 20 | + - 170 | |
| 21 | + - 171 | |
| 22 | + - 172 | |
| 23 | +body: | | |
| 24 | + ## Goal | |
| 25 | + Prove that mm can be used as a library from another language by driving `mm -acp` over ACP (JSON-RPC on stdin/stdout), with NO change to mm. Node.js first (user's choice, 2026-10-26); Python later if the prototype convinces. Prototype only: one demo folder, not a published package. | |
| 26 | + | |
| 27 | + This is the "other languages" path. It complements the Go SDK of ticket 139 (in-process, no binary) and does not replace it. Remote use (an agent in another machine or container) stays for `mm serve` (ticket 113 / proposal D of 139). | |
| 28 | + | |
| 29 | + ## Why ACP fits (checked in internal/acp and docs/en/reference/acp.md on 2026-10-26) | |
| 30 | + | Wanted | ACP, already in mm | | |
| 31 | + |---|---| | |
| 32 | + | run a completion | `session/prompt` | | |
| 33 | + | stream | `session/update` notifications (text chunks, thoughts, tool calls, plan) | | |
| 34 | + | not streamed | join the text chunks until `session/prompt` answers (its `stopReason`) | | |
| 35 | + | cancel | `session/cancel` | | |
| 36 | + | tool confirmation (`yolo: false`) | mm sends `session/request_permission` to the client → a callback | | |
| 37 | + | `ask_user` | `elicitation/create`, only when the client advertises `clientCapabilities.elicitation.form` at `initialize` | | |
| 38 | + | keep the conversation | one ACP session = one history; `session/load` / `session/resume` with `sessions.enabled` | | |
| 39 | + | |
| 40 | + - mm asks nothing else of the client: it reads files and runs commands itself, so the client does not have to implement `fs/*` or `terminal/*`. | |
| 41 | + - One agent = one process: several agents run side by side, the global state of the Go code (`config.Cfg`, `ui.SetActive`) is no problem here. | |
| 42 | + - Everything mm does works unchanged: hooks, MCP, `mmConfigs`, skill/tool routing, struggle signals. | |
| 43 | + | |
| 44 | + ## Proposed shape (to discuss in the prototype) | |
| 45 | + ```js | |
| 46 | + import { Agent } from "./minime.js"; | |
| 47 | + | |
| 48 | + const agent = await Agent.start({ | |
| 49 | + config: "agent.yaml", // passed as AGENT_CONFIG (absolute path) | |
| 50 | + cwd: process.cwd(), // the session's cwd (session/new) | |
| 51 | + onPermission: (call) => call.kind === "read", // no callback → refuse | |
| 52 | + }); | |
| 53 | + | |
| 54 | + const text = await agent.complete("Explain this repository"); // not streamed | |
| 55 | + | |
| 56 | + for await (const ev of agent.stream("And the tests?")) { // streamed | |
| 57 | + if (ev.kind === "text") process.stdout.write(ev.text); | |
| 58 | + } | |
| 59 | + | |
| 60 | + const ac = new AbortController(); | |
| 61 | + setTimeout(() => ac.abort(), 5000); | |
| 62 | + await agent.complete("A long task", { signal: ac.signal }); // abort → session/cancel | |
| 63 | + | |
| 64 | + await agent.close(); // closes stdin, mm exits | |
| 65 | + ``` | |
| 66 | + - Plain Node.js (built-in `child_process`, `readline`), no dependency, ESM, Node 22 (the sandbox has v22.22.1). | |
| 67 | + - Events reduced to a few kinds: `text`, `thinking`, `tool_call`, `tool_result`, `done` — not the editor-oriented detail of ACP (diffs, statuses). | |
| 68 | + - The `mm` binary is found on the PATH or through `MM_BIN` (the convention of the demo hooks). | |
| 69 | + - stderr of mm (banner, warnings, `[acp]` lines) is drained, optionally passed to a callback, never mixed with the answer. | |
| 70 | + - Check first whether the official ACP TypeScript library (`@agentclientprotocol/sdk` or similar — not verified, no network in the sandbox) is worth using instead of raw JSON-RPC; raw JSON-RPC keeps the prototype dependency-free. | |
| 71 | + | |
| 72 | + ## Limits to state in the README | |
| 73 | + - The `mm` binary must be installed (a later package could ship it per platform, as some npm packages do for native tools). | |
| 74 | + - Local only: stdio. | |
| 75 | + - The config is a file path (AGENT_CONFIG); no in-memory config. | |
| 76 | + | |
| 77 | + ## Done when | |
| 78 | + - `demo.acp.sdk/nodejs/` (location chosen by the user on 2026-10-26, instead of `demos/node-acp-client/`) holds `minime.js` (the client), one example each for not streamed, streamed and cancelled, and README EN + FR with the results measured on llmman. | |
| 79 | + - Permissions: a refused and an accepted tool call shown with an agent with `yolo: false`. | |
| 80 | + - Nothing changed in mm's Go code. | |
| 81 | +tasks: | |
| 82 | + - id: 1 | |
| 83 | + title: Check the official ACP TypeScript library vs raw JSON-RPC; decide | |
| 84 | + state: closed | |
| 85 | + priority: none | |
| 86 | + author: | |
| 87 | + name: k33g | |
| 88 | + email: ph.charriere@gmail.com | |
| 89 | + createdAt: 2026-10-26T06:00:00.000Z | |
| 90 | + - id: 2 | |
| 91 | + title: "minime.js: start (initialize + session/new), complete, stream, cancel, close" | |
| 92 | + state: closed | |
| 93 | + priority: none | |
| 94 | + author: | |
| 95 | + name: k33g | |
| 96 | + email: ph.charriere@gmail.com | |
| 97 | + createdAt: 2026-10-26T06:00:00.000Z | |
| 98 | + - id: 3 | |
| 99 | + title: Permission callback (session/request_permission), refuse by default | |
| 100 | + state: closed | |
| 101 | + priority: none | |
| 102 | + author: | |
| 103 | + name: k33g | |
| 104 | + email: ph.charriere@gmail.com | |
| 105 | + createdAt: 2026-10-26T06:00:00.000Z | |
| 106 | + - id: 4 | |
| 107 | + title: Examples (not streamed, streamed, cancelled, permission) run on llmman | |
| 108 | + state: closed | |
| 109 | + priority: none | |
| 110 | + author: | |
| 111 | + name: k33g | |
| 112 | + email: ph.charriere@gmail.com | |
| 113 | + createdAt: 2026-10-26T06:00:00.000Z | |
| 114 | + - id: 5 | |
| 115 | + title: README EN + FR with measured results and limits | |
| 116 | + state: closed | |
| 117 | + priority: none | |
| 118 | + author: | |
| 119 | + name: k33g | |
| 120 | + email: ph.charriere@gmail.com | |
| 121 | + createdAt: 2026-10-26T06:00:00.000Z | |
| 122 | +comments: | |
| 123 | + - id: 1 | |
| 124 | + author: | |
| 125 | + name: k33g | |
| 126 | + email: ph.charriere@gmail.com | |
| 127 | + createdAt: 2026-10-26T10:30:00.000Z | |
| 128 | + body: | | |
| 129 | + Done in `demo.acp.sdk/nodejs/` (with an index `demo.acp.sdk/README.{en,fr}.md` for the other languages, tickets 168–172). Nothing changed in mm's Go code. | |
| 130 | + | |
| 131 | + - `minime.js`, no dependency (Node >= 20): `Agent.start({config, cwd, mm, onPermission, onStderr})`, `complete(prompt, {signal, onEvent})` → `{text, stopReason, usage}`, `stream(prompt, {signal})` → async iterator (text, thinking, tool_call, tool_result, done), cancel with an `AbortSignal` (→ `session/cancel`, ends `cancelled` with the text so far; leaving the loop early cancels too), `reset()` (new session), `close()`. One Agent = one `mm -acp` process = one conversation; one turn at a time per Agent. Without `onPermission`, every call that needs a confirmation is refused. mm's JSON-RPC errors are rejected with mm's words (`data` included). | |
| 132 | + - Task 1: raw JSON-RPC chosen; the official ACP TypeScript library was NOT looked at (no network in the sandbox). | |
| 133 | + - Message shapes captured from `mm -acp` (v1.0.1) before writing: `session/update` kinds `agent_message_chunk`, `agent_thought_chunk`, `tool_call`, `tool_call_update` (rawOutput.output), `usage_update`, `available_commands_update`; `session/request_permission` with options allow / allow_always / reject / never; `session/prompt` result `{stopReason, usage}`. | |
| 134 | + - Tests: `npm test` (node --test test/minime.test.js), 11 tests against `test/fake-mm.js`, which plays mm's side (answers, a permission, a slow turn to cancel, a crash). A bare `node --test` would also run fake-mm.js and hang. | |
| 135 | + - Measured on llmman (Qwen3.5 4B, `yolo: false`): complete 2.9 s; stream with a read-only callback (`find … | wc -l` allowed, answer streamed); cancel after 3.0 s (`cancelled`, 383 characters kept; the next question works but does not know the story — mm drops an unanswered question); permission: `pwd` refused → the model invented `/root`, allowed → the real directory. Seen in three runs before `find` was allowed: the same refused command repeated until mm's loop detector stopped the turn, and `stream()` rejected with that message. | |
| 136 | + - Not done: npm package (it would have to ship the binary), `session/load` / `session/resume`, images and attachments, `ask_user` forms. | |
| new file mode 100644 | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | ||
| 2 | +id: 167 | ||
| 3 | +title: "Node.js client over ACP: a first SDK prototype for other languages" | ||
| 4 | +state: closed | ||
| 5 | +author: | ||
| 6 | + name: k33g | ||
| 7 | + email: ph.charriere@gmail.com | ||
| 8 | +createdAt: 2026-10-26T06:00:00.000Z | ||
| 9 | +updatedAt: 2026-10-26T11:00:00.000Z | ||
| 10 | +labels: | ||
| 11 | + - kind::feature | ||
| 12 | +milestone: v1.0.2 | ||
| 13 | +epic: SDK | ||
| 14 | +related: | ||
| 15 | + - 139 | ||
| 16 | + - 71 | ||
| 17 | + - 113 | ||
| 18 | + - 168 | ||
| 19 | + - 169 | ||
| 20 | + - 170 | ||
| 21 | + - 171 | ||
| 22 | + - 172 | ||
| 23 | +body: | | ||
| 24 | + ## Goal | ||
| 25 | + Prove that mm can be used as a library from another language by driving `mm -acp` over ACP (JSON-RPC on stdin/stdout), with NO change to mm. Node.js first (user's choice, 2026-10-26); Python later if the prototype convinces. Prototype only: one demo folder, not a published package. | ||
| 26 | + | ||
| 27 | + This is the "other languages" path. It complements the Go SDK of ticket 139 (in-process, no binary) and does not replace it. Remote use (an agent in another machine or container) stays for `mm serve` (ticket 113 / proposal D of 139). | ||
| 28 | + | ||
| 29 | + ## Why ACP fits (checked in internal/acp and docs/en/reference/acp.md on 2026-10-26) | ||
| 30 | + | Wanted | ACP, already in mm | | ||
| 31 | + |---|---| | ||
| 32 | + | run a completion | `session/prompt` | | ||
| 33 | + | stream | `session/update` notifications (text chunks, thoughts, tool calls, plan) | | ||
| 34 | + | not streamed | join the text chunks until `session/prompt` answers (its `stopReason`) | | ||
| 35 | + | cancel | `session/cancel` | | ||
| 36 | + | tool confirmation (`yolo: false`) | mm sends `session/request_permission` to the client → a callback | | ||
| 37 | + | `ask_user` | `elicitation/create`, only when the client advertises `clientCapabilities.elicitation.form` at `initialize` | | ||
| 38 | + | keep the conversation | one ACP session = one history; `session/load` / `session/resume` with `sessions.enabled` | | ||
| 39 | + | ||
| 40 | + - mm asks nothing else of the client: it reads files and runs commands itself, so the client does not have to implement `fs/*` or `terminal/*`. | ||
| 41 | + - One agent = one process: several agents run side by side, the global state of the Go code (`config.Cfg`, `ui.SetActive`) is no problem here. | ||
| 42 | + - Everything mm does works unchanged: hooks, MCP, `mmConfigs`, skill/tool routing, struggle signals. | ||
| 43 | + | ||
| 44 | + ## Proposed shape (to discuss in the prototype) | ||
| 45 | + ```js | ||
| 46 | + import { Agent } from "./minime.js"; | ||
| 47 | + | ||
| 48 | + const agent = await Agent.start({ | ||
| 49 | + config: "agent.yaml", // passed as AGENT_CONFIG (absolute path) | ||
| 50 | + cwd: process.cwd(), // the session's cwd (session/new) | ||
| 51 | + onPermission: (call) => call.kind === "read", // no callback → refuse | ||
| 52 | + }); | ||
| 53 | + | ||
| 54 | + const text = await agent.complete("Explain this repository"); // not streamed | ||
| 55 | + | ||
| 56 | + for await (const ev of agent.stream("And the tests?")) { // streamed | ||
| 57 | + if (ev.kind === "text") process.stdout.write(ev.text); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + const ac = new AbortController(); | ||
| 61 | + setTimeout(() => ac.abort(), 5000); | ||
| 62 | + await agent.complete("A long task", { signal: ac.signal }); // abort → session/cancel | ||
| 63 | + | ||
| 64 | + await agent.close(); // closes stdin, mm exits | ||
| 65 | + ``` | ||
| 66 | + - Plain Node.js (built-in `child_process`, `readline`), no dependency, ESM, Node 22 (the sandbox has v22.22.1). | ||
| 67 | + - Events reduced to a few kinds: `text`, `thinking`, `tool_call`, `tool_result`, `done` — not the editor-oriented detail of ACP (diffs, statuses). | ||
| 68 | + - The `mm` binary is found on the PATH or through `MM_BIN` (the convention of the demo hooks). | ||
| 69 | + - stderr of mm (banner, warnings, `[acp]` lines) is drained, optionally passed to a callback, never mixed with the answer. | ||
| 70 | + - Check first whether the official ACP TypeScript library (`@agentclientprotocol/sdk` or similar — not verified, no network in the sandbox) is worth using instead of raw JSON-RPC; raw JSON-RPC keeps the prototype dependency-free. | ||
| 71 | + | ||
| 72 | + ## Limits to state in the README | ||
| 73 | + - The `mm` binary must be installed (a later package could ship it per platform, as some npm packages do for native tools). | ||
| 74 | + - Local only: stdio. | ||
| 75 | + - The config is a file path (AGENT_CONFIG); no in-memory config. | ||
| 76 | + | ||
| 77 | + ## Done when | ||
| 78 | + - `demo.acp.sdk/nodejs/` (location chosen by the user on 2026-10-26, instead of `demos/node-acp-client/`) holds `minime.js` (the client), one example each for not streamed, streamed and cancelled, and README EN + FR with the results measured on llmman. | ||
| 79 | + - Permissions: a refused and an accepted tool call shown with an agent with `yolo: false`. | ||
| 80 | + - Nothing changed in mm's Go code. | ||
| 81 | +tasks: | ||
| 82 | + - id: 1 | ||
| 83 | + title: Check the official ACP TypeScript library vs raw JSON-RPC; decide | ||
| 84 | + state: closed | ||
| 85 | + priority: none | ||
| 86 | + author: | ||
| 87 | + name: k33g | ||
| 88 | + email: ph.charriere@gmail.com | ||
| 89 | + createdAt: 2026-10-26T06:00:00.000Z | ||
| 90 | + - id: 2 | ||
| 91 | + title: "minime.js: start (initialize + session/new), complete, stream, cancel, close" | ||
| 92 | + state: closed | ||
| 93 | + priority: none | ||
| 94 | + author: | ||
| 95 | + name: k33g | ||
| 96 | + email: ph.charriere@gmail.com | ||
| 97 | + createdAt: 2026-10-26T06:00:00.000Z | ||
| 98 | + - id: 3 | ||
| 99 | + title: Permission callback (session/request_permission), refuse by default | ||
| 100 | + state: closed | ||
| 101 | + priority: none | ||
| 102 | + author: | ||
| 103 | + name: k33g | ||
| 104 | + email: ph.charriere@gmail.com | ||
| 105 | + createdAt: 2026-10-26T06:00:00.000Z | ||
| 106 | + - id: 4 | ||
| 107 | + title: Examples (not streamed, streamed, cancelled, permission) run on llmman | ||
| 108 | + state: closed | ||
| 109 | + priority: none | ||
| 110 | + author: | ||
| 111 | + name: k33g | ||
| 112 | + email: ph.charriere@gmail.com | ||
| 113 | + createdAt: 2026-10-26T06:00:00.000Z | ||
| 114 | + - id: 5 | ||
| 115 | + title: README EN + FR with measured results and limits | ||
| 116 | + state: closed | ||
| 117 | + priority: none | ||
| 118 | + author: | ||
| 119 | + name: k33g | ||
| 120 | + email: ph.charriere@gmail.com | ||
| 121 | + createdAt: 2026-10-26T06:00:00.000Z | ||
| 122 | +comments: | ||
| 123 | + - id: 1 | ||
| 124 | + author: | ||
| 125 | + name: k33g | ||
| 126 | + email: ph.charriere@gmail.com | ||
| 127 | + createdAt: 2026-10-26T10:30:00.000Z | ||
| 128 | + body: | | ||
| 129 | + Done in `demo.acp.sdk/nodejs/` (with an index `demo.acp.sdk/README.{en,fr}.md` for the other languages, tickets 168–172). Nothing changed in mm's Go code. | ||
| 130 | + | ||
| 131 | + - `minime.js`, no dependency (Node >= 20): `Agent.start({config, cwd, mm, onPermission, onStderr})`, `complete(prompt, {signal, onEvent})` → `{text, stopReason, usage}`, `stream(prompt, {signal})` → async iterator (text, thinking, tool_call, tool_result, done), cancel with an `AbortSignal` (→ `session/cancel`, ends `cancelled` with the text so far; leaving the loop early cancels too), `reset()` (new session), `close()`. One Agent = one `mm -acp` process = one conversation; one turn at a time per Agent. Without `onPermission`, every call that needs a confirmation is refused. mm's JSON-RPC errors are rejected with mm's words (`data` included). | ||
| 132 | + - Task 1: raw JSON-RPC chosen; the official ACP TypeScript library was NOT looked at (no network in the sandbox). | ||
| 133 | + - Message shapes captured from `mm -acp` (v1.0.1) before writing: `session/update` kinds `agent_message_chunk`, `agent_thought_chunk`, `tool_call`, `tool_call_update` (rawOutput.output), `usage_update`, `available_commands_update`; `session/request_permission` with options allow / allow_always / reject / never; `session/prompt` result `{stopReason, usage}`. | ||
| 134 | + - Tests: `npm test` (node --test test/minime.test.js), 11 tests against `test/fake-mm.js`, which plays mm's side (answers, a permission, a slow turn to cancel, a crash). A bare `node --test` would also run fake-mm.js and hang. | ||
| 135 | + - Measured on llmman (Qwen3.5 4B, `yolo: false`): complete 2.9 s; stream with a read-only callback (`find … | wc -l` allowed, answer streamed); cancel after 3.0 s (`cancelled`, 383 characters kept; the next question works but does not know the story — mm drops an unanswered question); permission: `pwd` refused → the model invented `/root`, allowed → the real directory. Seen in three runs before `find` was allowed: the same refused command repeated until mm's loop detector stopped the turn, and `stream()` rejected with that message. | ||
| 136 | + - Not done: npm package (it would have to ship the binary), `session/load` / `session/resume`, images and attachments, `ask_user` forms. | ||
added
.tickets/issues/0168-go-client-over-acp-sdk-prototype.yaml +72 -0 | new file mode 100644 | ||
| @@ -0,0 +1,72 @@ | ||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | |
| 2 | +id: 168 | |
| 3 | +title: "Go client over ACP: an out-of-process SDK prototype" | |
| 4 | +state: open | |
| 5 | +author: | |
| 6 | + name: k33g | |
| 7 | + email: ph.charriere@gmail.com | |
| 8 | +createdAt: 2026-10-26T06:30:00.000Z | |
| 9 | +updatedAt: 2026-10-26T06:30:00.000Z | |
| 10 | +labels: | |
| 11 | + - kind::feature | |
| 12 | +epic: SDK | |
| 13 | +related: | |
| 14 | + - 167 | |
| 15 | + - 139 | |
| 16 | +body: | | |
| 17 | + ## Goal | |
| 18 | + A Go program drives `mm -acp` as a child process. Unlike ticket 139 (in-process library, no binary), this one needs the binary but works TODAY with no change to mm, and gives process isolation (one agent per process, so the global state of mm's Go code is no problem). | |
| 19 | + | |
| 20 | + ## Shared ground | |
| 21 | + Same idea as ticket 167 (Node.js), in another language: drive `mm -acp` over ACP (JSON-RPC, one message per line on stdin/stdout), with NO change to mm. Ticket 167 holds the ACP mapping (`initialize`, `session/new`, `session/prompt`, `session/update`, `session/cancel`, `session/request_permission`, `elicitation/create`) and the limits (the `mm` binary must be installed, local stdio only, config by `AGENT_CONFIG`). The same API as 167: start an agent from a config file, `complete` (not streamed), `stream`, cancel, a permission callback that refuses by default, close. Start once the Node.js prototype has settled the API. | |
| 22 | + | |
| 23 | + ## Specific to Go | |
| 24 | + - mm already IS such a client: `internal/ask` (pool.go, client.go, elicit.go) starts child `mm -acp` processes with `github.com/coder/acp-go-sdk` (`sdk.NewClientSideConnection`) for `/ask`, `ask_agent` and `mm --ask`. The prototype can follow that code — but as its own module under `demos/` (like `demos/decision-chat-golang/hooks/ask-decider`), since `internal/` cannot be imported. | |
| 25 | + - API in Go idiom: `agent.Complete(ctx, prompt, opts...)`, streaming through a callback or an `iter.Seq2[Event, error]`, cancellation by `ctx` (→ `session/cancel`). | |
| 26 | + - Worth writing down in the README: when to choose this (isolation, works now) vs ticket 139 (no binary, in-process). | |
| 27 | + | |
| 28 | + ## Done when | |
| 29 | + - `demos/go-acp-client/` holds the client, one example each for not streamed, streamed, cancelled and a permission (agent with `yolo: false`), and README EN + FR with results measured on llmman. The module has `go test`-ed parsing of the events (no model needed). | |
| 30 | + - Nothing changed in mm's Go code. | |
| 31 | +tasks: | |
| 32 | + - id: 1 | |
| 33 | + title: "Decide: reuse acp-go-sdk (as internal/ask does) — expected — or raw JSON-RPC" | |
| 34 | + state: open | |
| 35 | + priority: none | |
| 36 | + author: | |
| 37 | + name: k33g | |
| 38 | + email: ph.charriere@gmail.com | |
| 39 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 40 | + - id: 2 | |
| 41 | + title: "Client: start (initialize + session/new), complete, stream, cancel, close" | |
| 42 | + state: open | |
| 43 | + priority: none | |
| 44 | + author: | |
| 45 | + name: k33g | |
| 46 | + email: ph.charriere@gmail.com | |
| 47 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 48 | + - id: 3 | |
| 49 | + title: "Permission callback (session/request_permission), refuse by default" | |
| 50 | + state: open | |
| 51 | + priority: none | |
| 52 | + author: | |
| 53 | + name: k33g | |
| 54 | + email: ph.charriere@gmail.com | |
| 55 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 56 | + - id: 4 | |
| 57 | + title: "Examples (not streamed, streamed, cancelled, permission) run on llmman" | |
| 58 | + state: open | |
| 59 | + priority: none | |
| 60 | + author: | |
| 61 | + name: k33g | |
| 62 | + email: ph.charriere@gmail.com | |
| 63 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 64 | + - id: 5 | |
| 65 | + title: "README EN + FR with measured results and limits" | |
| 66 | + state: open | |
| 67 | + priority: none | |
| 68 | + author: | |
| 69 | + name: k33g | |
| 70 | + email: ph.charriere@gmail.com | |
| 71 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 72 | +comments: [] | |
| new file mode 100644 | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | ||
| 2 | +id: 168 | ||
| 3 | +title: "Go client over ACP: an out-of-process SDK prototype" | ||
| 4 | +state: open | ||
| 5 | +author: | ||
| 6 | + name: k33g | ||
| 7 | + email: ph.charriere@gmail.com | ||
| 8 | +createdAt: 2026-10-26T06:30:00.000Z | ||
| 9 | +updatedAt: 2026-10-26T06:30:00.000Z | ||
| 10 | +labels: | ||
| 11 | + - kind::feature | ||
| 12 | +epic: SDK | ||
| 13 | +related: | ||
| 14 | + - 167 | ||
| 15 | + - 139 | ||
| 16 | +body: | | ||
| 17 | + ## Goal | ||
| 18 | + A Go program drives `mm -acp` as a child process. Unlike ticket 139 (in-process library, no binary), this one needs the binary but works TODAY with no change to mm, and gives process isolation (one agent per process, so the global state of mm's Go code is no problem). | ||
| 19 | + | ||
| 20 | + ## Shared ground | ||
| 21 | + Same idea as ticket 167 (Node.js), in another language: drive `mm -acp` over ACP (JSON-RPC, one message per line on stdin/stdout), with NO change to mm. Ticket 167 holds the ACP mapping (`initialize`, `session/new`, `session/prompt`, `session/update`, `session/cancel`, `session/request_permission`, `elicitation/create`) and the limits (the `mm` binary must be installed, local stdio only, config by `AGENT_CONFIG`). The same API as 167: start an agent from a config file, `complete` (not streamed), `stream`, cancel, a permission callback that refuses by default, close. Start once the Node.js prototype has settled the API. | ||
| 22 | + | ||
| 23 | + ## Specific to Go | ||
| 24 | + - mm already IS such a client: `internal/ask` (pool.go, client.go, elicit.go) starts child `mm -acp` processes with `github.com/coder/acp-go-sdk` (`sdk.NewClientSideConnection`) for `/ask`, `ask_agent` and `mm --ask`. The prototype can follow that code — but as its own module under `demos/` (like `demos/decision-chat-golang/hooks/ask-decider`), since `internal/` cannot be imported. | ||
| 25 | + - API in Go idiom: `agent.Complete(ctx, prompt, opts...)`, streaming through a callback or an `iter.Seq2[Event, error]`, cancellation by `ctx` (→ `session/cancel`). | ||
| 26 | + - Worth writing down in the README: when to choose this (isolation, works now) vs ticket 139 (no binary, in-process). | ||
| 27 | + | ||
| 28 | + ## Done when | ||
| 29 | + - `demos/go-acp-client/` holds the client, one example each for not streamed, streamed, cancelled and a permission (agent with `yolo: false`), and README EN + FR with results measured on llmman. The module has `go test`-ed parsing of the events (no model needed). | ||
| 30 | + - Nothing changed in mm's Go code. | ||
| 31 | +tasks: | ||
| 32 | + - id: 1 | ||
| 33 | + title: "Decide: reuse acp-go-sdk (as internal/ask does) — expected — or raw JSON-RPC" | ||
| 34 | + state: open | ||
| 35 | + priority: none | ||
| 36 | + author: | ||
| 37 | + name: k33g | ||
| 38 | + email: ph.charriere@gmail.com | ||
| 39 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 40 | + - id: 2 | ||
| 41 | + title: "Client: start (initialize + session/new), complete, stream, cancel, close" | ||
| 42 | + state: open | ||
| 43 | + priority: none | ||
| 44 | + author: | ||
| 45 | + name: k33g | ||
| 46 | + email: ph.charriere@gmail.com | ||
| 47 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 48 | + - id: 3 | ||
| 49 | + title: "Permission callback (session/request_permission), refuse by default" | ||
| 50 | + state: open | ||
| 51 | + priority: none | ||
| 52 | + author: | ||
| 53 | + name: k33g | ||
| 54 | + email: ph.charriere@gmail.com | ||
| 55 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 56 | + - id: 4 | ||
| 57 | + title: "Examples (not streamed, streamed, cancelled, permission) run on llmman" | ||
| 58 | + state: open | ||
| 59 | + priority: none | ||
| 60 | + author: | ||
| 61 | + name: k33g | ||
| 62 | + email: ph.charriere@gmail.com | ||
| 63 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 64 | + - id: 5 | ||
| 65 | + title: "README EN + FR with measured results and limits" | ||
| 66 | + state: open | ||
| 67 | + priority: none | ||
| 68 | + author: | ||
| 69 | + name: k33g | ||
| 70 | + email: ph.charriere@gmail.com | ||
| 71 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 72 | +comments: [] | ||
added
.tickets/issues/0169-python-client-over-acp-sdk-prototype.yaml +73 -0 | new file mode 100644 | ||
| @@ -0,0 +1,73 @@ | ||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | |
| 2 | +id: 169 | |
| 3 | +title: "Python client over ACP: an SDK prototype" | |
| 4 | +state: open | |
| 5 | +author: | |
| 6 | + name: k33g | |
| 7 | + email: ph.charriere@gmail.com | |
| 8 | +createdAt: 2026-10-26T06:30:00.000Z | |
| 9 | +updatedAt: 2026-10-26T06:30:00.000Z | |
| 10 | +labels: | |
| 11 | + - kind::feature | |
| 12 | +epic: SDK | |
| 13 | +related: | |
| 14 | + - 167 | |
| 15 | + - 139 | |
| 16 | +body: | | |
| 17 | + ## Goal | |
| 18 | + A Python program uses mm as a library by driving `mm -acp`. The first language asked for (2026-10-26), started after the Node.js prototype (ticket 167). | |
| 19 | + | |
| 20 | + ## Shared ground | |
| 21 | + Same idea as ticket 167 (Node.js), in another language: drive `mm -acp` over ACP (JSON-RPC, one message per line on stdin/stdout), with NO change to mm. Ticket 167 holds the ACP mapping (`initialize`, `session/new`, `session/prompt`, `session/update`, `session/cancel`, `session/request_permission`, `elicitation/create`) and the limits (the `mm` binary must be installed, local stdio only, config by `AGENT_CONFIG`). The same API as 167: start an agent from a config file, `complete` (not streamed), `stream`, cancel, a permission callback that refuses by default, close. Start once the Node.js prototype has settled the API. | |
| 22 | + | |
| 23 | + ## Specific to Python | |
| 24 | + - `asyncio` with the standard library only (`asyncio.create_subprocess_exec`, stdin/stdout streams, `json`): no dependency. Python 3.14 is in the sandbox. | |
| 25 | + - API: `async with minime.Agent("agent.yaml", cwd=".", on_permission=...) as agent`, `await agent.complete(...)`, `async for ev in agent.stream(...)`, cancellation by cancelling the task (→ `session/cancel`). | |
| 26 | + - Check whether an official ACP Python library exists (`agent-client-protocol` on PyPI is believed to — NOT verified, no network in the sandbox) and whether it is worth a dependency. | |
| 27 | + - Later, if it becomes a package: ship the `mm` binary inside per-platform wheels (as ruff or uv do) so `pip install` is enough. | |
| 28 | + | |
| 29 | + ## Done when | |
| 30 | + - `demos/python-acp-client/` holds the client, one example each for not streamed, streamed, cancelled and a permission (agent with `yolo: false`), and README EN + FR with results measured on llmman. | |
| 31 | + - Nothing changed in mm's Go code. | |
| 32 | +tasks: | |
| 33 | + - id: 1 | |
| 34 | + title: "Check the official ACP Python library vs stdlib JSON-RPC; decide" | |
| 35 | + state: open | |
| 36 | + priority: none | |
| 37 | + author: | |
| 38 | + name: k33g | |
| 39 | + email: ph.charriere@gmail.com | |
| 40 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 41 | + - id: 2 | |
| 42 | + title: "Client: start (initialize + session/new), complete, stream, cancel, close" | |
| 43 | + state: open | |
| 44 | + priority: none | |
| 45 | + author: | |
| 46 | + name: k33g | |
| 47 | + email: ph.charriere@gmail.com | |
| 48 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 49 | + - id: 3 | |
| 50 | + title: "Permission callback (session/request_permission), refuse by default" | |
| 51 | + state: open | |
| 52 | + priority: none | |
| 53 | + author: | |
| 54 | + name: k33g | |
| 55 | + email: ph.charriere@gmail.com | |
| 56 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 57 | + - id: 4 | |
| 58 | + title: "Examples (not streamed, streamed, cancelled, permission) run on llmman" | |
| 59 | + state: open | |
| 60 | + priority: none | |
| 61 | + author: | |
| 62 | + name: k33g | |
| 63 | + email: ph.charriere@gmail.com | |
| 64 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 65 | + - id: 5 | |
| 66 | + title: "README EN + FR with measured results and limits" | |
| 67 | + state: open | |
| 68 | + priority: none | |
| 69 | + author: | |
| 70 | + name: k33g | |
| 71 | + email: ph.charriere@gmail.com | |
| 72 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 73 | +comments: [] | |
| new file mode 100644 | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | ||
| 2 | +id: 169 | ||
| 3 | +title: "Python client over ACP: an SDK prototype" | ||
| 4 | +state: open | ||
| 5 | +author: | ||
| 6 | + name: k33g | ||
| 7 | + email: ph.charriere@gmail.com | ||
| 8 | +createdAt: 2026-10-26T06:30:00.000Z | ||
| 9 | +updatedAt: 2026-10-26T06:30:00.000Z | ||
| 10 | +labels: | ||
| 11 | + - kind::feature | ||
| 12 | +epic: SDK | ||
| 13 | +related: | ||
| 14 | + - 167 | ||
| 15 | + - 139 | ||
| 16 | +body: | | ||
| 17 | + ## Goal | ||
| 18 | + A Python program uses mm as a library by driving `mm -acp`. The first language asked for (2026-10-26), started after the Node.js prototype (ticket 167). | ||
| 19 | + | ||
| 20 | + ## Shared ground | ||
| 21 | + Same idea as ticket 167 (Node.js), in another language: drive `mm -acp` over ACP (JSON-RPC, one message per line on stdin/stdout), with NO change to mm. Ticket 167 holds the ACP mapping (`initialize`, `session/new`, `session/prompt`, `session/update`, `session/cancel`, `session/request_permission`, `elicitation/create`) and the limits (the `mm` binary must be installed, local stdio only, config by `AGENT_CONFIG`). The same API as 167: start an agent from a config file, `complete` (not streamed), `stream`, cancel, a permission callback that refuses by default, close. Start once the Node.js prototype has settled the API. | ||
| 22 | + | ||
| 23 | + ## Specific to Python | ||
| 24 | + - `asyncio` with the standard library only (`asyncio.create_subprocess_exec`, stdin/stdout streams, `json`): no dependency. Python 3.14 is in the sandbox. | ||
| 25 | + - API: `async with minime.Agent("agent.yaml", cwd=".", on_permission=...) as agent`, `await agent.complete(...)`, `async for ev in agent.stream(...)`, cancellation by cancelling the task (→ `session/cancel`). | ||
| 26 | + - Check whether an official ACP Python library exists (`agent-client-protocol` on PyPI is believed to — NOT verified, no network in the sandbox) and whether it is worth a dependency. | ||
| 27 | + - Later, if it becomes a package: ship the `mm` binary inside per-platform wheels (as ruff or uv do) so `pip install` is enough. | ||
| 28 | + | ||
| 29 | + ## Done when | ||
| 30 | + - `demos/python-acp-client/` holds the client, one example each for not streamed, streamed, cancelled and a permission (agent with `yolo: false`), and README EN + FR with results measured on llmman. | ||
| 31 | + - Nothing changed in mm's Go code. | ||
| 32 | +tasks: | ||
| 33 | + - id: 1 | ||
| 34 | + title: "Check the official ACP Python library vs stdlib JSON-RPC; decide" | ||
| 35 | + state: open | ||
| 36 | + priority: none | ||
| 37 | + author: | ||
| 38 | + name: k33g | ||
| 39 | + email: ph.charriere@gmail.com | ||
| 40 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 41 | + - id: 2 | ||
| 42 | + title: "Client: start (initialize + session/new), complete, stream, cancel, close" | ||
| 43 | + state: open | ||
| 44 | + priority: none | ||
| 45 | + author: | ||
| 46 | + name: k33g | ||
| 47 | + email: ph.charriere@gmail.com | ||
| 48 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 49 | + - id: 3 | ||
| 50 | + title: "Permission callback (session/request_permission), refuse by default" | ||
| 51 | + state: open | ||
| 52 | + priority: none | ||
| 53 | + author: | ||
| 54 | + name: k33g | ||
| 55 | + email: ph.charriere@gmail.com | ||
| 56 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 57 | + - id: 4 | ||
| 58 | + title: "Examples (not streamed, streamed, cancelled, permission) run on llmman" | ||
| 59 | + state: open | ||
| 60 | + priority: none | ||
| 61 | + author: | ||
| 62 | + name: k33g | ||
| 63 | + email: ph.charriere@gmail.com | ||
| 64 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 65 | + - id: 5 | ||
| 66 | + title: "README EN + FR with measured results and limits" | ||
| 67 | + state: open | ||
| 68 | + priority: none | ||
| 69 | + author: | ||
| 70 | + name: k33g | ||
| 71 | + email: ph.charriere@gmail.com | ||
| 72 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 73 | +comments: [] | ||
added
.tickets/issues/0170-rust-client-over-acp-sdk-prototype.yaml +80 -0 | new file mode 100644 | ||
| @@ -0,0 +1,80 @@ | ||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | |
| 2 | +id: 170 | |
| 3 | +title: "Rust client over ACP: an SDK prototype" | |
| 4 | +state: open | |
| 5 | +author: | |
| 6 | + name: k33g | |
| 7 | + email: ph.charriere@gmail.com | |
| 8 | +createdAt: 2026-10-26T06:30:00.000Z | |
| 9 | +updatedAt: 2026-10-26T06:30:00.000Z | |
| 10 | +labels: | |
| 11 | + - kind::feature | |
| 12 | +epic: SDK | |
| 13 | +related: | |
| 14 | + - 167 | |
| 15 | + - 139 | |
| 16 | +body: | | |
| 17 | + ## Goal | |
| 18 | + A Rust program uses mm as a library by driving `mm -acp`. | |
| 19 | + | |
| 20 | + ## Shared ground | |
| 21 | + Same idea as ticket 167 (Node.js), in another language: drive `mm -acp` over ACP (JSON-RPC, one message per line on stdin/stdout), with NO change to mm. Ticket 167 holds the ACP mapping (`initialize`, `session/new`, `session/prompt`, `session/update`, `session/cancel`, `session/request_permission`, `elicitation/create`) and the limits (the `mm` binary must be installed, local stdio only, config by `AGENT_CONFIG`). The same API as 167: start an agent from a config file, `complete` (not streamed), `stream`, cancel, a permission callback that refuses by default, close. Start once the Node.js prototype has settled the API. | |
| 22 | + | |
| 23 | + ## Specific to Rust | |
| 24 | + - ACP comes from Zed, written in Rust: a Rust crate for the protocol (`agent-client-protocol`) is believed to exist — NOT verified (no network in the sandbox). Otherwise `tokio` (`tokio::process::Command`, line-delimited JSON) + `serde_json`. | |
| 25 | + - API: `Agent::start(config).await?`, `agent.complete(prompt).await?`, `agent.stream(prompt)` returning a `Stream<Item = Result<Event>>`, cancellation by dropping the future or a `CancellationToken` (→ `session/cancel`). | |
| 26 | + - No Rust toolchain in this sandbox (`cargo` absent): it must be installed, or the prototype built and run on the user's machine. | |
| 27 | + | |
| 28 | + ## Done when | |
| 29 | + - `demos/rust-acp-client/` holds the client, one example each for not streamed, streamed, cancelled and a permission (agent with `yolo: false`), and README EN + FR with results measured on llmman. | |
| 30 | + - Nothing changed in mm's Go code. | |
| 31 | +tasks: | |
| 32 | + - id: 1 | |
| 33 | + title: "Check the Rust ACP crate vs tokio + serde_json; decide" | |
| 34 | + state: open | |
| 35 | + priority: none | |
| 36 | + author: | |
| 37 | + name: k33g | |
| 38 | + email: ph.charriere@gmail.com | |
| 39 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 40 | + - id: 2 | |
| 41 | + title: "Get a Rust toolchain (sandbox kit or the user's machine)" | |
| 42 | + state: open | |
| 43 | + priority: none | |
| 44 | + author: | |
| 45 | + name: k33g | |
| 46 | + email: ph.charriere@gmail.com | |
| 47 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 48 | + - id: 3 | |
| 49 | + title: "Client: start (initialize + session/new), complete, stream, cancel, close" | |
| 50 | + state: open | |
| 51 | + priority: none | |
| 52 | + author: | |
| 53 | + name: k33g | |
| 54 | + email: ph.charriere@gmail.com | |
| 55 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 56 | + - id: 4 | |
| 57 | + title: "Permission callback (session/request_permission), refuse by default" | |
| 58 | + state: open | |
| 59 | + priority: none | |
| 60 | + author: | |
| 61 | + name: k33g | |
| 62 | + email: ph.charriere@gmail.com | |
| 63 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 64 | + - id: 5 | |
| 65 | + title: "Examples (not streamed, streamed, cancelled, permission) run on llmman" | |
| 66 | + state: open | |
| 67 | + priority: none | |
| 68 | + author: | |
| 69 | + name: k33g | |
| 70 | + email: ph.charriere@gmail.com | |
| 71 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 72 | + - id: 6 | |
| 73 | + title: "README EN + FR with measured results and limits" | |
| 74 | + state: open | |
| 75 | + priority: none | |
| 76 | + author: | |
| 77 | + name: k33g | |
| 78 | + email: ph.charriere@gmail.com | |
| 79 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 80 | +comments: [] | |
| new file mode 100644 | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | ||
| 2 | +id: 170 | ||
| 3 | +title: "Rust client over ACP: an SDK prototype" | ||
| 4 | +state: open | ||
| 5 | +author: | ||
| 6 | + name: k33g | ||
| 7 | + email: ph.charriere@gmail.com | ||
| 8 | +createdAt: 2026-10-26T06:30:00.000Z | ||
| 9 | +updatedAt: 2026-10-26T06:30:00.000Z | ||
| 10 | +labels: | ||
| 11 | + - kind::feature | ||
| 12 | +epic: SDK | ||
| 13 | +related: | ||
| 14 | + - 167 | ||
| 15 | + - 139 | ||
| 16 | +body: | | ||
| 17 | + ## Goal | ||
| 18 | + A Rust program uses mm as a library by driving `mm -acp`. | ||
| 19 | + | ||
| 20 | + ## Shared ground | ||
| 21 | + Same idea as ticket 167 (Node.js), in another language: drive `mm -acp` over ACP (JSON-RPC, one message per line on stdin/stdout), with NO change to mm. Ticket 167 holds the ACP mapping (`initialize`, `session/new`, `session/prompt`, `session/update`, `session/cancel`, `session/request_permission`, `elicitation/create`) and the limits (the `mm` binary must be installed, local stdio only, config by `AGENT_CONFIG`). The same API as 167: start an agent from a config file, `complete` (not streamed), `stream`, cancel, a permission callback that refuses by default, close. Start once the Node.js prototype has settled the API. | ||
| 22 | + | ||
| 23 | + ## Specific to Rust | ||
| 24 | + - ACP comes from Zed, written in Rust: a Rust crate for the protocol (`agent-client-protocol`) is believed to exist — NOT verified (no network in the sandbox). Otherwise `tokio` (`tokio::process::Command`, line-delimited JSON) + `serde_json`. | ||
| 25 | + - API: `Agent::start(config).await?`, `agent.complete(prompt).await?`, `agent.stream(prompt)` returning a `Stream<Item = Result<Event>>`, cancellation by dropping the future or a `CancellationToken` (→ `session/cancel`). | ||
| 26 | + - No Rust toolchain in this sandbox (`cargo` absent): it must be installed, or the prototype built and run on the user's machine. | ||
| 27 | + | ||
| 28 | + ## Done when | ||
| 29 | + - `demos/rust-acp-client/` holds the client, one example each for not streamed, streamed, cancelled and a permission (agent with `yolo: false`), and README EN + FR with results measured on llmman. | ||
| 30 | + - Nothing changed in mm's Go code. | ||
| 31 | +tasks: | ||
| 32 | + - id: 1 | ||
| 33 | + title: "Check the Rust ACP crate vs tokio + serde_json; decide" | ||
| 34 | + state: open | ||
| 35 | + priority: none | ||
| 36 | + author: | ||
| 37 | + name: k33g | ||
| 38 | + email: ph.charriere@gmail.com | ||
| 39 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 40 | + - id: 2 | ||
| 41 | + title: "Get a Rust toolchain (sandbox kit or the user's machine)" | ||
| 42 | + state: open | ||
| 43 | + priority: none | ||
| 44 | + author: | ||
| 45 | + name: k33g | ||
| 46 | + email: ph.charriere@gmail.com | ||
| 47 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 48 | + - id: 3 | ||
| 49 | + title: "Client: start (initialize + session/new), complete, stream, cancel, close" | ||
| 50 | + state: open | ||
| 51 | + priority: none | ||
| 52 | + author: | ||
| 53 | + name: k33g | ||
| 54 | + email: ph.charriere@gmail.com | ||
| 55 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 56 | + - id: 4 | ||
| 57 | + title: "Permission callback (session/request_permission), refuse by default" | ||
| 58 | + state: open | ||
| 59 | + priority: none | ||
| 60 | + author: | ||
| 61 | + name: k33g | ||
| 62 | + email: ph.charriere@gmail.com | ||
| 63 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 64 | + - id: 5 | ||
| 65 | + title: "Examples (not streamed, streamed, cancelled, permission) run on llmman" | ||
| 66 | + state: open | ||
| 67 | + priority: none | ||
| 68 | + author: | ||
| 69 | + name: k33g | ||
| 70 | + email: ph.charriere@gmail.com | ||
| 71 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 72 | + - id: 6 | ||
| 73 | + title: "README EN + FR with measured results and limits" | ||
| 74 | + state: open | ||
| 75 | + priority: none | ||
| 76 | + author: | ||
| 77 | + name: k33g | ||
| 78 | + email: ph.charriere@gmail.com | ||
| 79 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 80 | +comments: [] | ||
added
.tickets/issues/0171-swift-client-over-acp-sdk-prototype.yaml +80 -0 | new file mode 100644 | ||
| @@ -0,0 +1,80 @@ | ||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | |
| 2 | +id: 171 | |
| 3 | +title: "Swift client over ACP: an SDK prototype" | |
| 4 | +state: open | |
| 5 | +author: | |
| 6 | + name: k33g | |
| 7 | + email: ph.charriere@gmail.com | |
| 8 | +createdAt: 2026-10-26T06:30:00.000Z | |
| 9 | +updatedAt: 2026-10-26T06:30:00.000Z | |
| 10 | +labels: | |
| 11 | + - kind::feature | |
| 12 | +epic: SDK | |
| 13 | +related: | |
| 14 | + - 167 | |
| 15 | + - 139 | |
| 16 | +body: | | |
| 17 | + ## Goal | |
| 18 | + A Swift program (a macOS app, a command-line tool) uses mm as a library by driving `mm -acp` — a path towards a native Mac front end for mm. | |
| 19 | + | |
| 20 | + ## Shared ground | |
| 21 | + Same idea as ticket 167 (Node.js), in another language: drive `mm -acp` over ACP (JSON-RPC, one message per line on stdin/stdout), with NO change to mm. Ticket 167 holds the ACP mapping (`initialize`, `session/new`, `session/prompt`, `session/update`, `session/cancel`, `session/request_permission`, `elicitation/create`) and the limits (the `mm` binary must be installed, local stdio only, config by `AGENT_CONFIG`). The same API as 167: start an agent from a config file, `complete` (not streamed), `stream`, cancel, a permission callback that refuses by default, close. Start once the Node.js prototype has settled the API. | |
| 22 | + | |
| 23 | + ## Specific to Swift | |
| 24 | + - Foundation `Process` + `Pipe`, `FileHandle.bytes.lines` for the line-delimited JSON, `Codable` for the messages, Swift concurrency: `func complete(_:) async throws -> String`, `func stream(_:) -> AsyncThrowingStream<Event, Error>`, cancellation through `Task.cancel()` (→ `session/cancel`). | |
| 25 | + - Check whether a Swift ACP package exists (not verified). | |
| 26 | + - No Swift toolchain in this sandbox (Linux, `swift` absent): built and run on the user's Mac. A sandboxed macOS app cannot start an arbitrary binary without entitlements — fine for a command-line prototype, to note for an app. | |
| 27 | + | |
| 28 | + ## Done when | |
| 29 | + - `demos/swift-acp-client/` holds the client, one example each for not streamed, streamed, cancelled and a permission (agent with `yolo: false`), and README EN + FR with results measured on llmman. A Swift package (`Package.swift`) with an executable target. | |
| 30 | + - Nothing changed in mm's Go code. | |
| 31 | +tasks: | |
| 32 | + - id: 1 | |
| 33 | + title: "Check for a Swift ACP package; decide" | |
| 34 | + state: open | |
| 35 | + priority: none | |
| 36 | + author: | |
| 37 | + name: k33g | |
| 38 | + email: ph.charriere@gmail.com | |
| 39 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 40 | + - id: 2 | |
| 41 | + title: "Build and run on the user's Mac (no Swift in the sandbox)" | |
| 42 | + state: open | |
| 43 | + priority: none | |
| 44 | + author: | |
| 45 | + name: k33g | |
| 46 | + email: ph.charriere@gmail.com | |
| 47 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 48 | + - id: 3 | |
| 49 | + title: "Client: start (initialize + session/new), complete, stream, cancel, close" | |
| 50 | + state: open | |
| 51 | + priority: none | |
| 52 | + author: | |
| 53 | + name: k33g | |
| 54 | + email: ph.charriere@gmail.com | |
| 55 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 56 | + - id: 4 | |
| 57 | + title: "Permission callback (session/request_permission), refuse by default" | |
| 58 | + state: open | |
| 59 | + priority: none | |
| 60 | + author: | |
| 61 | + name: k33g | |
| 62 | + email: ph.charriere@gmail.com | |
| 63 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 64 | + - id: 5 | |
| 65 | + title: "Examples (not streamed, streamed, cancelled, permission) run on llmman" | |
| 66 | + state: open | |
| 67 | + priority: none | |
| 68 | + author: | |
| 69 | + name: k33g | |
| 70 | + email: ph.charriere@gmail.com | |
| 71 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 72 | + - id: 6 | |
| 73 | + title: "README EN + FR with measured results and limits" | |
| 74 | + state: open | |
| 75 | + priority: none | |
| 76 | + author: | |
| 77 | + name: k33g | |
| 78 | + email: ph.charriere@gmail.com | |
| 79 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 80 | +comments: [] | |
| new file mode 100644 | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | ||
| 2 | +id: 171 | ||
| 3 | +title: "Swift client over ACP: an SDK prototype" | ||
| 4 | +state: open | ||
| 5 | +author: | ||
| 6 | + name: k33g | ||
| 7 | + email: ph.charriere@gmail.com | ||
| 8 | +createdAt: 2026-10-26T06:30:00.000Z | ||
| 9 | +updatedAt: 2026-10-26T06:30:00.000Z | ||
| 10 | +labels: | ||
| 11 | + - kind::feature | ||
| 12 | +epic: SDK | ||
| 13 | +related: | ||
| 14 | + - 167 | ||
| 15 | + - 139 | ||
| 16 | +body: | | ||
| 17 | + ## Goal | ||
| 18 | + A Swift program (a macOS app, a command-line tool) uses mm as a library by driving `mm -acp` — a path towards a native Mac front end for mm. | ||
| 19 | + | ||
| 20 | + ## Shared ground | ||
| 21 | + Same idea as ticket 167 (Node.js), in another language: drive `mm -acp` over ACP (JSON-RPC, one message per line on stdin/stdout), with NO change to mm. Ticket 167 holds the ACP mapping (`initialize`, `session/new`, `session/prompt`, `session/update`, `session/cancel`, `session/request_permission`, `elicitation/create`) and the limits (the `mm` binary must be installed, local stdio only, config by `AGENT_CONFIG`). The same API as 167: start an agent from a config file, `complete` (not streamed), `stream`, cancel, a permission callback that refuses by default, close. Start once the Node.js prototype has settled the API. | ||
| 22 | + | ||
| 23 | + ## Specific to Swift | ||
| 24 | + - Foundation `Process` + `Pipe`, `FileHandle.bytes.lines` for the line-delimited JSON, `Codable` for the messages, Swift concurrency: `func complete(_:) async throws -> String`, `func stream(_:) -> AsyncThrowingStream<Event, Error>`, cancellation through `Task.cancel()` (→ `session/cancel`). | ||
| 25 | + - Check whether a Swift ACP package exists (not verified). | ||
| 26 | + - No Swift toolchain in this sandbox (Linux, `swift` absent): built and run on the user's Mac. A sandboxed macOS app cannot start an arbitrary binary without entitlements — fine for a command-line prototype, to note for an app. | ||
| 27 | + | ||
| 28 | + ## Done when | ||
| 29 | + - `demos/swift-acp-client/` holds the client, one example each for not streamed, streamed, cancelled and a permission (agent with `yolo: false`), and README EN + FR with results measured on llmman. A Swift package (`Package.swift`) with an executable target. | ||
| 30 | + - Nothing changed in mm's Go code. | ||
| 31 | +tasks: | ||
| 32 | + - id: 1 | ||
| 33 | + title: "Check for a Swift ACP package; decide" | ||
| 34 | + state: open | ||
| 35 | + priority: none | ||
| 36 | + author: | ||
| 37 | + name: k33g | ||
| 38 | + email: ph.charriere@gmail.com | ||
| 39 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 40 | + - id: 2 | ||
| 41 | + title: "Build and run on the user's Mac (no Swift in the sandbox)" | ||
| 42 | + state: open | ||
| 43 | + priority: none | ||
| 44 | + author: | ||
| 45 | + name: k33g | ||
| 46 | + email: ph.charriere@gmail.com | ||
| 47 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 48 | + - id: 3 | ||
| 49 | + title: "Client: start (initialize + session/new), complete, stream, cancel, close" | ||
| 50 | + state: open | ||
| 51 | + priority: none | ||
| 52 | + author: | ||
| 53 | + name: k33g | ||
| 54 | + email: ph.charriere@gmail.com | ||
| 55 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 56 | + - id: 4 | ||
| 57 | + title: "Permission callback (session/request_permission), refuse by default" | ||
| 58 | + state: open | ||
| 59 | + priority: none | ||
| 60 | + author: | ||
| 61 | + name: k33g | ||
| 62 | + email: ph.charriere@gmail.com | ||
| 63 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 64 | + - id: 5 | ||
| 65 | + title: "Examples (not streamed, streamed, cancelled, permission) run on llmman" | ||
| 66 | + state: open | ||
| 67 | + priority: none | ||
| 68 | + author: | ||
| 69 | + name: k33g | ||
| 70 | + email: ph.charriere@gmail.com | ||
| 71 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 72 | + - id: 6 | ||
| 73 | + title: "README EN + FR with measured results and limits" | ||
| 74 | + state: open | ||
| 75 | + priority: none | ||
| 76 | + author: | ||
| 77 | + name: k33g | ||
| 78 | + email: ph.charriere@gmail.com | ||
| 79 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 80 | +comments: [] | ||
added
.tickets/issues/0172-nushell-script-client-over-acp.yaml +84 -0 | new file mode 100644 | ||
| @@ -0,0 +1,84 @@ | ||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | |
| 2 | +id: 172 | |
| 3 | +title: "Nushell script client over ACP: mm from a shell script" | |
| 4 | +state: open | |
| 5 | +author: | |
| 6 | + name: k33g | |
| 7 | + email: ph.charriere@gmail.com | |
| 8 | +createdAt: 2026-10-26T06:30:00.000Z | |
| 9 | +updatedAt: 2026-10-26T06:30:00.000Z | |
| 10 | +labels: | |
| 11 | + - kind::feature | |
| 12 | +epic: SDK | |
| 13 | +related: | |
| 14 | + - 167 | |
| 15 | + - 139 | |
| 16 | +body: | | |
| 17 | + ## Goal | |
| 18 | + Use mm from a Nushell script with structured data (tables, records) instead of text: send a question, get the answer and the tool calls as Nushell values. | |
| 19 | + | |
| 20 | + ## Shared ground | |
| 21 | + Same idea as ticket 167 (Node.js), in another language: drive `mm -acp` over ACP (JSON-RPC, one message per line on stdin/stdout), with NO change to mm. Ticket 167 holds the ACP mapping (`initialize`, `session/new`, `session/prompt`, `session/update`, `session/cancel`, `session/request_permission`, `elicitation/create`) and the limits (the `mm` binary must be installed, local stdio only, config by `AGENT_CONFIG`). The same API as 167: start an agent from a config file, `complete` (not streamed), `stream`, cancel, a permission callback that refuses by default, close. Start once the Node.js prototype has settled the API. | |
| 22 | + | |
| 23 | + ## Specific to Nushell — the hard part | |
| 24 | + - ACP is a conversation on ONE long-lived process: `session/prompt` needs the `sessionId` that `session/new` returns (mm's ids are time-sortable and random, so it cannot be guessed). A Nushell pipeline writes stdin and reads stdout, but answering a message read from stdout on the same process's stdin is not something a plain pipeline does. To study first: | |
| 25 | + - Nushell's background jobs / `job` commands and whether a pipe can be held open both ways; | |
| 26 | + - a small helper (a Nushell plugin, or reuse of the Node/Python client of tickets 167/169 behind a command) — then Nushell only parses its JSON output; | |
| 27 | + - for the simplest case, plain `mm -p` already gives one answer and is easy from Nushell — the ACP client only adds streaming, cancellation and permissions. | |
| 28 | + - Permissions cannot be asked interactively in a pipeline: refuse by default, or a closure given by the script. | |
| 29 | + - Streaming: each `session/update` becomes a record, so the script can `where kind == text` or `each` over them. | |
| 30 | + - `nu` is not installed in this sandbox. | |
| 31 | + | |
| 32 | + ## Done when | |
| 33 | + - `demos/nushell-acp-client/` holds the client, one example each for not streamed, streamed, cancelled and a permission (agent with `yolo: false`), and README EN + FR with results measured on llmman. The README compares it with plain `mm -p` from Nushell, so the reader knows when ACP is worth it. | |
| 34 | + - Nothing changed in mm's Go code. | |
| 35 | +tasks: | |
| 36 | + - id: 1 | |
| 37 | + title: "Study: can Nushell hold a two-way pipe to one process? Pick the approach (pure nu, plugin, or a helper)" | |
| 38 | + state: open | |
| 39 | + priority: none | |
| 40 | + author: | |
| 41 | + name: k33g | |
| 42 | + email: ph.charriere@gmail.com | |
| 43 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 44 | + - id: 2 | |
| 45 | + title: "Get Nushell (sandbox or the user's machine)" | |
| 46 | + state: open | |
| 47 | + priority: none | |
| 48 | + author: | |
| 49 | + name: k33g | |
| 50 | + email: ph.charriere@gmail.com | |
| 51 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 52 | + - id: 3 | |
| 53 | + title: "Client: start (initialize + session/new), complete, stream, cancel, close" | |
| 54 | + state: open | |
| 55 | + priority: none | |
| 56 | + author: | |
| 57 | + name: k33g | |
| 58 | + email: ph.charriere@gmail.com | |
| 59 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 60 | + - id: 4 | |
| 61 | + title: "Permission callback (session/request_permission), refuse by default" | |
| 62 | + state: open | |
| 63 | + priority: none | |
| 64 | + author: | |
| 65 | + name: k33g | |
| 66 | + email: ph.charriere@gmail.com | |
| 67 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 68 | + - id: 5 | |
| 69 | + title: "Examples (not streamed, streamed, cancelled, permission) run on llmman" | |
| 70 | + state: open | |
| 71 | + priority: none | |
| 72 | + author: | |
| 73 | + name: k33g | |
| 74 | + email: ph.charriere@gmail.com | |
| 75 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 76 | + - id: 6 | |
| 77 | + title: "README EN + FR with measured results and limits" | |
| 78 | + state: open | |
| 79 | + priority: none | |
| 80 | + author: | |
| 81 | + name: k33g | |
| 82 | + email: ph.charriere@gmail.com | |
| 83 | + createdAt: 2026-10-26T06:30:00.000Z | |
| 84 | +comments: [] | |
| new file mode 100644 | |||
| @@ -0,0 +1,84 @@ | |||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | ||
| 2 | +id: 172 | ||
| 3 | +title: "Nushell script client over ACP: mm from a shell script" | ||
| 4 | +state: open | ||
| 5 | +author: | ||
| 6 | + name: k33g | ||
| 7 | + email: ph.charriere@gmail.com | ||
| 8 | +createdAt: 2026-10-26T06:30:00.000Z | ||
| 9 | +updatedAt: 2026-10-26T06:30:00.000Z | ||
| 10 | +labels: | ||
| 11 | + - kind::feature | ||
| 12 | +epic: SDK | ||
| 13 | +related: | ||
| 14 | + - 167 | ||
| 15 | + - 139 | ||
| 16 | +body: | | ||
| 17 | + ## Goal | ||
| 18 | + Use mm from a Nushell script with structured data (tables, records) instead of text: send a question, get the answer and the tool calls as Nushell values. | ||
| 19 | + | ||
| 20 | + ## Shared ground | ||
| 21 | + Same idea as ticket 167 (Node.js), in another language: drive `mm -acp` over ACP (JSON-RPC, one message per line on stdin/stdout), with NO change to mm. Ticket 167 holds the ACP mapping (`initialize`, `session/new`, `session/prompt`, `session/update`, `session/cancel`, `session/request_permission`, `elicitation/create`) and the limits (the `mm` binary must be installed, local stdio only, config by `AGENT_CONFIG`). The same API as 167: start an agent from a config file, `complete` (not streamed), `stream`, cancel, a permission callback that refuses by default, close. Start once the Node.js prototype has settled the API. | ||
| 22 | + | ||
| 23 | + ## Specific to Nushell — the hard part | ||
| 24 | + - ACP is a conversation on ONE long-lived process: `session/prompt` needs the `sessionId` that `session/new` returns (mm's ids are time-sortable and random, so it cannot be guessed). A Nushell pipeline writes stdin and reads stdout, but answering a message read from stdout on the same process's stdin is not something a plain pipeline does. To study first: | ||
| 25 | + - Nushell's background jobs / `job` commands and whether a pipe can be held open both ways; | ||
| 26 | + - a small helper (a Nushell plugin, or reuse of the Node/Python client of tickets 167/169 behind a command) — then Nushell only parses its JSON output; | ||
| 27 | + - for the simplest case, plain `mm -p` already gives one answer and is easy from Nushell — the ACP client only adds streaming, cancellation and permissions. | ||
| 28 | + - Permissions cannot be asked interactively in a pipeline: refuse by default, or a closure given by the script. | ||
| 29 | + - Streaming: each `session/update` becomes a record, so the script can `where kind == text` or `each` over them. | ||
| 30 | + - `nu` is not installed in this sandbox. | ||
| 31 | + | ||
| 32 | + ## Done when | ||
| 33 | + - `demos/nushell-acp-client/` holds the client, one example each for not streamed, streamed, cancelled and a permission (agent with `yolo: false`), and README EN + FR with results measured on llmman. The README compares it with plain `mm -p` from Nushell, so the reader knows when ACP is worth it. | ||
| 34 | + - Nothing changed in mm's Go code. | ||
| 35 | +tasks: | ||
| 36 | + - id: 1 | ||
| 37 | + title: "Study: can Nushell hold a two-way pipe to one process? Pick the approach (pure nu, plugin, or a helper)" | ||
| 38 | + state: open | ||
| 39 | + priority: none | ||
| 40 | + author: | ||
| 41 | + name: k33g | ||
| 42 | + email: ph.charriere@gmail.com | ||
| 43 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 44 | + - id: 2 | ||
| 45 | + title: "Get Nushell (sandbox or the user's machine)" | ||
| 46 | + state: open | ||
| 47 | + priority: none | ||
| 48 | + author: | ||
| 49 | + name: k33g | ||
| 50 | + email: ph.charriere@gmail.com | ||
| 51 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 52 | + - id: 3 | ||
| 53 | + title: "Client: start (initialize + session/new), complete, stream, cancel, close" | ||
| 54 | + state: open | ||
| 55 | + priority: none | ||
| 56 | + author: | ||
| 57 | + name: k33g | ||
| 58 | + email: ph.charriere@gmail.com | ||
| 59 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 60 | + - id: 4 | ||
| 61 | + title: "Permission callback (session/request_permission), refuse by default" | ||
| 62 | + state: open | ||
| 63 | + priority: none | ||
| 64 | + author: | ||
| 65 | + name: k33g | ||
| 66 | + email: ph.charriere@gmail.com | ||
| 67 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 68 | + - id: 5 | ||
| 69 | + title: "Examples (not streamed, streamed, cancelled, permission) run on llmman" | ||
| 70 | + state: open | ||
| 71 | + priority: none | ||
| 72 | + author: | ||
| 73 | + name: k33g | ||
| 74 | + email: ph.charriere@gmail.com | ||
| 75 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 76 | + - id: 6 | ||
| 77 | + title: "README EN + FR with measured results and limits" | ||
| 78 | + state: open | ||
| 79 | + priority: none | ||
| 80 | + author: | ||
| 81 | + name: k33g | ||
| 82 | + email: ph.charriere@gmail.com | ||
| 83 | + createdAt: 2026-10-26T06:30:00.000Z | ||
| 84 | +comments: [] | ||
added
.tickets/issues/0173-switch-the-license-from-mit-to-fsl-1-1-mit.yaml +92 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
.tickets/issues/0174-testing-model-check-test-a-model-as-a-working-agent.yaml +86 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
.tickets/issues/0175-testing-function-calling-test-a-model-s-tool-calls.yaml +88 -0 Large diff collapsed to keep the page fast — load it when you need it.
modified
README.md +2 -0 | @@ -141,6 +141,8 @@ The `demos/` directory holds ready-made set-ups (most with a README explaining h | ||
| 141 | 141 | | `demos/mini-mes/` | small teams of agents (`riker` coordinating a specifier and a coder, `gopher` a Go specialist) | |
| 142 | 142 | | `demos/little-coder/` | a Go + htmx web-app generator for a small local model (Mellum2 12B on llmman, 32 GB RAM): a tested starter, skills, `AGENTS.md`, measured results | |
| 143 | 143 | | `demos/team/` | a team of four agents (lead, specifier, coder, tester) on one small model (Ornith 1.5 9B on llmman) building Go + htmx apps through spec and test files, with measured results | |
| 144 | +| `demo.acp.sdk/` | mini-me as a library from other languages, by driving `mm -acp` over ACP: `nodejs/minime.js` (ticket 167) — `Agent.start`, `complete`, `stream`, cancel with an `AbortSignal`, a permission callback; no dependency, 11 tests with a fake mm, four examples measured on llmman | | |
| 145 | +| `testing/` | model testers run by mm itself: a coordinator (Gemma 4 12B) follows a skill, gives small tasks to a `candidate` agent whose model is under test (Qwen3.5 4B for model-check, Luciole 1B for function-calling), checks each result with a command and writes a Markdown report — `model-check/` (tool calls, exact instructions, Go code that must run, several steps) and `function-calling/` (only tools declared in the candidate's `tools:`) | | |
| 144 | 146 | | `dev.team/` | a team of five agents for a 32 GB laptop (ticket 152): a Qwen3.5 4B coordinator hands each request to a reformulator, a coder, a reviewer or a tutor (Gemma 4 12B, Mellum2 12B); hooks keep one model in memory at a time; measured: bug fixes, lessons and reviews work, new features not yet | |
| 145 | 147 | | `demos/blog-team/` | the smallest team: a lead, a coder and a reviewer in 75 lines of YAML, one model (Mellum2 12B Q8_0 on llmman), written for a blog post | |
| 146 | 148 | | `demos/translator/` | a French ↔ English translator (Gemma 4 E4B on llmman): `AGENTS.md` for the style, skills for technical texts, e-mails and idioms, `$styles` for professional / informal / Slack versions, `translate.sh` for files | |
| @@ -141,6 +141,8 @@ The `demos/` directory holds ready-made set-ups (most with a README explaining h | |||
| 141 | | `demos/mini-mes/` | small teams of agents (`riker` coordinating a specifier and a coder, `gopher` a Go specialist) | | 141 | | `demos/mini-mes/` | small teams of agents (`riker` coordinating a specifier and a coder, `gopher` a Go specialist) | |
| 142 | | `demos/little-coder/` | a Go + htmx web-app generator for a small local model (Mellum2 12B on llmman, 32 GB RAM): a tested starter, skills, `AGENTS.md`, measured results | | 142 | | `demos/little-coder/` | a Go + htmx web-app generator for a small local model (Mellum2 12B on llmman, 32 GB RAM): a tested starter, skills, `AGENTS.md`, measured results | |
| 143 | | `demos/team/` | a team of four agents (lead, specifier, coder, tester) on one small model (Ornith 1.5 9B on llmman) building Go + htmx apps through spec and test files, with measured results | | 143 | | `demos/team/` | a team of four agents (lead, specifier, coder, tester) on one small model (Ornith 1.5 9B on llmman) building Go + htmx apps through spec and test files, with measured results | |
| 144 | +| `demo.acp.sdk/` | mini-me as a library from other languages, by driving `mm -acp` over ACP: `nodejs/minime.js` (ticket 167) — `Agent.start`, `complete`, `stream`, cancel with an `AbortSignal`, a permission callback; no dependency, 11 tests with a fake mm, four examples measured on llmman | | ||
| 145 | +| `testing/` | model testers run by mm itself: a coordinator (Gemma 4 12B) follows a skill, gives small tasks to a `candidate` agent whose model is under test (Qwen3.5 4B for model-check, Luciole 1B for function-calling), checks each result with a command and writes a Markdown report — `model-check/` (tool calls, exact instructions, Go code that must run, several steps) and `function-calling/` (only tools declared in the candidate's `tools:`) | | ||
| 144 | | `dev.team/` | a team of five agents for a 32 GB laptop (ticket 152): a Qwen3.5 4B coordinator hands each request to a reformulator, a coder, a reviewer or a tutor (Gemma 4 12B, Mellum2 12B); hooks keep one model in memory at a time; measured: bug fixes, lessons and reviews work, new features not yet | | 146 | | `dev.team/` | a team of five agents for a 32 GB laptop (ticket 152): a Qwen3.5 4B coordinator hands each request to a reformulator, a coder, a reviewer or a tutor (Gemma 4 12B, Mellum2 12B); hooks keep one model in memory at a time; measured: bug fixes, lessons and reviews work, new features not yet | |
| 145 | | `demos/blog-team/` | the smallest team: a lead, a coder and a reviewer in 75 lines of YAML, one model (Mellum2 12B Q8_0 on llmman), written for a blog post | | 147 | | `demos/blog-team/` | the smallest team: a lead, a coder and a reviewer in 75 lines of YAML, one model (Mellum2 12B Q8_0 on llmman), written for a blog post | |
| 146 | | `demos/translator/` | a French ↔ English translator (Gemma 4 E4B on llmman): `AGENTS.md` for the style, skills for technical texts, e-mails and idioms, `$styles` for professional / informal / Slack versions, `translate.sh` for files | | 148 | | `demos/translator/` | a French ↔ English translator (Gemma 4 E4B on llmman): `AGENTS.md` for the style, skills for technical texts, e-mails and idioms, `$styles` for professional / informal / Slack versions, `translate.sh` for files | |
added
demo.acp.sdk/README.en.md +23 -0 | new file mode 100644 | ||
| @@ -0,0 +1,23 @@ | ||
| 1 | +# Using mini-me as a library, over ACP | |
| 2 | + | |
| 3 | +*[Version française](README.fr.md)* | |
| 4 | + | |
| 5 | +Programs in other languages use mini-me as a library by driving `mm -acp`: the same JSON-RPC protocol an editor such as Zed speaks, on the child process's stdin and stdout. Nothing changes in mm; every agent file works, with its tools, hooks, MCP servers and other agents. | |
| 6 | + | |
| 7 | +| Folder | Language | Status | | |
| 8 | +|---|---|---| | |
| 9 | +| [`nodejs/`](nodejs/README.en.md) | Node.js (ticket 167) | done: `minime.js`, four examples, tests | | |
| 10 | +| `go/`, `python/`, `rust/`, `swift/`, `nushell/` | tickets 168–172 | not started | | |
| 11 | + | |
| 12 | +What every client does: | |
| 13 | + | |
| 14 | +| Wanted | ACP message | | |
| 15 | +|---|---| | |
| 16 | +| start | `initialize`, then `session/new {cwd}` | | |
| 17 | +| run a completion | `session/prompt` | | |
| 18 | +| stream | the `session/update` notifications: `agent_message_chunk`, `agent_thought_chunk`, `tool_call`, `tool_call_update` | | |
| 19 | +| not streamed | join the text chunks until `session/prompt` answers with its `stopReason` | | |
| 20 | +| cancel | the `session/cancel` notification; the prompt then answers `stopReason: "cancelled"` | | |
| 21 | +| confirm a tool (`yolo: false`) | answer mm's `session/request_permission` request | | |
| 22 | + | |
| 23 | +Limits shared by all: the `mm` binary must be installed, it runs locally (stdio), and the agent is a file (`mm -acp -config <file>`). For a Go program without the binary, see ticket 139 (an in-process SDK). | |
| new file mode 100644 | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | +# Using mini-me as a library, over ACP | ||
| 2 | + | ||
| 3 | +*[Version française](README.fr.md)* | ||
| 4 | + | ||
| 5 | +Programs in other languages use mini-me as a library by driving `mm -acp`: the same JSON-RPC protocol an editor such as Zed speaks, on the child process's stdin and stdout. Nothing changes in mm; every agent file works, with its tools, hooks, MCP servers and other agents. | ||
| 6 | + | ||
| 7 | +| Folder | Language | Status | | ||
| 8 | +|---|---|---| | ||
| 9 | +| [`nodejs/`](nodejs/README.en.md) | Node.js (ticket 167) | done: `minime.js`, four examples, tests | | ||
| 10 | +| `go/`, `python/`, `rust/`, `swift/`, `nushell/` | tickets 168–172 | not started | | ||
| 11 | + | ||
| 12 | +What every client does: | ||
| 13 | + | ||
| 14 | +| Wanted | ACP message | | ||
| 15 | +|---|---| | ||
| 16 | +| start | `initialize`, then `session/new {cwd}` | | ||
| 17 | +| run a completion | `session/prompt` | | ||
| 18 | +| stream | the `session/update` notifications: `agent_message_chunk`, `agent_thought_chunk`, `tool_call`, `tool_call_update` | | ||
| 19 | +| not streamed | join the text chunks until `session/prompt` answers with its `stopReason` | | ||
| 20 | +| cancel | the `session/cancel` notification; the prompt then answers `stopReason: "cancelled"` | | ||
| 21 | +| confirm a tool (`yolo: false`) | answer mm's `session/request_permission` request | | ||
| 22 | + | ||
| 23 | +Limits shared by all: the `mm` binary must be installed, it runs locally (stdio), and the agent is a file (`mm -acp -config <file>`). For a Go program without the binary, see ticket 139 (an in-process SDK). | ||
added
demo.acp.sdk/README.fr.md +23 -0 | new file mode 100644 | ||
| @@ -0,0 +1,23 @@ | ||
| 1 | +# Utiliser mini-me comme bibliothèque, par ACP | |
| 2 | + | |
| 3 | +*[English version](README.en.md)* | |
| 4 | + | |
| 5 | +Des programmes écrits dans d'autres langages utilisent mini-me comme bibliothèque en pilotant `mm -acp` : le même protocole JSON-RPC que parle un éditeur comme Zed, sur l'entrée et la sortie standard du processus enfant. Rien ne change dans mm ; tout fichier d'agent fonctionne, avec ses outils, ses hooks, ses serveurs MCP et ses autres agents. | |
| 6 | + | |
| 7 | +| Dossier | Langage | État | | |
| 8 | +|---|---|---| | |
| 9 | +| [`nodejs/`](nodejs/README.fr.md) | Node.js (ticket 167) | fait : `minime.js`, quatre exemples, des tests | | |
| 10 | +| `go/`, `python/`, `rust/`, `swift/`, `nushell/` | tickets 168–172 | pas commencés | | |
| 11 | + | |
| 12 | +Ce que fait chaque client : | |
| 13 | + | |
| 14 | +| Besoin | Message ACP | | |
| 15 | +|---|---| | |
| 16 | +| démarrer | `initialize`, puis `session/new {cwd}` | | |
| 17 | +| lancer une complétion | `session/prompt` | | |
| 18 | +| stream | les notifications `session/update` : `agent_message_chunk`, `agent_thought_chunk`, `tool_call`, `tool_call_update` | | |
| 19 | +| sans stream | concaténer les morceaux de texte jusqu'à la réponse de `session/prompt` et son `stopReason` | | |
| 20 | +| annuler | la notification `session/cancel` ; le prompt répond alors `stopReason: "cancelled"` | | |
| 21 | +| confirmer un outil (`yolo: false`) | répondre à la requête `session/request_permission` de mm | | |
| 22 | + | |
| 23 | +Limites communes : le binaire `mm` doit être installé, tout tourne en local (stdio), et l'agent est un fichier (`mm -acp -config <fichier>`). Pour un programme Go sans le binaire, voir le ticket 139 (un SDK en mémoire). | |
| new file mode 100644 | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | +# Utiliser mini-me comme bibliothèque, par ACP | ||
| 2 | + | ||
| 3 | +*[English version](README.en.md)* | ||
| 4 | + | ||
| 5 | +Des programmes écrits dans d'autres langages utilisent mini-me comme bibliothèque en pilotant `mm -acp` : le même protocole JSON-RPC que parle un éditeur comme Zed, sur l'entrée et la sortie standard du processus enfant. Rien ne change dans mm ; tout fichier d'agent fonctionne, avec ses outils, ses hooks, ses serveurs MCP et ses autres agents. | ||
| 6 | + | ||
| 7 | +| Dossier | Langage | État | | ||
| 8 | +|---|---|---| | ||
| 9 | +| [`nodejs/`](nodejs/README.fr.md) | Node.js (ticket 167) | fait : `minime.js`, quatre exemples, des tests | | ||
| 10 | +| `go/`, `python/`, `rust/`, `swift/`, `nushell/` | tickets 168–172 | pas commencés | | ||
| 11 | + | ||
| 12 | +Ce que fait chaque client : | ||
| 13 | + | ||
| 14 | +| Besoin | Message ACP | | ||
| 15 | +|---|---| | ||
| 16 | +| démarrer | `initialize`, puis `session/new {cwd}` | | ||
| 17 | +| lancer une complétion | `session/prompt` | | ||
| 18 | +| stream | les notifications `session/update` : `agent_message_chunk`, `agent_thought_chunk`, `tool_call`, `tool_call_update` | | ||
| 19 | +| sans stream | concaténer les morceaux de texte jusqu'à la réponse de `session/prompt` et son `stopReason` | | ||
| 20 | +| annuler | la notification `session/cancel` ; le prompt répond alors `stopReason: "cancelled"` | | ||
| 21 | +| confirmer un outil (`yolo: false`) | répondre à la requête `session/request_permission` de mm | | ||
| 22 | + | ||
| 23 | +Limites communes : le binaire `mm` doit être installé, tout tourne en local (stdio), et l'agent est un fichier (`mm -acp -config <fichier>`). Pour un programme Go sans le binaire, voir le ticket 139 (un SDK en mémoire). | ||
added
demo.acp.sdk/nodejs/README.en.md +86 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
demo.acp.sdk/nodejs/README.fr.md +86 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
demo.acp.sdk/nodejs/agent.yaml +43 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
demo.acp.sdk/nodejs/examples/cancel.js +21 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
demo.acp.sdk/nodejs/examples/complete.js +10 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
demo.acp.sdk/nodejs/examples/permission.js +18 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
demo.acp.sdk/nodejs/examples/stream.js +40 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
demo.acp.sdk/nodejs/minime.js +307 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
demo.acp.sdk/nodejs/package.json +10 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
demo.acp.sdk/nodejs/test/fake-mm.js +89 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
demo.acp.sdk/nodejs/test/minime.test.js +110 -0 Large diff collapsed to keep the page fast — load it when you need it.
modified
demos/coding-helpers-unload-option/README.en.md +4 -0 | @@ -12,6 +12,10 @@ The same team as [`demos/coding-helpers`](../coding-helpers/) — a coordinator | ||
| 12 | 12 | |
| 13 | 13 | The two specialists are copies of those of `demos/coding-helpers`. Only `agent.yaml` differs: `unloadModels: true` on both entries, and no `hooks:` block. |
| 14 | 14 | |
| 15 | + | |
| 16 | + | |
| 17 | +Editable source: `coding-helpers-unload-option-flow.drawio` (draw.io); `coding-helpers-unload-option-flow.svg` is the same picture. | |
| 18 | + | |
| 15 | 19 | ## Run |
| 16 | 20 | |
| 17 | 21 | ```bash |
| @@ -12,6 +12,10 @@ The same team as [`demos/coding-helpers`](../coding-helpers/) — a coordinator | |||
| 12 | 12 | ||
| 13 | The two specialists are copies of those of `demos/coding-helpers`. Only `agent.yaml` differs: `unloadModels: true` on both entries, and no `hooks:` block. | 13 | The two specialists are copies of those of `demos/coding-helpers`. Only `agent.yaml` differs: `unloadModels: true` on both entries, and no `hooks:` block. |
| 14 | 14 | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +Editable source: `coding-helpers-unload-option-flow.drawio` (draw.io); `coding-helpers-unload-option-flow.svg` is the same picture. | ||
| 18 | + | ||
| 15 | ## Run | 19 | ## Run |
| 16 | 20 | ||
| 17 | ```bash | 21 | ```bash |
modified
demos/coding-helpers-unload-option/README.fr.md +4 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
demos/coding-helpers-unload-option/coding-helpers-unload-option-flow.drawio +31 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
demos/coding-helpers-unload-option/coding-helpers-unload-option-flow.png +0 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
demos/coding-helpers-unload-option/coding-helpers-unload-option-flow.svg +66 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/README.en.md +12 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/README.fr.md +12 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/common/bench-lib.sh +174 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/common/run.sh +28 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/function-calling/.gitignore +6 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/function-calling/README.en.md +88 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/function-calling/README.fr.md +88 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/function-calling/agent.candidate.yaml +98 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/function-calling/agent.yaml +95 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/function-calling/bench.sh +5 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/function-calling/reports/2026-10-26-0901-Luciole-1B-Instruct-1.1-GGUF-Q4_K_M.md +46 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/function-calling/skills/function-calling-check/SKILL.md +31 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/function-calling/skills/function-calling-check/bench.sh +38 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/function-calling/skills/function-calling-check/check.py +143 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/function-calling/skills/function-calling-check/tests.tsv +8 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/function-calling/tools/call.sh +50 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/.gitignore +6 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/README.en.md +92 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/README.fr.md +92 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/agent.candidate.yaml +52 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/agent.yaml +96 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/bench.sh +5 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/reports/.current +1 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/reports/2026-10-26-1242-Luciole-8B-Instruct-1.1-GGUF-Q4_K_M .md +44 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/skills/model-check/SKILL.md +32 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/skills/model-check/bench.sh +80 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/skills/model-check/fixtures/calc/calc.go +12 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/skills/model-check/fixtures/calc/calc_test.go +21 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/skills/model-check/fixtures/calc/go.mod +3 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/skills/model-check/fixtures/config.yaml +4 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/skills/model-check/fixtures/go.mod +5 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/skills/model-check/fixtures/numbers.txt +37 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/skills/model-check/fixtures/todo.txt +8 -0 Large diff collapsed to keep the page fast — load it when you need it.
added
testing/model-check/skills/model-check/tests.tsv +8 -0 Large diff collapsed to keep the page fast — load it when you need it.