pi-opencode-go-provider 1.0.4 → 1.0.6

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
@@ -7,6 +7,7 @@ The following files are **idempotent** and regenerated by `scripts/update-models
7
7
  | File | Why it's auto-generated |
8
8
  |------|------------------------|
9
9
  | `models.json` | Built from the provider API. `update-models.js` fetches models, preserves curated data for known IDs, and writes this file. |
10
+ | `deprecated-models.json` | Graveyard for models the API delisted. update-models.js stamps them with deprecatedAt and pi keeps serving them for a 2-week grace period, then evicts them. Never edit by hand. |
10
11
  | `README.md` (model table) | The table under `## Available Models` is replaced in-place by `update-models.js` after merging base models → patch → custom models. |
11
12
 
12
13
  ## Correct Files to Edit
package/README.md CHANGED
@@ -69,13 +69,16 @@ pi
69
69
  | Model | API | Type | Context | Max Tokens | Input Cost | Output Cost |
70
70
  |-------|-----|------|---------|------------|------------|-------------|
71
71
  | DeepSeek V4 Flash | Completions | Text | 1.0M | 384K | $0.14 | $0.28 |
72
- | DeepSeek V4 Pro | Completions | Text | 1.0M | 384K | $1.74 | $3.48 |
72
+ | DeepSeek V4 Pro | Completions | Text | 1.0M | 384K | $0.43 | $0.87 |
73
73
  | GLM-5.1 | Completions | Text | 203K | 33K | $1.40 | $4.40 |
74
74
  | GLM-5.2 | Completions | Text | 1.0M | 131K | $1.40 | $4.40 |
75
+ | Grok 4.5 | Completions | Text + Image | 500K | 500K | $2.00 | $6.00 |
76
+ | Hy3 | Completions | Text | 256K | 64K | $0.14 | $0.58 |
75
77
  | Kimi K2.6 | Completions | Text + Image | 262K | 66K | $0.95 | $4.00 |
76
78
  | Kimi K2.7 Code | Completions | Text + Image | 262K | 262K | $0.95 | $4.00 |
79
+ | Kimi K3 (2x usage) | Completions | Text + Image | 1.0M | 131K | $3.00 | $15.00 |
77
80
  | MiMo V2.5 | Completions | Text + Image | 1.0M | 128K | $0.14 | $0.28 |
78
- | MiMo V2.5 Pro | Completions | Text | 1.0M | 128K | $1.74 | $3.48 |
81
+ | MiMo V2.5 Pro | Completions | Text | 1.0M | 128K | $0.43 | $0.87 |
79
82
  | MiniMax-M2.7 | Anthropic | Text | 205K | 131K | $0.30 | $1.20 |
80
83
  | MiniMax-M3 | Anthropic | Text + Image | 1.0M | 131K | $0.30 | $1.20 |
81
84
  | Qwen3.6 Plus | Anthropic | Text + Image | 1.0M | 66K | $0.50 | $3.00 |
@@ -0,0 +1 @@
1
+ {}
package/index.ts CHANGED
@@ -26,6 +26,7 @@ import { getAgentDir, type ExtensionAPI, type ModelRegistry } from "@earendil-wo
26
26
  import modelsData from "./models.json" with { type: "json" };
27
27
  import customModelsData from "./custom-models.json" with { type: "json" };
28
28
  import patchData from "./patch.json" with { type: "json" };
29
+ import deprecatedData from "./deprecated-models.json" with { type: "json" };
29
30
  import fs from "fs";
30
31
  import path from "path";
31
32
 
