pi-io-provider 1.1.47 → 1.1.49

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.48
4
+
5
+ - Pin Pi SDK development dependencies to 1.0.0 while retaining wildcard host peers.
6
+ - Verify real manifest loading, provider catalogs, startup/shutdown and native/bundled Pi hosts offline.
7
+ - Exercise real transport adapters with Unicode text, tool calls, empty responses, usage, request hooks and cancellation; no live provider calls.
8
+
3
9
  ## 1.1.45
4
10
 
5
11
  - Test against Pi 0.99.0 and declare host-provided modules as wildcard peers.
package/README.md CHANGED
@@ -16,38 +16,38 @@ A [pi](https://github.com/badlogic/pi-mono) extension that adds [IO Intelligence
16
16
 
17
17
  | Model | ID | Context | Max Output | Vision | Reasoning | Cache | Input $/M | Output $/M |
18
18
  |-------|----|---------|------------|--------|-----------|-------|-----------|------------|
19
- | DeepSeek V4.1 Flash | `deepseek-ai/DeepSeek-V4.1-Flash` | 1.0M | 131K | ✅ | ✅ | ✅ | $0.27 | $1.11 |
20
- | Gemma 4 26B A4B | `google/gemma-4-26b-a4b-it` | 262K | 262K | ✅ | ✅ | ✅ | $0.08 | $0.28 |
19
+ | DeepSeek V4.1 Flash | `deepseek-ai/DeepSeek-V4.1-Flash` | 1.0M | 131K | ✅ | ✅ | ✅ | $0.34 | $1.38 |
20
+ | Gemma 4 26B A4B | `google/gemma-4-26b-a4b-it` | 262K | 262K | ✅ | ✅ | ✅ | $0.08 | $0.30 |
21
21
  | Gemma 4 31B Instruct | `google/gemma-4-31b-it` | 262K | 16K | ✅ | ✅ | ✅ | $0.38 | $1.15 |
22
22
  | Kimi K2.5 | `moonshotai/Kimi-K2.5` | 262K | 262K | ✅ | ✅ | ✅ | $0.53 | $2.79 |
23
23
  | Kimi K2.6 | `moonshotai/Kimi-K2.6` | 262K | 262K | ✅ | ✅ | ✅ | $0.79 | $3.49 |
24
24
  | Kimi K2.7 Code | `moonshotai/Kimi-K2.7-Code` | 262K | 262K | ✅ | ✅ | ✅ | $1.08 | $4.67 |
25
- | Kimi K3 | `moonshotai/Kimi-K3` | 1.0M | 1.0M | ✅ | ✅ | ✅ | $3.11 | $16.05 |
25
+ | Kimi K3 | `moonshotai/Kimi-K3` | 1.0M | 1.0M | ✅ | ✅ | ✅ | $3.26 | $16.30 |
26
26
  | Qwen3.6 27B | `Qwen/Qwen3.6-27B` | 33K | 33K | ✅ | ✅ | ✅ | $0.34 | $3.01 |
27
27
  | Qwen3.6 35B A3B | `Qwen/Qwen3.6-35B-A3B` | 262K | 262K | ✅ | ✅ | ✅ | $0.18 | $1.26 |
28
28
  | Qwen3.8 27B | `Qwen/Qwen3.8-27B` | 66K | 66K | ✅ | ✅ | ✅ | $0.34 | $2.69 |
29
29
  | GLM 5.3 Flash | `zai-org/GLM-5.3-Flash` | 262K | 131K | ✅ | ✅ | ✅ | $0.21 | $0.70 |
30
30
  | DeepSeek R1 0528 | `deepseek-ai/DeepSeek-R1-0528` | 128K | 128K | ❌ | ✅ | ✅ | $0.57 | $2.28 |
31
31
  | DeepSeek V3.2 | `deepseek-ai/DeepSeek-V3.2` | 164K | 164K | ❌ | ✅ | ✅ | $1.42 | $2.38 |
32
- | DeepSeek V4 Flash | `deepseek-ai/DeepSeek-V4-Flash` | 33K | 33K | ❌ | ✅ | ✅ | $0.13 | $0.52 |
33
- | DeepSeek V4 Flash 0731 | `deepseek-ai/DeepSeek-V4-Flash-0731` | 262K | 66K | ❌ | ✅ | ✅ | $0.23 | $0.74 |
34
- | DeepSeek V4 Pro | `deepseek-ai/DeepSeek-V4-Pro` | 1.0M | 600K | ❌ | ✅ | ✅ | $1.60 | $3.20 |
35
- | DeepSeek V4 Pro 0813 | `deepseek-ai/DeepSeek-V4-Pro-0813` | 1.0M | 393K | ❌ | ✅ | ✅ | $0.99 | $3.44 |
32
+ | DeepSeek V4 Flash | `deepseek-ai/DeepSeek-V4-Flash` | 33K | 33K | ❌ | ✅ | ✅ | $0.11 | $0.51 |
33
+ | DeepSeek V4 Flash 0731 | `deepseek-ai/DeepSeek-V4-Flash-0731` | 262K | 66K | ❌ | ✅ | ✅ | $0.20 | $0.70 |
34
+ | DeepSeek V4 Pro | `deepseek-ai/DeepSeek-V4-Pro` | 1.0M | 600K | ❌ | ✅ | ✅ | $1.37 | $3.32 |
35
+ | DeepSeek V4 Pro 0813 | `deepseek-ai/DeepSeek-V4-Pro-0813` | 1.0M | 393K | ❌ | ✅ | ✅ | $0.89 | $3.77 |
36
36
  | MiniMax M2.5 | `MiniMaxAI/MiniMax-M2.5` | 197K | 197K | ❌ | ✅ | ✅ | $0.29 | $1.18 |
37
- | MiniMax M2.7 | `MiniMaxAI/MiniMax-M2.7` | 262K | 66K | ❌ | ✅ | ✅ | $0.40 | $1.49 |
37
+ | MiniMax M2.7 | `MiniMaxAI/MiniMax-M2.7` | 262K | 66K | ❌ | ✅ | ✅ | $0.40 | $1.46 |
38
38
  | Kimi K2 Thinking | `moonshotai/Kimi-K2-Thinking` | 262K | 262K | ❌ | ✅ | ✅ | $0.60 | $2.50 |
39
39
  | Nemotron 3.5 Lightning | `nvidia/nemotron-3.5-lightning` | 262K | 131K | ❌ | ✅ | ✅ | $0.06 | $0.18 |
40
- | gpt-oss-120b | `openai/gpt-oss-120b` | 131K | 131K | ❌ | ✅ | ✅ | $0.18 | $0.68 |
41
- | gpt-oss-20b | `openai/gpt-oss-20b` | 64K | 64K | ❌ | ✅ | ✅ | $0.06 | $0.19 |
40
+ | gpt-oss-120b | `openai/gpt-oss-120b` | 131K | 131K | ❌ | ✅ | ✅ | $0.17 | $0.63 |
41
+ | gpt-oss-20b | `openai/gpt-oss-20b` | 64K | 64K | ❌ | ✅ | ✅ | $0.06 | $0.20 |
42
42
  | MiMo-V2.5 | `XiaomiMiMo/MiMo-V2.5` | 1.0M | 1.0M | ❌ | ✅ | ✅ | $0.20 | $0.64 |
43
43
  | GLM-4.5-Air | `zai-org/GLM-4.5-Air` | 131K | 131K | ❌ | ✅ | ✅ | $0.16 | $0.94 |
44
44
  | GLM 4.6 | `zai-org/GLM-4.6` | 131K | 131K | ❌ | ✅ | ✅ | $0.52 | $2.04 |
45
45
  | GLM 4.7 | `zai-org/GLM-4.7` | 203K | 203K | ❌ | ✅ | ✅ | $0.52 | $2.05 |
46
46
  | GLM 4.7 Flash | `zai-org/GLM-4.7-Flash` | 200K | 200K | ❌ | ✅ | ✅ | $0.06 | $0.40 |
47
- | GLM 5 | `zai-org/GLM-5` | 203K | 203K | ❌ | ✅ | ✅ | $0.86 | $2.75 |
48
- | GLM 5.1 | `zai-org/GLM-5.1` | 203K | 33K | ❌ | ✅ | ✅ | $1.32 | $4.23 |
49
- | GLM 5.2 | `zai-org/GLM-5.2` | 262K | 131K | ❌ | ✅ | ✅ | $1.85 | $6.00 |
50
- | GLM 5.3 | `zai-org/GLM-5.3` | 262K | 131K | ❌ | ✅ | ✅ | $1.68 | $5.28 |
47
+ | GLM 5 | `zai-org/GLM-5` | 203K | 203K | ❌ | ✅ | ✅ | $0.78 | $2.50 |
48
+ | GLM 5.1 | `zai-org/GLM-5.1` | 203K | 33K | ❌ | ✅ | ✅ | $1.27 | $3.99 |
49
+ | GLM 5.2 | `zai-org/GLM-5.2` | 262K | 131K | ❌ | ✅ | ✅ | $1.49 | $5.48 |
50
+ | GLM 5.3 | `zai-org/GLM-5.3` | 262K | 131K | ❌ | ✅ | ✅ | $1.47 | $4.62 |
51
51
  | Llama 4 Maverick 17B 128E Instruct FP8 | `meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8` | 430K | 430K | ✅ | ❌ | ✅ | $0.25 | $0.83 |
52
52
  | Qwen3 Coder 480B A35B Instruct INT4 Mixed AR | `Intel/Qwen3-Coder-480B-A35B-Instruct-int4-mixed-ar` | 106K | 106K | ❌ | ❌ | ✅ | $0.45 | $2.15 |
53
53
  | Llama 3.3 70B Instruct | `meta-llama/Llama-3.3-70B-Instruct` | 128K | 128K | ❌ | ❌ | ✅ | $0.57 | $1.06 |
package/bun.lock CHANGED
@@ -5,7 +5,7 @@
5
5
  "": {
6
6
  "name": "pi-io-provider",
7
7
  "devDependencies": {
8
- "@earendil-works/pi-coding-agent": "0.99.0",
8
+ "@earendil-works/pi-coding-agent": "1.0.0",
9
9
  "@types/node": "^25.9.1",
10
10
  "typescript": "6.0.3",
11
11
  },
@@ -57,21 +57,21 @@
57
57
 
58
58
  "@babel/runtime": ["@babel/runtime@7.29.7", "", {}, "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw=="],
59
59
 
60
- "@earendil-works/chord": ["@earendil-works/chord@0.99.0", "", { "dependencies": { "esbuild": "0.28.2" } }, "sha512-xGMjZqlbYOIs9hrKvTaW/rcwbjbrogBK2VU+z+YSR0v7G6WHMy6AivC4Sk0lMJjNaMsELFxG47X3BCkd6EqG4g=="],
60
+ "@earendil-works/chord": ["@earendil-works/chord@1.0.0", "", { "dependencies": { "esbuild": "0.28.2" } }, "sha512-BIfWfrByM0pKq6tfKYXuwx0ed2CvaqIM3EDA7Dps+fP+d3CiPWdlHi1cDsJC05llqKJoRz5//G0hz0yQwwjISQ=="],
61
61
 
62
- "@earendil-works/pi-agent-core": ["@earendil-works/pi-agent-core@0.99.0", "", { "dependencies": { "@earendil-works/chord": "^0.99.0", "@earendil-works/pi-ai": "^0.99.0", "@earendil-works/pi-telemetry": "^0.99.0", "diff": "8.0.4", "ignore": "7.0.8", "typebox": "1.3.27", "yaml": "2.9.0" } }, "sha512-3ZFAZ6GnR+r41VVcI47svtvHJ01T/RGDcm3fL8l25VeJ3RwDxnzZ7/XSD56ldE/EOoLA7riwiqc7UEOLyoib1g=="],
62
+ "@earendil-works/pi-agent-core": ["@earendil-works/pi-agent-core@1.0.0", "", { "dependencies": { "@earendil-works/pi-ai": "^1.0.0", "typebox": "1.3.27" } }, "sha512-bHFONjtEBDqiV+g1DmmtkSlfAaqHELr+XO+6I5Nah4gswwZrLJO4yZB/JjsnlI4AKWTayBLfgS6DCbeBBz9e2Q=="],
63
63
 
64
- "@earendil-works/pi-ai": ["@earendil-works/pi-ai@0.99.0", "", { "dependencies": { "@anthropic-ai/sdk": "0.124.0", "@aws-sdk/client-bedrock-runtime": "3.1127.0", "@earendil-works/pi-telemetry": "^0.99.0", "@google/genai": "2.21.0", "@smithy/node-http-handler": "4.12.1", "http-proxy-agent": "9.1.0", "https-proxy-agent": "9.1.0", "openai": "7.19.0", "partial-json": "0.1.7", "typebox": "1.3.27" }, "bin": { "pi-ai": "dist/cli.js" } }, "sha512-pJVG2aytD6x1xckiM4co7DlK6xXwfH7LKcMoI6SFDuJ4OvVqedUUAYFFekX4WkeqOoRNCIjUwka/l7mWb/mjsQ=="],
64
+ "@earendil-works/pi-ai": ["@earendil-works/pi-ai@1.0.0", "", { "dependencies": { "@anthropic-ai/sdk": "0.124.0", "@aws-sdk/client-bedrock-runtime": "3.1127.0", "@earendil-works/pi-telemetry": "^1.0.0", "@google/genai": "2.21.0", "@smithy/node-http-handler": "4.12.1", "http-proxy-agent": "9.1.0", "https-proxy-agent": "9.1.0", "openai": "7.19.0", "partial-json": "0.1.7", "typebox": "1.3.27" }, "bin": { "pi-ai": "dist/cli.js" } }, "sha512-3/W1vdDaVtpeMd23ElvJC12HLA5yS/BGqqcXF+0SK082dN7cbgNcCwguTBRBC258Ke8SzSvUW1B75iAf8w8IxA=="],
65
65
 
66
- "@earendil-works/pi-codemode": ["@earendil-works/pi-codemode@0.99.0", "", { "dependencies": { "quickjs-wasi": "3.6.2" } }, "sha512-zPbNpvVEtO3x++isZosWLuDhs+Xb0F3Pux6BsgIPg+aDmzwKnMruJNDuny2C3ig4HdQQfJ0hmfRsXV3ZmCWrWg=="],
66
+ "@earendil-works/pi-codemode": ["@earendil-works/pi-codemode@1.0.0", "", { "dependencies": { "quickjs-wasi": "3.6.2" } }, "sha512-LPpFI4+T9NzDnhBDs15izWAolaoM8xnwqdziRd6Zx8BQeoEPvzefD2vMMzSyF0rOtq34TfQqkZ0ki16f6cGdMg=="],
67
67
 
68
- "@earendil-works/pi-coding-agent": ["@earendil-works/pi-coding-agent@0.99.0", "", { "dependencies": { "@earendil-works/chord": "^0.99.0", "@earendil-works/pi-agent-core": "^0.99.0", "@earendil-works/pi-ai": "^0.99.0", "@earendil-works/pi-codemode": "^0.99.0", "@earendil-works/pi-mcp": "^0.99.0", "@earendil-works/pi-tui": "^0.99.0", "@silvia-odwyer/photon-node": "0.3.4", "chalk": "6.0.0", "cross-spawn": "7.0.6", "diff": "8.0.4", "grok-mermaid": "0.2.3", "highlight.js": "10.7.3", "hosted-git-info": "9.0.3", "ignore": "7.0.8", "jiti": "2.7.0", "minimatch": "10.2.6", "proper-lockfile": "4.1.2", "quickjs-wasi": "3.6.2", "semver": "7.8.5", "typebox": "1.3.27", "undici": "8.10.2", "yaml": "2.9.0" }, "bin": { "pi": "dist/bundle/cli.js" } }, "sha512-rZ8lLzqhrXrlZSt9VffVmVOcPBzbnK53UilkwEpDqU/WfE1RKCRqzYfGll/Xsk5XumjpeBT8IWzKNP6CyiEtrg=="],
68
+ "@earendil-works/pi-coding-agent": ["@earendil-works/pi-coding-agent@1.0.0", "", { "dependencies": { "@earendil-works/chord": "^1.0.0", "@earendil-works/pi-agent-core": "^1.0.0", "@earendil-works/pi-ai": "^1.0.0", "@earendil-works/pi-codemode": "^1.0.0", "@earendil-works/pi-mcp": "^1.0.0", "@earendil-works/pi-tui": "^1.0.0", "@silvia-odwyer/photon-node": "0.3.4", "chalk": "6.0.0", "cross-spawn": "7.0.6", "diff": "8.0.4", "grok-mermaid": "0.2.3", "highlight.js": "10.7.3", "hosted-git-info": "9.0.3", "ignore": "7.0.8", "jiti": "2.7.0", "minimatch": "10.2.6", "proper-lockfile": "4.1.2", "quickjs-wasi": "3.6.2", "semver": "7.8.5", "typebox": "1.3.27", "undici": "8.10.2", "yaml": "2.9.0" }, "bin": { "pi": "dist/bundle/cli.js" } }, "sha512-/FtbxoSQU/mEv1QnichJjRjqteqaIaMWxmhB4G367+MwZfX7/DI5B9YAg5lqbN7nztFskBEtUSZ+FlmMBECtMw=="],
69
69
 
70
- "@earendil-works/pi-mcp": ["@earendil-works/pi-mcp@0.99.0", "", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-9bt7QOZHMczU+bxO1VJdYMPeX54QdTSxE5Q+KbAi06GwOh+RUH2FTJCD7PZ1Av+oOJcTGTqr9CkvHtEUIFSuYg=="],
70
+ "@earendil-works/pi-mcp": ["@earendil-works/pi-mcp@1.0.0", "", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-rYra0aF5iPmJd+fsB+VBuqxWNABGJ3Iiyhg0CqIc/91ta2n7IvQmCp0Cac/YkUTNnvRsVZnjgAiBkRi6+ouWuw=="],
71
71
 
72
- "@earendil-works/pi-telemetry": ["@earendil-works/pi-telemetry@0.99.0", "", {}, "sha512-WH/sYgk7gY5OlunzPdtFPfPUr/M95F3GV+0yva3nHkcw2h463fWrJxn6hWuEC6vGktBMV41sA6QLB/ejllv1mw=="],
72
+ "@earendil-works/pi-telemetry": ["@earendil-works/pi-telemetry@1.0.0", "", {}, "sha512-WjNBj5TYIiPZFQEz2WlULcDwPLaKwIlmsKjVeYM+LJSbnSp38kWsHUJysIDGnu23IcLbKoPtywsvYfUJCeZePA=="],
73
73
 
74
- "@earendil-works/pi-tui": ["@earendil-works/pi-tui@0.99.0", "", { "dependencies": { "get-east-asian-width": "1.6.0", "marked": "18.0.11" } }, "sha512-6S8VIb0RnD2xHlssyL8XsLXmM9LC7pjL2e9+UVkh5FCgNxslb35bl3glhDyJZzovBUrGnUM872CbBGiSiMRbGw=="],
74
+ "@earendil-works/pi-tui": ["@earendil-works/pi-tui@1.0.0", "", { "dependencies": { "get-east-asian-width": "1.6.0", "marked": "18.0.11" } }, "sha512-JsT7kXnpZA2YOtQu6RyriyxEO0eJIzPyfiH09bH+OLN5+s18HYkwaUD/tBkjhnSfMu6/50CQPRYJagzSP6HdPw=="],
75
75
 
76
76
  "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ=="],
77
77
 
package/models.json CHANGED
@@ -8,9 +8,9 @@
8
8
  "image"
9
9
  ],
10
10
  "cost": {
11
- "input": 0.2688,
12
- "output": 1.11,
13
- "cacheRead": 0.1344,
11
+ "input": 0.34494,
12
+ "output": 1.37976,
13
+ "cacheRead": 0.17247,
14
14
  "cacheWrite": 0
15
15
  },
16
16
  "contextWindow": 1048320,
@@ -53,9 +53,9 @@
53
53
  "text"
54
54
  ],
55
55
  "cost": {
56
- "input": 1.68,
57
- "output": 5.28,
58
- "cacheRead": 0.84,
56
+ "input": 1.47,
57
+ "output": 4.62,
58
+ "cacheRead": 0.735,
59
59
  "cacheWrite": 0
60
60
  },
61
61
  "contextWindow": 262144,
@@ -98,9 +98,9 @@
98
98
  "text"
99
99
  ],
