@gtrabanco/pi-nan-provider 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -51,7 +51,7 @@ maintainer's quota, so they are **permission-gated**:
51
51
  gate covers ad-hoc diagnosis only, never `bun test`.
52
52
  - **Default to `qwen3.6` — it is unlimited.**
53
53
  - **For massive/bulk probes prefer a model the maintainer uses less with a large
54
- token budget, e.g. `mimo-v2.5`** (1M context).
54
+ token budget, e.g. `mimo-v2.6-flash`** (1M context, 1.0B monthly quota).
55
55
  - **When the model under investigation is the point** (e.g. reproducing a
56
56
  model-specific 400), use it, but minimize tokens: smallest viable prompt,
57
57
  lowest `max_tokens`, stop at the first decisive response.
@@ -77,7 +77,7 @@ Every PR that changes code MUST bump `package.json` version in the same PR; CI p
77
77
 
78
78
  ## Verified API facts (do not re-derive from stale docs)
79
79
 
80
- - **Extension-side pi-ai imports + streaming-API instance binding (v0.6.10; verified on pi-ai 0.83.0–0.84.4 and pi 0.87.0):**
80
+ - **Extension-side pi-ai imports + streaming-API instance binding (v0.6.10; verified on pi-ai 0.83.0–0.87.1 and pi 0.87.1):**
81
81
  statically import ONLY the bare `@earendil-works/pi-ai` root from `src/`. pi's
82
82
  extension loader maps that specifier to the compat entrypoint on the bundled
83
83
  CLI, Node-mode jiti aliases and compiled-binary virtualModules, and compat
@@ -107,7 +107,7 @@ Every PR that changes code MUST bump `package.json` version in the same PR; CI p
107
107
  `openAICompletionsApi`; `createProvider` and `envApiKeyAuth(name, envVars)` are
108
108
  on the root. `envApiKeyAuth` implements exactly: stored credential key wins →
109
109
  first set env var → unconfigured; `login()` prompts with `{ type: "secret" }`.
110
- - pi awaits extension factories (`await factory(api)`) on 0.83.0 and 0.84.4
110
+ - pi awaits extension factories (`await factory(api)`) on 0.83.0 through 0.87.1
111
111
  alike, so the extension entrypoint may be async (v0.5.0: streaming-API
112
112
  resolution needs it).
113
113
  - pi-ai 0.83.0 runtime surface verified identical for this package's needs:
@@ -116,6 +116,26 @@ Every PR that changes code MUST bump `package.json` version in the same PR; CI p
116
116
  `fetchModels(context)`, `filterModels(models, credential)`, `api`) and
117
117
  `RefreshModelsContext.credential` match 0.84.4; `registerProvider` has both
118
118
  the full-`Provider` and `(name, config)` overloads in 0.83's ExtensionAPI.