@@ -116,7 +117,10 @@ function applyPatch(model: JsonModel, patch: PatchEntry): JsonModel {
116
117
  function buildModels(base: JsonModel[], custom: JsonModel[], patch: PatchData): JsonModel[] {
117
118
  const modelMap = new Map<string, JsonModel>();
118
119
 
119
- for (const model of base) {
120
+ // Seed with the base list plus grace-period deprecated models so patch.json
121
+ // entries apply to deprecated models exactly as while the model was live
122
+ // (withDeprecated keeps live data on id conflicts).
123
+ for (const model of withDeprecated(base)) {
120
124
  modelMap.set(model.id, model);
121
125
  }
122
126
 
@@ -257,6 +261,35 @@ function mergeWithEmbedded(liveModels: JsonModel[], embeddedModels: JsonModel[])
257
261
  return result;
258
262
  }
259
263
 
264
+ // Grace period for delisted models. When the provider API stops listing a
265
+ // model, update-models.js moves its last-known definition into
266
+ // deprecated-models.json (stamped with deprecatedAt) instead of dropping it.
267
+ // For 14 days the model keeps working here so in-flight sessions and saved
268
+ // model settings do not break; afterwards it is evicted permanently.
269
+ const DEPRECATED_MODEL_TTL_MS = 14 * 24 * 60 * 60 * 1000;
270
+
271
+ // Grace-period deprecated models with deprecation metadata stripped.
272
+ function activeDeprecatedModels(): JsonModel[] {
273
+ const now = Date.now();
274
+ const result: JsonModel[] = [];
275
+ for (const entry of Object.values(deprecatedData as Record<string, JsonModel & { deprecatedAt?: string }>)) {
276
+ if (!entry?.id) continue;
277
+ const removedAt = Date.parse(entry.deprecatedAt ?? "");
278
+ if (Number.isNaN(removedAt) || now - removedAt > DEPRECATED_MODEL_TTL_MS) continue;
279
+ const model = { ...entry } as JsonModel & { deprecatedAt?: string };
280
+ delete model.deprecatedAt;
281
+ result.push(model);
282
+ }
283
+ return result;
284
+ }
285
+
286
+ // Append grace-period deprecated models the list does not already have (live data wins).
287
+ function withDeprecated(models: JsonModel[]): JsonModel[] {
288
+ const seen = new Set(models.map((m) => m.id));
289
+ const extras = activeDeprecatedModels().filter((m) => !seen.has(m.id));
290
+ return extras.length > 0 ? [...models, ...extras] : models;
291
+ }
292
+
260
293
  function loadStaleModels(embeddedModels: JsonModel[]): JsonModel[] {
261
294
  const cached = loadCachedModels();
262
295
  if (!cached || cached.length === 0) return embeddedModels;
package/models.json CHANGED
@@ -1,4 +1,41 @@
1
1
  [
2
+ {
3
+ "id": "qwen3.7-plus",
4
+ "name": "Qwen3.7 Plus",
5
+ "api": "anthropic-messages",
6
+ "baseUrl": "https://opencode.ai/zen/go",
7
+ "reasoning": true,
8
+ "input": [
9
+ "text",
10
+ "image"
11
+ ],
12
+ "cost": {
13
+ "input": 0.4,
14
+ "output": 1.6,
15
+ "cacheRead": 0.04,
16
+ "cacheWrite": 0.5
17
+ },
18
+ "contextWindow": 1000000,
19
+ "maxTokens": 65536
20
+ },
21
+ {
22
+ "id": "glm-5.1",
23
+ "name": "GLM-5.1",
24
+ "api": "openai-completions",
25
+ "baseUrl": "https://opencode.ai/zen/go/v1",
26
+ "reasoning": true,
27
+ "input": [
28
+ "text"
29
+ ],
30
+ "cost": {
31
+ "input": 1.4,
32
+ "output": 4.4,
33
+ "cacheRead": 0.26,
34
+ "cacheWrite": 0
35
+ },
36
+ "contextWindow": 202752,
37
+ "maxTokens": 32768
38
+ },
2
39
  {
3
40
  "id": "deepseek-v4-flash",
4
41
  "name": "DeepSeek V4 Flash",
@@ -18,23 +55,40 @@
18
55
  "maxTokens": 384000
19
56
  },
20
57
  {
21
- "id": "qwen3.7-plus",
22
- "name": "Qwen3.7 Plus",
58
+ "id": "minimax-m2.7",
59
+ "name": "MiniMax-M2.7",
23
60
  "api": "anthropic-messages",
24
61
  "baseUrl": "https://opencode.ai/zen/go",
25
62
  "reasoning": true,
26
63
  "input": [
27
- "text",
28
- "image"
64
+ "text"
29
65
  ],
30
66
  "cost": {
31
- "input": 0.4,
32
- "output": 1.6,
33
- "cacheRead": 0.04,
34
- "cacheWrite": 0.5
67
+ "input": 0.3,
68
+ "output": 1.2,
69
+ "cacheRead": 0.06,
70
+ "cacheWrite": 0
71
+ },
72
+ "contextWindow": 204800,
73
+ "maxTokens": 131072
74
+ },
75
+ {
76
+ "id": "glm-5.2",
77
+ "name": "GLM-5.2",
78
+ "api": "openai-completions",
79
+ "baseUrl": "https://opencode.ai/zen/go/v1",
80
+ "reasoning": true,
81
+ "input": [
82
+ "text"
83
+ ],
84
+ "cost": {
85
+ "input": 1.4,
86
+ "output": 4.4,
87
+ "cacheRead": 0.26,
88
+ "cacheWrite": 0
35
89
  },
36
90
  "contextWindow": 1000000,
37
- "maxTokens": 65536
91
+ "maxTokens": 131072
38
92
  },
39
93
  {
40
94
  "id": "qwen3.7-max",
@@ -55,8 +109,8 @@
55
109
  "maxTokens": 65536
56
110
  },
57
111
  {
58
- "id": "kimi-k2.7-code",
59
- "name": "Kimi K2.7 Code",
112
+ "id": "kimi-k2.6",
113
+ "name": "Kimi K2.6",
60
114
  "api": "openai-completions",
61
115
  "baseUrl": "https://opencode.ai/zen/go/v1",
62
116
  "reasoning": true,
@@ -67,33 +121,34 @@
67
121
  "cost": {
68
122
  "input": 0.95,
69
123
  "output": 4,
70
- "cacheRead": 0.19,
124
+ "cacheRead": 0.16,
71
125
  "cacheWrite": 0
72
126
  },
73
127
  "contextWindow": 262144,
74
- "maxTokens": 262144
128
+ "maxTokens": 65536
75
129
  },
76
130
  {
77
- "id": "glm-5.1",
78
- "name": "GLM-5.1",
79
- "api": "openai-completions",
80
- "baseUrl": "https://opencode.ai/zen/go/v1",
131
+ "id": "minimax-m3",
132
+ "name": "MiniMax-M3",
133
+ "api": "anthropic-messages",
134
+ "baseUrl": "https://opencode.ai/zen/go",
81
135
  "reasoning": true,
82
136
  "input": [
83
- "text"
137
+ "text",
138
+ "image"
84
139
  ],
85
140
  "cost": {
86
- "input": 1.4,
87
- "output": 4.4,
88
- "cacheRead": 0.26,
141
+ "input": 0.3,
142
+ "output": 1.2,
143
+ "cacheRead": 0.06,
89
144
  "cacheWrite": 0
90
145
  },
91
- "contextWindow": 202752,
92
- "maxTokens": 32768
146
+ "contextWindow": 1000000,
147
+ "maxTokens": 131072
93
148
  },
94
149
  {
95
- "id": "deepseek-v4-pro",
96
- "name": "DeepSeek V4 Pro",
150
+ "id": "hy3",
151
+ "name": "Hy3",
97
152
  "api": "openai-completions",
98
153
  "baseUrl": "https://opencode.ai/zen/go/v1",
99
154
  "reasoning": true,
@@ -101,17 +156,17 @@
101
156
  "text"
102
157
  ],
103
158
  "cost": {
104
- "input": 1.74,
105
- "output": 3.48,
106
- "cacheRead": 0.0145,
159
+ "input": 0.14,
160
+ "output": 0.58,
161
+ "cacheRead": 0.035,
107
162
  "cacheWrite": 0
108
163
  },
109
- "contextWindow": 1000000,
110
- "maxTokens": 384000
164
+ "contextWindow": 256000,
165
+ "maxTokens": 64000
111
166
  },
112
167
  {
113
- "id": "glm-5.2",
114
- "name": "GLM-5.2",
168
+ "id": "deepseek-v4-pro",
169
+ "name": "DeepSeek V4 Pro",
115
170
  "api": "openai-completions",
116
171
  "baseUrl": "https://opencode.ai/zen/go/v1",
117
172
  "reasoning": true,
@@ -119,54 +174,55 @@
119
174
  "text"
120
175
  ],
121
176
  "cost": {
122
- "input": 1.4,
123
- "output": 4.4,
124
- "cacheRead": 0.26,
177
+ "input": 0.435,
178
+ "output": 0.87,
179
+ "cacheRead": 0.003625,
125
180
  "cacheWrite": 0
126
181
  },
127
182
  "contextWindow": 1000000,
128
- "maxTokens": 131072
183
+ "maxTokens": 384000
129
184
  },
130
185
  {
131
- "id": "minimax-m3",
132
- "name": "MiniMax-M3",
133
- "api": "anthropic-messages",
134
- "baseUrl": "https://opencode.ai/zen/go",
186
+ "id": "mimo-v2.5",
187
+ "name": "MiMo V2.5",
188
+ "api": "openai-completions",
189
+ "baseUrl": "https://opencode.ai/zen/go/v1",
135
190
  "reasoning": true,
136
191
  "input": [
137
192
  "text",
138
193
  "image"
139
194
  ],
140
195
  "cost": {
141
- "input": 0.3,
142
- "output": 1.2,
143
- "cacheRead": 0.06,
196
+ "input": 0.14,
197
+ "output": 0.28,
198
+ "cacheRead": 0.0028,
144
199
  "cacheWrite": 0
145
200
  },
146
201
  "contextWindow": 1000000,
147
- "maxTokens": 131072
202
+ "maxTokens": 128000
148
203
  },
149
204
  {
150
- "id": "minimax-m2.7",
151
- "name": "MiniMax-M2.7",
152
- "api": "anthropic-messages",
153
- "baseUrl": "https://opencode.ai/zen/go",
205
+ "id": "grok-4.5",
206
+ "name": "Grok 4.5",
207
+ "api": "openai-completions",
208
+ "baseUrl": "https://opencode.ai/zen/go/v1",
154
209
  "reasoning": true,
155
210
  "input": [
156
- "text"
211
+ "text",
212
+ "image"
157
213
  ],
158
214
  "cost": {
159
- "input": 0.3,
160
- "output": 1.2,
161
- "cacheRead": 0.06,
215
+ "input": 2,
216
+ "output": 6,
217
+ "cacheRead": 0.5,
162
218
  "cacheWrite": 0
163
219
  },
164
- "contextWindow": 204800,
165
- "maxTokens": 131072
220
+ "contextWindow": 500000,
221
+ "maxTokens": 500000
166
222
  },
167
223
  {
168
- "id": "mimo-v2.5",
169
- "name": "MiMo V2.5",
224
+ "id": "kimi-k2.7-code",
225
+ "name": "Kimi K2.7 Code",
170
226
  "api": "openai-completions",
171
227
  "baseUrl": "https://opencode.ai/zen/go/v1",
172
228
  "reasoning": true,
@@ -175,17 +231,17 @@
175
231
  "image"
176
232
  ],
177
233
  "cost": {
178
- "input": 0.14,
179
- "output": 0.28,
180
- "cacheRead": 0.0028,
234
+ "input": 0.95,
235
+ "output": 4,
236
+ "cacheRead": 0.19,
181
237
  "cacheWrite": 0
182
238
  },
183
- "contextWindow": 1000000,
184
- "maxTokens": 128000
239
+ "contextWindow": 262144,
240
+ "maxTokens": 262144
185
241
  },
186
242
  {
187
- "id": "kimi-k2.6",
188
- "name": "Kimi K2.6",
243
+ "id": "kimi-k3",
244
+ "name": "Kimi K3 (2x usage)",
189
245
  "api": "openai-completions",
190
246
  "baseUrl": "https://opencode.ai/zen/go/v1",
191
247
  "reasoning": true,
@@ -194,13 +250,13 @@
194
250
  "image"
195
251
  ],
196
252
  "cost": {
197
- "input": 0.95,
198
- "output": 4,
199
- "cacheRead": 0.16,
253
+ "input": 3,
254
+ "output": 15,
255
+ "cacheRead": 0.3,
200
256
  "cacheWrite": 0
201
257
  },
202
- "contextWindow": 262144,
203
- "maxTokens": 65536
258
+ "contextWindow": 1048576,
259
+ "maxTokens": 131072
204
260
  },