100
100
  "cost": {
101
- "input": 0.2276,
102
- "output": 0.7426,
103
- "cacheRead": 0.1138,
101
+ "input": 0.2026,
102
+ "output": 0.7028,
103
+ "cacheRead": 0.1013,
104
104
  "cacheWrite": 0
105
105
  },
106
106
  "contextWindow": 262100,
@@ -121,9 +121,9 @@
121
121
  "image"
122
122
  ],
123
123
  "cost": {
124
- "input": 3.108,
125
- "output": 16.05,
126
- "cacheRead": 1.554,
124
+ "input": 3.26,
125
+ "output": 16.3,
126
+ "cacheRead": 1.63,
127
127
  "cacheWrite": 0
128
128
  },
129
129
  "contextWindow": 1048576,
@@ -165,9 +165,9 @@
165
165
  "text"
166
166
  ],
167
167
  "cost": {
168
- "input": 1.85,
169
- "output": 6,
170
- "cacheRead": 0.925,
168
+ "input": 1.49498,
169
+ "output": 5.478,
170
+ "cacheRead": 0.74749,
171
171
  "cacheWrite": 0
172
172
  },
173
173
  "contextWindow": 262144,
@@ -256,9 +256,9 @@
256
256
  "text"
257
257
  ],
258
258
  "cost": {
259
- "input": 0.402,
260
- "output": 1.488,
261
- "cacheRead": 0.201,
259
+ "input": 0.396,
260
+ "output": 1.464,
261
+ "cacheRead": 0.198,
262
262
  "cacheWrite": 0
263
263
  },