119
+ (pi-ai 0.86 changed provider stream entry points to a branded `TranscriptContext`
120
+ — see next bullet — but this package's runtime is untouched.)
121
+ - **pi-ai 0.86.0 `TranscriptContext` migration (verified 2026-09-29):** provider
122
+ stream entry points (`Provider.stream`, `ProviderStreams.stream/streamSimple`,
123
+ `StreamFunction`) now take a branded `TranscriptContext = { messages: Message[] }`
124
+ instead of the public `Context = { systemPrompt?: string; messages; tools? }`.
125
+ `normalizeContext(context: Context): TranscriptContext` (exported from the pi-ai
126
+ root) folds prompt + tools into a leading system message
127
+ `{ role: "system", content: <prompt>, toolsAdded: <tools>, timestamp: 0 }`.
128
+ `Context` itself still exists and is what the public entry points accept, so raw
129
+ `Context` literals only fail where a *provider-level* stream is called directly
130
+ (the 6 test call sites). Tests now build contexts with `normalizeContext(...)`.
131
+ This package's `src/` files never hand-build a provider context, so no `src/`
132
+ code changed: `wrapApiForStrictSanitization` forwards `context` untouched, and
133
+ `withContextOverflowClassification` only *reads* it for the estimate, which still
134
+ counts prompt + tools because they ride inside `messages[0]`.
135
+ - pi 0.87 rejects extension tools that declare no parameter schema at registration;
136
+ all this package's MCP tools declare TypeBox `parameters`, so they register fine.
137
+ pi 0.87 also changed `pi.on()` to return an unsubscribe function (unused by
138
+ this package).
119
139
  - `pi.registerProvider(provider)` accepts a complete pi-ai `Provider`; pi's Models
120
140
  runtime then drives `fetchModels` refreshes (network refresh at interactive
121
141
  startup and periodically, cache-only at registration) and persists the overlay.
@@ -132,20 +152,37 @@ Every PR that changes code MUST bump `package.json` version in the same PR; CI p
132
152
  override (maintainer-confirmed 2026-09-05) was withdrawn 2026-09-07 — the
133
153
  updated NaN docs still say 262K "the model's native window" and models.dev
134
154
  agrees at 262,144.
135
- - NaN's official chat model list (https://nan.builders/openapi.json `model`
136
- param description + https://nan.builders/docs/models, checked 2026-09-07):
137
- community `deepseek-v4-flash`, `mimo-v2.5`, `qwen3.8-flash`, `glm5.3-flash`,
138
- `qwen3.6`, `gemma4` (all text+image vision) + premium-tier `glm5.3`
155
+ - **Which models exist**: https://nan.builders/docs/models is the source of
156
+ truth (maintainer instruction, reaffirmed 2026-09-29), cross-checked against
157
+ the `model` param description in https://nan.builders/openapi.json; models.dev
158
+ (provider `nan`) supplies the numeric limits for the ids it documents, and
159
+ every divergence or exclusion is recorded with provenance.
160
+ Official chat models (checked 2026-09-29): community `deepseek-v4-flash`,
161
+ `mimo-v2.6-flash`, `qwen3.8-flash`, `glm5.3-flash`, `qwen3.6`, `gemma4`
162
+ (all text+image vision) + premium-tier `glm5.3`
139
163
  (~753B MoE, text-only input, 1M context, 400M tokens/rolling 4h window).
140
164
  glm5.3 is now documented by models.dev too (1M context / 131,072 max output,
141
165
  checked 2026-09-13) but stays out of the static catalog via
142
166
  `LIVE_ONLY_MODEL_IDS` (premium tier) so a non-premium key never sees a
143
167
  model it cannot call when the live `/models` fetch is unavailable; premium
144
168
  keys still receive it live with conservative placeholder limits.
145
- `glm5.2` was removed by the provider (2026-09-05); models.dev may still list
146
- it — the generator excludes it via `PROVIDER_REMOVED_MODEL_IDS`. Non-chat
147
- endpoints: qwen3-embedding, rerank, kokoro (TTS), whisper (STT),
148
- flux-2-klein (images) — MCP-bridge territory, not chat catalog models.
169
+ Removed by the provider: `glm5.2` (2026-09-05) and `mimo-v2.5` (checked
170
+ 2026-09-29 — zero mentions in the docs and in openapi.json, and models.dev
171
+ dropped it too; superseded by `mimo-v2.6-flash`). models.dev may re-list a
172
+ removed id, so the generator excludes them via `PROVIDER_REMOVED_MODEL_IDS`,
173
+ and the four community ids the docs require (`qwen3.6`, `gemma4`,
174
+ `deepseek-v4-flash`, `mimo-v2.6-flash`) are `REQUIRED_MODEL_IDS` — if one
175
+ never reaches the catalog (from models.dev or from a manual-only entry),
176
+ generation exits non-zero instead of inventing data.
177
+ `mimo-v2.6-flash` entered through `MANUAL_ONLY_MODEL_IDS` while models.dev
178
+ lacked it; models.dev started listing it on 2026-09-29, and the generator
179
+ then skips the manual entry (attaching its note to the models.dev-derived
180
+ entry) so the id is never emitted twice.
181
+ Non-chat endpoints: qwen3-embedding, rerank, kokoro (TTS), whisper (STT),
182
+ flux-2-klein and qwen-image-2.1 (images) — MCP-bridge territory, not chat
183
+ catalog models; `qwen-image-2.1` also sits on models.dev with
184
+ `limit.output: 0`, so generation excludes it via `NON_CHAT_MODEL_IDS`
185
+ instead of flagging it "needs manual verification" forever.
149
186
  - NaN can close an SSE stream **before** `finish_reason`. The catalog sets
150
187
  `supportsFinishReason: true` so pi-ai raises the retryable
151
188
  `Stream ended without finish_reason` (pi-ai's `RETRYABLE_PROVIDER_ERROR_PATTERN`
@@ -178,19 +215,29 @@ Every PR that changes code MUST bump `package.json` version in the same PR; CI p
178
215
  `test/context-overflow-classifier.test.ts`; issue #3.
179
216
  - Relative imports inside this package use `.ts` extensions (pi's official
180
217
  extension examples do the same; pi transpiles extension sources).
181
- - pi intentionally has NO built-in MCP client (docs/usage.md). MCP integration
182
- happens by bridging servers into pi custom tools via `pi.registerTool()`:
218
+ - pi 0.87 breaking changes NOT used by this package: `ContextEditEntry` added to
219
+ the `SessionEntry` union, expanded `TurnEndEvent`, removed `shouldStopAfterTurn`,
220
+ `SessionManager` canonical for provider context. The package touches none of them.
221
+ - Since 0.10.0, this package uses native MCP via `pi.registerMcpServer()`.
222
+ Both servers are session-scoped, visible in `/mcp` with source "extension".
223
+ Server names and tool IDs:
224
+ - `nan-search` → tools: `mcp__nan-search__web_search`
225
+ (official remote, HTTP, `Authorization: Bearer <key>` header)
226
+ - `nan-media` → tools: `mcp__nan-media__generate_image/edit_image/text_to_speech/list_voices/speech_to_text`
227
+ (community stdio, `npx -y nan-mcp-server@1.1.2`, 120s timeout)
228
+ - `exposure: "direct"` on both — tools are declared to the model.
229
+ - User `mcp.json` entries with the same name take precedence.
230
+ - `/nan-mcp enable/disable` calls `pi.registerMcpServer()` / `pi.unregisterMcpServer()` directly.
231
+ - Missing `NAN_API_KEY` at load: web-search server NOT registered (console.warn).
232
+ NaN integration facts remain:
183
233
  - NaN's official remote MCP server: `https://api.nan.builders/mcp` (host
184
234
  root, NOT /v1; JSON-RPC 2.0 over streamable HTTP, stateless; same `sk-`
185
235
  key, shared rate limit/quota). Spec: https://nan.builders/openapi.json
186
- (tag "MCP"). Currently exposes `web_search` (same args as POST /v1/search);
187
- "growing registry" — use tools/list to discover.
236
+ (tag "MCP"). Currently exposes `web_search`.
188
237
  - Community `nan-mcp-server` (https://github.com/luciferfran/nan-mcp-server):
189
- stdio MCP server, spawned per tool call (lazy), opt-in NAN_MEDIA_MCP=1,
190
- version-pinned via NAN_MEDIA_MCP_VERSION (default 1.0.8) or a full command
191
- override via NAN_MEDIA_MCP_COMMAND. Tools: generate_image, edit_image,
192
- text_to_speech, list_voices, speech_to_text, embed, rerank, list_models
193
- (we bridge the audio/image/transcription scope). An automated check
194
- (scripts/check-nan-mcp-server.ts + .github/workflows/check-nan-mcp-server-update.yml)
195
- compares the npm registry against the pin weekly and files a `dependencies` issue
196
- with a breaking/safe verdict from the live server tool surface (unpkg).
238
+ stdio MCP server, version-pinned via NAN_MEDIA_MCP_VERSION (default 1.1.2)
239
+ or full command override via NAN_MEDIA_MCP_COMMAND. Tools: generate_image,
240
+ edit_image, text_to_speech, list_voices, speech_to_text.
241
+ - Automated check (scripts/check-nan-mcp-server.ts) compares the npm registry
242
+ against the pin weekly and files a `dependencies` issue when a newer release
243
+ exists, with a breaking/safe verdict from the live server tool surface.
package/README.es.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # @gtrabanco/pi-nan-provider
2
2
 
3
3
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
- [![Version](https://img.shields.io/badge/version-0.8.0-blue)](https://github.com/gtrabanco/pi-nan-provider/releases)
4
+ [![Version](https://img.shields.io/badge/version-0.10.0-blue)](https://github.com/gtrabanco/pi-nan-provider/releases)
5
5
 
6
- [NaN Builders](https://nan.builders) model provider + MCP bridges para [pi](https://github.com/earendil-works/pi).
6
+ [NaN Builders](https://nan.builders) model provider + MCP nativo para [pi](https://github.com/earendil-works/pi).
7
7
 
8
- Registra el proveedor `nan` vía `pi.registerProvider()` usando la API OpenAI-compatible de NaN (`https://api.nan.builders/v1`), y conecta las herramientas MCP de NaN en pi con `pi.registerTool()`.
8
+ Registra el proveedor `nan` vía `pi.registerProvider()` usando la API OpenAI-compatible de NaN (`https://api.nan.builders/v1`), y registra servidores MCP nativos con `pi.registerMcpServer()`.
9
9
 
10
10
  ---
11
11
 
@@ -97,11 +97,11 @@ Puedes sobrescribir el `compat` de cualquier modelo en `~/.pi/agent/models.json`
97
97
 
98
98
  Consigue una clave en la [plataforma NaN](https://cloud.nan.builders/r/7GK06FX8) (ajustes de usuario → API Keys; enlace de referidos).
99
99
 
100
- ## 🔌 Puentes MCP
100
+ ## 🔌 MCP nativo
101
101
 
102
- Dado que [pi no incluye un cliente MCP integrado](https://github.com/earendil-works/pi/blob/main/docs/usage.md), este paquete conecta los servidores MCP como **herramientas nativas de pi**.
102
+ Dado que [pi 0.99.0 incluye un cliente MCP integrado](https://github.com/earendil-works/pi/blob/main/docs/usage.md), este paquete migra sus puentes a MCP nativo (requiere `pi >=0.99` — cambio rotundo).
103
103
 
104
- Ambos puentes están **activados y son perezosos (lazy) por defecto**. Usa `/nan-mcp` para gestionarlos.
104
+ Ambos puentes están **activados por defecto** (por sesión, visibles en `/mcp`). Usa `/nan-mcp` para gestionarlos.
105
105
 
106
106
  ### 🛠️ Comando de Gestión: `/nan-mcp`
107
107
 
@@ -116,21 +116,21 @@ Ambos puentes están **activados y son perezosos (lazy) por defecto**. Usa `/nan
116
116
  ### 1. Servidor MCP oficial de NaN
117
117
  *Puente oficial para herramientas remotas vía [https://api.nan.builders/mcp](https://nan.builders/docs/api).*
118
118
 
119
- - **`nan_web_search(query, ...)`**: Realiza búsquedas web a través del gateway de NaN.
119
+ - **`mcp__nan-search__web_search(query, ...)`**: Realiza búsquedas web a través del gateway de NaN.
120
120
 
121
121
  ### 2. Servidor MCP de Media (Comunidad)
122
122
  *Conecta [`nan-mcp-server`](https://github.com/luciferfran/nan-mcp-server) mediante un cliente stdio local mínimo.*
123
123
 
124
- - **Carga Perezosa (Lazy)**: El proceso del servidor se lanza **solo** cuando se invoca una herramienta y se cierra inmediatamente después.
124
+ - **Por sesión**: El servidor se conecta al registrarse (eager). Aparece en `/mcp` con fuente "extension".
125
125
  - **Configuración**: Los archivos se guardan en `~/nan-mcp-output/`.
126
126
 
127
127
  | Herramienta | Propósito |
128
128
  | :--- | :--- |
129
- | `nan_generate_image` | Generación de imágenes (flux-2-klein) |
130
- | `nan_edit_image` | Edición imagen→imagen (flux-2-klein) |
131
- | `nan_text_to_speech` | Síntesis de audio (kokoro) |
132
- | `nan_list_voices` | Listar voces disponibles |
133
- | `nan_speech_to_text` | Transcripción de audio (whisper) |
129
+ | `mcp__nan-media__generate_image` | Generación de imágenes (flux-2-klein) |
130
+ | `mcp__nan-media__edit_image` | Edición imagen→imagen (flux-2-klein) |
131
+ | `mcp__nan-media__text_to_speech` | Síntesis de audio (kokoro) |
132
+ | `mcp__nan-media__list_voices` | Listar voces disponibles |
133
+ | `mcp__nan-media__speech_to_text` | Transcripción de audio (whisper) |
134
134
 
135
135
  #### 🔧 Configuración del Puente de Media
136
136
 
@@ -260,10 +260,15 @@ Catálogo base (verificado contra [docs de NaN](https://nan.builders/docs/models
260
260
  | `qwen3.6` | 262,144 | 65,536 | texto, imagen | ✅ |
261
261
  | `gemma4` | 262,144 | 32,768 | texto, imagen | ✅ |
262
262
  | `deepseek-v4-flash` | 1,000,000 | 384,000 | texto, imagen | ✅ |
263
- | `mimo-v2.5` | 1,048,576 | 131,072 | texto, imagen | ✅ |
263
+ | `mimo-v2.6-flash` | 1,048,576 | 131,072 | texto, imagen | ✅ |
264
264
  | `glm5.3-flash` | 1,000,000 | 131,072 | texto, imagen | ✅ |
265
265
  | `qwen3.8-flash` | 262,144 | 131,072 | texto, imagen | ✅ |
266
266
 
267
+ > [!NOTE]
268
+ > `mimo-v2.6-flash` está servido por NaN y desde el 2026-09-29 aparece también en models.dev (proveedor `nan`); la entrada manual que lo sostenía se conserva como fallback y su nota de procedencia sigue unida a la entrada generada.
269
+ >
270
+ > `mimo-v2.5` fue eliminado por NaN: ya no aparece ni en [los docs de NaN](https://nan.builders/docs/models) ni en la [lista de modelos del OpenAPI](https://nan.builders/openapi.json) (comprobado 2026-09-29), así que sale del catálogo — lo sustituye `mimo-v2.6-flash` (mismos límites 1,048,576 / 131,072 y cuota mensual de 1.0B). El consumo histórico de `mimo-v2.5` en `/nan-usage` sigue apareciendo en la sección de modelos no documentados.
271
+
267
272
  ---
268
273
 
269
274
  ## 🚀 Desarrollo
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # @gtrabanco/pi-nan-provider
2
2
 
3
3
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
- [![Version](https://img.shields.io/badge/version-0.8.0-blue)](https://github.com/gtrabanco/pi-nan-provider/releases)
4
+ [![Version](https://img.shields.io/badge/version-0.10.0-blue)](https://github.com/gtrabanco/pi-nan-provider/releases)
5
5
 
6
- [NaN Builders](https://nan.builders) model provider + MCP bridges for [pi](https://github.com/earendil-works/pi).
6
+ [NaN Builders](https://nan.builders) model provider + native MCP servers for [pi](https://github.com/earendil-works/pi).
7
7
 
8
- Registers the `nan` provider via `pi.registerProvider()` using NaN's OpenAI-compatible API (`https://api.nan.builders/v1`), and bridges NaN's MCP tools into pi with `pi.registerTool()`.
8
+ Registers the `nan` provider via `pi.registerProvider()` using NaN's OpenAI-compatible API (`https://api.nan.builders/v1`), and registers MCP servers via pi's native `pi.registerMcpServer()`.
9
9
 
10
10
  ---
11
11
 
@@ -97,11 +97,11 @@ You can override any model's `compat` per-model in `~/.pi/agent/models.json` (pi
97
97
 
98
98
  Get a key from the [NaN platform](https://cloud.nan.builders/r/7GK06FX8) (user settings → API Keys; referral link).
99
99
 
100
- ## 🔌 MCP Bridges
100
+ ## 🔌 Native MCP Servers
101
101
 
102
- Since [pi does not include a built-in MCP client](https://github.com/earendil-works/pi/blob/main/docs/usage.md), this package bridges MCP servers as **native pi tools**.
102
+ Since [pi 0.99.0 ships a built-in MCP client](https://github.com/earendil-works/pi/blob/main/docs/usage.md), this package migrates its bridges to native MCP (requires `pi >=0.99` — breaking).
103
103
 
104
- Both bridges are **enabled and lazy by default**. Use `/nan-mcp` to manage them.
104
+ Both bridges are **enabled by default** (session-scoped, visible in `/mcp`). Use `/nan-mcp` to manage them.
105
105
 
106
106
  ### 🛠️ Management Command: `/nan-mcp`
107
107
 
@@ -114,23 +114,23 @@ Both bridges are **enabled and lazy by default**. Use `/nan-mcp` to manage them.
114
114
  ---
115
115
 
116
116
  ### 1. Official NaN MCP Server
117
- *Official bridge for remote tools via [https://api.nan.builders/mcp](https://nan.builders/docs/api).*
117
+ *Official remote MCP server registered natively via `pi.registerMcpServer()`.*
118
118
 
119
- - **`nan_web_search(query, ...)`**: Performs web searches through NaN's gateway.
119
+ - **`mcp__nan-search__web_search(query, ...)`**: Performs web searches through NaN's gateway.
120
120
 
121
121
  ### 2. Community Media MCP Server
122
122
  *Bridges [`nan-mcp-server`](https://github.com/luciferfran/nan-mcp-server) via a minimal local stdio client.*
123
123
 
124
- - **Lazy Loading**: The server process is spawned **only** when a tool is invoked and terminated immediately after.
124
+ - **Session-scoped**: The server connects at registration time (eager). Appears in `/mcp` with source "extension".
125
125
  - **Config**: Files land in `~/nan-mcp-output/`.
126
126
 
127
127
  | Tool | Purpose |
128
128
  | :--- | :--- |
129
- | `nan_generate_image` | Image generation (flux-2-klein) |
130
- | `nan_edit_image` | Image-to-image editing (flux-2-klein) |
131
- | `nan_text_to_speech` | Audio synthesis (kokoro) |
132
- | `nan_list_voices` | List available voices |
133
- | `nan_speech_to_text` | Audio transcription (whisper) |
129
+ | `mcp__nan-media__generate_image` | Image generation (flux-2-klein) |
130
+ | `mcp__nan-media__edit_image` | Image-to-image editing (flux-2-klein) |
131
+ | `mcp__nan-media__text_to_speech` | Audio synthesis (kokoro) |
132
+ | `mcp__nan-media__list_voices` | List available voices |
133
+ | `mcp__nan-media__speech_to_text` | Audio transcription (whisper) |
134
134
 
135
135
  #### 🔧 Media Bridge Configuration
136
136
 
@@ -260,13 +260,14 @@ Baseline catalog (verified against [NaN docs](https://nan.builders/docs/models)
260
260
  | `qwen3.6` | 262,144 | 65,536 | text, image | ✅ |
261
261
  | `gemma4` | 262,144 | 32,768 | text, image | ✅ |
262
262
  | `deepseek-v4-flash` | 1,000,000 | 384,000 | text, image | ✅ |
263
- | `mimo-v2.5` | 1,048,576 | 131,072 | text, image | ✅ |
264
263
  | `mimo-v2.6-flash` | 1,048,576 | 131,072 | text, image | ✅ |
265
264
  | `glm5.3-flash` | 1,000,000 | 131,072 | text, image | ✅ |
266
265
  | `qwen3.8-flash` | 262,144 | 131,072 | text, image | ✅ |
267
266
 
268
267
  > [!NOTE]
269
- > `mimo-v2.6-flash` is served by NaN but not yet listed on models.dev provider `nan`; it enters the catalog through a manual-only entry with the same limits as `mimo-v2.5`. The model will be auto-detected from models.dev once added there.
268
+ > `mimo-v2.6-flash` is served by NaN and has been listed by models.dev provider `nan` since 2026-09-29. Before that it entered the catalog through a manual-only entry; that entry is kept as a fallback (re-emitted automatically if models.dev drops the model again) and its provenance note stays attached to the generated entry.
269
+ >
270
+ > `mimo-v2.5` was removed by NaN: it no longer appears in the [NaN docs](https://nan.builders/docs/models) nor in the [OpenAPI model list](https://nan.builders/openapi.json) (checked 2026-09-29), so it is out of the catalog — `mimo-v2.6-flash` replaces it (same 1,048,576 / 131,072 limits, 1.0B monthly quota). Historical `/nan-usage` consumption for `mimo-v2.5` still shows under the undocumented-models section.
270
271
 
271
272
  ---
272
273
 
@@ -278,7 +279,7 @@ NaN's `reasoning_effort` parameter controls how much the model thinks before ans
278
279
  | :--- | :--- | :--- |
279
280
  | `glm5.3`, `glm5.3-flash` | `low` · `medium` · `high` · `max` | Fully controllable — higher values let the model reason longer |
280
281
  | `qwen3.6`, `gemma4` | `none` · `minimal` · `low` · `medium` · `high` · `max` | `none`/`minimal` skip reasoning entirely; others cap at 2K / 8K / 16K / 32K tokens |
281
- | `deepseek-v4-flash`, `qwen3.8-flash`, `mimo-v2.5`, `mimo-v2.6-flash` | *(accepted but not adjustable)* | The parameter is accepted and never rejected, but the model manages its own reasoning depth — it is never an error to send a value these models don't adjust |
282
+ | `deepseek-v4-flash`, `qwen3.8-flash`, `mimo-v2.6-flash` | *(accepted but not adjustable)* | The parameter is accepted and never rejected, but the model manages its own reasoning depth — it is never an error to send a value these models don't adjust |
282
283
 
283
284
  ---
284
285
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtrabanco/pi-nan-provider",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "NaN Builders (api.nan.builders) model provider for pi - OpenAI-compatible registration with a models.dev-generated fallback, tier-aware live catalog, and MCP bridges (official web search + optional community media server)",
5
5
  "keywords": [
6
6
  "pi",
@@ -42,12 +42,12 @@
42
42
  "check-pi-sdk-versions": "node scripts/check-pi-sdk-versions.mjs --report"
43
43
  },
44
44
  "peerDependencies": {
45
- "@earendil-works/pi-ai": ">=0.83.0 <1",
46
- "@earendil-works/pi-coding-agent": ">=0.83.0 <1"
45
+ "@earendil-works/pi-ai": ">=0.99.0 <1",
46
+ "@earendil-works/pi-coding-agent": ">=0.99.0 <1"
47
47
  },
48
48
  "devDependencies": {
49
- "@earendil-works/pi-ai": "0.84.4",
50
- "@earendil-works/pi-coding-agent": "0.84.4",
49
+ "@earendil-works/pi-ai": "0.99.1",
50
+ "@earendil-works/pi-coding-agent": "0.99.1",
51
51
  "@types/bun": "1.4",
52
52
  "typescript": "7"
53
53
  },
@@ -42,8 +42,16 @@
42
42
 
43
43
  import {
44
44
  DEFAULT_NAN_MEDIA_MCP_VERSION,
45
- NAN_MEDIA_MCP_SERVER_TOOLS,
46
- } from "../src/mcp/nan-media.ts";
45
+ } from "../src/mcp/media-server.ts";
46
+
47
+ /** The audio/image/transcription scope we bridge from the community server. */
48
+ const bridgedtools = [
49
+ "generate_image",
50
+ "edit_image",
51
+ "text_to_speech",
52
+ "list_voices",
53
+ "speech_to_text",
54
+ ] as const;
47
55
 
48
56
  /** npm package name of the community stdio MCP server we bridge. */
49
57
  export const NAN_MCP_SERVER_PACKAGE = "nan-mcp-server";
@@ -143,7 +151,7 @@ export function assessUpdate(latestTools: string[]): {
143
151
  removedTools: string[];
144
152
  reason: string;
145
153
  } {
146
- const bridged: readonly string[] = [...NAN_MEDIA_MCP_SERVER_TOOLS];
154
+ const bridged: readonly string[] = [...bridgedtools];
147
155
  const latest = new Set(latestTools.map((t) => t.trim()));
148
156
 
149
157
  const removedTools = bridged.filter((tool) => !latest.has(tool));
@@ -8,16 +8,28 @@
8
8
  * package. Run before publishing (`bun run generate-models`, wired into
9
9
  * `prepublishOnly`).
10
10
  *
11
+ * Sources of truth (maintainer instruction, reaffirmed 2026-09-29):
12
+ * - https://nan.builders/docs/models decides WHICH models exist — which ids
13
+ * belong in the chat catalog, which are premium/live-only, which were
14
+ * removed by the provider, which are not chat models at all. Every
15
+ * exclusion cites it.
16
+ * - models.dev (provider "nan") supplies the numeric limits for the ids it
17
+ * documents; a divergence from the docs is recorded in MANUAL_OVERRIDES
18
+ * with a source note.
19
+ *
11
20
  * Provenance rules (enforced, not decorative):
12
21
  * - Every emitted number must come from models.dev or an explicit
13
22
  * MANUAL_OVERRIDES note recording where it was confirmed. Nothing invented.
14
- * - The founding models (qwen3.6, gemma4, deepseek-v4-flash, mimo-v2.5) MUST
15
- * exist on models.dev with complete limits, or this script exits non-zero.
23
+ * - The official community chat models (qwen3.6, gemma4, deepseek-v4-flash,
24
+ * mimo-v2.6-flash) MUST reach the catalog — from models.dev or from a
25
+ * MANUAL_ONLY entry — or this script exits non-zero.
16
26
  * - Any other models.dev entry missing `limit.context`/`limit.output` is
17
- * skipped and flagged "needs manual verification" — never guessed.
27
+ * skipped and flagged "needs manual verification" — never guessed. Entries
28
+ * that are not chat models are excluded via NON_CHAT_MODEL_IDS instead.
18
29
  * - models.dev modalities are intersected with pi's supported input set
19
- * ("text" | "image"); e.g. mimo-v2.5's audio input is not representable in
20
- * pi's Model type and is dropped from `input` (noted on the entry).
30
+ * ("text" | "image"); e.g. mimo-v2.6-flash's audio input is not
31
+ * representable in pi's Model type and is dropped from `input` (noted on
32
+ * the entry).
21
33
  */
22
34
 
23
35
  import type { GeneratedModelEntry } from "../src/fetch-models.ts";
@@ -39,17 +51,23 @@ const REASONING_EFFORT_VALUES_FROM_DOCS: Record<string, string[]> = {
39
51
  ...REASONING_EFFORT_VALUES,
40
52
  // deepseek-v4-flash: any value (no effect) — model decides per request
41
53
  "deepseek-v4-flash": [],
42
- // qwen3.8-flash, mimo-v2.5: accepted, depth not adjustable
54
+ // qwen3.8-flash, mimo-v2.6-flash: accepted, depth not adjustable
43
55
  "qwen3.8-flash": [],
44
- "mimo-v2.5": [],
56
+ "mimo-v2.6-flash": [],
45
57
  };
46
58
 
47
59
  const MODELS_DEV_API_URL = "https://models.dev/api.json";
48
60
  const SOURCE_PROVIDER_ID = "nan";
49
61
  const FETCH_TIMEOUT_MS = 15_000;
50
62
 
51
- /** Models the founding prompt requires in the catalog; absence is fatal. */
52
- const REQUIRED_MODEL_IDS = ["qwen3.6", "gemma4", "deepseek-v4-flash", "mimo-v2.5"] as const;
63
+ /**
64
+ * Official community chat models per https://nan.builders/docs/models
65
+ * (checked 2026-09-29; glm5.3 is premium-tier and deliberately live-only).
66
+ * Each MUST reach the catalog — from models.dev or from a MANUAL_ONLY entry —
67
+ * or generation fails. The docs decide this set, not models.dev: models.dev
68
+ * dropped mimo-v2.5 together with the docs in 2026-09.
69
+ */
70
+ const REQUIRED_MODEL_IDS = ["qwen3.6", "gemma4", "deepseek-v4-flash", "mimo-v2.6-flash"] as const;
53
71
 
54
72
  /** pi's Model.input only supports these values. */
55
73
  const PI_SUPPORTED_INPUT = new Set(["text", "image"]);
@@ -75,6 +93,20 @@ const MANUAL_NOTES: Record<string, string> = {
75
93
  const PROVIDER_REMOVED_MODEL_IDS: Record<string, string> = {
76
94
  "glm5.2":
77
95
  "removed by NaN (2026-09-05); absent from the official chat model list in https://nan.builders/openapi.json and https://nan.builders/docs/models (checked 2026-09-07) while models.dev provider nan still listed it — excluded so regeneration does not resurrect it",
96
+ "mimo-v2.5":
97
+ "removed by NaN (checked 2026-09-29): zero mentions in https://nan.builders/docs/models and in the model param description of https://nan.builders/openapi.json, and models.dev provider nan dropped it as well — superseded by mimo-v2.6-flash; excluded so regeneration does not resurrect it",
98
+ };
99
+
100
+ /**
101
+ * models.dev entries that are not chat models. They can never join a chat
102
+ * catalog (their output is an image, and models.dev reports limit.output 0),
103
+ * so they are excluded with the docs as the reason instead of being flagged
104
+ * "needs manual verification" forever — that flag would suggest limits worth
105
+ * confirming for a model this package must not serve as chat.
106
+ */
107
+ const NON_CHAT_MODEL_IDS: Record<string, string> = {
108
+ "qwen-image-2.1":
109
+ "image-generation model (text→image; output modality image) documented in https://nan.builders/docs/models and https://nan.builders/openapi.json (checked 2026-09-29) — not a chat model, out of the static chat catalog (models.dev reports limit.output 0)",
78
110
  };
79
111
 
80
112
  /**
@@ -221,6 +253,10 @@ function convertModel(modelId: string, m: ModelsDevModel): GeneratedModel | { sk
221
253
  if (removedReason) {
222
254
  return { skip: `provider-removed: "${modelId}" excluded from the catalog (${removedReason})` };
223
255
  }
256
+ const nonChatReason = NON_CHAT_MODEL_IDS[modelId];
257
+ if (nonChatReason) {
258
+ return { skip: `non-chat: "${modelId}" excluded from the chat catalog (${nonChatReason})` };
259
+ }
224
260
  const liveOnlyReason = LIVE_ONLY_MODEL_IDS[modelId];
225
261
  if (liveOnlyReason) {
226
262
  return { skip: `live-only: "${modelId}" kept out of the static catalog (${liveOnlyReason})` };
@@ -301,8 +337,20 @@ async function main(): Promise<void> {
301
337
  }
302
338
  }
303
339
 
304
- // Add manual-only models (not yet on models.dev).
340
+ // Add manual-only models (models.dev provider "nan" does not list them).
341
+ // When models.dev starts listing one, the manual entry is NOT emitted —
342
+ // otherwise the id would reach the catalog twice — and the reason plus the
343
+ // original manual provenance are attached to the models.dev-derived entry,
344
+ // so the manual data never disappears silently.
345
+ const entryById = new Map(entries.map((entry) => [entry.id, entry]));
305
346
  for (const [modelId, detail] of Object.entries(MANUAL_ONLY_MODEL_IDS)) {
347
+ const existing = entryById.get(modelId);
348
+ if (existing) {
349
+ const note = `manual-only: "${modelId}" skipped: models.dev provider "${SOURCE_PROVIDER_ID}" now lists it, so the manual-only entry is not emitted and the models.dev values are used (${detail})`;
350
+ existing.notes = [...(existing.notes ?? []), note];
351
+ console.warn(`generate-models: skipped ${note}`);
352
+ continue;
353
+ }
306
354
  const override = MANUAL_OVERRIDES[modelId];
307
355
  if (override) {
308
356
  console.log(`generate-models: manual override for "${modelId}" (${Object.keys(override).filter((k) => k !== "note").join(", ")})`);
@@ -318,8 +366,9 @@ async function main(): Promise<void> {
318
366
  const missingRequired = REQUIRED_MODEL_IDS.filter((id) => !entryIds.has(id));
319
367
  if (missingRequired.length > 0) {
320
368
  fail(
321
- `required models missing from models.dev provider "${SOURCE_PROVIDER_ID}": ${missingRequired.join(", ")} — ` +
322
- "do NOT invent their data; confirm and add them via MANUAL_OVERRIDES with a source note.",
369
+ `required models missing from the catalog: ${missingRequired.join(", ")} (official community chat models per ` +
370
+ "https://nan.builders/docs/models) — do NOT invent their data; confirm them in the docs and add them " +
371
+ "via MANUAL_OVERRIDES (models.dev lists them) or MANUAL_ONLY_MODEL_IDS (models.dev does not), with a source note.",
323
372
  );
324
373
  }
325
374
 
@@ -333,6 +382,9 @@ async function main(): Promise<void> {
333
382
  ...Object.entries(PROVIDER_REMOVED_MODEL_IDS).map(
334
383
  ([id, reason]) => `provider-removed: "${id}" excluded from the catalog (${reason})`,
335
384
  ),
385
+ ...Object.entries(NON_CHAT_MODEL_IDS).map(
386
+ ([id, reason]) => `non-chat: "${id}" excluded from the chat catalog (${reason})`,
387
+ ),
336
388
  ...Object.entries(LIVE_ONLY_MODEL_IDS).map(
337
389
  ([id, reason]) => `live-only: "${id}" kept out of the static catalog (${reason})`,
338
390
  ),
@@ -43,7 +43,7 @@ export interface ManualModelOverride {
43
43
 
44
44
  /**
45
45
  * Reasoning effort values sourced from the NaN docs (https://nan.builders/docs/models
46
- * #controlling-reasoning, checked 2026-09-25).
46
+ * #controlling-reasoning, checked 2026-09-29).
47
47
  *
48
48
  * models.dev has NO `reasoning_effort_values` field — only `reasoning_options`
49
49
  * (which is [{type:"toggle"}] or []), so this mapping must be hand-maintained
@@ -55,7 +55,10 @@ export interface ManualModelOverride {
55
55
  * - glm5.3, glm5.3-flash: fully controllable (low/medium/high/max)
56
56
  * - qwen3.6, gemma4: none/minimal skip reasoning, others cap depth
57
57
  * - deepseek-v4-flash: any value accepted but model decides per-request
58
- * - qwen3.8-flash, mimo-v2.5, mimo-v2.6-flash: accepted, depth not adjustable
58
+ * - qwen3.8-flash, mimo-v2.6-flash: accepted, depth not adjustable
59
+ *
60
+ * (mimo-v2.5, which used to share that last row, was removed by NaN — absent
61
+ * from the docs and from openapi.json, checked 2026-09-29.)
59
62
  *
60
63
  * A model with reasoning_effort_values=[] means the parameter is accepted but
61
64
  * the model manages its own reasoning depth — it is never an error.
@@ -68,13 +71,22 @@ export const REASONING_EFFORT_VALUES: Record<string, string[]> = {
68
71
  };
69
72
 
70
73
  /**
71
- * Models that NaN serves but models.dev provider nan hasn't listed yet.
72
- * Mirrors the generator's MANUAL_ONLY_MODEL_IDS — kept in sync so the
73
- * generator and the tests share the same source.
74
+ * Provenance for the manual-only mimo-v2.6-flash entry, shared by
75
+ * MANUAL_ONLY_MODEL_IDS (generator) and MANUAL_ONLY_MODELS (tests) so both
76
+ * sides assert the exact same string.
77
+ */
78
+ const MANUAL_ONLY_NOTE =
79
+ "omnimodal model (text, image, audio input) served by NaN; limits and modalities from https://nan.builders/docs/models#mimo-v2-6-flash (checked 2026-09-25, quotas re-checked 2026-09-29): 1,048,576 context / 131,072 max output / 1.0B monthly quota per member. Reasoning: accepted, depth not adjustable (docs table, checked 2026-09-29). Tool calling: yes. Streaming: yes. Input modalities (pi-representable): text, image (audio not representable in pi's Model type). Fallback entry: emitted only while models.dev provider nan does not list the model — models.dev started listing it on 2026-09-29, so the generator then keeps the models.dev values and attaches this note to that entry instead of emitting a second one.";
80
+
81
+ /**
82
+ * Models NaN serves that models.dev provider nan may not list. Mirrors the
83
+ * generator's MANUAL_ONLY_MODEL_IDS — kept in sync so the generator and the
84
+ * tests share the same source. An entry stops being emitted as soon as
85
+ * models.dev lists the model; the note above then rides on the models.dev
86
+ * entry, so the manual data never vanishes silently.
74
87
  */
75
88
  export const MANUAL_ONLY_MODEL_IDS: Record<string, string> = {
76
- "mimo-v2.6-flash":
77
- "omnimodal model (text, image, audio input) served by NaN, not yet listed on models.dev provider nan (checked 2026-09-25); included so the live /models refresh does not hand it UNKNOWN_MODEL_LIMITS. Same limits as mimo-v2.5: 1,048,576 / 131,072 / 1.0B monthly quota per member. Reasoning: accepted, depth not adjustable. Tool calling: yes. Streaming: yes. Input modalities (pi-representable): text, image (audio not representable in pi's Model type).",
89
+ "mimo-v2.6-flash": MANUAL_ONLY_NOTE,
78
90
  };
79
91
 
80
92
  /**
@@ -89,7 +101,7 @@ export const MANUAL_ONLY_MODELS: Record<string, ManualModelOverride> = {
89
101
  reasoningEffortValues: [],
90
102
  contextWindow: 1_048_576,
91
103
  maxTokens: 131_072,
92
- note: "omnimodal model (text, image, audio input) served by NaN, not yet listed on models.dev provider nan (checked 2026-09-25); included so the live /models refresh does not hand it UNKNOWN_MODEL_LIMITS. Same limits as mimo-v2.5: 1,048,576 / 131,072 / 1.0B monthly quota per member. Reasoning: accepted, depth not adjustable. Tool calling: yes. Streaming: yes. Input modalities (pi-representable): text, image (audio not representable in pi's Model type).",
104
+ note: MANUAL_ONLY_NOTE,
93
105
  },
94
106
  };
95
107