205
261
  {
206
262
  "id": "mimo-v2.5-pro",
@@ -212,9 +268,9 @@
212
268
  "text"
213
269
  ],
214
270
  "cost": {
215
- "input": 1.74,
216
- "output": 3.48,
217
- "cacheRead": 0.0145,
271
+ "input": 0.435,
272
+ "output": 0.87,
273
+ "cacheRead": 0.003625,
218
274
  "cacheWrite": 0
219
275
  },
220
276
  "contextWindow": 1048576,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-opencode-go-provider",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Opencode Go provider extension for pi - Fast, efficient GLM, Kimi, and MiniMax models through the opencode.ai API",
5
5
  "type": "module",
6
6
  "main": "index.ts",
package/patch.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "deepseek-v4-flash": {
3
3
  "compat": { "requiresReasoningContentOnAssistantMessages": true, "thinkingFormat": "deepseek" },
4
- "thinkingLevelMap": { "minimal": null, "low": null, "medium": null, "high": "high", "xhigh": "max" }
4
+ "thinkingLevelMap": { "minimal": null, "low": null, "medium": null, "high": "high", "max": "max" }
5
5
  },
6
6
  "deepseek-v4-pro": {
7
7
  "compat": { "requiresReasoningContentOnAssistantMessages": true, "thinkingFormat": "deepseek" },
8
- "thinkingLevelMap": { "minimal": null, "low": null, "medium": null, "high": "high", "xhigh": "max" }
8
+ "thinkingLevelMap": { "minimal": null, "low": null, "medium": null, "high": "high", "max": "max" }
9
9
  },