264
264
  "contextWindow": 262100,
@@ -278,9 +278,9 @@
278
278
  "text"
279
279
  ],
280
280
  "cost": {
281
- "input": 0.1268,
282
- "output": 0.5216,
283
- "cacheRead": 0.0634,
281
+ "input": 0.113008,
282
+ "output": 0.509296,
283
+ "cacheRead": 0.056504,
284
284
  "cacheWrite": 0
285
285
  },
286
286
  "contextWindow": 32768,
@@ -300,9 +300,9 @@
300
300
  "text"
301
301
  ],
302
302
  "cost": {
303
- "input": 1.5968,
304
- "output": 3.1956,
305
- "cacheRead": 0.7984,
303
+ "input": 1.3656,
304
+ "output": 3.322,
305
+ "cacheRead": 0.6828,
306
306
  "cacheWrite": 0
307
307
  },
308
308
  "contextWindow": 1048576,
@@ -345,9 +345,9 @@
345
345
  "text"
346
346
  ],
347
347
  "cost": {
348
- "input": 1.32,
349
- "output": 4.228,
350
- "cacheRead": 0.66,
348
+ "input": 1.27092,
349
+ "output": 3.99432,
350
+ "cacheRead": 0.63546,
351
351
  "cacheWrite": 0
352
352
  },
