pi-freeflow 1.35.0 → 1.36.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 +29 -0
- package/README.md +6 -5
- package/package.json +1 -1
- package/src/index.ts +1 -1
- package/src/models.ts +23 -2
- package/src/tool-args.ts +63 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.36.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- bf358da: A new free model on the OpenCode Zen lane: Cat Jam
|
|
8
|
+
|
|
9
|
+
`cat-jam-free` joins the picker, bringing the catalog to 37 models (11 OpenCode, 15 Kilo, 2 Cline, 9 OVHcloud). No account, no key, no setup — it is keyless like every other Zen free model.
|
|
10
|
+
|
|
11
|
+
It thinks, and it has a 262,144-token window. That number was measured the hard way: the gateway happily accepts prompts four times that size and answers normally, but the model can no longer read them. A marker planted at the end of a 248,232-token prompt comes back; at 250,272 tokens it does not. So the advertised window is what the model can actually use, not merely what the service tolerates.
|
|
12
|
+
|
|
13
|
+
Two honest limits worth knowing before you pick it:
|
|
14
|
+
|
|
15
|
+
- **It is not available on the Responses API.** Requests sent that way are refused with a protocol error. It works on chat completions, which is what hosts use by default, but a host pinned specifically to Responses will not see it.
|
|
16
|
+
- **Thinking depth starts at "low".** The lowest setting is refused, so "minimal" is not offered; low, medium, high and extra-high all work.
|
|
17
|
+
|
|
18
|
+
The output budget is 131,072, the published figure for this model.
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- bf358da: Long sessions no longer stall on a tool call whose arguments arrived spliced
|
|
23
|
+
|
|
24
|
+
Some models open their next tool call by echoing the tail of the one they had just seen, and occasionally stop before the closing brace. The result is a single argument string carrying both faults at once: the previous call's leftover text in front, and the last brace missing. The whole call was then rejected, the turn failed, and the host retried — which resent the same broken payload, so a long session could get stuck repeating it.
|
|
25
|
+
|
|
26
|
+
Both faults are now repaired together, so the caller's own arguments arrive intact.
|
|
27
|
+
|
|
28
|
+
The repair stays deliberately narrow. It only fires when a leftover fragment is actually present in front of the object, which is the evidence that the model was mid-call rather than that the request itself was malformed, and only when what follows is a complete object merely missing its closing brace. Arguments cut off part-way through a value are still refused untouched, because completing those would silently shorten them — and these arguments carry shell commands.
|
|
29
|
+
|
|
30
|
+
Tool calls that were already well-formed are unaffected and pass through byte-identically.
|
|
31
|
+
|
|
3
32
|
## 1.35.0
|
|
4
33
|
|
|
5
34
|
### Minor 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,7 +16,7 @@ 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** | 11 from OpenCode Zen, 15 from KiloCode Gateway, 2 from Cline, 9 from OVHcloud AI Endpoints, context windows up to 1M. Full list below. | **$0** |
|
|
20
20
|
| **Relay pool** | Route requests through your own Cloudflare Workers, Deno Deploy, and Vercel Edge relays. 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 platforms' free tiers |
|
|
21
21
|
| **Automatic fallback** | When every relay is cooling down, requests go direct to upstream instead of failing. | **$0** |
|
|
22
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 two Cline models have no alias and must be typed in full. | **$0** |
|
|
@@ -185,18 +185,19 @@ The same command set works identically in OMP and Pi:
|
|
|
185
185
|
|
|
186
186
|
---
|
|
187
187
|
|
|
188
|
-
###
|
|
188
|
+
### 37 models, one command
|
|
189
189
|
|
|
190
190
|
```bash
|
|
191
191
|
/model → freeflow → pick
|
|
192
192
|
```
|
|
193
193
|
|
|
194
|
-
#### OpenCode Zen (
|
|
194
|
+
#### OpenCode Zen (11 models), Responses and Chat API
|
|
195
195
|
|
|
196
196
|
Good defaults for long coding sessions and agentic work.
|
|
197
197
|
|
|
198
198
|
| Model ID | Creator / Lab | Context | Max Output | Thinking | Vision |
|
|
199
199
|
| :--- | :--- | :--- | :--- | :--- | :--- |
|
|
200
|
+
| `cat-jam-free` | Cat Jam | **256K** (262.144) | **131K** (131.072) | `low … xhigh` | ❌ |
|
|
200
201
|
| `muse-spark-1.2-contributor-free` | Meta Superintelligence Labs | **1M** (1.048.576) | **131K** (131.072) | `minimal … xhigh` | ✅ |
|
|
201
202
|
| `muse-spark-1.3-contributor-free` | Meta Superintelligence Labs | **1M** (1.048.576) | **131K** (131.072) | `minimal … xhigh` | ✅ |
|
|
202
203
|
| `mimo-v2.6-flash-free` | Xiaomi MiMo | **1M** (1.048.576) | **131K** (131.072) | `minimal … xhigh`\* | ✅ |
|
|
@@ -358,7 +359,7 @@ pnpm smoke # verifies extensions/index.ts loads without crashing
|
|
|
358
359
|
```
|
|
359
360
|
src/
|
|
360
361
|
├── index.ts # extension entry, lifecycle hooks
|
|
361
|
-
├── models.ts #
|
|
362
|
+
├── models.ts # 37-model catalog definitions
|
|
362
363
|
├── catalog.ts # model catalog cache (24h disk)
|
|
363
364
|
├── proxy.ts # local proxy server (127.0.0.1:28180)
|
|
364
365
|
├── client.ts # session-side daemon attach/spawn and heartbeat
|
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.36.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/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* pi-freeflow — Modular, high-resiliency LLM extension for Pi & Oh My Pi (OMP)
|
|
3
3
|
*
|
|
4
|
-
* Provides access to
|
|
4
|
+
* Provides access to 37 free models (11 OpenCode Zen + 15 KiloCode Gateway + 2 Cline + 9 OVH) with:
|
|
5
5
|
* - Single-port daemon reuse on 28180 across concurrent subagents
|
|
6
6
|
* - Multi-cloud rolling egress relays (Vercel Edge, Cloudflare, Deno)
|
|
7
7
|
* - 0ms instant startup with verified static catalog and background live health checks
|
package/src/models.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Static model definitions and upstream routing catalogs for pi-freeflow
|
|
3
3
|
*
|
|
4
|
-
* Defines the
|
|
5
|
-
* -
|
|
4
|
+
* Defines the 37 verified free models (live-verified 2026-10-13):
|
|
5
|
+
* - 11 OpenCode Zen models (2 Responses API + 9 Chat Completions)
|
|
6
6
|
* - 15 KiloCode Keyless Gateway models (OpenRouter format)
|
|
7
7
|
* - 2 Cline direct-only models (per-user pool)
|
|
8
8
|
* - 9 OVHcloud AI Endpoints models (keyless, direct-only)
|
|
@@ -15,6 +15,27 @@ import type { ModelDef, ThinkingLevelMap, Upstream } from "./types.ts";
|
|
|
15
15
|
* Endpoint: https://opencode.ai/zen/v1
|
|
16
16
|
*/
|
|
17
17
|
export const OPENCODE_MODELS: ModelDef[] = [
|
|
18
|
+
{
|
|
19
|
+
id: "cat-jam-free",
|
|
20
|
+
name: "Cat Jam [OpenCode]",
|
|
21
|
+
reasoning: true,
|
|
22
|
+
// Context is 262_144, NOT the 1M an acceptance probe suggests: the gateway
|
|
23
|
+
// answers 200 to oversized prompts and silently truncates. A needle planted
|
|
24
|
+
// at the end of a 248,232-token prompt is recalled; at 250,272 it is not.
|
|
25
|
+
// Cross-checked against the published spec, which agrees.
|
|
26
|
+
contextWindow: 262_144,
|
|
27
|
+
maxTokens: 131_072,
|
|
28
|
+
input: ["text"],
|
|
29
|
+
thinkingLevelMap: {
|
|
30
|
+
off: null,
|
|
31
|
+
minimal: null,
|
|
32
|
+
low: "low",
|
|
33
|
+
medium: "medium",
|
|
34
|
+
high: "high",
|
|
35
|
+
xhigh: "xhigh",
|
|
36
|
+
max: null,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
18
39
|
{
|
|
19
40
|
id: "muse-spark-1.2-contributor-free",
|
|
20
41
|
name: "Muse Spark 1.2 [OpenCode]",
|
package/src/tool-args.ts
CHANGED
|
@@ -357,8 +357,12 @@ export function buildArgRepairSchemas(tools: unknown[]): Map<string, Record<stri
|
|
|
357
357
|
*
|
|
358
358
|
* Both are one complete JSON object with junk in front of it. Only strings that
|
|
359
359
|
* do not parse as-is are considered, and only a position that yields a complete
|
|
360
|
-
* object is accepted, so a well-formed call never reaches this path
|
|
361
|
-
*
|
|
360
|
+
* object is accepted, so a well-formed call never reaches this path.
|
|
361
|
+
*
|
|
362
|
+
* A second live shape (2026-10-13) carried BOTH faults at once — the previous
|
|
363
|
+
* call's tail in front AND the closing brace missing — so the scan also offers
|
|
364
|
+
* the candidate with its closers appended. That offer is made only at the first
|
|
365
|
+
* brace and only when a fragment precedes it; see appendClosers.
|
|
362
366
|
*
|
|
363
367
|
* Returns undefined when nothing salvageable is present.
|
|
364
368
|
*/
|
|
@@ -370,18 +374,67 @@ function parseArguments(argsJson: string): { value: Record<string, unknown>; sal
|
|
|
370
374
|
} catch {
|
|
371
375
|
// fall through to the salvage scan
|
|
372
376
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
377
|
+
for (let i = argsJson.indexOf("{"); i !== -1; i = argsJson.indexOf("{", i + 1)) {
|
|
378
|
+
const candidate = argsJson.slice(i);
|
|
379
|
+
// Two shapes to try, in order: the candidate as written, then the candidate
|
|
380
|
+
// with its missing closers appended. The closer is only offered at the FIRST
|
|
381
|
+
// brace and only when something precedes it — that fragment is the evidence
|
|
382
|
+
// the model was mid-call and lost the tail. A bare `{"op":"init"` stays
|
|
383
|
+
// refused, and so does an inner `{"phase":…` inside an already-truncated
|
|
384
|
+
// object, which is why this is not offered at every brace.
|
|
385
|
+
const candidates = i === 0 ? [candidate] : [candidate, appendClosers(candidate)];
|
|
386
|
+
for (const attempt of candidates) {
|
|
387
|
+
if (attempt === undefined) continue;
|
|
388
|
+
try {
|
|
389
|
+
const value = JSON.parse(attempt);
|
|
390
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
391
|
+
return { value: value as Record<string, unknown>, salvaged: true };
|
|
381
392
|
}
|
|
393
|
+
} catch {
|
|
394
|
+
// not a start position; try the next brace
|
|
395
|
+
}
|
|
382
396
|
}
|
|
397
|
+
}
|
|
383
398
|
return undefined;
|
|
384
399
|
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Append the closers an unterminated JSON object is missing, or undefined when
|
|
403
|
+
* that would mean guessing.
|
|
404
|
+
*
|
|
405
|
+
* Only ever called on a candidate that starts at a `{` preceded by a fragment
|
|
406
|
+
* of the previous call, and only when the candidate's own contents already
|
|
407
|
+
* end cleanly. Two refusals keep this honest:
|
|
408
|
+
*
|
|
409
|
+
* - Cut inside a string literal (an unterminated `"`) returns undefined. The
|
|
410
|
+
* value itself is incomplete, so any closing brace would silently shorten
|
|
411
|
+
* it — and these arguments carry shell commands.
|
|
412
|
+
* - Nothing left open returns undefined, so a well-formed candidate never
|
|
413
|
+
* reaches this path.
|
|
414
|
+
*
|
|
415
|
+
* Appending a closer never adds or removes a value, so a caller whose payload
|
|
416
|
+
* parsed to a complete object here is getting back exactly what the model
|
|
417
|
+
* emitted, minus the previous call's echo.
|
|
418
|
+
*/
|
|
419
|
+
function appendClosers(text: string): string | undefined {
|
|
420
|
+
const open: string[] = [];
|
|
421
|
+
let inString = false;
|
|
422
|
+
let escaped = false;
|
|
423
|
+
for (const ch of text) {
|
|
424
|
+
if (inString) {
|
|
425
|
+
if (escaped) escaped = false;
|
|
426
|
+
else if (ch === "\\") escaped = true;
|
|
427
|
+
else if (ch === '"') inString = false;
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
if (ch === '"') inString = true;
|
|
431
|
+
else if (ch === "{" || ch === "[") open.push(ch === "{" ? "}" : "]");
|
|
432
|
+
else if (ch === "}" || ch === "]") open.pop();
|
|
433
|
+
}
|
|
434
|
+
// Inside a string at the end: the value was cut mid-token, so refuse.
|
|
435
|
+
if (inString || open.length === 0) return undefined;
|
|
436
|
+
return text + open.reverse().join("");
|
|
437
|
+
}
|
|
385
438
|
/**
|
|
386
439
|
* Repair one tool call's arguments against its declared schema. `name` is the
|
|
387
440
|
* downstream (restored) caller tool name. Returns the input string untouched
|