10
10
  "kimi-k2.6": {
11
11
  "compat": { "thinkingFormat": "deepseek", "supportsReasoningEffort": false },
@@ -149,6 +149,67 @@ ${tableRows}`;
149
149
  console.log(` Updated README.md with ${models.length} models`);
150
150
  }
151
151
 
152
+ // Grace period for delisted models: update-models.js moves models the API no
153
+ // longer lists into deprecated-models.json (stamped with deprecatedAt) instead
154
+ // of dropping them; the runtime appends them back so sessions and saved model
155
+ // settings keep working, and after 14 days they are evicted permanently.
156
+ const DEPRECATED_MODEL_TTL_MS = 14 * 24 * 60 * 60 * 1000;
157
+
158
+ /**
159
+ * Reconcile deprecated-models.json against the freshly fetched model list.
160
+ * - in old models.json but not the API: moved into the deprecated file
161
+ * (deprecatedAt = now; preserved on repeat runs so the grace clock is not reset)
162
+ * - back in the API: resurrected (dropped from the deprecated file)
163
+ * - deprecatedAt older than 14 days: evicted permanently
164
+ * Must run BEFORE the new models.json is written; it reads the old file itself.
165
+ */
166
+ function updateDeprecatedModels(modelsJsonPath, newModels) {
167
+ const deprecatedPath = path.join(path.dirname(modelsJsonPath), 'deprecated-models.json');
168
+
169
+ let oldModels = [];
170
+ try {
171
+ const parsed = JSON.parse(fs.readFileSync(modelsJsonPath, 'utf8'));
172
+ if (Array.isArray(parsed)) oldModels = parsed;
173
+ } catch { /* first run: no previous models.json */ }
174
+
175
+ let deprecated = {};
176
+ try {
177
+ const parsed = JSON.parse(fs.readFileSync(deprecatedPath, 'utf8'));
178
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) deprecated = parsed;
179
+ } catch { /* no graveyard yet */ }
180
+
181
+ const currentIds = new Set(newModels.map(m => m.id));
182
+ const now = new Date().toISOString();
183
+ const added = [];
184
+ const resurrected = [];
185
+ const evicted = [];
186
+
187
+ for (const old of oldModels) {
188
+ if (old && old.id && !currentIds.has(old.id) && !deprecated[old.id]) {
189
+ deprecated[old.id] = { ...old, deprecatedAt: now };
190
+ added.push(old.id);
191
+ }
192
+ }
193
+
194
+ for (const [id, entry] of Object.entries(deprecated)) {
195
+ if (currentIds.has(id)) {
196
+ delete deprecated[id];
197
+ resurrected.push(id);
198
+ continue;
199
+ }
200
+ const removedAt = Date.parse(entry && entry.deprecatedAt ? entry.deprecatedAt : '');
201
+ if (Number.isNaN(removedAt) || Date.now() - removedAt > DEPRECATED_MODEL_TTL_MS) {
202
+ delete deprecated[id];
203
+ evicted.push(id);
204
+ }
205
+ }
206
+
207
+ if (added.length > 0 || resurrected.length > 0 || evicted.length > 0) {
208
+ fs.writeFileSync(deprecatedPath, JSON.stringify(deprecated, null, 2) + '\n');
209
+ console.log('Updated deprecated-models.json ' + JSON.stringify({ added, resurrected, evicted }));
210
+ }
211
+ }
212
+
152
213
  async function main() {
153
214
  console.log('Fetching models from API...');
154
215
 
@@ -172,6 +233,8 @@ async function main() {
172
233
  // Convert to Pi-native format and save to models.json
173
234
  const models = apiModels.map(convertModel);
174
235
  const modelsPath = path.join(process.cwd(), 'models.json');
236
+ // Move delisted models to deprecated-models.json BEFORE models.json is overwritten
237
+ updateDeprecatedModels(modelsPath, models);
175
238
  fs.writeFileSync(modelsPath, JSON.stringify(models, null, 2) + '\n');
176
239
  console.log(` Saved ${models.length} models to models.json`);
177
240