353
353
  "contextWindow": 202750,
@@ -367,9 +367,9 @@
367
367
  "text"
368
368
  ],
369
369
  "cost": {
370
- "input": 0.294,
370
+ "input": 0.293,
371
371
  "output": 1.176,
372
- "cacheRead": 0.147,
372
+ "cacheRead": 0.1465,
373
373
  "cacheWrite": 0
374
374
  },
375
375
  "contextWindow": 196600,
@@ -412,9 +412,9 @@
412
412
  "text"
413
413
  ],
414
414
  "cost": {
415
- "input": 0.86,
416
- "output": 2.752,
417
- "cacheRead": 0.43,
415
+ "input": 0.78,
416
+ "output": 2.496,
417
+ "cacheRead": 0.39,
418
418
  "cacheWrite": 0
419
419
  },
420
420
  "contextWindow": 202752,
@@ -501,9 +501,9 @@
501
501
  "image"
502
502
  ],
503
503
  "cost": {
504
- "input": 0.0833,
505
- "output": 0.275,
506
- "cacheRead": 0.04165,
504
+ "input": 0.0813,
505
+ "output": 0.305,
506
+ "cacheRead": 0.04065,
507
507
  "cacheWrite": 0
508
508
  },
509
509
  "contextWindow": 262142,
