pi-freeflow 1.40.1 → 1.41.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/CHANGELOG.md +55 -0
- package/README.md +36 -19
- package/package.json +1 -1
- package/src/catalog.ts +33 -21
- package/src/cline-accounts.ts +4 -3
- package/src/config.ts +13 -0
- package/src/models.ts +240 -64
- package/src/opencode-fingerprint.ts +78 -17
- package/src/proxy.ts +339 -12
- package/src/relay.ts +38 -0
- package/src/responses.ts +6 -0
- package/src/stream-pipe.ts +391 -67
- package/src/tool-translation.ts +15 -12
- package/src/toolcall-audit.ts +580 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,60 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.41.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Add seven new free models — all keyless — for 37 in total (12 OpenCode Zen + 16 KiloCode Gateway + 1 Cline + 8 OVHcloud AI Endpoints).
|
|
8
|
+
|
|
9
|
+
New on OpenCode Zen:
|
|
10
|
+
|
|
11
|
+
- `minimax-m3.1-flash-free` (MiniMax M3.1 Flash): 1M context, 524,288 output, vision. Effort `low` through `max`.
|
|
12
|
+
- `flint-40b-free` (Flint 40B): 262,144 context, 65,536 output, text-only. Effort `low` through `max`. Availability is bursty — an upstream 503 burst means retry, not that the model is gone.
|
|
13
|
+
|
|
14
|
+
New on KiloCode Gateway (OpenRouter-style reasoning, keyless):
|
|
15
|
+
|
|
16
|
+
- `minimax/minimax-m3.1-flash:free` (MiniMax M3.1 Flash): 1M context, 524,288 output, vision. Reasoning is mandatory upstream and there are no reasoning deltas on tool turns.
|
|
17
|
+
- `nvidia/nemotron-3.5-super-vl:free` (Nemotron 3.5 Super VL): 1M context, 131,072 output, vision. NVIDIA's trial terms allow training on prompts, and `max` effort can draw a transient 502/503 worth retrying.
|
|
18
|
+
- `reflection/beam-free` (Beam 1.0), alias `beam-kilo`: 1,048,576 context, 131,072 output, text-only. Reasoning is always on.
|
|
19
|
+
- `recursive/flint-v1.0:free` (Flint 40B), alias `flint-kilo`: 262,144 context, 65,536 output, text-only. Thinking is mandatory and can consume a tight output budget.
|
|
20
|
+
- `xingchen-agi/xing4.0-29b-a4b:free` (Xing4.0 29B A4B), alias `xing4.0`: 262,144 context, 235,929 output, text-only. Expect the shared upstream 429 quota often; that is a rate limit, not a dead model.
|
|
21
|
+
|
|
22
|
+
Short names: `minimax-m3.1-flash` and `flint` stay on the Zen lane. The Kilo twins answer to `flint-kilo` and `beam-kilo`, while `beam` and `nemotron-3.5-super-vl` keep pointing at their existing Zen entries, so no saved selection changes lane.
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- Protocol cleanup around reasoning effort, failover limits and tool definitions.
|
|
27
|
+
|
|
28
|
+
Three small drifts between what a lane accepted and what it advertised are settled. Unsupported reasoning-effort knobs are now clamped on the Chat Completions path exactly as the Responses path already clamped them, so a too-high or unsupported level keeps the turn on the same model instead of walking into an upstream refusal. Failover bodies headed to Kilo now clamp `max_completion_tokens` alongside `max_tokens`, saving a wasted round trip. The user-facing tool definitions now carry `strict` in the position the Chat Completions shape canonically uses, with both positions still accepted when reading a caller's tools back.
|
|
29
|
+
|
|
30
|
+
The Anthropic-shaped messages lane no longer deletes Anthropic-native forced tool choices (`any` or a named tool) — it was documented to keep the caller's own choice semantics and now actually does; the chat and Responses lanes still normalize choices to `auto`-or-absent as upstream requires.
|
|
31
|
+
- Two OVH-hosted models no longer lose their answer to an upstream quirk, and an empty upstream stream now reports itself instead of looking like a blank reply.
|
|
32
|
+
|
|
33
|
+
Qwen3.8 27B on OVH answers a request carrying a thinking-effort knob with an empty stream; the proxy now drops that knob for this one model, so the turn produces the model's own reasoning and answer as usual. Mistral Small 3.2 24B requires tool-call ids of exactly nine letters and digits and discards the whole turn otherwise, so ids replayed from your session history are rewritten to that shape — consistently, so an assistant call and the tool result that answers it still match.
|
|
34
|
+
|
|
35
|
+
When OVH does return nothing at all, a non-streaming request now answers with a clear `502` and "upstream returned an empty stream" instead of a fabricated empty completion.
|
|
36
|
+
- Retire Cline's `cline-free/muse-spark-1.3-contributor` from the catalog.
|
|
37
|
+
|
|
38
|
+
Cline stopped resolving the id: every request answers HTTP 404 `model not found` with the hint `Cline has no such model`, while the sibling Cline model still resolves on the same account. The picker no longer offers Muse Spark 1.3. The id stays on the retired list, so a stale local cache or a refreshed upstream listing cannot bring it back.
|
|
39
|
+
|
|
40
|
+
The Cline model that remains (`z-ai/glm-5.3-flash`) still works once the signed-in account has free credit. Clients that already saved the retired model simply stop seeing it.
|
|
41
|
+
- Retire `exo-free` from the catalog.
|
|
42
|
+
|
|
43
|
+
The free endpoint answers HTTP 410 "Model exo-free has been deprecated" on every request path and the model is no longer on the service's model list, so the picker no longer offers it. The id stays on the retired list, which means a stale local cache or a refreshed upstream listing cannot put it back.
|
|
44
|
+
- Parallel tool calls streamed through the proxy no longer corrupt each other.
|
|
45
|
+
|
|
46
|
+
Several free models label every parallel tool call with the same index while still giving each call its own id. On streamed turns the proxy merged calls by index alone, so the argument fragments of two parallel calls interleaved into one packet: one call could vanish entirely, or the arguments came through as glued-alongside JSON the host had to reject (which looked like the model failing at tool calling and the turn being retried). Streamed tool calls are now tracked the same way the assembled (non-streamed) ones always were — per call id, falling back to the wire index only when a call carries none — and a call whose index collides with another is renumbered so the host still sees two well-formed, re-executable calls. Streams with a single tool call are byte-for-byte unchanged.
|
|
47
|
+
- Streamed tool calls now reach the client with the tool name exactly once.
|
|
48
|
+
|
|
49
|
+
One upstream (OpenCode Zen) streams a single tool call as two frames that BOTH carry the complete name: a name-only frame, then a frame repeating that name together with the arguments. The proxy appended every name it saw, so the assembled call came out named `get_weatherget_weather` — a name no tool declaration matches. Streamed hosts bound the call by index and happened to tolerate it; anything reading the non-streamed aggregate, and any host that trusts the assembled packet, got a call it could not execute. A name that arrives alongside arguments is now treated as the authoritative whole name and replaces what was accumulated, while name-only deltas keep appending so ordinary piecewise-streamed names still reassemble. Both the streamed path and the assembled path apply the same rule.
|
|
50
|
+
- Failed and retried tool-call turns are now recorded locally so they can be diagnosed after the fact.
|
|
51
|
+
|
|
52
|
+
Every streamed turn that declared tools — and every non-streamed Chat Completions turn — leaves one line in `<data dir>/toolcall-audit.jsonl` describing how it ended: the caller's declared tool names, the calls the model actually made (name, argument size, whether the arguments parsed, whether the name was one the caller declared), the finish reason, and the classified failure. A turn where the model answered with plain text instead of calling a tool, or called a name the host never declared, now says so instead of vanishing into a generic red error. A Responses or Messages body served without an event stream is recorded as unclassifiable rather than counted against the model.
|
|
53
|
+
|
|
54
|
+
Each upstream attempt abandoned in favour of another is recorded with its reason — rate limit, gateway timeout, payload limit, relay auth rejection, header timeout, socket fault — and, when the host re-sends the same conversation within ten minutes, the new turn carries the earlier one's request id and failure, so a failed first attempt and its host retry are attributable to one conversation.
|
|
55
|
+
|
|
56
|
+
The log records names, sizes, booleans and reasons only: message text, tool schemas and tool-argument values are never written. It is best-effort, never affects a request, stays empty for turns that declared no tools, and rotates to `.1` at 5 MiB. Two `jq` one-liners in the architecture docs list failures by model and retried conversations.
|
|
57
|
+
|
|
3
58
|
## 1.40.1
|
|
4
59
|
|
|
5
60
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pi-freeflow
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 37 free models with up to 1M context. No API keys to manage. Add your own relays to spread requests across more IPs.
|
|
4
4
|
|
|
5
5
|
Thin by design: a model list, a relay proxy, and a log. The host (`pi-ai`) handles thinking, normalization, and provider behavior.
|
|
6
6
|
|
|
@@ -16,12 +16,13 @@ Thin by design: a model list, a relay proxy, and a log. The host (`pi-ai`) handl
|
|
|
16
16
|
|
|
17
17
|
| Feature | What it does | Cost |
|
|
18
18
|
| :--- | :--- | :--- |
|
|
19
|
-
| **
|
|
19
|
+
| **37 free models** | 12 from OpenCode Zen, 16 from KiloCode Gateway, 1 from Cline, 8 from OVHcloud AI Endpoints, context windows up to 1M. Full list below. | **$0** |
|
|
20
20
|
| **Relay pool** | Route requests through your own relays: a self-hosted egress pool (many egress IPs on one small host) or Cloudflare Workers / Deno Deploy / Vercel Edge workers. Traffic spreads across the pool, or stays on one sticky relay. A relay that rate-limits, times out, or drops the connection cools down while healthy ones take its traffic. | **$0** beyond your host or platforms' free tiers |
|
|
21
21
|
| **Automatic fallback** | When every relay is cooling down, requests go direct to upstream instead of failing. | **$0** |
|
|
22
|
-
| **Short model names** | Most models have a slash-free, colon-free alias, plus an optional `:effort` suffix for thinking depth. You type `freeflow/<name>`; the
|
|
22
|
+
| **Short model names** | Most models have a slash-free, colon-free alias, plus an optional `:effort` suffix for thinking depth. You type `freeflow/<name>`; the Cline model has no alias and must be typed in full. | **$0** |
|
|
23
23
|
| **Shared local proxy** | One daemon on `127.0.0.1:28180` serves every session on the machine, so parallel subagents reuse it instead of opening their own connections. Loopback-only and token-gated: hosts send the token automatically, external tools read `~/.pi/agent/token`. | **$0** |
|
|
24
24
|
| **Logs you can read** | `~/.pi/agent/pi-freeflow.log`, rotated at 10MB. Tail it with `/freeflow logs`. | **$0** |
|
|
25
|
+
| **Tool-call diagnostics** | `~/.pi/agent/toolcall-audit.jsonl`, rotated at 5MB. One line per streamed turn that declared tools (and per non-streamed Chat Completions turn) — the calls it made, argument size and validity, finish reason, classified failure — plus one line per abandoned upstream attempt. Names, sizes and reasons only. | **$0** |
|
|
25
26
|
|
|
26
27
|
---
|
|
27
28
|
|
|
@@ -188,21 +189,21 @@ The same command set works identically in OMP and Pi:
|
|
|
188
189
|
/freeflow use <url|index|label> # Switch active relay
|
|
189
190
|
/freeflow url <url> # Set the active relay URL directly
|
|
190
191
|
/freeflow add <url> [label] # Add new relay to the pool
|
|
191
|
-
/freeflow label <index|url> <name
|
|
192
|
+
/freeflow label|rename <index|url> <name> # Assign a friendly label to a relay
|
|
192
193
|
/freeflow remove <index|url|label># Remove a relay from the pool
|
|
193
194
|
/freeflow test <index|url|label> # Probe a relay for reachability and latency
|
|
194
195
|
/freeflow on|off|auto|sticky|spread # Relay mode (auto/sticky = sticky primary, spread = rotate pool)
|
|
195
196
|
/freeflow deploy <platform> # Guided relay deploy: vercel|cloudflare|deno, token in-memory, auto-adds
|
|
196
|
-
/freeflow logs [lines] # Inspect recent proxy logs
|
|
197
|
+
/freeflow logs [lines] # Inspect recent proxy logs (alias: log)
|
|
197
198
|
/freeflow trace [req-id] # Tail logs filtered by request correlation ID
|
|
198
199
|
/freeflow refresh # Reload the model catalog from live upstreams
|
|
199
200
|
/freeflow reload models # Alias of refresh
|
|
200
201
|
/freeflow update # Check for and install a package update
|
|
201
|
-
/freeflow autoupdate [on|off] # View or toggle automatic background updates
|
|
202
|
+
/freeflow autoupdate [on|off] # View or toggle automatic background updates (also: update auto on|off)
|
|
202
203
|
/freeflow install-startup # Install the login-startup hook so the daemon comes up with your session
|
|
203
204
|
/freeflow uninstall-startup # Remove the login-startup hook
|
|
204
205
|
/freeflow debug on | off # Toggle full HTTP lifecycle debug logging
|
|
205
|
-
/freeflow kill # Stop the shared proxy daemon now (restarts on next use)
|
|
206
|
+
/freeflow kill # Stop the shared proxy daemon now (restarts on next use; aliases: stop, shutdown)
|
|
206
207
|
/freeflow cline login # Sign in through the browser, save to the per-user pool
|
|
207
208
|
/freeflow cline accounts # List saved Cline logins
|
|
208
209
|
/freeflow cline logout # Remove a saved Cline login
|
|
@@ -213,17 +214,17 @@ The same command set works identically in OMP and Pi:
|
|
|
213
214
|
|
|
214
215
|
---
|
|
215
216
|
|
|
216
|
-
###
|
|
217
|
+
### 37 models, one command
|
|
217
218
|
|
|
218
219
|
```bash
|
|
219
220
|
/model → freeflow → pick
|
|
220
221
|
```
|
|
221
222
|
|
|
222
|
-
Every model specification is reconciled against [models.dev](https://models.dev/), [OpenRouter](https://openrouter.ai/), and OMP's built-in catalog on 2026-10-
|
|
223
|
+
Every model specification is reconciled against [models.dev](https://models.dev/), [OpenRouter](https://openrouter.ai/), and OMP's built-in catalog on 2026-10-22: a free and a non-free variant of the same model share the highest published context window, output ceiling, and input modalities across its twins. The catalog follows that published spec even where a free lane enforces a lower runtime cap; the authentic upstream error is surfaced above it. Where the lane itself refuses above a published figure, the served boundary wins (MiniMax M3.1 Flash: published 512,000, served 524,288). (Measured runtime caps: MiMo ~348K, GLM 5.3 Flash ~297K, Laguna S 262K.) Lane-published hard caps stay authoritative where the lane refuses above them: OVH's advertised context windows, the 1,000,000-token Nemotron 3 Ultra ceiling, and Dots3-Note's 393,216 served window (its only provider refused a 460,800 output budget on 2026-10-20).
|
|
223
224
|
|
|
224
225
|
The host catalog carries text and image inputs only. Upstream video and audio support (MiMo: audio + video; GLM 5.3 Flash: video + PDF; Nemotron Super VL and Nano Omni: video) is therefore not selectable from the picker and is not declared in `input`.
|
|
225
226
|
|
|
226
|
-
#### OpenCode Zen (
|
|
227
|
+
#### OpenCode Zen (12 models), Chat Completions API
|
|
227
228
|
|
|
228
229
|
Good defaults for long coding sessions and agentic work.
|
|
229
230
|
|
|
@@ -238,19 +239,20 @@ Good defaults for long coding sessions and agentic work.
|
|
|
238
239
|
| `longcat-2.5-preview-free` | Meituan LongCat | **1M** (1.048.576) | **131K** (131.072) | `minimal … xhigh` | ✅ |
|
|
239
240
|
| `glm-5.3-flash-free` | Zhipu AI | **1M** (1.048.576) | **131K** (131.072) | `low / high / max` | ✅ |
|
|
240
241
|
| `beam-free` | Reflection AI | **1M** (1.048.576) | **131K** (131.072) | `low … max` | ❌ |
|
|
241
|
-
| `exo-free` | Undisclosed | **1M** (1.048.576) | **131K** (131.072) | `minimal … xhigh` | ✅ |
|
|
242
242
|
| `deepseek-v4.1-flash-free` | DeepSeek | **1M** (1.048.576) | **393K** (393.216) | `low / high / max` | ✅ |
|
|
243
|
+
| `minimax-m3.1-flash-free` | MiniMax | **1M** (1.000.000) | **512K** (524.288) | `low … max` | ✅ |
|
|
244
|
+
| `flint-40b-free` | Recursive | **262K** (262.144) | **65K** (65.536) | `low … max` | ❌ |
|
|
243
245
|
|
|
244
|
-
#### KiloCode Gateway (
|
|
246
|
+
#### KiloCode Gateway (16 models), OpenRouter compatible
|
|
245
247
|
|
|
246
|
-
Keyless access.
|
|
248
|
+
Keyless access. Most rows have a short alias (shown with the full ID in parentheses); a row that shows only the full ID has no short form.
|
|
247
249
|
|
|
248
250
|
| Model ID | Creator / Lab | Context | Max Output | Thinking | Vision |
|
|
249
251
|
| :--- | :--- | :--- | :--- | :--- | :--- |
|
|
250
252
|
| `dots-3-note-preview` (`dots-studio/...:free`) | Dots Studio | **384K** (393.216) | **256K** (262.144) | `minimal…xhigh`\* | ✅ |
|
|
251
253
|
| `nemotron-3-nano-omni` (`nvidia/...:free`) | NVIDIA | **262K** (262.144) | **65K** (65.536) | `minimal…xhigh`\* | ✅ |
|
|
252
254
|
| `nemotron-3-ultra-550b` (`nvidia/...:free`) | NVIDIA | **1M** (1.000.000) | **182K** (182.520) | `minimal…xhigh`\* | ❌ |
|
|
253
|
-
| `nvidia/nemotron-3.5-lightning:free` | NVIDIA | **1M** (1.000.000) | **235K** (235.929) | `minimal…xhigh`\* | ❌ |
|
|
255
|
+
| `nemotron-3.5-lightning` (`nvidia/nemotron-3.5-lightning:free`) | NVIDIA | **1M** (1.000.000) | **235K** (235.929) | `minimal…xhigh`\* | ❌ |
|
|
254
256
|
| `north-mini-code` (`cohere/...:free`) | Cohere | **256K** (256.000) | **64K** (64.000) | `minimal…xhigh`\* | ❌ |
|
|
255
257
|
| `laguna-s-2.1:free` (`poolside/...:free`) | Poolside | **1M** (1.048.576) | **131K** (131.072) | `minimal…xhigh`\* | ❌ |
|
|
256
258
|
| `laguna-xs-2.1:free` (`poolside/...:free`) | Poolside | **262K** (262.144) | **32K** (32.768) | `minimal…xhigh`\* | ❌ |
|
|
@@ -258,10 +260,15 @@ Keyless access. Short aliases work for every row (the full ID is in parentheses)
|
|
|
258
260
|
| `kilo-auto` (`kilo-auto/free`) | Kilo Gateway Auto | **256K** (256.000) | **10K** (10.000) | `minimal…xhigh`\* | ❌ |
|
|
259
261
|
| `openrouter` (`openrouter/free`) | OpenRouter Free | **200K** (200.000) | **65K** (65.536) | `minimal…xhigh`\* | ✅ |
|
|
260
262
|
| `glyph-cluster` (`stealth/glyph-cluster`) | Stealth preview (lab undisclosed) | **256K** (256.000) | **256K** (256.000) | `minimal…xhigh`\* | ❌ |
|
|
263
|
+
| `minimax/minimax-m3.1-flash:free` | MiniMax | **1M** (1.000.000) | **512K** (524.288) | `low…max`\* | ✅ |
|
|
264
|
+
| `nvidia/nemotron-3.5-super-vl:free` | NVIDIA | **1M** (1.000.000) | **131K** (131.072) | `minimal…xhigh`\* | ✅ |
|
|
265
|
+
| `beam-kilo` (`reflection/beam-free`) | Reflection AI | **1M** (1.048.576) | **131K** (131.072) | `low…max`\* | ❌ |
|
|
266
|
+
| `flint-kilo` (`recursive/flint-v1.0:free`) | Recursive | **262K** (262.144) | **65K** (65.536) | `minimal…xhigh`\* | ❌ |
|
|
267
|
+
| `xing4.0` (`xingchen-agi/xing4.0-29b-a4b:free`) | XingChen | **262K** (262.144) | **235K** (235.929) | `minimal…xhigh`\* | ❌ |
|
|
261
268
|
|
|
262
269
|
\* Levels are forwarded as-is through the OpenRouter-style nested `reasoning` parameter; effort mapping is decided by each model. MiMo collapses `minimal→low` and `xhigh→high` upstream, so its selector shows 5 labels but only 3 distinct effort values.
|
|
263
270
|
|
|
264
|
-
#### Cline (
|
|
271
|
+
#### Cline (1 model), OpenAI compatible
|
|
265
272
|
|
|
266
273
|
Rotating per-account promo, direct only (never through the relay pool). Sign in through your browser with `/freeflow cline login`, manage with `/freeflow cline accounts` and `/freeflow cline logout`. Requests roll across saved logins when one hits its daily free limit.
|
|
267
274
|
|
|
@@ -271,7 +278,6 @@ Each account can only be saved once: an extra login for an account you already h
|
|
|
271
278
|
|
|
272
279
|
| Model ID | Creator / Lab | Context | Max Output | Thinking | Vision |
|
|
273
280
|
| :--- | :--- | :--- | :--- | :--- | :--- |
|
|
274
|
-
| `cline-free/muse-spark-1.3-contributor` | Meta Superintelligence Labs | **1M** (1.048.576) | **131K** (131.072) | `minimal…xhigh`\* | ✅ |
|
|
275
281
|
| `z-ai/glm-5.3-flash` | Zhipu AI | **1M** (1.048.576) | **131K** (131.072) | `low/high/max` | ✅ |
|
|
276
282
|
#### OVHcloud AI Endpoints (8 models), OpenAI compatible
|
|
277
283
|
|
|
@@ -303,13 +309,21 @@ cat ~/.pi/agent/pi-freeflow.log | tail -n 50
|
|
|
303
309
|
/freeflow debug on
|
|
304
310
|
```
|
|
305
311
|
|
|
312
|
+
**Tool-call audit.** A streamed turn that declares tools — and a non-streamed Chat Completions turn — also writes a line to `~/.pi/agent/toolcall-audit.jsonl`, so a turn the host abandoned or retried can be explained without reproducing it:
|
|
313
|
+
|
|
314
|
+
- `summary` — one line per finished turn: the tool names the caller declared, the calls the model made (argument byte size and whether they parsed), the finish reason, and a classified failure when there was one.
|
|
315
|
+
- `attempt-failure` — one line per upstream attempt abandoned in favour of another, with the reason (rate limit, timeout, payload too large, rejected relay, final upstream verdict) and the HTTP status when one arrived.
|
|
316
|
+
- `retry-detected` — the host re-sent a conversation the proxy had already seen; `retryOf` names the earlier turn and carries its failure.
|
|
317
|
+
|
|
318
|
+
The log holds names, sizes, booleans and reasons only: no message text, no tool schemas, no argument values. It rotates once to `toolcall-audit.jsonl.1` at 5MB, and a turn that declared no tools writes nothing. `docs/pages/architecture.md` documents every field and has jq recipes for the common questions.
|
|
319
|
+
|
|
306
320
|
---
|
|
307
321
|
|
|
308
322
|
### Design
|
|
309
323
|
|
|
310
|
-
This package stays thin. It ships three things: a model catalog, a relay proxy, and
|
|
324
|
+
This package stays thin. It ships three things: a model catalog, a relay proxy, and logs. There is no build step and one runtime dependency (`undici`). Thinking and prompt normalization stay with the host (`pi-ai`).
|
|
311
325
|
|
|
312
|
-
About
|
|
326
|
+
About 57k lines including tests: `src/` is roughly 21.1k across 31 files, and the 93 test files add roughly 35.6k more. The full suite (sandboxed, network-mocked) and typecheck pass before every release. See CHANGELOG.md.
|
|
313
327
|
|
|
314
328
|
---
|
|
315
329
|
|
|
@@ -382,7 +396,7 @@ pnpm smoke # verifies extensions/index.ts loads without crashing
|
|
|
382
396
|
#### Project structure
|
|
383
397
|
|
|
384
398
|
```
|
|
385
|
-
├── models.ts #
|
|
399
|
+
├── models.ts # 37-model catalog definitions
|
|
386
400
|
├── index.ts # extension entry, lifecycle hooks
|
|
387
401
|
├── catalog.ts # model catalog cache (24h disk)
|
|
388
402
|
├── proxy.ts # local proxy server (127.0.0.1:28180)
|
|
@@ -398,6 +412,7 @@ pnpm smoke # verifies extensions/index.ts loads without crashing
|
|
|
398
412
|
├── probe.ts # relay reachability and latency probe
|
|
399
413
|
├── upstream-health.ts # Zen free-tier gating watch and Kilo failover
|
|
400
414
|
├── stream-pipe.ts # SSE stream piping and truncation resilience
|
|
415
|
+
├── toolcall-audit.ts # per-turn tool-call audit log (names and sizes only)
|
|
401
416
|
├── opencode-fingerprint.ts # Zen free-tier client fingerprint enforcement
|
|
402
417
|
├── system-ca-fetch.ts # OS-trust-store retry for intercepted TLS
|
|
403
418
|
├── commands.ts # /freeflow CLI subcommands
|
|
@@ -405,7 +420,9 @@ pnpm smoke # verifies extensions/index.ts loads without crashing
|
|
|
405
420
|
├── cline-accounts.ts # per-slot Cline key pool with failover
|
|
406
421
|
├── cline-device-auth.ts # Cline WorkOS device-flow login
|
|
407
422
|
├── update-checker.ts # npm registry version check, cached
|
|
423
|
+
├── update-core.ts # version facts and update step runner
|
|
408
424
|
├── updater.ts # package-manager choice for /freeflow update
|
|
425
|
+
├── atomic-write.ts # temp-file + rename writes, retried across Windows locks
|
|
409
426
|
├── config.ts # constants, whitelists, paths, and runtime settings
|
|
410
427
|
├── logger.ts # file logger with 10MB rotation
|
|
411
428
|
└── types.ts # shared type definitions
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-freeflow",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.41.0",
|
|
5
5
|
"description": "Thin provider for OMP/Pi — model list + dumb relay proxy + log; host pi-ai owns thinking/normalization",
|
|
6
6
|
"main": "extensions/index.ts",
|
|
7
7
|
"types": "src/index.ts",
|
package/src/catalog.ts
CHANGED
|
@@ -45,12 +45,11 @@ import type {
|
|
|
45
45
|
* - mimo-v2.5-free: decommissioned upstream (2026-10-07) — HTTP 401 "Model mimo-v2.5-free is not supported".
|
|
46
46
|
* Re-probed 2026-10-16: 410 "Model mimo-v2.5-free has been deprecated. Use
|
|
47
47
|
* mimo-v2.6-flash-free instead." The docs still list it as free; they are stale.
|
|
48
|
-
* - exo-free: dead-listed 2026-10-09 (410 "Model exo-free has been deprecated")
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* id back to this set unless it dies again.
|
|
48
|
+
* - exo-free: dead-listed 2026-10-09 (410 "Model exo-free has been deprecated"),
|
|
49
|
+
* revived upstream on 2026-10-16, and dead again on 2026-10-22: the endpoint
|
|
50
|
+
* answers 410 "Model exo-free has been deprecated" on every path through the
|
|
51
|
+
* relay pool and the id is absent from the live Zen listing. Permanently
|
|
52
|
+
* dead-listed so no disk cache or upstream merge can put it back.
|
|
54
53
|
* - fledge-alpha-free: withdrawn upstream (2026-10-09) — absent from the live
|
|
55
54
|
* Zen list, chat 401 "Model fledge-alpha-free is not supported".
|
|
56
55
|
* - inclusionai/ling-3.0-flash-sante:free: demoted to paid on Kilo (2026-10-09) —
|
|
@@ -75,10 +74,10 @@ import type {
|
|
|
75
74
|
* retired upstream (2026-10-13) — probed live twice each against the real
|
|
76
75
|
* Cline pool, every attempt answered HTTP 404 `{"error":"model not found",
|
|
77
76
|
* "success":false,"hint":"Cline has no such model. Refresh the model list and
|
|
78
|
-
* retry."}`, while the
|
|
79
|
-
* muse-spark-1.3-contributor
|
|
80
|
-
* same account and request shape. The lane is healthy; these three
|
|
81
|
-
* gone, so they must never re-enter the picker from disk cache or an
|
|
77
|
+
* retry."}`, while the sibling `z-ai/glm-5.3-flash` (and, until its own
|
|
78
|
+
* retirement below, `cline-free/muse-spark-1.3-contributor`) answered 200
|
|
79
|
+
* with the same account and request shape. The lane is healthy; these three
|
|
80
|
+
* ids are gone, so they must never re-enter the picker from disk cache or an
|
|
82
81
|
* upstream merge.
|
|
83
82
|
*/
|
|
84
83
|
export const DEAD_MODEL_IDS = new Set<string>([
|
|
@@ -136,8 +135,17 @@ export const DEAD_MODEL_IDS = new Set<string>([
|
|
|
136
135
|
// muse-spark-1.2-contributor-free: decommissioned upstream (2026-10-18) — answers
|
|
137
136
|
// HTTP 410 "Model muse-spark-1.2-contributor-free has been deprecated" while the
|
|
138
137
|
// same request shape on the surviving Zen models answers 200. Retired from the
|
|
139
|
-
// static catalog the same day
|
|
138
|
+
// static catalog the same day.
|
|
140
139
|
"muse-spark-1.2-contributor-free",
|
|
140
|
+
// cline-free/muse-spark-1.3-contributor: gone from Cline's model inventory
|
|
141
|
+
// (2026-10-22) — three live probes through the real pool, spaced over ~10
|
|
142
|
+
// minutes, each answered HTTP 404 `{"error":"model not found",
|
|
143
|
+
// "success":false,"hint":"Cline has no such model. Refresh the model list and
|
|
144
|
+
// retry."}`, while `z-ai/glm-5.3-flash` resolved on the same account (its 402
|
|
145
|
+
// insufficient_credits is raised after model resolution). The id was the last
|
|
146
|
+
// entry declaring `api: "openai-responses"`; it stays retired so a stale disk
|
|
147
|
+
// cache cannot put it back in the picker.
|
|
148
|
+
"cline-free/muse-spark-1.3-contributor",
|
|
141
149
|
// nvidia/nemotron-3-super-120b-a12b:free: dead route (2026-10-21) — Kilo answers
|
|
142
150
|
// 404 "Function '<uuid>': Not found for account ..." from its Nvidia provider on
|
|
143
151
|
// every attempt, from two independent egress IPs, while sibling Nvidia ids answer
|
|
@@ -147,6 +155,10 @@ export const DEAD_MODEL_IDS = new Set<string>([
|
|
|
147
155
|
// HTTP 404 "The requested model is currently unavailable" (unavailable_model) on
|
|
148
156
|
// every attempt, from two independent egress IPs; it is no longer served.
|
|
149
157
|
"thinkingmachines/inkling-small:free",
|
|
158
|
+
// exo-free: re-deprecated upstream (2026-10-22) — 410 ModelDeprecated on every
|
|
159
|
+
// path and gone from the live Zen listing, four days after it was re-registered
|
|
160
|
+
// from a 2026-10-16 revival. Permanently dead-listed.
|
|
161
|
+
"exo-free",
|
|
150
162
|
]);
|
|
151
163
|
/**
|
|
152
164
|
* Free-tier allowlist for anything entering the picker via network or stale disk.
|
|
@@ -163,10 +175,10 @@ export function isFreeCatalogId(id: string): boolean {
|
|
|
163
175
|
/**
|
|
164
176
|
* Purge paid/dead entries from a catalog list and repair known models against
|
|
165
177
|
* the static definitions. Stale disk caches predate the paid filter and the
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
* reinstall.
|
|
178
|
+
* responses-api entries (the last one, Cline's Muse Spark 1.3, retired on
|
|
179
|
+
* 2026-10-22), so loading them verbatim replays a wrong api (chat/completions
|
|
180
|
+
* for a responses-only model -> upstream 500) until the 24h TTL expires.
|
|
181
|
+
* Repairing here self-heals on the next refresh without a reinstall.
|
|
170
182
|
*/
|
|
171
183
|
export function sanitizeCatalogModels(models: RegisteredModel[]): RegisteredModel[] {
|
|
172
184
|
const out: RegisteredModel[] = [];
|
|
@@ -186,7 +198,7 @@ export function sanitizeCatalogModels(models: RegisteredModel[]): RegisteredMode
|
|
|
186
198
|
}
|
|
187
199
|
/**
|
|
188
200
|
* In-memory cache of currently active/available free models.
|
|
189
|
-
* Initialized with all
|
|
201
|
+
* Initialized with all 37 verified models for 0ms instant availability.
|
|
190
202
|
*/
|
|
191
203
|
let aliveCatalog: RegisteredModel[] = ALL_MODELS.map((m) => ({
|
|
192
204
|
...m,
|
|
@@ -305,11 +317,11 @@ export function enrichModelDef(raw: RawModelItem, source: Upstream): RegisteredM
|
|
|
305
317
|
maxTokens = 131_072;
|
|
306
318
|
}
|
|
307
319
|
|
|
308
|
-
// Muse Spark
|
|
309
|
-
//
|
|
310
|
-
// known id short-circuits above, so this only decides for
|
|
311
|
-
// dynamically — matching the family keeps a future muse-spark
|
|
312
|
-
// endpoint it would 500 on.
|
|
320
|
+
// Muse Spark was served on the responses API upstream: the Cline entry
|
|
321
|
+
// declared it until 2026-10-22, and the Zen 1.2/1.3 contributor ids retired
|
|
322
|
+
// before that. Every known id short-circuits above, so this only decides for
|
|
323
|
+
// ids Zen lists dynamically — matching the family keeps a future muse-spark
|
|
324
|
+
// id off the chat endpoint it would 500 on.
|
|
313
325
|
const isResponses = idLower.includes("muse-spark");
|
|
314
326
|
|
|
315
327
|
return {
|
package/src/cline-accounts.ts
CHANGED
|
@@ -130,9 +130,10 @@ const CLINE_CLIENT_VERSION = "3.5.54";
|
|
|
130
130
|
* `cline-free/*` id space (measured live 2026-09-22: that prefix answers 403
|
|
131
131
|
* "only available via Cline product surfaces" with a bare bearer, and 200 with
|
|
132
132
|
* this set; every non-prefixed id answers 200 either way). The header is kept
|
|
133
|
-
* because
|
|
134
|
-
*
|
|
135
|
-
*
|
|
133
|
+
* because `cline-free/*` ids can still appear in a stale host registration —
|
|
134
|
+
* until 2026-10-13 that prefix covered four models, and
|
|
135
|
+
* `cline-free/muse-spark-1.3-contributor` survived it until 2026-10-22. It also
|
|
136
|
+
* decided the free-model feed at the time: five entries for
|
|
136
137
|
* `cline-sdk` / `cline-cli` / the `VSCode Extension` label against six for
|
|
137
138
|
* `cline-desktop`, the extra one being `cline-free/kimi-k3`. That sixth model
|
|
138
139
|
* is retired upstream and no longer requested. Version and User-Agent stay
|
package/src/config.ts
CHANGED
|
@@ -450,6 +450,19 @@ export function resolveUpstreamHealthPath(): string {
|
|
|
450
450
|
}
|
|
451
451
|
}
|
|
452
452
|
|
|
453
|
+
/** JSONL audit of tool-call turns that failed or needed repair (src/toolcall-audit.ts). */
|
|
454
|
+
export function resolveToolcallAuditPath(): string {
|
|
455
|
+
try {
|
|
456
|
+
return dataFilePath("toolcall-audit.jsonl");
|
|
457
|
+
} catch {
|
|
458
|
+
return path.join(
|
|
459
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
460
|
+
"..",
|
|
461
|
+
".toolcall-audit.jsonl",
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
453
466
|
export function resolveSessionPinPath(): string {
|
|
454
467
|
try {
|
|
455
468
|
return dataFilePath("pi-freeflow-session-pins.json");
|