@@ -588,9 +588,9 @@
588
588
  "text"
589
589
  ],
590
590
  "cost": {
591
- "input": 0.178,
592
- "output": 0.68,
593
- "cacheRead": 0.089,
591
+ "input": 0.168,
592
+ "output": 0.63,
593
+ "cacheRead": 0.084,
594
594
  "cacheWrite": 0.04
595
595
  },
596
596
  "contextWindow": 131072,
@@ -739,9 +739,9 @@
739
739
  "text"
740
740
  ],
741
741
  "cost": {
742
- "input": 0.055,
743
- "output": 0.194,
744
- "cacheRead": 0.0275,
742
+ "input": 0.063,
743
+ "output": 0.196,
744
+ "cacheRead": 0.0315,
745
745
  "cacheWrite": 0.03
746
746
  },
747
747
  "contextWindow": 64000,
@@ -782,9 +782,9 @@
782
782
  "text"
783
783
  ],
784
784
  "cost": {
785
- "input": 0.9916,
786
- "output": 3.444048,
787
- "cacheRead": 0.4958,
785
+ "input": 0.89358,
786
+ "output": 3.774,
787
+ "cacheRead": 0.44679,
788
788
  "cacheWrite": 0
789
789
  },
790
790
  "contextWindow": 1048576,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-io-provider",
3
- "version": "1.1.47",
3
+ "version": "1.1.49",
4
4
  "description": "IO Intelligence provider extension for pi - Access DeepSeek, Kimi, GLM, Llama, Qwen, Mistral, and more through the IO Intelligence API",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -27,7 +27,7 @@
27
27
  ]
28
28
  },
29
29
  "devDependencies": {
30
- "@earendil-works/pi-coding-agent": "0.99.0",
30
+ "@earendil-works/pi-coding-agent": "1.0.0",
31
31
  "typescript": "6.0.3",
32
32
  "@types/node": "^25.9.1"
33
33
  },
@@ -1,76 +1,115 @@
1
+ // Offline Pi 1.0 manifest, lifecycle, catalog and real transport regression.
1
2
  import assert from 'node:assert/strict';
2
3
  import { mkdtemp, readFile, rm } from 'node:fs/promises';
3
4
  import { tmpdir } from 'node:os';
4
5
  import { dirname, join, resolve } from 'node:path';
5
- import { fileURLToPath } from 'node:url';
6
+ import { fileURLToPath, pathToFileURL } from 'node:url';
6
7
 
7
- // Import Pi only after selecting a disposable agent directory. Never read user auth/cache.
8
8
  const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
9
9
  const home = await mkdtemp(join(tmpdir(), 'pi-provider-compat-'));
10
10
  const previousHome = process.env.PI_CODING_AGENT_DIR;
11
11
  const previousFetch = globalThis.fetch;
12
12
  process.env.PI_CODING_AGENT_DIR = home;
13
+ // Never allow a background catalog/account request to reach a real endpoint.
13
14
  globalThis.fetch = async () => new Response('', { status: 503 });
14
15
  let session;
15
16
  try {
16
- const { createAgentSession, DefaultResourceLoader, ModelRuntime, SessionManager, SettingsManager, VERSION } = await import('@earendil-works/pi-coding-agent');
17
- assert.equal(VERSION, '0.99.0', 'run compatibility checks against the pinned host');
17
+ const host = process.env.PI1_HOST_PACKAGE;
18
+ const hostEntry = process.env.PI1_HOST_ENTRY === 'bundle' ? 'dist/bundle/index.js' : 'dist/index.js';
19
+ const sdk = await import(host ? pathToFileURL(join(host, hostEntry)).href : '@earendil-works/pi-coding-agent');
20
+ const { createAgentSession, DefaultResourceLoader, ModelRuntime, SessionManager, SettingsManager, VERSION } = sdk;
21
+ assert.equal(VERSION, '1.0.0', 'executing host version');
22
+ assert.equal((await import('@earendil-works/pi-coding-agent')).VERSION, '1.0.0', 'development host version');
18
23
  const manifest = JSON.parse(await readFile(join(root, 'package.json'), 'utf8'));
19
- const hostPackages = ['@earendil-works/pi-ai', '@earendil-works/pi-agent-core', '@earendil-works/pi-coding-agent', '@earendil-works/pi-tui', 'typebox'];
20
- for (const name of hostPackages) {
21
- assert.equal(manifest.dependencies?.[name], undefined, `${name} must not be installed as a runtime dependency`);
24
+ for (const name of ['@earendil-works/pi-ai', '@earendil-works/pi-agent-core', '@earendil-works/pi-coding-agent', '@earendil-works/pi-tui', 'typebox']) {
25
+ assert.equal(manifest.dependencies?.[name], undefined, `${name}: do not bundle host packages`);
22
26
  if (manifest.peerDependencies?.[name] !== undefined) assert.equal(manifest.peerDependencies[name], '*');
27
+ if (name !== 'typebox' && manifest.devDependencies?.[name]) assert.equal(manifest.devDependencies[name], '1.0.0');
23
28
  }
24
- const settingsManager = SettingsManager.inMemory({ compaction: { enabled: false }, retry: { enabled: false } });
25
- const resourceLoader = new DefaultResourceLoader({
26
- cwd: home, agentDir: home, settingsManager,
27
- additionalExtensionPaths: [join(root, 'index.ts')],
28
- noSkills: true, noPromptTemplates: true, noThemes: true, noContextFiles: true,
29
- });
29
+ const settingsManager = SettingsManager.inMemory({ packages: [root], compaction: { enabled: false }, retry: { enabled: false } });
30
+ const resourceLoader = new DefaultResourceLoader({ cwd: home, agentDir: home, settingsManager,
31
+ noSkills: true, noPromptTemplates: true, noThemes: true, noContextFiles: true });
30
32
  await resourceLoader.reload();
31
33
  const loaded = resourceLoader.getExtensions();
32
- assert.deepEqual(loaded.errors, [], 'extension must load in the real Pi loader');
33
- assert.deepEqual(loaded.warnings ?? [], [], 'extension must not produce host-dependency warnings');
34
- assert.equal(loaded.extensions.length, 1, 'load only this provider');
35
- const registrations = [...loaded.runtime.pendingProviderRegistrations];
36
- assert.ok(registrations.length > 0, 'factory must register a provider before session startup');
34
+ assert.deepEqual(loaded.errors, []);
35
+ assert.deepEqual(loaded.warnings ?? [], []);
36
+ assert.equal(loaded.extensions.length, manifest.pi.extensions.length, 'all manifest entrypoints load');
37
+ // Endpoint/header-only re-registration (e.g. MCR) retains the prior catalog.
38
+ const registrations = [...loaded.runtime.pendingProviderRegistrations.reduce((byName, { name, config }) =>
39
+ byName.set(name, { name, config: { ...byName.get(name)?.config, ...config } }), new Map()).values()];
40
+ assert(registrations.length > 0, 'providers register before session startup');
37
41
  const modelRuntime = await ModelRuntime.create({ authPath: join(home, 'auth.json'), modelsPath: null, modelsStorePath: join(home, 'models-cache'), allowModelNetwork: false });
38
- ({ session } = await createAgentSession({ cwd: home, agentDir: home, resourceLoader, modelRuntime, settingsManager, sessionManager: SessionManager.inMemory(home), noTools: true }));
42
+ ({ session } = await createAgentSession({ cwd: home, agentDir: home, resourceLoader, modelRuntime, settingsManager, sessionManager: SessionManager.inMemory(home) }));
39
43
  const errors = [];
40
- await session.bindExtensions({ mode: 'print', onError: (error) => errors.push(error) });
41
- await new Promise((done) => setImmediate(done));
42
- let modelCount = 0;
44
+ await session.bindExtensions({ mode: 'print', onError: error => errors.push(error) });
45
+ await new Promise(done => setImmediate(done));
46
+ const tools = new Set();
47
+ for (const extension of loaded.extensions) for (const [name, { definition }] of extension.tools) {
48
+ assert.equal(definition.name, name); assert.equal(typeof definition.execute, 'function');
49
+ assert.equal(typeof definition.parameters, 'object'); assert(!tools.has(name)); tools.add(name);
50
+ assert(session.getAllTools().some(tool => tool.name === name));
51
+ }
52
+ let modelCount = 0, streamChecks = 0;
43
53
  for (const { name, config } of registrations) {
44
54
  const models = modelRuntime.getAllModels(name);
45
- assert.equal(models.length, config.models.length, `${name}: all configured models must survive host registration`);
46
- assert.ok(models.length > 0, `${name}: nonempty catalog`);
47
- assert.equal(new Set(models.map((model) => model.id)).size, models.length, `${name}: unique model IDs`);
55
+ assert.equal(models.length, config.models.length, `${name}: catalog survives registration`);
56
+ assert(models.length > 0);
57
+ assert.equal(new Set(models.map(m => `${m.type}/${m.id}`)).size, models.length);
48
58
  for (const model of models) {
49
- assert.equal(model.provider, name);
50
- assert.ok(model.api && model.baseUrl && model.id && model.name);
51
- if (model.type === 'image') assert.ok(model.output.includes('image'));
52
- else assert.ok(model.contextWindow > 0, `${name}/${model.id}: positive context limit`);
53
- if (model.type === 'chat') assert.ok(model.maxTokens > 0, `${name}/${model.id}: positive output limit`);
54
- assert.ok(model.input.includes('text'));
55
- for (const cost of Object.values(model.cost)) assert.ok(Number.isFinite(cost) && cost >= 0);
59
+ assert.equal(model.provider, name); assert(model.api && model.baseUrl && model.id && model.name);
60
+ if (model.type === 'image') assert(model.output.includes('image'));
61
+ else assert(model.contextWindow > 0);
62
+ if ((model.type ?? 'chat') === 'chat') assert(model.maxTokens > 0);
63
+ assert(model.input.includes('text'));
64
+ for (const cost of Object.values(model.cost)) assert(Number.isFinite(cost) && cost >= 0);
56
65
  }
57
- if (name === 'opencode') {
58
- const { getAllBuiltinModels } = await import('@earendil-works/pi-ai/providers/all');
59
- for (const native of getAllBuiltinModels(name).filter(model => model.type !== 'chat')) {
60
- assert.ok(models.some(model => model.id === native.id && model.type === native.type), `${native.id}: preserve native operation`);
61
- assert.ok(!modelRuntime.getModels(name).some(model => model.id === native.id), `${native.id}: must not become a chat model`);
62
- }
63
- assert.equal(typeof modelRuntime.getProvider(name).classify, 'function');
66
+ const chatModels = modelRuntime.getModels(name);
67
+ const model = chatModels.find(m => m.api === 'openai-completions') ?? chatModels[0];
68
+ assert(model, 'a chat model is available for offline transport checks');
69
+ const user = { role: 'user', content: 'hello', timestamp: 1 };
70
+ async function probe(tool = false, empty = false) {
71
+ let payloadCalls = 0, responseCalls = 0, observed = 0, wire;
72
+ const fetch = async (_url, init) => {
73
+ wire = JSON.parse(init.body);
74
+ const delta = tool ? { tool_calls: [{ index: 0, id: 'call_probe', type: 'function', function: { name: 'probe', arguments: '{"value":7}' } }] } : { content: empty ? '' : 'Hello 界' };
75
+ const chunks = [{ id: 'offline', choices: [{ index: 0, delta: { role: 'assistant', ...delta }, finish_reason: null }] }, { id: 'offline', choices: [{ index: 0, delta: {}, finish_reason: tool ? 'tool_calls' : 'stop' }], usage: { prompt_tokens: 10, completion_tokens: 2, total_tokens: 12 } }];
76
+ return new Response(chunks.map(c => `data: ${JSON.stringify(c)}\n\n`).join('') + 'data: [DONE]\n\n', { headers: { 'content-type': 'text/event-stream' } });
77
+ };
78
+ const stream = modelRuntime.streamSimple(model, { messages: [{ role: 'system', content: 'Offline probe', toolsAdded: [{ name: 'probe', description: 'Probe', parameters: { type: 'object', properties: { value: { type: 'integer' } }, required: ['value'] } }], timestamp: 0 }, user] }, {
79
+ apiKey: 'offline-placeholder', maxTokens: 32, maxRetries: 0, fetch,
80
+ onPayload: p => { payloadCalls++; return { ...p, temperature: 0.123 }; },
81
+ onResponse: () => { responseCalls++; }, onProviderStreamEvent: () => { observed++; },
82
+ });
83
+ const events = []; for await (const event of stream) events.push(event.type);
84
+ const result = await stream.result();
85
+ assert.equal(result.stopReason, tool ? 'toolUse' : 'stop', result.errorMessage);
86
+ assert.equal(events.filter(e => e === 'start').length, 1);
87
+ assert.equal(events.filter(e => e === 'done' || e === 'error').length, 1);
88
+ assert.equal(events.at(-1), 'done');
89
+ assert.equal(payloadCalls, 1); assert.equal(responseCalls, 1); assert(observed >= 2);
90
+ assert.equal(wire.temperature, 0.123); assert.equal(wire.tools[0].function.name, 'probe');
91
+ assert.equal(result.usage.totalTokens, 12);
92
+ if (tool) assert.deepEqual(result.content.find(c => c.type === 'toolCall').arguments, { value: 7 });
93
+ else if (!empty) assert.equal(result.content.find(c => c.type === 'text').text, 'Hello 界');
94
+ streamChecks++;
64
95
  }
96
+ await probe(); await probe(true); await probe(false, true);
97
+ // Abort after preflight: the SDK's lazy preflight reports an already-aborted
98
+ // auth operation as an error; this checks provider transport cancellation.
99
+ const controller = new AbortController();
100
+ const aborted = await modelRuntime.streamSimple(model, { messages: [user] }, {
101
+ apiKey: 'offline-placeholder', signal: controller.signal, maxRetries: 0,
102
+ onPayload: () => { controller.abort(); },
103
+ fetch: async () => { throw new DOMException('Aborted', 'AbortError'); },
104
+ }).result();
105
+ assert.equal(aborted.stopReason, 'aborted', aborted.errorMessage); streamChecks++;
65
106
  modelCount += models.length;
66
107
  }
67
- await session.extensionRunner.emit({ type: 'session_shutdown' });
68
- assert.deepEqual(errors, [], 'startup and shutdown handlers must work with Pi 0.99 contexts');
69
- console.log(`${manifest.name}: Pi ${VERSION} loader, manifest, session startup, and ${modelCount} models OK`);
108
+ await session.extensionRunner.emit({ type: 'session_shutdown', reason: 'quit' });
109
+ assert.deepEqual(errors, []);
110
+ console.log(JSON.stringify({ repo: manifest.name, pi: VERSION, hostEntry, extensions: loaded.extensions.length, tools: tools.size, models: modelCount, streamChecks, lifecycle: 'passed' }));
70
111
  } finally {
71
- session?.dispose();
72
- globalThis.fetch = previousFetch;
73
- if (previousHome === undefined) delete process.env.PI_CODING_AGENT_DIR;
74
- else process.env.PI_CODING_AGENT_DIR = previousHome;
112
+ session?.dispose(); globalThis.fetch = previousFetch;
113
+ if (previousHome === undefined) delete process.env.PI_CODING_AGENT_DIR; else process.env.PI_CODING_AGENT_DIR = previousHome;
75
114
  await rm(home, { recursive: true, force: true });
76
115
  }