pi2dsh 0.13.2 → 0.14.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.
Files changed (106) hide show
  1. package/README.md +24 -19
  2. package/README.zh.md +20 -15
  3. package/dist/{analyzer-CJFwQlSG.mjs → analyzer-DPZ8LzSt.mjs} +2 -2
  4. package/dist/{analyzer-CJFwQlSG.mjs.map → analyzer-DPZ8LzSt.mjs.map} +1 -1
  5. package/dist/anthropic-messages-CaYTgvLF.mjs +6436 -0
  6. package/dist/anthropic-messages-CaYTgvLF.mjs.map +1 -0
  7. package/dist/azure-openai-responses-HKWkzl2m.mjs +208 -0
  8. package/dist/azure-openai-responses-HKWkzl2m.mjs.map +1 -0
  9. package/dist/cli.mjs +3 -2
  10. package/dist/cli.mjs.map +1 -1
  11. package/dist/compat/pi-ai.d.mts +3 -2636
  12. package/dist/compat/pi-ai.mjs +3 -2
  13. package/dist/compat/pi-coding-agent.d.mts +2 -2
  14. package/dist/compat/pi-coding-agent.mjs +2 -2
  15. package/dist/{mcp-config-_idn-pzI.mjs → compatibility-CP2tpyhg.mjs} +20 -185
  16. package/dist/compatibility-CP2tpyhg.mjs.map +1 -0
  17. package/dist/completionchunk-Dhk56lZl.mjs +5212 -0
  18. package/dist/completionchunk-Dhk56lZl.mjs.map +1 -0
  19. package/dist/{credentials-oauth-Txs6oew8.mjs → credentials-oauth-Didpkkjx.mjs} +3 -3
  20. package/dist/{credentials-oauth-Txs6oew8.mjs.map → credentials-oauth-Didpkkjx.mjs.map} +1 -1
  21. package/dist/credentials-oauth.mjs +1 -1
  22. package/dist/deferred-tools-BD4g5QUx.mjs +32 -0
  23. package/dist/deferred-tools-BD4g5QUx.mjs.map +1 -0
  24. package/dist/diagnostics-BqYkodgB.mjs +35 -0
  25. package/dist/diagnostics-BqYkodgB.mjs.map +1 -0
  26. package/dist/dist-QBBA-dL_.mjs +1246 -0
  27. package/dist/dist-QBBA-dL_.mjs.map +1 -0
  28. package/dist/error-body-DKA3lknR.mjs +104 -0
  29. package/dist/error-body-DKA3lknR.mjs.map +1 -0
  30. package/dist/event-stream-QxN1yvIF.mjs +64 -0
  31. package/dist/event-stream-QxN1yvIF.mjs.map +1 -0
  32. package/dist/from-BCscBQ9P.mjs +3856 -0
  33. package/dist/from-BCscBQ9P.mjs.map +1 -0
  34. package/dist/github-copilot-headers-CZo2QHrt.mjs +25 -0
  35. package/dist/github-copilot-headers-CZo2QHrt.mjs.map +1 -0
  36. package/dist/google-generative-ai-2gCFYp-t.mjs +356 -0
  37. package/dist/google-generative-ai-2gCFYp-t.mjs.map +1 -0
  38. package/dist/google-shared-75IdVyrx.mjs +29041 -0
  39. package/dist/google-shared-75IdVyrx.mjs.map +1 -0
  40. package/dist/google-vertex-Bhyo4z2i.mjs +400 -0
  41. package/dist/google-vertex-Bhyo4z2i.mjs.map +1 -0
  42. package/dist/hash-Dbggamac.mjs +19 -0
  43. package/dist/hash-Dbggamac.mjs.map +1 -0
  44. package/dist/headers-CPzZhJQn.mjs +17 -0
  45. package/dist/headers-CPzZhJQn.mjs.map +1 -0
  46. package/dist/host.d.mts +14 -2
  47. package/dist/host.d.mts.map +1 -1
  48. package/dist/host.mjs +44 -5
  49. package/dist/host.mjs.map +1 -1
  50. package/dist/index.d.mts +35 -3
  51. package/dist/index.d.mts.map +1 -1
  52. package/dist/index.mjs +128 -22
  53. package/dist/index.mjs.map +1 -1
  54. package/dist/json-parse-CZ0U0sj_.mjs +384 -0
  55. package/dist/json-parse-CZ0U0sj_.mjs.map +1 -0
  56. package/dist/mcp-config-jmzbgdHa.mjs +181 -0
  57. package/dist/mcp-config-jmzbgdHa.mjs.map +1 -0
  58. package/dist/mistral-conversations-DJyeTo8C.mjs +36921 -0
  59. package/dist/mistral-conversations-DJyeTo8C.mjs.map +1 -0
  60. package/dist/multipart-parser-SRcGkVuX.mjs +301 -0
  61. package/dist/multipart-parser-SRcGkVuX.mjs.map +1 -0
  62. package/dist/{pi-ai-D7PqVeWs.mjs → openai-DkzRh5_G.mjs} +78 -5063
  63. package/dist/openai-DkzRh5_G.mjs.map +1 -0
  64. package/dist/openai-codex-responses-DedxQjNG.mjs +1220 -0
  65. package/dist/openai-codex-responses-DedxQjNG.mjs.map +1 -0
  66. package/dist/openai-completions-DrjrWRdX.mjs +945 -0
  67. package/dist/openai-completions-DrjrWRdX.mjs.map +1 -0
  68. package/dist/openai-prompt-cache-BDWtZRKh.mjs +11 -0
  69. package/dist/openai-prompt-cache-BDWtZRKh.mjs.map +1 -0
  70. package/dist/openai-responses-BRdGpBX6.mjs +251 -0
  71. package/dist/openai-responses-BRdGpBX6.mjs.map +1 -0
  72. package/dist/openai-responses-shared-BmnSvdKL.mjs +570 -0
  73. package/dist/openai-responses-shared-BmnSvdKL.mjs.map +1 -0
  74. package/dist/otel-CBhmS-NR.mjs +24935 -0
  75. package/dist/otel-CBhmS-NR.mjs.map +1 -0
  76. package/dist/pi-ai-DpqiLdPA.mjs +3244 -0
  77. package/dist/pi-ai-DpqiLdPA.mjs.map +1 -0
  78. package/dist/pi-ai-ZU5MelXy.d.mts +2678 -0
  79. package/dist/pi-ai-ZU5MelXy.d.mts.map +1 -0
  80. package/dist/{pi-coding-agent-B0PT23gE.d.mts → pi-coding-agent-69yY0ke-.d.mts} +4 -2
  81. package/dist/{pi-coding-agent-B0PT23gE.d.mts.map → pi-coding-agent-69yY0ke-.d.mts.map} +1 -1
  82. package/dist/{pi-coding-agent-BVZu0dHG.mjs → pi-coding-agent-DK8hbJP5.mjs} +0 -0
  83. package/dist/{pi-coding-agent-BVZu0dHG.mjs.map → pi-coding-agent-DK8hbJP5.mjs.map} +1 -1
  84. package/dist/pi-messages-D61G2tgs.mjs +307 -0
  85. package/dist/pi-messages-D61G2tgs.mjs.map +1 -0
  86. package/dist/provider-env-C72pF4vP.mjs +40 -0
  87. package/dist/provider-env-C72pF4vP.mjs.map +1 -0
  88. package/dist/provider-retry-Cw6YJ4cr.mjs +80 -0
  89. package/dist/provider-retry-Cw6YJ4cr.mjs.map +1 -0
  90. package/dist/rolldown-runtime-CTfmNlz1.mjs +44 -0
  91. package/dist/{runtime-DEjs_Qlf.mjs → runtime-Bb9Bfk4M.mjs} +715 -130
  92. package/dist/runtime-Bb9Bfk4M.mjs.map +1 -0
  93. package/dist/runtime.d.mts +149 -26
  94. package/dist/runtime.d.mts.map +1 -1
  95. package/dist/runtime.mjs +1 -1
  96. package/dist/simple-options-B_GO8FDE.mjs +456 -0
  97. package/dist/simple-options-B_GO8FDE.mjs.map +1 -0
  98. package/dist/src-DlTagfW5.mjs +1186 -0
  99. package/dist/src-DlTagfW5.mjs.map +1 -0
  100. package/dist/{types-7IWJPPvS.d.mts → types-B9VBF8le.d.mts} +2 -9
  101. package/dist/{types-7IWJPPvS.d.mts.map → types-B9VBF8le.d.mts.map} +1 -1
  102. package/package.json +9 -7
  103. package/dist/compat/pi-ai.d.mts.map +0 -1
  104. package/dist/mcp-config-_idn-pzI.mjs.map +0 -1
  105. package/dist/pi-ai-D7PqVeWs.mjs.map +0 -1
  106. package/dist/runtime-DEjs_Qlf.mjs.map +0 -1
@@ -0,0 +1,3244 @@
1
+
2
+ import { i as __require } from "./rolldown-runtime-CTfmNlz1.mjs";
3
+ import { t as AssistantMessageEventStream$1 } from "./event-stream-QxN1yvIF.mjs";
4
+ import { AsyncLocalStorage } from "node:async_hooks";
5
+ import { Type as Type$1 } from "typebox";
6
+ //#region src/compat/vendor/pi-uuid.ts
7
+ let lastTimestamp = -Infinity;
8
+ let sequence = 0;
9
+ function fillRandomBytes(bytes) {
10
+ if (globalThis.crypto?.getRandomValues) {
11
+ globalThis.crypto.getRandomValues(bytes);
12
+ return;
13
+ }
14
+ for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);
15
+ }
16
+ /** Generate a time-ordered UUIDv7. */
17
+ function uuidv7() {
18
+ const random = /* @__PURE__ */ new Uint8Array(16);
19
+ fillRandomBytes(random);
20
+ const timestamp = Date.now();
21
+ if (timestamp > lastTimestamp) {
22
+ sequence = random[6] * 16777216 + random[7] * 65536 + random[8] * 256 + random[9];
23
+ lastTimestamp = timestamp;
24
+ } else {
25
+ sequence = sequence + 1 >>> 0;
26
+ if (sequence === 0) lastTimestamp++;
27
+ }
28
+ const bytes = /* @__PURE__ */ new Uint8Array(16);
29
+ bytes[0] = lastTimestamp / 1099511627776 & 255;
30
+ bytes[1] = lastTimestamp / 4294967296 & 255;
31
+ bytes[2] = lastTimestamp / 16777216 & 255;
32
+ bytes[3] = lastTimestamp / 65536 & 255;
33
+ bytes[4] = lastTimestamp / 256 & 255;
34
+ bytes[5] = lastTimestamp & 255;
35
+ bytes[6] = 112 | sequence >>> 28 & 15;
36
+ bytes[7] = sequence >>> 20 & 255;
37
+ bytes[8] = 128 | sequence >>> 14 & 63;
38
+ bytes[9] = sequence >>> 6 & 255;
39
+ bytes[10] = (sequence & 63) << 2 | random[10] & 3;
40
+ bytes[11] = random[11];
41
+ bytes[12] = random[12];
42
+ bytes[13] = random[13];
43
+ bytes[14] = random[14];
44
+ bytes[15] = random[15];
45
+ const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0"));
46
+ return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`;
47
+ }
48
+ //#endregion
49
+ //#region src/compat/vendor/pi-ai-retry.ts
50
+ function buildProviderErrorPattern(patterns) {
51
+ return new RegExp(patterns.join("|"), "i");
52
+ }
53
+ const NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN = buildProviderErrorPattern([
54
+ "GoUsageLimitError",
55
+ "FreeUsageLimitError",
56
+ "Monthly usage limit reached",
57
+ "available balance",
58
+ "insufficient_quota",
59
+ "out of budget",
60
+ "quota exceeded",
61
+ "billing"
62
+ ]);
63
+ const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
64
+ "overloaded",
65
+ "rate.?limit",
66
+ "too many requests",
67
+ "429",
68
+ "500",
69
+ "502",
70
+ "503",
71
+ "504",
72
+ "524",
73
+ "service.?unavailable",
74
+ "server.?error",
75
+ "internal.?error",
76
+ "provider.?returned.?error",
77
+ "exceeded request buffer limit while retrying upstream",
78
+ "network.?error",
79
+ "connection.?error",
80
+ "connection.?refused",
81
+ "connection.?lost",
82
+ "other side closed",
83
+ "fetch failed",
84
+ "getaddrinfo",
85
+ "ENOTFOUND",
86
+ "EAI_AGAIN",
87
+ "upstream.?connect",
88
+ "reset before headers",
89
+ "socket hang up",
90
+ "socket connection was closed",
91
+ "timed? out",
92
+ "timeout",
93
+ "terminated",
94
+ "websocket.?closed",
95
+ "websocket.?error",
96
+ "ended without",
97
+ "stream ended before message_stop",
98
+ "stream ended before a terminal response event",
99
+ "http2 request did not get a response",
100
+ "retry delay",
101
+ "you can retry your request",
102
+ "try your request again",
103
+ "please retry your request",
104
+ "ResourceExhausted"
105
+ ]);
106
+ var RetrySleepAbortError = class extends Error {
107
+ constructor() {
108
+ super("Aborted");
109
+ }
110
+ };
111
+ function sleep$1(ms, signal) {
112
+ return new Promise((resolve, reject) => {
113
+ if (signal?.aborted) {
114
+ reject(new RetrySleepAbortError());
115
+ return;
116
+ }
117
+ const timeout = setTimeout(resolve, ms);
118
+ signal?.addEventListener("abort", () => {
119
+ clearTimeout(timeout);
120
+ reject(new RetrySleepAbortError());
121
+ }, { once: true });
122
+ });
123
+ }
124
+ /**
125
+ * Run a single assistant-producing call with bounded retry on transient errors.
126
+ *
127
+ * Behavior:
128
+ * - A successful response is returned immediately. Aborts are terminal and never
129
+ * retried, but reported as unsuccessful if they happen after a retry was scheduled.
130
+ * Aborts during the backoff sleep are normalized to an aborted `AssistantMessage`
131
+ * too, so callers do not need to care when cancellation happened.
132
+ * - A non-retryable error (per {@link isRetryableAssistantError}, including quota/
133
+ * billing exhaustion) is returned immediately so deterministic errors fail fast.
134
+ * - Otherwise retries up to `maxRetries` times with exponential backoff, emitting
135
+ * `onRetryScheduled` before each sleep, `onRetryAttemptStart` after each sleep before
136
+ * the retried call starts, and `onRetryFinished` once at the end (whether the loop
137
+ * ends in success, exhausted retries, or an aborted backoff).
138
+ *
139
+ * When `policy` is undefined or disabled, the first response is returned unchanged
140
+ * (equivalent to calling `produce()` directly).
141
+ */
142
+ async function retryAssistantCall(produce, policy, signal, callbacks) {
143
+ const maxAttempts = policy?.enabled ? policy.maxRetries : 0;
144
+ let attempt = 0;
145
+ let lastRetry;
146
+ for (;;) {
147
+ const response = await produce();
148
+ if (response.stopReason === "aborted") {
149
+ if (lastRetry) await callbacks?.onRetryFinished?.(false, lastRetry.attempt);
150
+ return response;
151
+ }
152
+ if (response.stopReason !== "error") {
153
+ if (lastRetry) await callbacks?.onRetryFinished?.(true, lastRetry.attempt);
154
+ return response;
155
+ }
156
+ if (attempt >= maxAttempts || !isRetryableAssistantError(response)) {
157
+ if (lastRetry) await callbacks?.onRetryFinished?.(false, lastRetry.attempt, response.errorMessage);
158
+ return response;
159
+ }
160
+ attempt++;
161
+ lastRetry = {
162
+ attempt,
163
+ errorMessage: response.errorMessage || "Unknown error"
164
+ };
165
+ const delayMs = policy.baseDelayMs * 2 ** (attempt - 1);
166
+ await callbacks?.onRetryScheduled?.(attempt, maxAttempts, delayMs, lastRetry.errorMessage);
167
+ try {
168
+ await sleep$1(delayMs, signal);
169
+ } catch (error) {
170
+ await callbacks?.onRetryFinished?.(false, attempt, lastRetry.errorMessage);
171
+ if (error instanceof RetrySleepAbortError) return {
172
+ ...response,
173
+ stopReason: "aborted",
174
+ errorMessage: void 0
175
+ };
176
+ throw error;
177
+ }
178
+ await callbacks?.onRetryAttemptStart?.();
179
+ }
180
+ }
181
+ /**
182
+ * Classifies whether a failed assistant message looks like a transient provider
183
+ * or transport error, so callers can decide if the last assistant turn should be
184
+ * restarted.
185
+ *
186
+ * This does not implement retry policy. Callers should first handle context
187
+ * overflow separately, then apply their own retry budget, backoff, and reporting
188
+ * before restarting the assistant turn.
189
+ */
190
+ function isRetryableAssistantError(message) {
191
+ if (message.stopReason !== "error" || !message.errorMessage) return false;
192
+ const errorMessage = message.errorMessage;
193
+ if (NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN.test(errorMessage)) return false;
194
+ return RETRYABLE_PROVIDER_ERROR_PATTERN.test(errorMessage);
195
+ }
196
+ //#endregion
197
+ //#region src/compat/vendor/pi-ai-overflow.ts
198
+ /**
199
+ * Regex patterns to detect context overflow errors from different providers.
200
+ *
201
+ * These patterns match error messages returned when the input exceeds
202
+ * the model's context window.
203
+ *
204
+ * Provider-specific patterns (with example error messages):
205
+ *
206
+ * - Anthropic: "prompt is too long: 213462 tokens > 200000 maximum"
207
+ * - Anthropic: "413 {\"error\":{\"type\":\"request_too_large\",\"message\":\"Request exceeds the maximum size\"}}"
208
+ * - OpenAI: "Your input exceeds the context window of this model"
209
+ * - OpenAI/LiteLLM: "Requested token count exceeds the model's maximum context length of 131072 tokens"
210
+ * - OpenAI-compatible: "Input length (265330) exceeds model's maximum context length (262144)."
211
+ * - Google: "The input token count (1196265) exceeds the maximum number of tokens allowed (1048575)"
212
+ * - xAI: "This model's maximum prompt length is 131072 but the request contains 537812 tokens"
213
+ * - Groq: "Please reduce the length of the messages or completion"
214
+ * - OpenRouter: "This endpoint's maximum context length is X tokens. However, you requested about Y tokens"
215
+ * - OpenRouter/Poolside: "Input length X exceeds the maximum allowed input length of Y tokens."
216
+ * - Together AI: "The input (X tokens) is longer than the model's context length (Y tokens)."
217
+ * - llama.cpp: "the request exceeds the available context size, try increasing it"
218
+ * - LM Studio: "tokens to keep from the initial prompt is greater than the context length"
219
+ * - GitHub Copilot: "prompt token count of X exceeds the limit of Y"
220
+ * - MiniMax: "invalid params, context window exceeds limit"
221
+ * - Kimi For Coding: "Your request exceeded model token limit: X (requested: Y)"
222
+ * - DS4: "Prompt has X tokens, but the configured context size is Y tokens"
223
+ * - Cerebras: "400/413 status code (no body)"
224
+ * - Mistral: "Prompt contains X tokens ... too large for model with Y maximum context length"
225
+ * - z.ai: Does NOT error, accepts overflow silently - handled via usage.input > contextWindow
226
+ * - Xiaomi MiMo: Truncates input to fill contextWindow exactly, then returns finish_reason "length"
227
+ * with output=0 (no room left to generate). Detected via stopReason "length" + zero output +
228
+ * input filling the context window.
229
+ * - DashScope/Qwen: "Range of input length should be [1, X]" (HTTP 400 invalid_parameter_error)
230
+ * - Ollama: Some deployments truncate silently, others return errors like "prompt too long; exceeded max context length by X tokens"
231
+ */
232
+ const OVERFLOW_PATTERNS = [
233
+ /prompt is too long/i,
234
+ /request_too_large/i,
235
+ /input is too long for requested model/i,
236
+ /exceeds the context window/i,
237
+ /exceeds (?:the )?(?:model'?s )?maximum context length(?: of [\d,]+ tokens?|\s*\([\d,]+\))/i,
238
+ /input token count.*exceeds the maximum/i,
239
+ /maximum prompt length is \d+/i,
240
+ /reduce the length of the messages/i,
241
+ /maximum context length is \d+ tokens/i,
242
+ /exceeds (?:the )?maximum allowed input length of [\d,]+ tokens?/i,
243
+ /input \(\d+ tokens\) is longer than the model'?s context length \(\d+ tokens\)/i,
244
+ /exceeds the limit of \d+/i,
245
+ /exceeds the available context size/i,
246
+ /greater than the context length/i,
247
+ /context window exceeds limit/i,
248
+ /exceeded model token limit/i,
249
+ /too large for model with \d+ maximum context length/i,
250
+ /prompt has [\d,]+ tokens?, but the configured context size is [\d,]+ tokens?/i,
251
+ /model_context_window_exceeded/i,
252
+ /prompt too long; exceeded (?:max )?context length/i,
253
+ /range of input length should be/i,
254
+ /context[_ ]length[_ ]exceeded/i,
255
+ /too many tokens/i,
256
+ /token limit exceeded/i,
257
+ /^4(?:00|13)\s*(?:status code)?\s*\(no body\)/i
258
+ ];
259
+ /**
260
+ * Patterns that indicate non-overflow errors (e.g. rate limiting, server errors).
261
+ * Error messages matching any of these are excluded from overflow detection
262
+ * even if they also match an OVERFLOW_PATTERN.
263
+ *
264
+ * Example: Bedrock formats throttling errors as "ThrottlingException: Too many tokens,
265
+ * please wait before trying again." which would match the /too many tokens/i overflow
266
+ * pattern without this exclusion.
267
+ */
268
+ const NON_OVERFLOW_PATTERNS = [
269
+ /^(Throttling error|Service unavailable):/i,
270
+ /rate limit/i,
271
+ /too many requests/i
272
+ ];
273
+ /**
274
+ * Check if an assistant message represents a context overflow error.
275
+ *
276
+ * This handles three cases:
277
+ * 1. Error-based overflow: Most providers return stopReason "error" with a
278
+ * specific error message pattern.
279
+ * 2. Silent overflow: Some providers accept overflow requests and return
280
+ * successfully. For these, we check if usage.input exceeds the context window.
281
+ * 3. Length-stop overflow: Xiaomi MiMo can return "length" with zero output when
282
+ * the input fills the context window.
283
+ *
284
+ * ## Reliability by Provider
285
+ *
286
+ * **Reliable detection (returns error with detectable message):**
287
+ * - Anthropic: "prompt is too long: X tokens > Y maximum" or "request_too_large"
288
+ * - OpenAI (Completions & Responses): "exceeds the context window", "exceeds the model's maximum context length of X tokens", or "exceeds model's maximum context length (X)"
289
+ * - Google Gemini: "input token count exceeds the maximum"
290
+ * - xAI (Grok): "maximum prompt length is X but request contains Y"
291
+ * - Groq: "reduce the length of the messages"
292
+ * - Cerebras: 400/413 status code (no body)
293
+ * - Mistral: "Prompt contains X tokens ... too large for model with Y maximum context length"
294
+ * - OpenRouter (most backends): "maximum context length is X tokens"
295
+ * - OpenRouter/Poolside: "Input length X exceeds the maximum allowed input length of Y tokens."
296
+ * - Together AI: "The input (X tokens) is longer than the model's context length (Y tokens)."
297
+ * - llama.cpp: "exceeds the available context size"
298
+ * - LM Studio: "greater than the context length"
299
+ * - Kimi For Coding: "exceeded model token limit: X (requested: Y)"
300
+ * - DS4: "Prompt has X tokens, but the configured context size is Y tokens"
301
+ * - DashScope/Qwen: "Range of input length should be [1, X]"
302
+ *
303
+ * **Unreliable detection:**
304
+ * - z.ai: Sometimes accepts overflow silently (detectable via usage.input > contextWindow),
305
+ * sometimes returns rate limit errors. Pass contextWindow param to detect silent overflow.
306
+ * - Xiaomi MiMo: Truncates input to fit contextWindow then returns stopReason "length" with
307
+ * output=0. Pass contextWindow param to detect via the "filled context + zero output" signal.
308
+ * - Ollama: May truncate input silently for some setups, but may also return explicit
309
+ * overflow errors that match the patterns above. Silent truncation still cannot be
310
+ * detected here because we do not know the expected token count.
311
+ *
312
+ * ## Custom Providers
313
+ *
314
+ * If you've added custom models via settings.json, this function may not detect
315
+ * overflow errors from those providers. To add support:
316
+ *
317
+ * 1. Send a request that exceeds the model's context window
318
+ * 2. Check the errorMessage in the response
319
+ * 3. Create a regex pattern that matches the error
320
+ * 4. The pattern should be added to OVERFLOW_PATTERNS in this file, or
321
+ * check the errorMessage yourself before calling this function
322
+ *
323
+ * @param message - The assistant message to check
324
+ * @param contextWindow - Optional context window size for detecting silent overflow (z.ai)
325
+ * @returns true if the message indicates a context overflow
326
+ */
327
+ function isContextOverflow(message, contextWindow) {
328
+ if (message.stopReason === "error" && message.errorMessage) {
329
+ if (!NON_OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage)) && OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage))) return true;
330
+ }
331
+ if (contextWindow && message.stopReason === "stop") {
332
+ if (message.usage.input + message.usage.cacheRead > contextWindow) return true;
333
+ }
334
+ if (contextWindow && message.stopReason === "length" && message.usage.output === 0) {
335
+ if (message.usage.input + message.usage.cacheRead >= contextWindow * .99) return true;
336
+ }
337
+ return false;
338
+ }
339
+ /**
340
+ * Check whether a length stop ended below the caller or model's intended output limit.
341
+ * Such responses may be caused by context pressure or provider-side truncation, so callers
342
+ * can make one bounded compact-and-retry attempt. `desiredMaxOutput` must be the original
343
+ * limit before any context-based clamping.
344
+ */
345
+ function isRecoverableLength(message, desiredMaxOutput) {
346
+ return message.stopReason === "length" && desiredMaxOutput > 0 && message.usage.output < desiredMaxOutput;
347
+ }
348
+ //#endregion
349
+ //#region src/compat/vendor/pi-ai-event-stream.ts
350
+ var EventStream = class {
351
+ queue = [];
352
+ waiting = [];
353
+ done = false;
354
+ finalResultPromise;
355
+ resolveFinalResult;
356
+ isComplete;
357
+ extractResult;
358
+ constructor(isComplete, extractResult) {
359
+ this.isComplete = isComplete;
360
+ this.extractResult = extractResult;
361
+ this.finalResultPromise = new Promise((resolve) => {
362
+ this.resolveFinalResult = resolve;
363
+ });
364
+ }
365
+ push(event) {
366
+ if (this.done) return;
367
+ if (this.isComplete(event)) {
368
+ this.done = true;
369
+ this.resolveFinalResult(this.extractResult(event));
370
+ }
371
+ const waiter = this.waiting.shift();
372
+ if (waiter) waiter({
373
+ value: event,
374
+ done: false
375
+ });
376
+ else this.queue.push(event);
377
+ }
378
+ end(result) {
379
+ this.done = true;
380
+ if (result !== void 0) this.resolveFinalResult(result);
381
+ while (this.waiting.length > 0) this.waiting.shift()({
382
+ value: void 0,
383
+ done: true
384
+ });
385
+ }
386
+ async *[Symbol.asyncIterator]() {
387
+ while (true) if (this.queue.length > 0) yield this.queue.shift();
388
+ else if (this.done) return;
389
+ else {
390
+ const result = await new Promise((resolve) => this.waiting.push(resolve));
391
+ if (result.done) return;
392
+ yield result.value;
393
+ }
394
+ }
395
+ result() {
396
+ return this.finalResultPromise;
397
+ }
398
+ };
399
+ var AssistantMessageEventStream = class extends EventStream {
400
+ constructor() {
401
+ super((event) => event.type === "done" || event.type === "error", (event) => {
402
+ if (event.type === "done") return event.message;
403
+ else if (event.type === "error") return event.error;
404
+ throw new Error("Unexpected event type for final result");
405
+ });
406
+ }
407
+ };
408
+ //#endregion
409
+ //#region src/compat/vendor/pi-ai-lazy.ts
410
+ function createSetupErrorMessage$1(model, error) {
411
+ return {
412
+ role: "assistant",
413
+ content: [],
414
+ api: model.api,
415
+ provider: model.provider,
416
+ model: model.id,
417
+ usage: {
418
+ input: 0,
419
+ output: 0,
420
+ cacheRead: 0,
421
+ cacheWrite: 0,
422
+ totalTokens: 0,
423
+ cost: {
424
+ input: 0,
425
+ output: 0,
426
+ cacheRead: 0,
427
+ cacheWrite: 0,
428
+ total: 0
429
+ }
430
+ },
431
+ stopReason: "error",
432
+ errorMessage: error instanceof Error ? error.message : String(error),
433
+ timestamp: Date.now()
434
+ };
435
+ }
436
+ function hasResult$1(source) {
437
+ return typeof source.result === "function";
438
+ }
439
+ async function forwardStream$1(target, source) {
440
+ for await (const event of source) target.push(event);
441
+ target.end(hasResult$1(source) ? await source.result() : void 0);
442
+ }
443
+ /**
444
+ * Returns a stream synchronously while running async setup (auth resolution,
445
+ * lazy module loading) behind it. Setup failures terminate the stream with an
446
+ * error event.
447
+ */
448
+ function lazyStream$1(model, setup) {
449
+ const outer = new AssistantMessageEventStream();
450
+ setup().then((inner) => forwardStream$1(outer, inner)).catch((error) => {
451
+ const message = createSetupErrorMessage$1(model, error);
452
+ outer.push({
453
+ type: "error",
454
+ reason: "error",
455
+ error: message
456
+ });
457
+ outer.end(message);
458
+ });
459
+ return outer;
460
+ }
461
+ function lazyApi$1(load, capabilities) {
462
+ const api = {
463
+ stream: (model, context, options) => lazyStream$1(model, async () => (await load()).stream(model, context, options)),
464
+ streamSimple: (model, context, options) => lazyStream$1(model, async () => (await load()).streamSimple(model, context, options))
465
+ };
466
+ if (capabilities?.fetchDeferred) api.fetchDeferred = (model, handle, options) => lazyStream$1(model, async () => {
467
+ const implementation = await load();
468
+ if (!implementation.fetchDeferred) throw new Error("API does not support deferred responses");
469
+ return implementation.fetchDeferred(model, handle, options);
470
+ });
471
+ if (capabilities?.cancelDeferred) api.cancelDeferred = async (model, handle, options) => {
472
+ const implementation = await load();
473
+ if (!implementation.cancelDeferred) throw new Error("API cannot cancel deferred responses");
474
+ await implementation.cancelDeferred(model, handle, options);
475
+ };
476
+ return api;
477
+ }
478
+ //#endregion
479
+ //#region src/compat/vendor/pi-ai-provider.ts
480
+ function withCauseDetail(message, cause) {
481
+ if (cause === void 0 || cause === null) return message;
482
+ const detail = formatThrownValue(cause).trim();
483
+ if (!detail || message.includes(detail)) return message;
484
+ return `${message}: ${detail}`;
485
+ }
486
+ var ModelsError = class extends Error {
487
+ code;
488
+ constructor(code, message, options) {
489
+ super(withCauseDetail(message, options?.cause), options);
490
+ this.name = "ModelsError";
491
+ this.code = code;
492
+ }
493
+ };
494
+ /**
495
+ * Builds a provider from parts. Built-in provider factories and models.json
496
+ * custom providers both go through this. A single `api` streams all models;
497
+ * an `api` map dispatches on `model.api`, and a model whose api has no entry
498
+ * produces a stream error.
499
+ */
500
+ function createProvider(input) {
501
+ const baselineModels = input.models;
502
+ let dynamicModels = [];
503
+ const fetchModels = input.fetchModels;
504
+ const currentModels = () => {
505
+ const merged = [...baselineModels];
506
+ for (const model of dynamicModels) {
507
+ const index = merged.findIndex((entry) => entry.id === model.id);
508
+ if (index >= 0) merged[index] = model;
509
+ else merged.push(model);
510
+ }
511
+ return merged;
512
+ };
513
+ const single = typeof input.api.stream === "function" ? input.api : void 0;
514
+ const byApi = single ? void 0 : input.api;
515
+ const apiFor = (model) => single ?? byApi?.[model.api];
516
+ const dispatch = (model, run) => {
517
+ const streams = apiFor(model);
518
+ if (!streams) return lazyStream$1(model, async () => {
519
+ throw new ModelsError("stream", `Provider ${input.id} has no API implementation for "${model.api}"`);
520
+ });
521
+ return run(streams);
522
+ };
523
+ const provider = {
524
+ id: input.id,
525
+ name: input.name ?? input.id,
526
+ baseUrl: input.baseUrl,
527
+ headers: input.headers,
528
+ auth: input.auth,
529
+ getModels: currentModels,
530
+ refreshModels: fetchModels ? async (context) => {
531
+ if (context.stored) {
532
+ const restored = context.stored.models.filter((model) => model.provider === input.id).map((model) => model);
533
+ if (!await context.publish({ update: () => {
534
+ dynamicModels = restored;
535
+ } })) return;
536
+ }
537
+ if (!context.allowNetwork || context.signal.aborted) return;
538
+ const refreshed = await fetchModels(context);
539
+ if (context.signal.aborted) return;
540
+ await context.publish({
541
+ persist: {
542
+ models: refreshed,
543
+ checkedAt: Date.now()
544
+ },
545
+ update: () => {
546
+ dynamicModels = refreshed;
547
+ }
548
+ });
549
+ } : void 0,
550
+ filterModels: input.filterModels,
551
+ stream: (model, context, options) => dispatch(model, (streams) => streams.stream(model, context, options)),
552
+ streamSimple: (model, context, options) => dispatch(model, (streams) => streams.streamSimple(model, context, options))
553
+ };
554
+ const streams = single ? [single] : Object.values(byApi ?? {}).filter((entry) => entry !== void 0);
555
+ if (streams.some((entry) => entry.fetchDeferred !== void 0)) provider.fetchDeferred = (model, handle, options) => lazyStream$1(model, async () => {
556
+ const implementation = apiFor(model);
557
+ if (!implementation?.fetchDeferred) throw new ModelsError("provider", `Provider ${input.id} does not support deferred responses for "${model.api}"`);
558
+ return implementation.fetchDeferred(model, handle, options);
559
+ });
560
+ if (streams.some((entry) => entry.cancelDeferred !== void 0)) provider.cancelDeferred = async (model, handle, options) => {
561
+ const implementation = apiFor(model);
562
+ if (!implementation?.cancelDeferred) throw new ModelsError("provider", `Provider ${input.id} cannot cancel deferred responses for "${model.api}"`);
563
+ await implementation.cancelDeferred(model, handle, options);
564
+ };
565
+ return provider;
566
+ }
567
+ //#endregion
568
+ //#region node_modules/.pnpm/@earendil-works+pi-ai@0.84.1_ws@8.21.3_zod@4.4.3/node_modules/@earendil-works/pi-ai/dist/api/lazy.js
569
+ function createSetupErrorMessage(model, error) {
570
+ return {
571
+ role: "assistant",
572
+ content: [],
573
+ api: model.api,
574
+ provider: model.provider,
575
+ model: model.id,
576
+ usage: {
577
+ input: 0,
578
+ output: 0,
579
+ cacheRead: 0,
580
+ cacheWrite: 0,
581
+ totalTokens: 0,
582
+ cost: {
583
+ input: 0,
584
+ output: 0,
585
+ cacheRead: 0,
586
+ cacheWrite: 0,
587
+ total: 0
588
+ }
589
+ },
590
+ stopReason: "error",
591
+ errorMessage: error instanceof Error ? error.message : String(error),
592
+ timestamp: Date.now()
593
+ };
594
+ }
595
+ function hasResult(source) {
596
+ return typeof source.result === "function";
597
+ }
598
+ async function forwardStream(target, source) {
599
+ for await (const event of source) target.push(event);
600
+ target.end(hasResult(source) ? await source.result() : void 0);
601
+ }
602
+ /**
603
+ * Returns a stream synchronously while running async setup (auth resolution,
604
+ * lazy module loading) behind it. Setup failures terminate the stream with an
605
+ * error event.
606
+ */
607
+ function lazyStream(model, setup) {
608
+ const outer = new AssistantMessageEventStream$1();
609
+ setup().then((inner) => forwardStream(outer, inner)).catch((error) => {
610
+ const message = createSetupErrorMessage(model, error);
611
+ outer.push({
612
+ type: "error",
613
+ reason: "error",
614
+ error: message
615
+ });
616
+ outer.end(message);
617
+ });
618
+ return outer;
619
+ }
620
+ function lazyApi(load, capabilities) {
621
+ const api = {
622
+ stream: (model, context, options) => lazyStream(model, async () => (await load()).stream(model, context, options)),
623
+ streamSimple: (model, context, options) => lazyStream(model, async () => (await load()).streamSimple(model, context, options))
624
+ };
625
+ if (capabilities?.fetchDeferred) api.fetchDeferred = (model, handle, options) => lazyStream(model, async () => {
626
+ const implementation = await load();
627
+ if (!implementation.fetchDeferred) throw new Error("API does not support deferred responses");
628
+ return implementation.fetchDeferred(model, handle, options);
629
+ });
630
+ if (capabilities?.cancelDeferred) api.cancelDeferred = async (model, handle, options) => {
631
+ const implementation = await load();
632
+ if (!implementation.cancelDeferred) throw new Error("API cannot cancel deferred responses");
633
+ await implementation.cancelDeferred(model, handle, options);
634
+ };
635
+ return api;
636
+ }
637
+ //#endregion
638
+ //#region src/compat/vendor/pi-oauth-flows/provider-env.ts
639
+ let procEnvCache = null;
640
+ /**
641
+ * Fallback for https://github.com/oven-sh/bun/issues/27802.
642
+ * Bun compiled binaries can expose an empty process.env inside Linux sandboxes
643
+ * even though /proc/self/environ contains the environment.
644
+ *
645
+ * This intentionally duplicates restoreSandboxEnv() in
646
+ * packages/coding-agent/src/bun/restore-sandbox-env.ts. The ai package can be
647
+ * used directly, without going through that entrypoint, so provider env lookup
648
+ * must not depend on process.env having been patched.
649
+ */
650
+ function getBunSandboxEnvValue(name) {
651
+ if (typeof process === "undefined" || !process.versions?.bun || Object.keys(process.env).length > 0) return;
652
+ if (procEnvCache === null) {
653
+ procEnvCache = /* @__PURE__ */ new Map();
654
+ try {
655
+ const { readFileSync } = __require("node:fs");
656
+ const data = readFileSync("/proc/self/environ", "utf-8");
657
+ for (const entry of data.split("\0")) {
658
+ const idx = entry.indexOf("=");
659
+ if (idx > 0) procEnvCache.set(entry.slice(0, idx), entry.slice(idx + 1));
660
+ }
661
+ } catch {}
662
+ }
663
+ return procEnvCache.get(name);
664
+ }
665
+ /**
666
+ * Resolve a provider env value from scoped overrides, normal process.env, then
667
+ * the duplicated Bun sandbox fallback for direct pi-ai consumers.
668
+ */
669
+ function getProviderEnvValue(name, env) {
670
+ return env?.[name] || (typeof process !== "undefined" ? process.env[name] : void 0) || getBunSandboxEnvValue(name) || void 0;
671
+ }
672
+ //#endregion
673
+ //#region src/compat/vendor/pi-oauth-flows/device-code.ts
674
+ const CANCEL_MESSAGE = "Login cancelled";
675
+ const TIMEOUT_MESSAGE = "Device flow timed out";
676
+ const SLOW_DOWN_TIMEOUT_MESSAGE = "Device flow timed out after one or more slow_down responses. This is often caused by clock drift in WSL or VM environments. Please sync or restart the VM clock and try again.";
677
+ const MINIMUM_INTERVAL_MS = 1e3;
678
+ const DEFAULT_POLL_INTERVAL_SECONDS$1 = 5;
679
+ const SLOW_DOWN_INTERVAL_INCREMENT_MS = 5e3;
680
+ function abortableSleep(ms, signal, cancelMessage) {
681
+ return new Promise((resolve, reject) => {
682
+ if (signal.aborted) {
683
+ reject(new Error(cancelMessage));
684
+ return;
685
+ }
686
+ const onAbort = () => {
687
+ clearTimeout(timeout);
688
+ reject(new Error(cancelMessage));
689
+ };
690
+ const timeout = setTimeout(() => {
691
+ signal.removeEventListener("abort", onAbort);
692
+ resolve();
693
+ }, ms);
694
+ signal.addEventListener("abort", onAbort, { once: true });
695
+ });
696
+ }
697
+ async function pollOAuthDeviceCodeFlow(options) {
698
+ const deadline = typeof options.expiresInSeconds === "number" ? Date.now() + options.expiresInSeconds * 1e3 : Number.POSITIVE_INFINITY;
699
+ let intervalMs = Math.max(MINIMUM_INTERVAL_MS, Math.floor((options.intervalSeconds ?? DEFAULT_POLL_INTERVAL_SECONDS$1) * 1e3));
700
+ let slowDownResponses = 0;
701
+ if (options.waitBeforeFirstPoll) {
702
+ const remainingMs = deadline - Date.now();
703
+ if (remainingMs > 0) await abortableSleep(Math.min(intervalMs, remainingMs), options.signal, CANCEL_MESSAGE);
704
+ }
705
+ while (Date.now() < deadline) {
706
+ if (options.signal.aborted) throw new Error(CANCEL_MESSAGE);
707
+ const result = await options.poll();
708
+ if (result.status === "complete") return result.value;
709
+ if (result.status === "failed") throw new Error(result.message);
710
+ if (result.status === "slow_down") {
711
+ slowDownResponses += 1;
712
+ intervalMs = typeof result.intervalSeconds === "number" && Number.isFinite(result.intervalSeconds) && result.intervalSeconds > 0 ? Math.max(MINIMUM_INTERVAL_MS, Math.floor(result.intervalSeconds * 1e3)) : Math.max(MINIMUM_INTERVAL_MS, intervalMs + SLOW_DOWN_INTERVAL_INCREMENT_MS);
713
+ }
714
+ const remainingMs = deadline - Date.now();
715
+ if (remainingMs <= 0) break;
716
+ await abortableSleep(Math.min(intervalMs, remainingMs), options.signal, CANCEL_MESSAGE);
717
+ }
718
+ throw new Error(slowDownResponses > 0 ? SLOW_DOWN_TIMEOUT_MESSAGE : TIMEOUT_MESSAGE);
719
+ }
720
+ //#endregion
721
+ //#region src/compat/vendor/pi-oauth-flows/oauth-page.ts
722
+ const LOGO_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" aria-hidden="true"><path fill="#fff" fill-rule="evenodd" d="M165.29 165.29 H517.36 V400 H400 V517.36 H282.65 V634.72 H165.29 Z M282.65 282.65 V400 H400 V282.65 Z"/><path fill="#fff" d="M517.36 400 H634.72 V634.72 H517.36 Z"/></svg>`;
723
+ function escapeHtml(value) {
724
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
725
+ }
726
+ function renderPage(options) {
727
+ const title = escapeHtml(options.title);
728
+ const heading = escapeHtml(options.heading);
729
+ const message = escapeHtml(options.message);
730
+ const details = options.details ? escapeHtml(options.details) : void 0;
731
+ return `<!doctype html>
732
+ <html lang="en">
733
+ <head>
734
+ <meta charset="utf-8" />
735
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
736
+ <title>${title}</title>
737
+ <style>
738
+ :root {
739
+ --text: #fafafa;
740
+ --text-dim: #a1a1aa;
741
+ --page-bg: #09090b;
742
+ --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
743
+ --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
744
+ }
745
+ * { box-sizing: border-box; }
746
+ html { color-scheme: dark; }
747
+ body {
748
+ margin: 0;
749
+ min-height: 100vh;
750
+ display: flex;
751
+ align-items: center;
752
+ justify-content: center;
753
+ padding: 24px;
754
+ background: var(--page-bg);
755
+ color: var(--text);
756
+ font-family: var(--font-sans);
757
+ text-align: center;
758
+ }
759
+ main {
760
+ width: 100%;
761
+ max-width: 560px;
762
+ display: flex;
763
+ flex-direction: column;
764
+ align-items: center;
765
+ justify-content: center;
766
+ }
767
+ .logo {
768
+ width: 72px;
769
+ height: 72px;
770
+ display: block;
771
+ margin-bottom: 24px;
772
+ }
773
+ h1 {
774
+ margin: 0 0 10px;
775
+ font-size: 28px;
776
+ line-height: 1.15;
777
+ font-weight: 650;
778
+ color: var(--text);
779
+ }
780
+ p {
781
+ margin: 0;
782
+ line-height: 1.7;
783
+ color: var(--text-dim);
784
+ font-size: 15px;
785
+ }
786
+ .details {
787
+ margin-top: 16px;
788
+ font-family: var(--font-mono);
789
+ font-size: 13px;
790
+ color: var(--text-dim);
791
+ white-space: pre-wrap;
792
+ word-break: break-word;
793
+ }
794
+ </style>
795
+ </head>
796
+ <body>
797
+ <main>
798
+ <div class="logo">${LOGO_SVG}</div>
799
+ <h1>${heading}</h1>
800
+ <p>${message}</p>
801
+ ${details ? `<div class="details">${details}</div>` : ""}
802
+ </main>
803
+ </body>
804
+ </html>`;
805
+ }
806
+ function oauthSuccessHtml(message) {
807
+ return renderPage({
808
+ title: "Authentication successful",
809
+ heading: "Authentication successful",
810
+ message
811
+ });
812
+ }
813
+ function oauthErrorHtml(message, details) {
814
+ return renderPage({
815
+ title: "Authentication failed",
816
+ heading: "Authentication failed",
817
+ message,
818
+ details
819
+ });
820
+ }
821
+ //#endregion
822
+ //#region src/compat/vendor/pi-oauth-flows/pkce.ts
823
+ /**
824
+ * PKCE utilities using Web Crypto API.
825
+ * Works in both Node.js 20+ and browsers.
826
+ */
827
+ /**
828
+ * Encode bytes as base64url string.
829
+ */
830
+ function base64urlEncode(bytes) {
831
+ let binary = "";
832
+ for (const byte of bytes) binary += String.fromCharCode(byte);
833
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
834
+ }
835
+ /**
836
+ * Generate PKCE code verifier and challenge.
837
+ * Uses Web Crypto API for cross-platform compatibility.
838
+ */
839
+ async function generatePKCE() {
840
+ const verifierBytes = /* @__PURE__ */ new Uint8Array(32);
841
+ crypto.getRandomValues(verifierBytes);
842
+ const verifier = base64urlEncode(verifierBytes);
843
+ const data = new TextEncoder().encode(verifier);
844
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
845
+ return {
846
+ verifier,
847
+ challenge: base64urlEncode(new Uint8Array(hashBuffer))
848
+ };
849
+ }
850
+ //#endregion
851
+ //#region src/compat/vendor/pi-oauth-flows/openai-codex.ts
852
+ /**
853
+ * OpenAI Codex (ChatGPT OAuth) flow
854
+ *
855
+ * NOTE: This module uses Node.js crypto and http for the OAuth callback.
856
+ * It is only intended for CLI use, not browser environments.
857
+ */
858
+ let _randomBytes = null;
859
+ let _http = null;
860
+ if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
861
+ import("node:crypto").then((m) => {
862
+ _randomBytes = m.randomBytes;
863
+ });
864
+ import("node:http").then((m) => {
865
+ _http = m;
866
+ });
867
+ }
868
+ const CLIENT_ID$3 = "app_EMoamEEZ73f0CkXaXp7hrann";
869
+ const AUTH_BASE_URL = "https://auth.openai.com";
870
+ const AUTHORIZE_URL$1 = `${AUTH_BASE_URL}/oauth/authorize`;
871
+ const TOKEN_URL$1 = `${AUTH_BASE_URL}/oauth/token`;
872
+ const REDIRECT_URI$1 = "http://localhost:1455/auth/callback";
873
+ const DEVICE_USER_CODE_URL = `${AUTH_BASE_URL}/api/accounts/deviceauth/usercode`;
874
+ const DEVICE_TOKEN_URL = `${AUTH_BASE_URL}/api/accounts/deviceauth/token`;
875
+ const DEVICE_VERIFICATION_URI = `${AUTH_BASE_URL}/codex/device`;
876
+ const DEVICE_REDIRECT_URI = `${AUTH_BASE_URL}/deviceauth/callback`;
877
+ const DEVICE_CODE_TIMEOUT_SECONDS$1 = 900;
878
+ const OPENAI_CODEX_BROWSER_LOGIN_METHOD = "browser";
879
+ const OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD = "device_code";
880
+ const SCOPE = "openid profile email offline_access";
881
+ const JWT_CLAIM_PATH = "https://api.openai.com/auth";
882
+ function getCallbackHost() {
883
+ return getProviderEnvValue("PI_OAUTH_CALLBACK_HOST") || "127.0.0.1";
884
+ }
885
+ function createState() {
886
+ if (!_randomBytes) throw new Error("OpenAI Codex OAuth is only available in Node.js environments");
887
+ return _randomBytes(16).toString("hex");
888
+ }
889
+ function parseAuthorizationInput$1(input) {
890
+ const value = input.trim();
891
+ if (!value) return {};
892
+ try {
893
+ const url = new URL(value);
894
+ return {
895
+ code: url.searchParams.get("code") ?? void 0,
896
+ state: url.searchParams.get("state") ?? void 0
897
+ };
898
+ } catch {}
899
+ if (value.includes("#")) {
900
+ const [code, state] = value.split("#", 2);
901
+ return {
902
+ code,
903
+ state
904
+ };
905
+ }
906
+ if (value.includes("code=")) {
907
+ const params = new URLSearchParams(value);
908
+ return {
909
+ code: params.get("code") ?? void 0,
910
+ state: params.get("state") ?? void 0
911
+ };
912
+ }
913
+ return { code: value };
914
+ }
915
+ function decodeJwt(token) {
916
+ try {
917
+ const parts = token.split(".");
918
+ if (parts.length !== 3) return null;
919
+ const payload = parts[1] ?? "";
920
+ const decoded = atob(payload);
921
+ return JSON.parse(decoded);
922
+ } catch {
923
+ return null;
924
+ }
925
+ }
926
+ async function fetchWithLoginCancellation(input, init) {
927
+ try {
928
+ return await fetch(input, init);
929
+ } catch (error) {
930
+ if (init.signal?.aborted) throw new Error("Login cancelled");
931
+ throw error;
932
+ }
933
+ }
934
+ async function readTokenResponse(response, operation) {
935
+ if (!response.ok) {
936
+ const text = await response.text().catch(() => "");
937
+ throw new Error(`OpenAI Codex token ${operation} failed (${response.status}): ${text || response.statusText}`);
938
+ }
939
+ const json = await response.json();
940
+ if (!json?.access_token || !json.refresh_token || typeof json.expires_in !== "number") throw new Error(`OpenAI Codex token ${operation} response missing fields: ${JSON.stringify(json)}`);
941
+ return {
942
+ access: json.access_token,
943
+ refresh: json.refresh_token,
944
+ expires: Date.now() + json.expires_in * 1e3
945
+ };
946
+ }
947
+ async function exchangeAuthorizationCode$1(code, verifier, redirectUri, signal) {
948
+ return readTokenResponse(await fetchWithLoginCancellation(TOKEN_URL$1, {
949
+ method: "POST",
950
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
951
+ body: new URLSearchParams({
952
+ grant_type: "authorization_code",
953
+ client_id: CLIENT_ID$3,
954
+ code,
955
+ code_verifier: verifier,
956
+ redirect_uri: redirectUri
957
+ }),
958
+ signal
959
+ }), "exchange");
960
+ }
961
+ async function refreshAccessToken(refreshToken, signal) {
962
+ let response;
963
+ try {
964
+ response = await fetch(TOKEN_URL$1, {
965
+ method: "POST",
966
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
967
+ body: new URLSearchParams({
968
+ grant_type: "refresh_token",
969
+ refresh_token: refreshToken,
970
+ client_id: CLIENT_ID$3
971
+ }),
972
+ signal
973
+ });
974
+ } catch (error) {
975
+ throw new Error(`OpenAI Codex token refresh error: ${error instanceof Error ? error.message : String(error)}`);
976
+ }
977
+ return readTokenResponse(response, "refresh");
978
+ }
979
+ async function startOpenAICodexDeviceAuth(signal) {
980
+ const response = await fetchWithLoginCancellation(DEVICE_USER_CODE_URL, {
981
+ method: "POST",
982
+ headers: { "Content-Type": "application/json" },
983
+ body: JSON.stringify({ client_id: CLIENT_ID$3 }),
984
+ signal
985
+ });
986
+ if (!response.ok) {
987
+ if (response.status === 404) throw new Error("OpenAI Codex device code login is not enabled for this server. Use browser login or verify the server URL.");
988
+ const responseBody = await response.text().catch(() => "");
989
+ throw new Error(`OpenAI Codex device code request failed with status ${response.status}${responseBody ? `: ${responseBody}` : ""}`);
990
+ }
991
+ const json = await response.json();
992
+ const intervalSeconds = typeof json?.interval === "string" ? Number(json.interval.trim()) : json?.interval;
993
+ if (!json?.device_auth_id || !json.user_code || typeof intervalSeconds !== "number" || !Number.isFinite(intervalSeconds) || intervalSeconds < 0) throw new Error(`Invalid OpenAI Codex device code response: ${JSON.stringify(json)}`);
994
+ return {
995
+ deviceAuthId: json.device_auth_id,
996
+ userCode: json.user_code,
997
+ intervalSeconds
998
+ };
999
+ }
1000
+ async function pollOpenAICodexDeviceAuth(device, signal) {
1001
+ return pollOAuthDeviceCodeFlow({
1002
+ intervalSeconds: device.intervalSeconds,
1003
+ expiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS$1,
1004
+ signal,
1005
+ poll: async () => {
1006
+ const response = await fetchWithLoginCancellation(DEVICE_TOKEN_URL, {
1007
+ method: "POST",
1008
+ headers: { "Content-Type": "application/json" },
1009
+ body: JSON.stringify({
1010
+ device_auth_id: device.deviceAuthId,
1011
+ user_code: device.userCode
1012
+ }),
1013
+ signal
1014
+ });
1015
+ if (response.ok) {
1016
+ const json = await response.json();
1017
+ if (!json?.authorization_code || !json.code_verifier) return {
1018
+ status: "failed",
1019
+ message: `Invalid OpenAI Codex device auth token response: ${JSON.stringify(json)}`
1020
+ };
1021
+ return {
1022
+ status: "complete",
1023
+ value: {
1024
+ authorizationCode: json.authorization_code,
1025
+ codeVerifier: json.code_verifier
1026
+ }
1027
+ };
1028
+ }
1029
+ if (response.status === 403 || response.status === 404) return { status: "pending" };
1030
+ const responseBody = await response.text().catch(() => "");
1031
+ let errorCode;
1032
+ try {
1033
+ const error = JSON.parse(responseBody)?.error;
1034
+ errorCode = typeof error === "object" ? error?.code : error;
1035
+ } catch {}
1036
+ if (errorCode === "deviceauth_authorization_pending") return { status: "pending" };
1037
+ if (errorCode === "slow_down") return { status: "slow_down" };
1038
+ return {
1039
+ status: "failed",
1040
+ message: `OpenAI Codex device auth failed with status ${response.status}${responseBody ? `: ${responseBody}` : ""}`
1041
+ };
1042
+ }
1043
+ });
1044
+ }
1045
+ async function createAuthorizationFlow(originator = "pi") {
1046
+ const { verifier, challenge } = await generatePKCE();
1047
+ const state = createState();
1048
+ const url = new URL(AUTHORIZE_URL$1);
1049
+ url.searchParams.set("response_type", "code");
1050
+ url.searchParams.set("client_id", CLIENT_ID$3);
1051
+ url.searchParams.set("redirect_uri", REDIRECT_URI$1);
1052
+ url.searchParams.set("scope", SCOPE);
1053
+ url.searchParams.set("code_challenge", challenge);
1054
+ url.searchParams.set("code_challenge_method", "S256");
1055
+ url.searchParams.set("state", state);
1056
+ url.searchParams.set("id_token_add_organizations", "true");
1057
+ url.searchParams.set("codex_cli_simplified_flow", "true");
1058
+ url.searchParams.set("originator", originator);
1059
+ return {
1060
+ verifier,
1061
+ state,
1062
+ url: url.toString()
1063
+ };
1064
+ }
1065
+ function startLocalOAuthServer(state) {
1066
+ if (!_http) throw new Error("OpenAI Codex OAuth is only available in Node.js environments");
1067
+ let settleWait;
1068
+ const waitForCodePromise = new Promise((resolve) => {
1069
+ let settled = false;
1070
+ settleWait = (value) => {
1071
+ if (settled) return;
1072
+ settled = true;
1073
+ resolve(value);
1074
+ };
1075
+ });
1076
+ const server = _http.createServer((req, res) => {
1077
+ try {
1078
+ const url = new URL(req.url || "", "http://localhost");
1079
+ if (url.pathname !== "/auth/callback") {
1080
+ res.statusCode = 404;
1081
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
1082
+ res.end(oauthErrorHtml("Callback route not found."));
1083
+ return;
1084
+ }
1085
+ if (url.searchParams.get("state") !== state) {
1086
+ res.statusCode = 400;
1087
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
1088
+ res.end(oauthErrorHtml("State mismatch."));
1089
+ return;
1090
+ }
1091
+ const code = url.searchParams.get("code");
1092
+ if (!code) {
1093
+ res.statusCode = 400;
1094
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
1095
+ res.end(oauthErrorHtml("Missing authorization code."));
1096
+ return;
1097
+ }
1098
+ res.statusCode = 200;
1099
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
1100
+ res.end(oauthSuccessHtml("OpenAI authentication completed. You can close this window."));
1101
+ settleWait?.({ code });
1102
+ } catch {
1103
+ res.statusCode = 500;
1104
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
1105
+ res.end(oauthErrorHtml("Internal error while processing OAuth callback."));
1106
+ }
1107
+ });
1108
+ return new Promise((resolve) => {
1109
+ server.listen(1455, getCallbackHost(), () => {
1110
+ resolve({
1111
+ close: () => server.close(),
1112
+ cancelWait: () => {
1113
+ settleWait?.(null);
1114
+ },
1115
+ waitForCode: () => waitForCodePromise
1116
+ });
1117
+ }).on("error", (_err) => {
1118
+ settleWait?.(null);
1119
+ resolve({
1120
+ close: () => {
1121
+ try {
1122
+ server.close();
1123
+ } catch {}
1124
+ },
1125
+ cancelWait: () => {},
1126
+ waitForCode: async () => null
1127
+ });
1128
+ });
1129
+ });
1130
+ }
1131
+ function getAccountId(accessToken) {
1132
+ const accountId = (decodeJwt(accessToken)?.[JWT_CLAIM_PATH])?.chatgpt_account_id;
1133
+ return typeof accountId === "string" && accountId.length > 0 ? accountId : null;
1134
+ }
1135
+ function credentialsFromToken(token) {
1136
+ const accountId = getAccountId(token.access);
1137
+ if (!accountId) throw new Error("Failed to extract accountId from token");
1138
+ return {
1139
+ type: "oauth",
1140
+ access: token.access,
1141
+ refresh: token.refresh,
1142
+ expires: token.expires,
1143
+ accountId
1144
+ };
1145
+ }
1146
+ async function exchangeAuthorizationCodeForCredentials(code, verifier, redirectUri, signal) {
1147
+ return credentialsFromToken(await exchangeAuthorizationCode$1(code, verifier, redirectUri, signal));
1148
+ }
1149
+ async function loginOpenAICodexDeviceCode(interaction) {
1150
+ const device = await startOpenAICodexDeviceAuth(interaction.signal);
1151
+ interaction.notify({
1152
+ type: "device_code",
1153
+ userCode: device.userCode,
1154
+ verificationUri: DEVICE_VERIFICATION_URI,
1155
+ intervalSeconds: device.intervalSeconds,
1156
+ expiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS$1
1157
+ });
1158
+ const code = await pollOpenAICodexDeviceAuth(device, interaction.signal);
1159
+ return exchangeAuthorizationCodeForCredentials(code.authorizationCode, code.codeVerifier, DEVICE_REDIRECT_URI, interaction.signal);
1160
+ }
1161
+ async function loginOpenAICodex(interaction) {
1162
+ const { verifier, state, url } = await createAuthorizationFlow();
1163
+ const server = await startLocalOAuthServer(state);
1164
+ const manualAbort = new AbortController();
1165
+ const onAbort = () => server.cancelWait();
1166
+ interaction.signal.addEventListener("abort", onAbort, { once: true });
1167
+ if (interaction.signal.aborted) onAbort();
1168
+ let code;
1169
+ let manualCode;
1170
+ let manualError;
1171
+ interaction.notify({
1172
+ type: "auth_url",
1173
+ url,
1174
+ instructions: "A browser window should open. Complete login to finish."
1175
+ });
1176
+ try {
1177
+ const manualPromise = interaction.prompt({
1178
+ type: "manual_code",
1179
+ message: "Complete login in your browser, or paste the authorization code / redirect URL here:",
1180
+ placeholder: REDIRECT_URI$1,
1181
+ signal: manualAbort.signal
1182
+ }).then((input) => {
1183
+ manualCode = input;
1184
+ server.cancelWait();
1185
+ }).catch((error) => {
1186
+ manualError = error instanceof Error ? error : new Error(String(error));
1187
+ server.cancelWait();
1188
+ });
1189
+ const result = await server.waitForCode();
1190
+ if (manualError) throw manualError;
1191
+ if (result?.code) code = result.code;
1192
+ else if (manualCode) {
1193
+ const parsed = parseAuthorizationInput$1(manualCode);
1194
+ if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
1195
+ code = parsed.code;
1196
+ }
1197
+ if (!code) {
1198
+ await manualPromise;
1199
+ if (manualError) throw manualError;
1200
+ if (manualCode) {
1201
+ const parsed = parseAuthorizationInput$1(manualCode);
1202
+ if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
1203
+ code = parsed.code;
1204
+ }
1205
+ }
1206
+ if (!code) throw new Error("Missing authorization code");
1207
+ return exchangeAuthorizationCodeForCredentials(code, verifier, REDIRECT_URI$1, interaction.signal);
1208
+ } finally {
1209
+ interaction.signal.removeEventListener("abort", onAbort);
1210
+ manualAbort.abort();
1211
+ server.close();
1212
+ }
1213
+ }
1214
+ /**
1215
+ * Refresh OpenAI Codex OAuth token
1216
+ */
1217
+ async function refreshOpenAICodexToken(refreshToken, signal) {
1218
+ return credentialsFromToken(await refreshAccessToken(refreshToken, signal));
1219
+ }
1220
+ const openaiCodexOAuth = {
1221
+ name: "OpenAI (ChatGPT Plus/Pro)",
1222
+ isSubscription: true,
1223
+ async login(interaction) {
1224
+ const method = await interaction.prompt({
1225
+ type: "select",
1226
+ message: "Select OpenAI Codex login method:",
1227
+ options: [{
1228
+ id: OPENAI_CODEX_BROWSER_LOGIN_METHOD,
1229
+ label: "Browser login (default)"
1230
+ }, {
1231
+ id: OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD,
1232
+ label: "Device code login (headless)"
1233
+ }]
1234
+ });
1235
+ if (method === OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD) return loginOpenAICodexDeviceCode(interaction);
1236
+ if (method !== OPENAI_CODEX_BROWSER_LOGIN_METHOD) throw new Error(`Unknown OpenAI Codex login method: ${method}`);
1237
+ return loginOpenAICodex(interaction);
1238
+ },
1239
+ refresh: (credential, signal) => refreshOpenAICodexToken(credential.refresh, signal),
1240
+ async toAuth(credential) {
1241
+ return { apiKey: credential.access };
1242
+ }
1243
+ };
1244
+ //#endregion
1245
+ //#region src/compat/vendor/pi-oauth-flows/anthropic.ts
1246
+ /**
1247
+ * Anthropic OAuth flow (Claude Pro/Max)
1248
+ *
1249
+ * NOTE: This module uses Node.js http.createServer for the OAuth callback server.
1250
+ * It is only intended for CLI use, not browser environments.
1251
+ */
1252
+ let nodeApis = null;
1253
+ let nodeApisPromise = null;
1254
+ const decode$1 = (s) => atob(s);
1255
+ const CLIENT_ID$2 = decode$1("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
1256
+ const AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
1257
+ const TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
1258
+ const CALLBACK_HOST = getProviderEnvValue("PI_OAUTH_CALLBACK_HOST") || "127.0.0.1";
1259
+ const CALLBACK_PORT = 53692;
1260
+ const CALLBACK_PATH = "/callback";
1261
+ const REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;
1262
+ const SCOPES = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
1263
+ async function getNodeApis() {
1264
+ if (nodeApis) return nodeApis;
1265
+ if (!nodeApisPromise) {
1266
+ if (typeof process === "undefined" || !process.versions?.node && !process.versions?.bun) throw new Error("Anthropic OAuth is only available in Node.js environments");
1267
+ nodeApisPromise = import("node:http").then((httpModule) => ({ createServer: httpModule.createServer }));
1268
+ }
1269
+ nodeApis = await nodeApisPromise;
1270
+ return nodeApis;
1271
+ }
1272
+ function parseAuthorizationInput(input) {
1273
+ const value = input.trim();
1274
+ if (!value) return {};
1275
+ try {
1276
+ const url = new URL(value);
1277
+ return {
1278
+ code: url.searchParams.get("code") ?? void 0,
1279
+ state: url.searchParams.get("state") ?? void 0
1280
+ };
1281
+ } catch {}
1282
+ if (value.includes("#")) {
1283
+ const [code, state] = value.split("#", 2);
1284
+ return {
1285
+ code,
1286
+ state
1287
+ };
1288
+ }
1289
+ if (value.includes("code=")) {
1290
+ const params = new URLSearchParams(value);
1291
+ return {
1292
+ code: params.get("code") ?? void 0,
1293
+ state: params.get("state") ?? void 0
1294
+ };
1295
+ }
1296
+ return { code: value };
1297
+ }
1298
+ function formatErrorDetails(error) {
1299
+ if (error instanceof Error) {
1300
+ const details = [`${error.name}: ${error.message}`];
1301
+ const errorWithCode = error;
1302
+ if (errorWithCode.code) details.push(`code=${errorWithCode.code}`);
1303
+ if (typeof errorWithCode.errno !== "undefined") details.push(`errno=${String(errorWithCode.errno)}`);
1304
+ if (typeof error.cause !== "undefined") details.push(`cause=${formatErrorDetails(error.cause)}`);
1305
+ if (error.stack) details.push(`stack=${error.stack}`);
1306
+ return details.join("; ");
1307
+ }
1308
+ return String(error);
1309
+ }
1310
+ async function startCallbackServer(expectedState) {
1311
+ const { createServer } = await getNodeApis();
1312
+ return new Promise((resolve, reject) => {
1313
+ let settleWait;
1314
+ const waitForCodePromise = new Promise((resolveWait) => {
1315
+ let settled = false;
1316
+ settleWait = (value) => {
1317
+ if (settled) return;
1318
+ settled = true;
1319
+ resolveWait(value);
1320
+ };
1321
+ });
1322
+ const server = createServer((req, res) => {
1323
+ try {
1324
+ const url = new URL(req.url || "", "http://localhost");
1325
+ if (url.pathname !== CALLBACK_PATH) {
1326
+ res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
1327
+ res.end(oauthErrorHtml("Callback route not found."));
1328
+ return;
1329
+ }
1330
+ const code = url.searchParams.get("code");
1331
+ const state = url.searchParams.get("state");
1332
+ const error = url.searchParams.get("error");
1333
+ if (error) {
1334
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
1335
+ res.end(oauthErrorHtml("Anthropic authentication did not complete.", `Error: ${error}`));
1336
+ return;
1337
+ }
1338
+ if (!code || !state) {
1339
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
1340
+ res.end(oauthErrorHtml("Missing code or state parameter."));
1341
+ return;
1342
+ }
1343
+ if (state !== expectedState) {
1344
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
1345
+ res.end(oauthErrorHtml("State mismatch."));
1346
+ return;
1347
+ }
1348
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
1349
+ res.end(oauthSuccessHtml("Anthropic authentication completed. You can close this window."));
1350
+ settleWait?.({
1351
+ code,
1352
+ state
1353
+ });
1354
+ } catch {
1355
+ res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
1356
+ res.end("Internal error");
1357
+ }
1358
+ });
1359
+ server.on("error", (err) => {
1360
+ reject(err);
1361
+ });
1362
+ server.listen(CALLBACK_PORT, CALLBACK_HOST, () => {
1363
+ resolve({
1364
+ server,
1365
+ redirectUri: REDIRECT_URI,
1366
+ cancelWait: () => {
1367
+ settleWait?.(null);
1368
+ },
1369
+ waitForCode: () => waitForCodePromise
1370
+ });
1371
+ });
1372
+ });
1373
+ }
1374
+ async function postJson(url, body, signal) {
1375
+ const response = await fetch(url, {
1376
+ method: "POST",
1377
+ headers: {
1378
+ "Content-Type": "application/json",
1379
+ Accept: "application/json"
1380
+ },
1381
+ body: JSON.stringify(body),
1382
+ signal: AbortSignal.any([signal, AbortSignal.timeout(3e4)])
1383
+ });
1384
+ const responseBody = await response.text();
1385
+ if (!response.ok) throw new Error(`HTTP request failed. status=${response.status}; url=${url}; body=${responseBody}`);
1386
+ return responseBody;
1387
+ }
1388
+ async function exchangeAuthorizationCode(code, state, verifier, redirectUri, signal) {
1389
+ let responseBody;
1390
+ try {
1391
+ responseBody = await postJson(TOKEN_URL, {
1392
+ grant_type: "authorization_code",
1393
+ client_id: CLIENT_ID$2,
1394
+ code,
1395
+ state,
1396
+ redirect_uri: redirectUri,
1397
+ code_verifier: verifier
1398
+ }, signal);
1399
+ } catch (error) {
1400
+ throw new Error(`Token exchange request failed. url=${TOKEN_URL}; redirect_uri=${redirectUri}; response_type=authorization_code; details=${formatErrorDetails(error)}`);
1401
+ }
1402
+ let tokenData;
1403
+ try {
1404
+ tokenData = JSON.parse(responseBody);
1405
+ } catch (error) {
1406
+ throw new Error(`Token exchange returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`);
1407
+ }
1408
+ return {
1409
+ type: "oauth",
1410
+ refresh: tokenData.refresh_token,
1411
+ access: tokenData.access_token,
1412
+ expires: Date.now() + tokenData.expires_in * 1e3 - 3e5
1413
+ };
1414
+ }
1415
+ async function loginAnthropic(interaction) {
1416
+ const { verifier, challenge } = await generatePKCE();
1417
+ const server = await startCallbackServer(verifier);
1418
+ const manualAbort = new AbortController();
1419
+ const onAbort = () => server.cancelWait();
1420
+ interaction.signal.addEventListener("abort", onAbort, { once: true });
1421
+ if (interaction.signal.aborted) onAbort();
1422
+ let code;
1423
+ let state;
1424
+ let manualInput;
1425
+ let manualError;
1426
+ try {
1427
+ const authParams = new URLSearchParams({
1428
+ code: "true",
1429
+ client_id: CLIENT_ID$2,
1430
+ response_type: "code",
1431
+ redirect_uri: REDIRECT_URI,
1432
+ scope: SCOPES,
1433
+ code_challenge: challenge,
1434
+ code_challenge_method: "S256",
1435
+ state: verifier
1436
+ });
1437
+ interaction.notify({
1438
+ type: "auth_url",
1439
+ url: `${AUTHORIZE_URL}?${authParams.toString()}`,
1440
+ instructions: "Complete login in your browser. If the browser is on another machine, paste the final redirect URL here."
1441
+ });
1442
+ const manualPromise = interaction.prompt({
1443
+ type: "manual_code",
1444
+ message: "Complete login in your browser, or paste the authorization code / redirect URL here:",
1445
+ placeholder: REDIRECT_URI,
1446
+ signal: manualAbort.signal
1447
+ }).then((input) => {
1448
+ manualInput = input;
1449
+ server.cancelWait();
1450
+ }).catch((error) => {
1451
+ manualError = error instanceof Error ? error : new Error(String(error));
1452
+ server.cancelWait();
1453
+ });
1454
+ const result = await server.waitForCode();
1455
+ if (manualError) throw manualError;
1456
+ if (result?.code) {
1457
+ code = result.code;
1458
+ state = result.state;
1459
+ } else if (manualInput) {
1460
+ const parsed = parseAuthorizationInput(manualInput);
1461
+ if (parsed.state && parsed.state !== verifier) throw new Error("OAuth state mismatch");
1462
+ code = parsed.code;
1463
+ state = parsed.state ?? verifier;
1464
+ }
1465
+ if (!code) {
1466
+ await manualPromise;
1467
+ if (manualError) throw manualError;
1468
+ if (manualInput) {
1469
+ const parsed = parseAuthorizationInput(manualInput);
1470
+ if (parsed.state && parsed.state !== verifier) throw new Error("OAuth state mismatch");
1471
+ code = parsed.code;
1472
+ state = parsed.state ?? verifier;
1473
+ }
1474
+ }
1475
+ if (!code) throw new Error("Missing authorization code");
1476
+ if (!state) throw new Error("Missing OAuth state");
1477
+ interaction.notify({
1478
+ type: "progress",
1479
+ message: "Exchanging authorization code for tokens..."
1480
+ });
1481
+ return exchangeAuthorizationCode(code, state, verifier, REDIRECT_URI, interaction.signal);
1482
+ } finally {
1483
+ interaction.signal.removeEventListener("abort", onAbort);
1484
+ manualAbort.abort();
1485
+ server.server.close();
1486
+ }
1487
+ }
1488
+ /**
1489
+ * Refresh Anthropic OAuth token
1490
+ */
1491
+ async function refreshAnthropicToken(refreshToken, signal) {
1492
+ let responseBody;
1493
+ try {
1494
+ responseBody = await postJson(TOKEN_URL, {
1495
+ grant_type: "refresh_token",
1496
+ client_id: CLIENT_ID$2,
1497
+ refresh_token: refreshToken
1498
+ }, signal);
1499
+ } catch (error) {
1500
+ throw new Error(`Anthropic token refresh request failed. url=${TOKEN_URL}; details=${formatErrorDetails(error)}`);
1501
+ }
1502
+ let data;
1503
+ try {
1504
+ data = JSON.parse(responseBody);
1505
+ } catch (error) {
1506
+ throw new Error(`Anthropic token refresh returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`);
1507
+ }
1508
+ return {
1509
+ type: "oauth",
1510
+ refresh: data.refresh_token,
1511
+ access: data.access_token,
1512
+ expires: Date.now() + data.expires_in * 1e3 - 3e5
1513
+ };
1514
+ }
1515
+ const anthropicOAuth = {
1516
+ name: "Anthropic (Claude Pro/Max)",
1517
+ isSubscription: true,
1518
+ login: loginAnthropic,
1519
+ refresh: (credential, signal) => refreshAnthropicToken(credential.refresh, signal),
1520
+ async toAuth(credential) {
1521
+ return { apiKey: credential.access };
1522
+ }
1523
+ };
1524
+ //#endregion
1525
+ //#region src/compat/vendor/pi-oauth-flows/data/github-copilot.json
1526
+ var github_copilot_default = {
1527
+ "anthropic-messages": {
1528
+ "claude-haiku-4.5": {
1529
+ "id": "claude-haiku-4.5",
1530
+ "name": "Claude Haiku 4.5 (latest)",
1531
+ "api": "anthropic-messages",
1532
+ "provider": "github-copilot",
1533
+ "baseUrl": "https://api.individual.githubcopilot.com",
1534
+ "reasoning": true,
1535
+ "input": ["text", "image"],
1536
+ "cost": {
1537
+ "input": 1,
1538
+ "output": 5,
1539
+ "cacheRead": .1,
1540
+ "cacheWrite": 1.25
1541
+ },
1542
+ "contextWindow": 2e5,
1543
+ "maxTokens": 64e3,
1544
+ "headers": {
1545
+ "User-Agent": "GitHubCopilotChat/0.35.0",
1546
+ "Editor-Version": "vscode/1.107.0",
1547
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
1548
+ "Copilot-Integration-Id": "vscode-chat"
1549
+ },
1550
+ "compat": { "supportsEagerToolInputStreaming": false }
1551
+ },
1552
+ "claude-opus-4.5": {
1553
+ "id": "claude-opus-4.5",
1554
+ "name": "Claude Opus 4.5 (latest)",
1555
+ "api": "anthropic-messages",
1556
+ "provider": "github-copilot",
1557
+ "baseUrl": "https://api.individual.githubcopilot.com",
1558
+ "reasoning": true,
1559
+ "input": ["text", "image"],
1560
+ "cost": {
1561
+ "input": 5,
1562
+ "output": 25,
1563
+ "cacheRead": .5,
1564
+ "cacheWrite": 6.25
1565
+ },
1566
+ "contextWindow": 2e5,
1567
+ "maxTokens": 32e3,
1568
+ "headers": {
1569
+ "User-Agent": "GitHubCopilotChat/0.35.0",
1570
+ "Editor-Version": "vscode/1.107.0",
1571
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
1572
+ "Copilot-Integration-Id": "vscode-chat"
1573
+ }
1574
+ },
1575
+ "claude-opus-4.6": {
1576
+ "id": "claude-opus-4.6",
1577
+ "name": "Claude Opus 4.6",
1578
+ "api": "anthropic-messages",
1579
+ "provider": "github-copilot",
1580
+ "baseUrl": "https://api.individual.githubcopilot.com",
1581
+ "reasoning": true,
1582
+ "input": ["text", "image"],
1583
+ "cost": {
1584
+ "input": 5,
1585
+ "output": 25,
1586
+ "cacheRead": .5,
1587
+ "cacheWrite": 6.25
1588
+ },
1589
+ "contextWindow": 1e6,
1590
+ "maxTokens": 32e3,
1591
+ "headers": {
1592
+ "User-Agent": "GitHubCopilotChat/0.35.0",
1593
+ "Editor-Version": "vscode/1.107.0",
1594
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
1595
+ "Copilot-Integration-Id": "vscode-chat"
1596
+ },
1597
+ "thinkingLevelMap": { "max": "max" },
1598
+ "compat": { "forceAdaptiveThinking": true }
1599
+ },
1600
+ "claude-opus-4.7": {
1601
+ "id": "claude-opus-4.7",
1602
+ "name": "Claude Opus 4.7",
1603
+ "api": "anthropic-messages",
1604
+ "provider": "github-copilot",
1605
+ "baseUrl": "https://api.individual.githubcopilot.com",
1606
+ "reasoning": true,
1607
+ "input": ["text", "image"],
1608
+ "cost": {
1609
+ "input": 5,
1610
+ "output": 25,
1611
+ "cacheRead": .5,
1612
+ "cacheWrite": 6.25
1613
+ },
1614
+ "contextWindow": 1e6,
1615
+ "maxTokens": 32e3,
1616
+ "headers": {
1617
+ "User-Agent": "GitHubCopilotChat/0.35.0",
1618
+ "Editor-Version": "vscode/1.107.0",
1619
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
1620
+ "Copilot-Integration-Id": "vscode-chat"
1621
+ },
1622
+ "thinkingLevelMap": {
1623
+ "xhigh": "xhigh",
1624
+ "max": "max",
1625
+ "minimal": "low"
1626
+ },
1627
+ "compat": {
1628
+ "forceAdaptiveThinking": true,
1629
+ "supportsTemperature": false
1630
+ }
1631
+ },
1632
+ "claude-opus-4.8": {
1633
+ "id": "claude-opus-4.8",
1634
+ "name": "Claude Opus 4.8",
1635
+ "api": "anthropic-messages",
1636
+ "provider": "github-copilot",
1637
+ "baseUrl": "https://api.individual.githubcopilot.com",
1638
+ "reasoning": true,
1639
+ "input": ["text", "image"],
1640
+ "cost": {
1641
+ "input": 5,
1642
+ "output": 25,
1643
+ "cacheRead": .5,
1644
+ "cacheWrite": 6.25
1645
+ },
1646
+ "contextWindow": 1e6,
1647
+ "maxTokens": 64e3,
1648
+ "headers": {
1649
+ "User-Agent": "GitHubCopilotChat/0.35.0",
1650
+ "Editor-Version": "vscode/1.107.0",
1651
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
1652
+ "Copilot-Integration-Id": "vscode-chat"
1653
+ },
1654
+ "thinkingLevelMap": {
1655
+ "xhigh": "xhigh",
1656
+ "max": "max",
1657
+ "minimal": "low"
1658
+ },
1659
+ "compat": {
1660
+ "forceAdaptiveThinking": true,
1661
+ "supportsTemperature": false
1662
+ }
1663
+ },
1664
+ "claude-opus-5": {
1665
+ "id": "claude-opus-5",
1666
+ "name": "Claude Opus 5",
1667
+ "api": "anthropic-messages",
1668
+ "provider": "github-copilot",
1669
+ "baseUrl": "https://api.individual.githubcopilot.com",
1670
+ "reasoning": true,
1671
+ "input": ["text", "image"],
1672
+ "cost": {
1673
+ "input": 5,
1674
+ "output": 25,
1675
+ "cacheRead": .5,
1676
+ "cacheWrite": 6.25
1677
+ },
1678
+ "contextWindow": 1e6,
1679
+ "maxTokens": 64e3,
1680
+ "headers": {
1681
+ "User-Agent": "GitHubCopilotChat/0.35.0",
1682
+ "Editor-Version": "vscode/1.107.0",
1683
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
1684
+ "Copilot-Integration-Id": "vscode-chat"
1685
+ },
1686
+ "thinkingLevelMap": {
1687
+ "xhigh": "xhigh",
1688
+ "max": "max",
1689
+ "minimal": "low"
1690
+ },
1691
+ "compat": {
1692
+ "forceAdaptiveThinking": true,
1693
+ "supportsTemperature": false
1694
+ }
1695
+ },
1696
+ "claude-sonnet-4": {
1697
+ "id": "claude-sonnet-4",
1698
+ "name": "Claude Sonnet 4 (latest)",
1699
+ "api": "anthropic-messages",
1700
+ "provider": "github-copilot",
1701
+ "baseUrl": "https://api.individual.githubcopilot.com",
1702
+ "reasoning": true,
1703
+ "input": ["text", "image"],
1704
+ "cost": {
1705
+ "input": 3,
1706
+ "output": 15,
1707
+ "cacheRead": .3,
1708
+ "cacheWrite": 3.75
1709
+ },
1710
+ "contextWindow": 216e3,
1711
+ "maxTokens": 16e3,
1712
+ "headers": {
1713
+ "User-Agent": "GitHubCopilotChat/0.35.0",
1714
+ "Editor-Version": "vscode/1.107.0",
1715
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
1716
+ "Copilot-Integration-Id": "vscode-chat"
1717
+ },
1718
+ "compat": { "supportsEagerToolInputStreaming": false }
1719
+ },
1720
+ "claude-sonnet-4.5": {
1721
+ "id": "claude-sonnet-4.5",
1722
+ "name": "Claude Sonnet 4.5 (latest)",
1723
+ "api": "anthropic-messages",
1724
+ "provider": "github-copilot",
1725
+ "baseUrl": "https://api.individual.githubcopilot.com",
1726
+ "reasoning": true,
1727
+ "input": ["text", "image"],
1728
+ "cost": {
1729
+ "input": 3,
1730
+ "output": 15,
1731
+ "cacheRead": .3,
1732
+ "cacheWrite": 3.75
1733
+ },
1734
+ "contextWindow": 2e5,
1735
+ "maxTokens": 32e3,
1736
+ "headers": {
1737
+ "User-Agent": "GitHubCopilotChat/0.35.0",
1738
+ "Editor-Version": "vscode/1.107.0",
1739
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
1740
+ "Copilot-Integration-Id": "vscode-chat"
1741
+ },
1742
+ "compat": { "supportsEagerToolInputStreaming": false }
1743
+ },
1744
+ "claude-sonnet-4.6": {
1745
+ "id": "claude-sonnet-4.6",
1746
+ "name": "Claude Sonnet 4.6",
1747
+ "api": "anthropic-messages",
1748
+ "provider": "github-copilot",
1749
+ "baseUrl": "https://api.individual.githubcopilot.com",
1750
+ "reasoning": true,
1751
+ "input": ["text", "image"],
1752
+ "cost": {
1753
+ "input": 3,
1754
+ "output": 15,
1755
+ "cacheRead": .3,
1756
+ "cacheWrite": 3.75
1757
+ },
1758
+ "contextWindow": 1e6,
1759
+ "maxTokens": 32e3,
1760
+ "headers": {
1761
+ "User-Agent": "GitHubCopilotChat/0.35.0",
1762
+ "Editor-Version": "vscode/1.107.0",
1763
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
1764
+ "Copilot-Integration-Id": "vscode-chat"
1765
+ },
1766
+ "thinkingLevelMap": {
1767
+ "max": "max",
1768
+ "minimal": "low"
1769
+ },
1770
+ "compat": { "forceAdaptiveThinking": true }
1771
+ },
1772
+ "claude-sonnet-5": {
1773
+ "id": "claude-sonnet-5",
1774
+ "name": "Claude Sonnet 5",
1775
+ "api": "anthropic-messages",
1776
+ "provider": "github-copilot",
1777
+ "baseUrl": "https://api.individual.githubcopilot.com",
1778
+ "reasoning": true,
1779
+ "input": ["text", "image"],
1780
+ "cost": {
1781
+ "input": 2,
1782
+ "output": 10,
1783
+ "cacheRead": .2,
1784
+ "cacheWrite": 2.5
1785
+ },
1786
+ "contextWindow": 1e6,
1787
+ "maxTokens": 128e3,
1788
+ "headers": {
1789
+ "User-Agent": "GitHubCopilotChat/0.35.0",
1790
+ "Editor-Version": "vscode/1.107.0",
1791
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
1792
+ "Copilot-Integration-Id": "vscode-chat"
1793
+ },
1794
+ "thinkingLevelMap": {
1795
+ "xhigh": "xhigh",
1796
+ "max": "max"
1797
+ },
1798
+ "compat": { "forceAdaptiveThinking": true }
1799
+ }
1800
+ },
1801
+ "openai-completions": {
1802
+ "claude-fable-5": {
1803
+ "id": "claude-fable-5",
1804
+ "name": "Claude Fable 5",
1805
+ "api": "openai-completions",
1806
+ "provider": "github-copilot",
1807
+ "baseUrl": "https://api.individual.githubcopilot.com",
1808
+ "reasoning": true,
1809
+ "input": ["text", "image"],
1810
+ "cost": {
1811
+ "input": 10,
1812
+ "output": 50,
1813
+ "cacheRead": 1,
1814
+ "cacheWrite": 12.5
1815
+ },
1816
+ "contextWindow": 1e6,
1817
+ "maxTokens": 128e3,
1818
+ "headers": {
1819
+ "User-Agent": "GitHubCopilotChat/0.35.0",
1820
+ "Editor-Version": "vscode/1.107.0",
1821
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
1822
+ "Copilot-Integration-Id": "vscode-chat"
1823
+ },
1824
+ "compat": {
1825
+ "supportsStore": false,
1826
+ "supportsDeveloperRole": false,
1827
+ "supportsReasoningEffort": false
1828
+ },
1829
+ "thinkingLevelMap": {
1830
+ "off": null,
1831
+ "xhigh": "xhigh",
1832
+ "max": "max"
1833
+ }
1834
+ },
1835
+ "gemini-3.1-pro-preview": {
1836
+ "id": "gemini-3.1-pro-preview",
1837
+ "name": "Gemini 3.1 Pro Preview",
1838
+ "api": "openai-completions",
1839
+ "provider": "github-copilot",
1840
+ "baseUrl": "https://api.individual.githubcopilot.com",
1841
+ "reasoning": true,
1842
+ "input": ["text", "image"],
1843
+ "cost": {
1844
+ "input": 2,
1845
+ "output": 12,
1846
+ "cacheRead": .2,
1847
+ "cacheWrite": 0,
1848
+ "tiers": [{
1849
+ "inputTokensAbove": 2e5,
1850
+ "input": 4,
1851
+ "output": 18,
1852
+ "cacheRead": .4,
1853
+ "cacheWrite": 0
1854
+ }]
1855
+ },
1856
+ "contextWindow": 1e6,
1857
+ "maxTokens": 64e3,
1858
+ "headers": {
1859
+ "User-Agent": "GitHubCopilotChat/0.35.0",
1860
+ "Editor-Version": "vscode/1.107.0",
1861
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
1862
+ "Copilot-Integration-Id": "vscode-chat"
1863
+ },
1864
+ "compat": {
1865
+ "supportsStore": false,
1866
+ "supportsDeveloperRole": false,
1867
+ "supportsReasoningEffort": false
1868
+ }
1869
+ },
1870
+ "gemini-3.5-flash": {
1871
+ "id": "gemini-3.5-flash",
1872
+ "name": "Gemini 3.5 Flash",
1873
+ "api": "openai-completions",
1874
+ "provider": "github-copilot",
1875
+ "baseUrl": "https://api.individual.githubcopilot.com",
1876
+ "reasoning": true,
1877
+ "input": ["text", "image"],
1878
+ "cost": {
1879
+ "input": 1.5,
1880
+ "output": 9,
1881
+ "cacheRead": .15,
1882
+ "cacheWrite": 0
1883
+ },
1884
+ "contextWindow": 2e5,
1885
+ "maxTokens": 64e3,
1886
+ "headers": {
1887
+ "User-Agent": "GitHubCopilotChat/0.35.0",
1888
+ "Editor-Version": "vscode/1.107.0",
1889
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
1890
+ "Copilot-Integration-Id": "vscode-chat"
1891
+ },
1892
+ "compat": {
1893
+ "supportsStore": false,
1894
+ "supportsDeveloperRole": false,
1895
+ "supportsReasoningEffort": false
1896
+ }
1897
+ },
1898
+ "gemini-3.6-flash": {
1899
+ "id": "gemini-3.6-flash",
1900
+ "name": "Gemini 3.6 Flash",
1901
+ "api": "openai-completions",
1902
+ "provider": "github-copilot",
1903
+ "baseUrl": "https://api.individual.githubcopilot.com",
1904
+ "reasoning": true,
1905
+ "input": ["text", "image"],
1906
+ "cost": {
1907
+ "input": 1.5,
1908
+ "output": 7.5,
1909
+ "cacheRead": .15,
1910
+ "cacheWrite": 0
1911
+ },
1912
+ "contextWindow": 1e6,
1913
+ "maxTokens": 64e3,
1914
+ "headers": {
1915
+ "User-Agent": "GitHubCopilotChat/0.35.0",
1916
+ "Editor-Version": "vscode/1.107.0",
1917
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
1918
+ "Copilot-Integration-Id": "vscode-chat"
1919
+ },
1920
+ "compat": {
1921
+ "supportsStore": false,
1922
+ "supportsDeveloperRole": false,
1923
+ "supportsReasoningEffort": false
1924
+ }
1925
+ },
1926
+ "gpt-4.1": {
1927
+ "id": "gpt-4.1",
1928
+ "name": "GPT-4.1",
1929
+ "api": "openai-completions",
1930
+ "provider": "github-copilot",
1931
+ "baseUrl": "https://api.individual.githubcopilot.com",
1932
+ "reasoning": false,
1933
+ "input": ["text", "image"],
1934
+ "cost": {
1935
+ "input": 2,
1936
+ "output": 8,
1937
+ "cacheRead": .5,
1938
+ "cacheWrite": 0
1939
+ },
1940
+ "contextWindow": 128e3,
1941
+ "maxTokens": 16384,
1942
+ "headers": {
1943
+ "User-Agent": "GitHubCopilotChat/0.35.0",
1944
+ "Editor-Version": "vscode/1.107.0",
1945
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
1946
+ "Copilot-Integration-Id": "vscode-chat"
1947
+ },
1948
+ "compat": {
1949
+ "supportsStore": false,
1950
+ "supportsDeveloperRole": false,
1951
+ "supportsReasoningEffort": false
1952
+ }
1953
+ },
1954
+ "kimi-k2.7-code": {
1955
+ "id": "kimi-k2.7-code",
1956
+ "name": "Kimi K2.7 Code",
1957
+ "api": "openai-completions",
1958
+ "provider": "github-copilot",
1959
+ "baseUrl": "https://api.individual.githubcopilot.com",
1960
+ "reasoning": true,
1961
+ "input": ["text", "image"],
1962
+ "cost": {
1963
+ "input": .95,
1964
+ "output": 4,
1965
+ "cacheRead": .19,
1966
+ "cacheWrite": 0
1967
+ },
1968
+ "contextWindow": 256e3,
1969
+ "maxTokens": 32e3,
1970
+ "headers": {
1971
+ "User-Agent": "GitHubCopilotChat/0.35.0",
1972
+ "Editor-Version": "vscode/1.107.0",
1973
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
1974
+ "Copilot-Integration-Id": "vscode-chat"
1975
+ },
1976
+ "compat": {
1977
+ "supportsStore": false,
1978
+ "supportsDeveloperRole": false,
1979
+ "supportsReasoningEffort": false
1980
+ }
1981
+ },
1982
+ "kimi-k3": {
1983
+ "id": "kimi-k3",
1984
+ "name": "Kimi K3",
1985
+ "api": "openai-completions",
1986
+ "provider": "github-copilot",
1987
+ "baseUrl": "https://api.individual.githubcopilot.com",
1988
+ "reasoning": true,
1989
+ "input": ["text", "image"],
1990
+ "cost": {
1991
+ "input": .95,
1992
+ "output": 4,
1993
+ "cacheRead": .19,
1994
+ "cacheWrite": 0
1995
+ },
1996
+ "contextWindow": 1048576,
1997
+ "maxTokens": 131072,
1998
+ "headers": {
1999
+ "User-Agent": "GitHubCopilotChat/0.35.0",
2000
+ "Editor-Version": "vscode/1.107.0",
2001
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
2002
+ "Copilot-Integration-Id": "vscode-chat"
2003
+ },
2004
+ "compat": {
2005
+ "supportsStore": false,
2006
+ "supportsDeveloperRole": false,
2007
+ "supportsReasoningEffort": false
2008
+ }
2009
+ }
2010
+ },
2011
+ "openai-responses": {
2012
+ "gpt-5-mini": {
2013
+ "id": "gpt-5-mini",
2014
+ "name": "GPT-5 Mini",
2015
+ "api": "openai-responses",
2016
+ "provider": "github-copilot",
2017
+ "baseUrl": "https://api.individual.githubcopilot.com",
2018
+ "reasoning": true,
2019
+ "input": ["text", "image"],
2020
+ "cost": {
2021
+ "input": .25,
2022
+ "output": 2,
2023
+ "cacheRead": .025,
2024
+ "cacheWrite": 0
2025
+ },
2026
+ "contextWindow": 264e3,
2027
+ "maxTokens": 64e3,
2028
+ "headers": {
2029
+ "User-Agent": "GitHubCopilotChat/0.35.0",
2030
+ "Editor-Version": "vscode/1.107.0",
2031
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
2032
+ "Copilot-Integration-Id": "vscode-chat"
2033
+ },
2034
+ "thinkingLevelMap": {
2035
+ "off": null,
2036
+ "minimal": "low",
2037
+ "low": "low",
2038
+ "medium": "medium",
2039
+ "high": "high",
2040
+ "xhigh": null,
2041
+ "max": null
2042
+ },
2043
+ "compat": { "supportsOpenAIGrammarTools": true }
2044
+ },
2045
+ "gpt-5.2": {
2046
+ "id": "gpt-5.2",
2047
+ "name": "GPT-5.2",
2048
+ "api": "openai-responses",
2049
+ "provider": "github-copilot",
2050
+ "baseUrl": "https://api.individual.githubcopilot.com",
2051
+ "reasoning": true,
2052
+ "input": ["text", "image"],
2053
+ "cost": {
2054
+ "input": 1.75,
2055
+ "output": 14,
2056
+ "cacheRead": .175,
2057
+ "cacheWrite": 0
2058
+ },
2059
+ "contextWindow": 4e5,
2060
+ "maxTokens": 128e3,
2061
+ "headers": {
2062
+ "User-Agent": "GitHubCopilotChat/0.35.0",
2063
+ "Editor-Version": "vscode/1.107.0",
2064
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
2065
+ "Copilot-Integration-Id": "vscode-chat"
2066
+ },
2067
+ "thinkingLevelMap": {
2068
+ "off": null,
2069
+ "minimal": "low",
2070
+ "xhigh": "xhigh"
2071
+ },
2072
+ "compat": { "supportsOpenAIGrammarTools": true }
2073
+ },
2074
+ "gpt-5.2-codex": {
2075
+ "id": "gpt-5.2-codex",
2076
+ "name": "GPT-5.2 Codex",
2077
+ "api": "openai-responses",
2078
+ "provider": "github-copilot",
2079
+ "baseUrl": "https://api.individual.githubcopilot.com",
2080
+ "reasoning": true,
2081
+ "input": ["text", "image"],
2082
+ "cost": {
2083
+ "input": 1.75,
2084
+ "output": 14,
2085
+ "cacheRead": .175,
2086
+ "cacheWrite": 0
2087
+ },
2088
+ "contextWindow": 4e5,
2089
+ "maxTokens": 128e3,
2090
+ "headers": {
2091
+ "User-Agent": "GitHubCopilotChat/0.35.0",
2092
+ "Editor-Version": "vscode/1.107.0",
2093
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
2094
+ "Copilot-Integration-Id": "vscode-chat"
2095
+ },
2096
+ "thinkingLevelMap": {
2097
+ "off": null,
2098
+ "minimal": "low",
2099
+ "xhigh": "xhigh"
2100
+ },
2101
+ "compat": { "supportsOpenAIGrammarTools": true }
2102
+ },
2103
+ "gpt-5.3-codex": {
2104
+ "id": "gpt-5.3-codex",
2105
+ "name": "GPT-5.3 Codex",
2106
+ "api": "openai-responses",
2107
+ "provider": "github-copilot",
2108
+ "baseUrl": "https://api.individual.githubcopilot.com",
2109
+ "reasoning": true,
2110
+ "input": ["text", "image"],
2111
+ "cost": {
2112
+ "input": 1.75,
2113
+ "output": 14,
2114
+ "cacheRead": .175,
2115
+ "cacheWrite": 0
2116
+ },
2117
+ "contextWindow": 1e6,
2118
+ "maxTokens": 128e3,
2119
+ "headers": {
2120
+ "User-Agent": "GitHubCopilotChat/0.35.0",
2121
+ "Editor-Version": "vscode/1.107.0",
2122
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
2123
+ "Copilot-Integration-Id": "vscode-chat"
2124
+ },
2125
+ "thinkingLevelMap": {
2126
+ "off": null,
2127
+ "minimal": "low",
2128
+ "low": "low",
2129
+ "medium": "medium",
2130
+ "high": "high",
2131
+ "xhigh": "xhigh",
2132
+ "max": null
2133
+ },
2134
+ "compat": { "supportsOpenAIGrammarTools": true }
2135
+ },
2136
+ "gpt-5.4": {
2137
+ "id": "gpt-5.4",
2138
+ "name": "GPT-5.4",
2139
+ "api": "openai-responses",
2140
+ "provider": "github-copilot",
2141
+ "baseUrl": "https://api.individual.githubcopilot.com",
2142
+ "reasoning": true,
2143
+ "input": ["text", "image"],
2144
+ "cost": {
2145
+ "input": 2.5,
2146
+ "output": 15,
2147
+ "cacheRead": .25,
2148
+ "cacheWrite": 0,
2149
+ "tiers": [{
2150
+ "inputTokensAbove": 272e3,
2151
+ "input": 5,
2152
+ "output": 22.5,
2153
+ "cacheRead": .5,
2154
+ "cacheWrite": 0
2155
+ }]
2156
+ },
2157
+ "contextWindow": 1e6,
2158
+ "maxTokens": 128e3,
2159
+ "headers": {
2160
+ "User-Agent": "GitHubCopilotChat/0.35.0",
2161
+ "Editor-Version": "vscode/1.107.0",
2162
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
2163
+ "Copilot-Integration-Id": "vscode-chat"
2164
+ },
2165
+ "thinkingLevelMap": {
2166
+ "off": null,
2167
+ "minimal": "low",
2168
+ "low": "low",
2169
+ "medium": "medium",
2170
+ "high": "high",
2171
+ "xhigh": "xhigh",
2172
+ "max": null
2173
+ },
2174
+ "compat": { "supportsOpenAIGrammarTools": true }
2175
+ },
2176
+ "gpt-5.4-mini": {
2177
+ "id": "gpt-5.4-mini",
2178
+ "name": "GPT-5.4 mini",
2179
+ "api": "openai-responses",
2180
+ "provider": "github-copilot",
2181
+ "baseUrl": "https://api.individual.githubcopilot.com",
2182
+ "reasoning": true,
2183
+ "input": ["text", "image"],
2184
+ "cost": {
2185
+ "input": .75,
2186
+ "output": 4.5,
2187
+ "cacheRead": .075,
2188
+ "cacheWrite": 0
2189
+ },
2190
+ "contextWindow": 4e5,
2191
+ "maxTokens": 128e3,
2192
+ "headers": {
2193
+ "User-Agent": "GitHubCopilotChat/0.35.0",
2194
+ "Editor-Version": "vscode/1.107.0",
2195
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
2196
+ "Copilot-Integration-Id": "vscode-chat"
2197
+ },
2198
+ "thinkingLevelMap": {
2199
+ "off": null,
2200
+ "minimal": "low",
2201
+ "low": "low",
2202
+ "medium": "medium",
2203
+ "high": "high",
2204
+ "xhigh": "xhigh",
2205
+ "max": null
2206
+ },
2207
+ "compat": { "supportsOpenAIGrammarTools": true }
2208
+ },
2209
+ "gpt-5.4-nano": {
2210
+ "id": "gpt-5.4-nano",
2211
+ "name": "GPT-5.4 nano",
2212
+ "api": "openai-responses",
2213
+ "provider": "github-copilot",
2214
+ "baseUrl": "https://api.individual.githubcopilot.com",
2215
+ "reasoning": true,
2216
+ "input": ["text", "image"],
2217
+ "cost": {
2218
+ "input": .2,
2219
+ "output": 1.25,
2220
+ "cacheRead": .02,
2221
+ "cacheWrite": 0
2222
+ },
2223
+ "contextWindow": 4e5,
2224
+ "maxTokens": 128e3,
2225
+ "headers": {
2226
+ "User-Agent": "GitHubCopilotChat/0.35.0",
2227
+ "Editor-Version": "vscode/1.107.0",
2228
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
2229
+ "Copilot-Integration-Id": "vscode-chat"
2230
+ },
2231
+ "thinkingLevelMap": {
2232
+ "off": null,
2233
+ "minimal": "low",
2234
+ "xhigh": "xhigh"
2235
+ },
2236
+ "compat": { "supportsOpenAIGrammarTools": true }
2237
+ },
2238
+ "gpt-5.5": {
2239
+ "id": "gpt-5.5",
2240
+ "name": "GPT-5.5",
2241
+ "api": "openai-responses",
2242
+ "provider": "github-copilot",
2243
+ "baseUrl": "https://api.individual.githubcopilot.com",
2244
+ "reasoning": true,
2245
+ "input": ["text", "image"],
2246
+ "cost": {
2247
+ "input": 5,
2248
+ "output": 30,
2249
+ "cacheRead": .5,
2250
+ "cacheWrite": 0,
2251
+ "tiers": [{
2252
+ "inputTokensAbove": 272e3,
2253
+ "input": 10,
2254
+ "output": 45,
2255
+ "cacheRead": 1,
2256
+ "cacheWrite": 0
2257
+ }]
2258
+ },
2259
+ "contextWindow": 1e6,
2260
+ "maxTokens": 128e3,
2261
+ "headers": {
2262
+ "User-Agent": "GitHubCopilotChat/0.35.0",
2263
+ "Editor-Version": "vscode/1.107.0",
2264
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
2265
+ "Copilot-Integration-Id": "vscode-chat"
2266
+ },
2267
+ "thinkingLevelMap": {
2268
+ "off": null,
2269
+ "minimal": "low",
2270
+ "low": "low",
2271
+ "medium": "medium",
2272
+ "high": "high",
2273
+ "xhigh": "xhigh",
2274
+ "max": null
2275
+ },
2276
+ "compat": { "supportsOpenAIGrammarTools": true }
2277
+ },
2278
+ "gpt-5.6-luna": {
2279
+ "id": "gpt-5.6-luna",
2280
+ "name": "GPT-5.6 Luna",
2281
+ "api": "openai-responses",
2282
+ "provider": "github-copilot",
2283
+ "baseUrl": "https://api.individual.githubcopilot.com",
2284
+ "reasoning": true,
2285
+ "input": ["text", "image"],
2286
+ "cost": {
2287
+ "input": .2,
2288
+ "output": 1.2,
2289
+ "cacheRead": .02,
2290
+ "cacheWrite": 0,
2291
+ "tiers": [{
2292
+ "inputTokensAbove": 2e5,
2293
+ "input": .4,
2294
+ "output": 1.8,
2295
+ "cacheRead": .04,
2296
+ "cacheWrite": 0
2297
+ }]
2298
+ },
2299
+ "contextWindow": 105e4,
2300
+ "maxTokens": 128e3,
2301
+ "headers": {
2302
+ "User-Agent": "GitHubCopilotChat/0.35.0",
2303
+ "Editor-Version": "vscode/1.107.0",
2304
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
2305
+ "Copilot-Integration-Id": "vscode-chat"
2306
+ },
2307
+ "thinkingLevelMap": {
2308
+ "off": null,
2309
+ "minimal": "low",
2310
+ "low": "low",
2311
+ "medium": "medium",
2312
+ "high": "high",
2313
+ "xhigh": "xhigh",
2314
+ "max": "max"
2315
+ },
2316
+ "compat": { "supportsOpenAIGrammarTools": true }
2317
+ },
2318
+ "gpt-5.6-sol": {
2319
+ "id": "gpt-5.6-sol",
2320
+ "name": "GPT-5.6 Sol",
2321
+ "api": "openai-responses",
2322
+ "provider": "github-copilot",
2323
+ "baseUrl": "https://api.individual.githubcopilot.com",
2324
+ "reasoning": true,
2325
+ "input": ["text", "image"],
2326
+ "cost": {
2327
+ "input": 5,
2328
+ "output": 30,
2329
+ "cacheRead": .5,
2330
+ "cacheWrite": 6.25,
2331
+ "tiers": [{
2332
+ "inputTokensAbove": 272e3,
2333
+ "input": 10,
2334
+ "output": 45,
2335
+ "cacheRead": 1,
2336
+ "cacheWrite": 12.5
2337
+ }]
2338
+ },
2339
+ "contextWindow": 105e4,
2340
+ "maxTokens": 128e3,
2341
+ "headers": {
2342
+ "User-Agent": "GitHubCopilotChat/0.35.0",
2343
+ "Editor-Version": "vscode/1.107.0",
2344
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
2345
+ "Copilot-Integration-Id": "vscode-chat"
2346
+ },
2347
+ "thinkingLevelMap": {
2348
+ "off": null,
2349
+ "minimal": "low",
2350
+ "low": "low",
2351
+ "medium": "medium",
2352
+ "high": "high",
2353
+ "xhigh": "xhigh",
2354
+ "max": "max"
2355
+ },
2356
+ "compat": { "supportsOpenAIGrammarTools": true }
2357
+ },
2358
+ "gpt-5.6-terra": {
2359
+ "id": "gpt-5.6-terra",
2360
+ "name": "GPT-5.6 Terra",
2361
+ "api": "openai-responses",
2362
+ "provider": "github-copilot",
2363
+ "baseUrl": "https://api.individual.githubcopilot.com",
2364
+ "reasoning": true,
2365
+ "input": ["text", "image"],
2366
+ "cost": {
2367
+ "input": 2,
2368
+ "output": 12,
2369
+ "cacheRead": .2,
2370
+ "cacheWrite": 0,
2371
+ "tiers": [{
2372
+ "inputTokensAbove": 272e3,
2373
+ "input": 4,
2374
+ "output": 18,
2375
+ "cacheRead": .4,
2376
+ "cacheWrite": 0
2377
+ }]
2378
+ },
2379
+ "contextWindow": 105e4,
2380
+ "maxTokens": 128e3,
2381
+ "headers": {
2382
+ "User-Agent": "GitHubCopilotChat/0.35.0",
2383
+ "Editor-Version": "vscode/1.107.0",
2384
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
2385
+ "Copilot-Integration-Id": "vscode-chat"
2386
+ },
2387
+ "thinkingLevelMap": {
2388
+ "off": null,
2389
+ "minimal": "low",
2390
+ "low": "low",
2391
+ "medium": "medium",
2392
+ "high": "high",
2393
+ "xhigh": "xhigh",
2394
+ "max": "max"
2395
+ },
2396
+ "compat": { "supportsOpenAIGrammarTools": true }
2397
+ },
2398
+ "grok-4.5": {
2399
+ "id": "grok-4.5",
2400
+ "name": "Grok 4.5",
2401
+ "api": "openai-responses",
2402
+ "provider": "github-copilot",
2403
+ "baseUrl": "https://api.individual.githubcopilot.com",
2404
+ "reasoning": true,
2405
+ "input": ["text", "image"],
2406
+ "cost": {
2407
+ "input": 2,
2408
+ "output": 6,
2409
+ "cacheRead": .5,
2410
+ "cacheWrite": 0,
2411
+ "tiers": [{
2412
+ "inputTokensAbove": 2e5,
2413
+ "input": 4,
2414
+ "output": 12,
2415
+ "cacheRead": 1,
2416
+ "cacheWrite": 0
2417
+ }]
2418
+ },
2419
+ "contextWindow": 5e5,
2420
+ "maxTokens": 128e3,
2421
+ "headers": {
2422
+ "User-Agent": "GitHubCopilotChat/0.35.0",
2423
+ "Editor-Version": "vscode/1.107.0",
2424
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
2425
+ "Copilot-Integration-Id": "vscode-chat"
2426
+ },
2427
+ "thinkingLevelMap": {
2428
+ "off": null,
2429
+ "minimal": null,
2430
+ "low": "low",
2431
+ "medium": "medium",
2432
+ "high": "high",
2433
+ "xhigh": null,
2434
+ "max": null
2435
+ }
2436
+ },
2437
+ "mai-code-1-flash-picker": {
2438
+ "id": "mai-code-1-flash-picker",
2439
+ "name": "MAI-Code-1-Flash",
2440
+ "api": "openai-responses",
2441
+ "provider": "github-copilot",
2442
+ "baseUrl": "https://api.individual.githubcopilot.com",
2443
+ "reasoning": true,
2444
+ "input": ["text"],
2445
+ "cost": {
2446
+ "input": .75,
2447
+ "output": 4.5,
2448
+ "cacheRead": .075,
2449
+ "cacheWrite": 0
2450
+ },
2451
+ "contextWindow": 256e3,
2452
+ "maxTokens": 128e3,
2453
+ "headers": {
2454
+ "User-Agent": "GitHubCopilotChat/0.35.0",
2455
+ "Editor-Version": "vscode/1.107.0",
2456
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
2457
+ "Copilot-Integration-Id": "vscode-chat"
2458
+ },
2459
+ "thinkingLevelMap": {
2460
+ "off": null,
2461
+ "minimal": null,
2462
+ "low": "low",
2463
+ "medium": "medium",
2464
+ "high": "high",
2465
+ "xhigh": null,
2466
+ "max": null
2467
+ }
2468
+ }
2469
+ }
2470
+ };
2471
+ //#endregion
2472
+ //#region src/compat/vendor/pi-oauth-flows/model-catalog.ts
2473
+ function flattenModelCatalog(_provider, groups) {
2474
+ return Object.assign({}, ...Object.values(groups));
2475
+ }
2476
+ //#endregion
2477
+ //#region src/compat/vendor/pi-oauth-flows/github-copilot.models.ts
2478
+ const GITHUB_COPILOT_MODELS = flattenModelCatalog("github-copilot", github_copilot_default);
2479
+ //#endregion
2480
+ //#region src/compat/vendor/pi-oauth-flows/github-copilot.ts
2481
+ /**
2482
+ * GitHub Copilot OAuth flow
2483
+ */
2484
+ const decode = (s) => atob(s);
2485
+ const CLIENT_ID$1 = decode("SXYxLmI1MDdhMDhjODdlY2ZlOTg=");
2486
+ const COPILOT_HEADERS = {
2487
+ "User-Agent": "GitHubCopilotChat/0.35.0",
2488
+ "Editor-Version": "vscode/1.107.0",
2489
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
2490
+ "Copilot-Integration-Id": "vscode-chat"
2491
+ };
2492
+ const COPILOT_API_VERSION = "2026-06-01";
2493
+ function normalizeDomain(input) {
2494
+ const trimmed = input.trim();
2495
+ if (!trimmed) return null;
2496
+ try {
2497
+ return (trimmed.includes("://") ? new URL(trimmed) : new URL(`https://${trimmed}`)).hostname;
2498
+ } catch {
2499
+ return null;
2500
+ }
2501
+ }
2502
+ function getUrls(domain) {
2503
+ return {
2504
+ deviceCodeUrl: `https://${domain}/login/device/code`,
2505
+ accessTokenUrl: `https://${domain}/login/oauth/access_token`,
2506
+ copilotTokenUrl: `https://api.${domain}/copilot_internal/v2/token`
2507
+ };
2508
+ }
2509
+ /**
2510
+ * Parse the proxy-ep from a Copilot token and convert to API base URL.
2511
+ * Token format: tid=...;exp=...;proxy-ep=proxy.individual.githubcopilot.com;...
2512
+ * Returns API URL like https://api.individual.githubcopilot.com
2513
+ */
2514
+ function getBaseUrlFromToken(token) {
2515
+ const match = token.match(/proxy-ep=([^;]+)/);
2516
+ if (!match) return null;
2517
+ return `https://${match[1].replace(/^proxy\./, "api.")}`;
2518
+ }
2519
+ function getGitHubCopilotBaseUrl(token, enterpriseDomain) {
2520
+ if (token) {
2521
+ const urlFromToken = getBaseUrlFromToken(token);
2522
+ if (urlFromToken) return urlFromToken;
2523
+ }
2524
+ if (enterpriseDomain) return `https://copilot-api.${enterpriseDomain}`;
2525
+ return "https://api.individual.githubcopilot.com";
2526
+ }
2527
+ function asRecord(value) {
2528
+ return value && typeof value === "object" ? value : void 0;
2529
+ }
2530
+ function parseAvailableCopilotModelIds(raw, allowPolicyFallback) {
2531
+ const data = asRecord(raw)?.data;
2532
+ if (!Array.isArray(data)) throw new Error("Invalid Copilot models response");
2533
+ const pickerIds = [];
2534
+ const policyEnabledIds = [];
2535
+ for (const rawItem of data) {
2536
+ const item = asRecord(rawItem);
2537
+ const id = item?.id;
2538
+ if (!item || typeof id !== "string") continue;
2539
+ if (asRecord(asRecord(item.capabilities)?.supports)?.tool_calls === false) continue;
2540
+ const policy = asRecord(item.policy);
2541
+ if (item.model_picker_enabled === true && policy?.state !== "disabled") pickerIds.push(id);
2542
+ if (policy?.state === "enabled") policyEnabledIds.push(id);
2543
+ }
2544
+ return pickerIds.length > 0 || !allowPolicyFallback ? pickerIds : policyEnabledIds;
2545
+ }
2546
+ async function fetchAvailableGitHubCopilotModelIds(copilotToken, enterpriseDomain, signal) {
2547
+ const baseUrl = getGitHubCopilotBaseUrl(copilotToken, enterpriseDomain);
2548
+ const allowPolicyFallback = baseUrl === "https://api.individual.githubcopilot.com";
2549
+ return parseAvailableCopilotModelIds(await fetchJson(`${baseUrl}/models`, {
2550
+ headers: {
2551
+ Accept: "application/json",
2552
+ Authorization: `Bearer ${copilotToken}`,
2553
+ ...COPILOT_HEADERS,
2554
+ "X-GitHub-Api-Version": COPILOT_API_VERSION
2555
+ },
2556
+ signal: AbortSignal.any([signal, AbortSignal.timeout(5e3)])
2557
+ }), allowPolicyFallback);
2558
+ }
2559
+ async function fetchJson(url, init) {
2560
+ const response = await fetch(url, init);
2561
+ if (!response.ok) {
2562
+ const text = await response.text();
2563
+ throw new Error(`${response.status} ${response.statusText}: ${text}`);
2564
+ }
2565
+ return response.json();
2566
+ }
2567
+ async function startDeviceFlow(domain, signal) {
2568
+ const data = await fetchJson(getUrls(domain).deviceCodeUrl, {
2569
+ method: "POST",
2570
+ headers: {
2571
+ Accept: "application/json",
2572
+ "Content-Type": "application/x-www-form-urlencoded",
2573
+ "User-Agent": "GitHubCopilotChat/0.35.0"
2574
+ },
2575
+ body: new URLSearchParams({
2576
+ client_id: CLIENT_ID$1,
2577
+ scope: "read:user"
2578
+ }),
2579
+ signal
2580
+ });
2581
+ if (!data || typeof data !== "object") throw new Error("Invalid device code response");
2582
+ const deviceCode = data.device_code;
2583
+ const userCode = data.user_code;
2584
+ const verificationUri = data.verification_uri;
2585
+ const interval = data.interval;
2586
+ const expiresIn = data.expires_in;
2587
+ if (typeof deviceCode !== "string" || typeof userCode !== "string" || typeof verificationUri !== "string" || interval !== void 0 && typeof interval !== "number" || typeof expiresIn !== "number") throw new Error("Invalid device code response fields");
2588
+ let parsedUri;
2589
+ try {
2590
+ parsedUri = new URL(verificationUri);
2591
+ } catch {
2592
+ throw new Error("Untrusted verification_uri in device code response");
2593
+ }
2594
+ if (parsedUri.protocol !== "https:" && parsedUri.protocol !== "http:") throw new Error("Untrusted verification_uri in device code response");
2595
+ return {
2596
+ device_code: deviceCode,
2597
+ user_code: userCode,
2598
+ verification_uri: parsedUri.href,
2599
+ interval,
2600
+ expires_in: expiresIn
2601
+ };
2602
+ }
2603
+ async function pollForGitHubAccessToken(domain, device, signal) {
2604
+ const urls = getUrls(domain);
2605
+ return pollOAuthDeviceCodeFlow({
2606
+ intervalSeconds: device.interval,
2607
+ expiresInSeconds: device.expires_in,
2608
+ waitBeforeFirstPoll: true,
2609
+ signal,
2610
+ poll: async () => {
2611
+ const raw = await fetchJson(urls.accessTokenUrl, {
2612
+ method: "POST",
2613
+ headers: {
2614
+ Accept: "application/json",
2615
+ "Content-Type": "application/x-www-form-urlencoded",
2616
+ "User-Agent": "GitHubCopilotChat/0.35.0"
2617
+ },
2618
+ body: new URLSearchParams({
2619
+ client_id: CLIENT_ID$1,
2620
+ device_code: device.device_code,
2621
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
2622
+ }),
2623
+ signal
2624
+ });
2625
+ if (raw && typeof raw === "object" && typeof raw.access_token === "string") return {
2626
+ status: "complete",
2627
+ value: raw.access_token
2628
+ };
2629
+ if (raw && typeof raw === "object" && typeof raw.error === "string") {
2630
+ const { error, error_description: description, interval } = raw;
2631
+ if (error === "authorization_pending") return { status: "pending" };
2632
+ if (error === "slow_down") return {
2633
+ status: "slow_down",
2634
+ intervalSeconds: typeof interval === "number" ? interval : void 0
2635
+ };
2636
+ return {
2637
+ status: "failed",
2638
+ message: `Device flow failed: ${error}${description ? `: ${description}` : ""}`
2639
+ };
2640
+ }
2641
+ return {
2642
+ status: "failed",
2643
+ message: "Invalid device token response"
2644
+ };
2645
+ }
2646
+ });
2647
+ }
2648
+ async function refreshGitHubCopilotAccessToken(refreshToken, enterpriseDomain, signal) {
2649
+ const raw = await fetchJson(getUrls(enterpriseDomain || "github.com").copilotTokenUrl, {
2650
+ headers: {
2651
+ Accept: "application/json",
2652
+ Authorization: `Bearer ${refreshToken}`,
2653
+ ...COPILOT_HEADERS
2654
+ },
2655
+ signal
2656
+ });
2657
+ if (!raw || typeof raw !== "object") throw new Error("Invalid Copilot token response");
2658
+ const token = raw.token;
2659
+ const expiresAt = raw.expires_at;
2660
+ if (typeof token !== "string" || typeof expiresAt !== "number") throw new Error("Invalid Copilot token response fields");
2661
+ return {
2662
+ type: "oauth",
2663
+ refresh: refreshToken,
2664
+ access: token,
2665
+ expires: expiresAt * 1e3 - 3e5,
2666
+ enterpriseUrl: enterpriseDomain
2667
+ };
2668
+ }
2669
+ /**
2670
+ * Refresh GitHub Copilot token
2671
+ */
2672
+ async function refreshGitHubCopilotToken(refreshToken, enterpriseDomain, signal) {
2673
+ const credentials = await refreshGitHubCopilotAccessToken(refreshToken, enterpriseDomain, signal);
2674
+ return {
2675
+ ...credentials,
2676
+ availableModelIds: await fetchAvailableGitHubCopilotModelIds(credentials.access, enterpriseDomain, signal)
2677
+ };
2678
+ }
2679
+ /**
2680
+ * Enable a model for the user's GitHub Copilot account.
2681
+ * This is required for some models (like Claude, Grok) before they can be used.
2682
+ */
2683
+ async function enableGitHubCopilotModel(token, modelId, enterpriseDomain, signal) {
2684
+ const url = `${getGitHubCopilotBaseUrl(token, enterpriseDomain)}/models/${modelId}/policy`;
2685
+ try {
2686
+ return (await fetch(url, {
2687
+ method: "POST",
2688
+ headers: {
2689
+ "Content-Type": "application/json",
2690
+ Authorization: `Bearer ${token}`,
2691
+ ...COPILOT_HEADERS,
2692
+ "openai-intent": "chat-policy",
2693
+ "x-interaction-type": "chat-policy"
2694
+ },
2695
+ body: JSON.stringify({ state: "enabled" }),
2696
+ signal
2697
+ })).ok;
2698
+ } catch (error) {
2699
+ if (signal.aborted) throw error;
2700
+ return false;
2701
+ }
2702
+ }
2703
+ /**
2704
+ * Enable all known GitHub Copilot models that may require policy acceptance.
2705
+ * Called after successful login to ensure all models are available.
2706
+ */
2707
+ async function enableAllGitHubCopilotModels(token, enterpriseDomain, signal) {
2708
+ const models = Object.values(GITHUB_COPILOT_MODELS);
2709
+ await Promise.all(models.map(async (model) => {
2710
+ await enableGitHubCopilotModel(token, model.id, enterpriseDomain, signal);
2711
+ }));
2712
+ }
2713
+ async function loginGitHubCopilot(interaction) {
2714
+ const input = await interaction.prompt({
2715
+ type: "text",
2716
+ message: "GitHub Enterprise URL/domain (blank for github.com)",
2717
+ placeholder: "company.ghe.com"
2718
+ });
2719
+ if (interaction.signal.aborted) throw new Error("Login cancelled");
2720
+ const trimmed = input.trim();
2721
+ const enterpriseDomain = normalizeDomain(input);
2722
+ if (trimmed && !enterpriseDomain) throw new Error("Invalid GitHub Enterprise URL/domain");
2723
+ const domain = enterpriseDomain || "github.com";
2724
+ const device = await startDeviceFlow(domain, interaction.signal);
2725
+ interaction.notify({
2726
+ type: "device_code",
2727
+ userCode: device.user_code,
2728
+ verificationUri: device.verification_uri,
2729
+ intervalSeconds: device.interval,
2730
+ expiresInSeconds: device.expires_in
2731
+ });
2732
+ const credentials = await refreshGitHubCopilotAccessToken(await pollForGitHubAccessToken(domain, device, interaction.signal), enterpriseDomain ?? void 0, interaction.signal);
2733
+ interaction.notify({
2734
+ type: "progress",
2735
+ message: "Enabling models..."
2736
+ });
2737
+ await enableAllGitHubCopilotModels(credentials.access, enterpriseDomain ?? void 0, interaction.signal);
2738
+ return {
2739
+ ...credentials,
2740
+ availableModelIds: await fetchAvailableGitHubCopilotModelIds(credentials.access, enterpriseDomain ?? void 0, interaction.signal)
2741
+ };
2742
+ }
2743
+ function copilotEnterpriseDomain(credential) {
2744
+ const enterpriseUrl = credential.enterpriseUrl;
2745
+ if (typeof enterpriseUrl !== "string" || !enterpriseUrl) return void 0;
2746
+ return normalizeDomain(enterpriseUrl) ?? void 0;
2747
+ }
2748
+ const githubCopilotOAuth = {
2749
+ name: "GitHub Copilot",
2750
+ isSubscription: true,
2751
+ login: loginGitHubCopilot,
2752
+ refresh: (credential, signal) => refreshGitHubCopilotToken(credential.refresh, copilotEnterpriseDomain(credential), signal),
2753
+ /** Derive the credential-specific proxy endpoint for each request. */
2754
+ async toAuth(credential) {
2755
+ return {
2756
+ apiKey: credential.access,
2757
+ baseUrl: getGitHubCopilotBaseUrl(credential.access, copilotEnterpriseDomain(credential))
2758
+ };
2759
+ }
2760
+ };
2761
+ //#endregion
2762
+ //#region src/compat/vendor/pi-oauth-flows/kimi-coding.ts
2763
+ /**
2764
+ * Kimi Code (subscription) OAuth flow
2765
+ *
2766
+ * RFC 8628 device authorization grant against https://auth.kimi.com with JSON
2767
+ * responses. The access token authenticates requests to
2768
+ * https://api.kimi.com/coding as an `Authorization: Bearer` header.
2769
+ */
2770
+ const CLIENT_ID = "17e5f671-d194-4dfb-9706-5516cb48c098";
2771
+ const DEFAULT_OAUTH_HOST = "https://auth.kimi.com";
2772
+ const DEVICE_CODE_TIMEOUT_SECONDS = 900;
2773
+ const DEFAULT_POLL_INTERVAL_SECONDS = 5;
2774
+ const REQUEST_TIMEOUT_MS = 3e4;
2775
+ const REFRESH_MAX_RETRIES = 3;
2776
+ function getOauthHost() {
2777
+ return (getProviderEnvValue("KIMI_CODE_OAUTH_HOST") || getProviderEnvValue("KIMI_OAUTH_HOST") || DEFAULT_OAUTH_HOST).replace(/\/+$/, "");
2778
+ }
2779
+ function requestSignal(signal) {
2780
+ return AbortSignal.any([AbortSignal.timeout(REQUEST_TIMEOUT_MS), signal]);
2781
+ }
2782
+ function formUrlEncode(fields) {
2783
+ return new URLSearchParams(fields).toString();
2784
+ }
2785
+ async function readJson(response) {
2786
+ try {
2787
+ const json = await response.json();
2788
+ return json && typeof json === "object" ? json : null;
2789
+ } catch {
2790
+ return null;
2791
+ }
2792
+ }
2793
+ /** The verification URI is opened in the user's browser; only http(s) URLs are trusted. */
2794
+ function trustedHttpUrl(value) {
2795
+ if (typeof value !== "string" || !value) return null;
2796
+ try {
2797
+ const url = new URL(value);
2798
+ if (url.protocol !== "https:" && url.protocol !== "http:") return null;
2799
+ return url.href;
2800
+ } catch {
2801
+ return null;
2802
+ }
2803
+ }
2804
+ async function startDeviceAuthorization(oauthHost, signal) {
2805
+ const response = await fetch(`${oauthHost}/api/oauth/device_authorization`, {
2806
+ method: "POST",
2807
+ headers: {
2808
+ "Content-Type": "application/x-www-form-urlencoded",
2809
+ Accept: "application/json"
2810
+ },
2811
+ body: formUrlEncode({ client_id: CLIENT_ID }),
2812
+ signal: requestSignal(signal)
2813
+ });
2814
+ if (!response.ok) {
2815
+ const text = await response.text().catch(() => "");
2816
+ throw new Error(`Kimi Code device authorization failed with status ${response.status}${text ? `: ${text}` : ""}`);
2817
+ }
2818
+ const json = await readJson(response);
2819
+ const deviceCode = json?.device_code;
2820
+ const userCode = json?.user_code;
2821
+ const verificationUri = json?.verification_uri;
2822
+ const verificationUriComplete = json?.verification_uri_complete;
2823
+ if (typeof deviceCode !== "string" || typeof userCode !== "string" || typeof verificationUri !== "string" || typeof verificationUriComplete !== "string" || !trustedHttpUrl(verificationUriComplete) || !trustedHttpUrl(verificationUri)) throw new Error(`Invalid Kimi Code device authorization response: ${JSON.stringify(json)}`);
2824
+ const interval = json?.interval;
2825
+ const expiresIn = json?.expires_in;
2826
+ return {
2827
+ deviceCode,
2828
+ userCode,
2829
+ verificationUri,
2830
+ verificationUriComplete,
2831
+ intervalSeconds: typeof interval === "number" && Number.isFinite(interval) && interval > 0 ? interval : DEFAULT_POLL_INTERVAL_SECONDS,
2832
+ expiresInSeconds: typeof expiresIn === "number" && Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn : DEVICE_CODE_TIMEOUT_SECONDS
2833
+ };
2834
+ }
2835
+ function parseTokenResponse(json, operation) {
2836
+ const accessToken = json?.access_token;
2837
+ const refreshToken = json?.refresh_token;
2838
+ const expiresIn = json?.expires_in;
2839
+ if (typeof accessToken !== "string" || !accessToken || typeof refreshToken !== "string" || !refreshToken || typeof expiresIn !== "number" || !Number.isFinite(expiresIn) || expiresIn <= 0) throw new Error(`Kimi Code token ${operation} response missing fields: ${JSON.stringify(json)}`);
2840
+ return {
2841
+ access: accessToken,
2842
+ refresh: refreshToken,
2843
+ expires: Date.now() + expiresIn * 1e3
2844
+ };
2845
+ }
2846
+ async function pollForToken(oauthHost, device, signal) {
2847
+ return pollOAuthDeviceCodeFlow({
2848
+ intervalSeconds: device.intervalSeconds,
2849
+ expiresInSeconds: device.expiresInSeconds,
2850
+ waitBeforeFirstPoll: true,
2851
+ signal,
2852
+ poll: async () => {
2853
+ const response = await fetch(`${oauthHost}/api/oauth/token`, {
2854
+ method: "POST",
2855
+ headers: {
2856
+ "Content-Type": "application/x-www-form-urlencoded",
2857
+ Accept: "application/json"
2858
+ },
2859
+ body: formUrlEncode({
2860
+ client_id: CLIENT_ID,
2861
+ device_code: device.deviceCode,
2862
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
2863
+ }),
2864
+ signal: requestSignal(signal)
2865
+ });
2866
+ if (response.status >= 500) {
2867
+ const text = await response.text().catch(() => "");
2868
+ return {
2869
+ status: "failed",
2870
+ message: `Kimi Code device token request failed with status ${response.status}${text ? `: ${text}` : ""}`
2871
+ };
2872
+ }
2873
+ const json = await readJson(response);
2874
+ if (response.ok && typeof json?.access_token === "string") try {
2875
+ return {
2876
+ status: "complete",
2877
+ value: parseTokenResponse(json, "poll")
2878
+ };
2879
+ } catch (error) {
2880
+ return {
2881
+ status: "failed",
2882
+ message: error instanceof Error ? error.message : String(error)
2883
+ };
2884
+ }
2885
+ const error = json?.error;
2886
+ const description = typeof json?.error_description === "string" ? `: ${json.error_description}` : "";
2887
+ if (error === "authorization_pending") return { status: "pending" };
2888
+ if (error === "slow_down") {
2889
+ const interval = json?.interval;
2890
+ return {
2891
+ status: "slow_down",
2892
+ intervalSeconds: typeof interval === "number" && interval > 0 ? interval : void 0
2893
+ };
2894
+ }
2895
+ if (error === "expired_token") return {
2896
+ status: "failed",
2897
+ message: "Kimi Code device authorization expired. Please restart login."
2898
+ };
2899
+ if (error === "access_denied") return {
2900
+ status: "failed",
2901
+ message: "Kimi Code login was denied."
2902
+ };
2903
+ return {
2904
+ status: "failed",
2905
+ message: `Kimi Code device token request failed (status ${response.status})${typeof error === "string" ? `: ${error}${description}` : ""}`
2906
+ };
2907
+ }
2908
+ });
2909
+ }
2910
+ function sleep(ms, signal) {
2911
+ return new Promise((resolve, reject) => {
2912
+ signal.throwIfAborted();
2913
+ const onAbort = () => {
2914
+ clearTimeout(timeout);
2915
+ reject(signal.reason);
2916
+ };
2917
+ const timeout = setTimeout(() => {
2918
+ signal.removeEventListener("abort", onAbort);
2919
+ resolve();
2920
+ }, ms);
2921
+ signal.addEventListener("abort", onAbort, { once: true });
2922
+ });
2923
+ }
2924
+ function isRetryableRefreshFailure(response) {
2925
+ return response.status === 429 || response.status >= 500;
2926
+ }
2927
+ async function refreshToken(oauthHost, refreshTokenValue, signal) {
2928
+ let lastError;
2929
+ for (let attempt = 0; attempt <= REFRESH_MAX_RETRIES; attempt++) {
2930
+ if (attempt > 0) await sleep(1e3 * 2 ** (attempt - 1), signal);
2931
+ if (signal.aborted) throw new Error("Kimi Code token refresh aborted");
2932
+ let response;
2933
+ try {
2934
+ response = await fetch(`${oauthHost}/api/oauth/token`, {
2935
+ method: "POST",
2936
+ headers: {
2937
+ "Content-Type": "application/x-www-form-urlencoded",
2938
+ Accept: "application/json"
2939
+ },
2940
+ body: formUrlEncode({
2941
+ client_id: CLIENT_ID,
2942
+ grant_type: "refresh_token",
2943
+ refresh_token: refreshTokenValue
2944
+ }),
2945
+ signal: requestSignal(signal)
2946
+ });
2947
+ } catch (error) {
2948
+ lastError = error instanceof Error ? error : new Error(String(error));
2949
+ continue;
2950
+ }
2951
+ const json = await readJson(response);
2952
+ if (response.ok) return parseTokenResponse(json, "refresh");
2953
+ if (response.status === 401 || response.status === 403 || json?.error === "invalid_grant") {
2954
+ const description = typeof json?.error_description === "string" ? `: ${json.error_description}` : "";
2955
+ throw new Error(`Kimi Code token refresh unauthorized (status ${response.status})${description}`);
2956
+ }
2957
+ if (isRetryableRefreshFailure(response) && attempt < REFRESH_MAX_RETRIES) {
2958
+ lastError = /* @__PURE__ */ new Error(`Kimi Code token refresh failed with status ${response.status}`);
2959
+ continue;
2960
+ }
2961
+ const text = JSON.stringify(json);
2962
+ throw new Error(`Kimi Code token refresh failed with status ${response.status}${text ? `: ${text}` : ""}`);
2963
+ }
2964
+ throw lastError ?? /* @__PURE__ */ new Error("Kimi Code token refresh failed");
2965
+ }
2966
+ async function loginKimiCoding(interaction) {
2967
+ const oauthHost = getOauthHost();
2968
+ const device = await startDeviceAuthorization(oauthHost, interaction.signal);
2969
+ interaction.notify({
2970
+ type: "device_code",
2971
+ userCode: device.userCode,
2972
+ verificationUri: device.verificationUriComplete,
2973
+ intervalSeconds: device.intervalSeconds,
2974
+ expiresInSeconds: device.expiresInSeconds
2975
+ });
2976
+ const token = await pollForToken(oauthHost, device, interaction.signal);
2977
+ return {
2978
+ type: "oauth",
2979
+ access: token.access,
2980
+ refresh: token.refresh,
2981
+ expires: token.expires
2982
+ };
2983
+ }
2984
+ const kimiCodingOAuth = {
2985
+ name: "Kimi Code (subscription)",
2986
+ isSubscription: true,
2987
+ loginLabel: "Sign in with Kimi Code",
2988
+ login: loginKimiCoding,
2989
+ refresh: async (credential, signal) => {
2990
+ const token = await refreshToken(getOauthHost(), credential.refresh, signal);
2991
+ return {
2992
+ type: "oauth",
2993
+ access: token.access,
2994
+ refresh: token.refresh,
2995
+ expires: token.expires
2996
+ };
2997
+ },
2998
+ async toAuth(credential) {
2999
+ return { headers: { Authorization: `Bearer ${credential.access}` } };
3000
+ }
3001
+ };
3002
+ //#endregion
3003
+ //#region node_modules/.pnpm/@earendil-works+pi-ai@0.84.1_ws@8.21.3_zod@4.4.3/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.lazy.js
3004
+ const anthropicMessagesApi = () => lazyApi(() => import("./anthropic-messages-CaYTgvLF.mjs"));
3005
+ //#endregion
3006
+ //#region node_modules/.pnpm/@earendil-works+pi-ai@0.84.1_ws@8.21.3_zod@4.4.3/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.lazy.js
3007
+ const openAICompletionsApi = () => lazyApi(() => import("./openai-completions-DrjrWRdX.mjs"));
3008
+ //#endregion
3009
+ //#region node_modules/.pnpm/@earendil-works+pi-ai@0.84.1_ws@8.21.3_zod@4.4.3/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.lazy.js
3010
+ const openAIResponsesApi = () => lazyApi(() => import("./openai-responses-BRdGpBX6.mjs").then((n) => n.t));
3011
+ //#endregion
3012
+ //#region node_modules/.pnpm/@earendil-works+pi-ai@0.84.1_ws@8.21.3_zod@4.4.3/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.lazy.js
3013
+ const openAICodexResponsesApi = () => lazyApi(() => import("./openai-codex-responses-DedxQjNG.mjs"));
3014
+ //#endregion
3015
+ //#region node_modules/.pnpm/@earendil-works+pi-ai@0.84.1_ws@8.21.3_zod@4.4.3/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.lazy.js
3016
+ const azureOpenAIResponsesApi = () => lazyApi(() => import("./azure-openai-responses-HKWkzl2m.mjs"));
3017
+ //#endregion
3018
+ //#region node_modules/.pnpm/@earendil-works+pi-ai@0.84.1_ws@8.21.3_zod@4.4.3/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.lazy.js
3019
+ const googleGenerativeAIApi = () => lazyApi(() => import("./google-generative-ai-2gCFYp-t.mjs"));
3020
+ //#endregion
3021
+ //#region node_modules/.pnpm/@earendil-works+pi-ai@0.84.1_ws@8.21.3_zod@4.4.3/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.lazy.js
3022
+ const googleVertexApi = () => lazyApi(() => import("./google-vertex-Bhyo4z2i.mjs"));
3023
+ //#endregion
3024
+ //#region node_modules/.pnpm/@earendil-works+pi-ai@0.84.1_ws@8.21.3_zod@4.4.3/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.lazy.js
3025
+ const mistralConversationsApi = () => lazyApi(() => import("./mistral-conversations-DJyeTo8C.mjs"));
3026
+ //#endregion
3027
+ //#region node_modules/.pnpm/@earendil-works+pi-ai@0.84.1_ws@8.21.3_zod@4.4.3/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.lazy.js
3028
+ var __rewriteRelativeImportExtension = function(path, preserveJsx) {
3029
+ if (typeof path === "string" && /^\.\.?\//.test(path)) return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
3030
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : d + ext + "." + cm.toLowerCase() + "js";
3031
+ });
3032
+ return path;
3033
+ };
3034
+ /**
3035
+ * Loads the bedrock implementation through a variable specifier so bundlers
3036
+ * (browser smoke, Bun compile) cannot follow the import into the Node-only
3037
+ * AWS SDK. The `.ts`/`.js` rewrite keeps the trick working from both source
3038
+ * and built output.
3039
+ */
3040
+ const importNodeOnlyApi = (specifier) => {
3041
+ return import(__rewriteRelativeImportExtension(import.meta.url.endsWith(".js") ? specifier.replace(/\.ts$/, ".js") : specifier));
3042
+ };
3043
+ const bedrockConverseStreamApi = () => lazyApi(async () => await importNodeOnlyApi("./bedrock-converse-stream.ts"));
3044
+ //#endregion
3045
+ //#region node_modules/.pnpm/@earendil-works+pi-ai@0.84.1_ws@8.21.3_zod@4.4.3/node_modules/@earendil-works/pi-ai/dist/api/pi-messages.lazy.js
3046
+ const piMessagesApi = () => lazyApi(() => import("./pi-messages-D61G2tgs.mjs"));
3047
+ //#endregion
3048
+ //#region src/compat/pi-ai.ts
3049
+ /**
3050
+ * Pi's standard stored-key → ordered environment fallback auth helper.
3051
+ * Semantics copied unchanged from Pi 0.84.1
3052
+ * (`packages/ai/src/auth/helpers.ts`, commit
3053
+ * 6f707eb36064e82af9c1320a7634f4dfad21049b).
3054
+ */
3055
+ function envApiKeyAuth(name, envVars) {
3056
+ return {
3057
+ name,
3058
+ login: async (interaction) => {
3059
+ interaction.signal.throwIfAborted();
3060
+ const key = await interaction.prompt({
3061
+ type: "secret",
3062
+ message: `Enter ${name}`
3063
+ });
3064
+ interaction.signal.throwIfAborted();
3065
+ return {
3066
+ type: "api_key",
3067
+ key
3068
+ };
3069
+ },
3070
+ resolve: async ({ ctx, credential, signal }) => {
3071
+ signal.throwIfAborted();
3072
+ if (credential?.key) return {
3073
+ auth: { apiKey: credential.key },
3074
+ env: credential.env,
3075
+ source: "stored credential"
3076
+ };
3077
+ for (const envVar of envVars) {
3078
+ const value = await ctx.env(envVar);
3079
+ signal.throwIfAborted();
3080
+ if (value) return {
3081
+ auth: { apiKey: value },
3082
+ source: envVar
3083
+ };
3084
+ }
3085
+ }
3086
+ };
3087
+ }
3088
+ const EXTENDED_THINKING_LEVELS = [
3089
+ "off",
3090
+ "minimal",
3091
+ "low",
3092
+ "medium",
3093
+ "high",
3094
+ "xhigh",
3095
+ "max"
3096
+ ];
3097
+ function getSupportedThinkingLevels(model) {
3098
+ return EXTENDED_THINKING_LEVELS.filter((level) => {
3099
+ const mapped = model.thinkingLevelMap?.[level];
3100
+ if (mapped === null) return false;
3101
+ if (level === "xhigh" || level === "max") return mapped !== void 0;
3102
+ return true;
3103
+ });
3104
+ }
3105
+ function clampThinkingLevel(model, level) {
3106
+ const availableLevels = getSupportedThinkingLevels(model);
3107
+ if (availableLevels.includes(level)) return level;
3108
+ const requestedIndex = EXTENDED_THINKING_LEVELS.indexOf(level);
3109
+ if (requestedIndex === -1) return availableLevels[0] ?? "off";
3110
+ for (let i = requestedIndex; i < EXTENDED_THINKING_LEVELS.length; i += 1) {
3111
+ const candidate = EXTENDED_THINKING_LEVELS[i];
3112
+ if (availableLevels.includes(candidate)) return candidate;
3113
+ }
3114
+ for (let i = requestedIndex - 1; i >= 0; i -= 1) {
3115
+ const candidate = EXTENDED_THINKING_LEVELS[i];
3116
+ if (availableLevels.includes(candidate)) return candidate;
3117
+ }
3118
+ return availableLevels[0] ?? "off";
3119
+ }
3120
+ function modelsAreEqual(a, b) {
3121
+ if (a === null || a === void 0 || b === null || b === void 0) return false;
3122
+ return a.id === b.id && a.provider === b.provider;
3123
+ }
3124
+ function contentText(content, separator = "\n") {
3125
+ if (typeof content === "string") return content;
3126
+ return content.filter((block) => block.type === "text").map((block) => String(block.text ?? "")).join(separator);
3127
+ }
3128
+ const globalRegistry = {
3129
+ compatProviders: /* @__PURE__ */ new Map(),
3130
+ apiProviders: /* @__PURE__ */ new Map()
3131
+ };
3132
+ const scopedRegistry = new AsyncLocalStorage();
3133
+ const registry = () => scopedRegistry.getStore() ?? globalRegistry;
3134
+ /** Create one isolated copy of Pi's process registries for an Agent runtime. */
3135
+ function __createPiAiRuntimeRegistry() {
3136
+ return {
3137
+ compatProviders: /* @__PURE__ */ new Map(),
3138
+ apiProviders: /* @__PURE__ */ new Map()
3139
+ };
3140
+ }
3141
+ function registerProvider(name, provider) {
3142
+ registry().compatProviders.set(name, provider);
3143
+ }
3144
+ function getProviders() {
3145
+ return [...registry().compatProviders.keys()];
3146
+ }
3147
+ function getProvider(name) {
3148
+ return registry().compatProviders.get(name);
3149
+ }
3150
+ function getModel(_provider, _id) {}
3151
+ const getBuiltinModels = (provider) => getModels(provider);
3152
+ const getBuiltinModel = (provider, id) => void 0;
3153
+ const getBuiltinProviders = () => getProviders();
3154
+ function getModels(_provider) {
3155
+ return [];
3156
+ }
3157
+ function builtinProviders() {
3158
+ const entry = (id, name, baseUrl, oauth) => ({
3159
+ id,
3160
+ name,
3161
+ api: "openai-completions",
3162
+ baseUrl,
3163
+ models: [],
3164
+ auth: { oauth }
3165
+ });
3166
+ return [
3167
+ entry("openai-codex", "OpenAI (ChatGPT Plus/Pro)", "https://chatgpt.com/backend-api/codex", openaiCodexOAuth),
3168
+ entry("anthropic", "Anthropic", "https://api.anthropic.com", anthropicOAuth),
3169
+ entry("github-copilot", "GitHub Copilot", "https://api.githubcopilot.com", githubCopilotOAuth),
3170
+ entry("kimi-coding", "Kimi Code", "https://api.moonshot.ai/anthropic", kimiCodingOAuth)
3171
+ ];
3172
+ }
3173
+ const loadOpenAICodexOAuth = async () => openaiCodexOAuth;
3174
+ const loadAnthropicOAuth = async () => anthropicOAuth;
3175
+ const loadGitHubCopilotOAuth = async () => githubCopilotOAuth;
3176
+ const loadKimiCodingOAuth = async () => kimiCodingOAuth;
3177
+ function guardApi(api, fn) {
3178
+ return (model, ...rest) => {
3179
+ const modelApi = model?.api;
3180
+ if (modelApi !== api) throw new Error(`Mismatched api: ${String(modelApi)} expected ${api}`);
3181
+ return fn(model, ...rest);
3182
+ };
3183
+ }
3184
+ function registerApiProvider(provider, sourceId) {
3185
+ registry().apiProviders.set(provider.api, {
3186
+ provider: {
3187
+ api: provider.api,
3188
+ stream: guardApi(provider.api, provider.stream),
3189
+ streamSimple: guardApi(provider.api, provider.streamSimple)
3190
+ },
3191
+ sourceId
3192
+ });
3193
+ }
3194
+ function getApiProvider(api) {
3195
+ return registry().apiProviders.get(api)?.provider;
3196
+ }
3197
+ function getApiProviders() {
3198
+ return Array.from(registry().apiProviders.values(), (entry) => entry.provider);
3199
+ }
3200
+ function unregisterApiProviders(sourceId) {
3201
+ const providers = registry().apiProviders;
3202
+ for (const [api, entry] of providers.entries()) if (entry.sourceId === sourceId) providers.delete(api);
3203
+ }
3204
+ let activeLlmBridge;
3205
+ const scopedLlmBridge = new AsyncLocalStorage();
3206
+ function __setPiAiLlmBridge(bridge) {
3207
+ activeLlmBridge = bridge;
3208
+ }
3209
+ /** Run extension-owned work with the exact Agent runtime's DSH llm bridge. */
3210
+ function __runWithPiAiLlmBridge(bridge, callback) {
3211
+ return scopedLlmBridge.run(bridge, callback);
3212
+ }
3213
+ /** Bind both designated-model routing and Pi's registries to one Agent runtime. */
3214
+ function __runWithPiAiRuntime(bridge, runtimeRegistry, callback) {
3215
+ return scopedRegistry.run(runtimeRegistry, () => scopedLlmBridge.run(bridge, callback));
3216
+ }
3217
+ function __getPiAiLlmBridge() {
3218
+ return scopedLlmBridge.getStore() ?? activeLlmBridge;
3219
+ }
3220
+ function requireLlmBridge(api) {
3221
+ const bridge = __getPiAiLlmBridge();
3222
+ if (bridge === void 0) throw new Error(`pi2dsh: pi-ai ${api}() needs a DSH llm service; this composition mounts none, so model calls cannot be routed`);
3223
+ return bridge;
3224
+ }
3225
+ async function complete(model, context, options) {
3226
+ const result = await requireLlmBridge("complete")(model, context, options).result();
3227
+ if (result instanceof Error) throw result;
3228
+ return result;
3229
+ }
3230
+ function stream(model, context, options) {
3231
+ return requireLlmBridge("stream")(model, context, options);
3232
+ }
3233
+ function StringEnum(values, options = {}) {
3234
+ return {
3235
+ type: "string",
3236
+ enum: values,
3237
+ ...options.description !== void 0 ? { description: options.description } : {},
3238
+ ...options.default !== void 0 ? { default: options.default } : {}
3239
+ };
3240
+ }
3241
+ //#endregion
3242
+ export { isContextOverflow as $, stream as A, openAICompletionsApi as B, loadAnthropicOAuth as C, modelsAreEqual as D, loadOpenAICodexOAuth as E, googleVertexApi as F, openaiCodexOAuth as G, kimiCodingOAuth as H, googleGenerativeAIApi as I, ModelsError as J, generatePKCE as K, azureOpenAIResponsesApi as L, piMessagesApi as M, bedrockConverseStreamApi as N, registerApiProvider as O, mistralConversationsApi as P, AssistantMessageEventStream as Q, openAICodexResponsesApi as R, getSupportedThinkingLevels as S, loadKimiCodingOAuth as T, githubCopilotOAuth as U, anthropicMessagesApi as V, anthropicOAuth as W, lazyApi$1 as X, createProvider as Y, lazyStream$1 as Z, getBuiltinProviders as _, __runWithPiAiLlmBridge as a, getProvider as b, builtinProviders as c, contentText as d, isRecoverableLength as et, envApiKeyAuth as f, getBuiltinModels as g, getBuiltinModel as h, __getPiAiLlmBridge as i, unregisterApiProviders as j, registerProvider as k, clampThinkingLevel as l, getApiProviders as m, Type$1 as n, retryAssistantCall as nt, __runWithPiAiRuntime as o, getApiProvider as p, pollOAuthDeviceCodeFlow as q, __createPiAiRuntimeRegistry as r, uuidv7 as rt, __setPiAiLlmBridge as s, StringEnum as t, isRetryableAssistantError as tt, complete as u, getModel as v, loadGitHubCopilotOAuth as w, getProviders as x, getModels as y, openAIResponsesApi as z };
3243
+
3244
+ //# sourceMappingURL=pi-ai-DpqiLdPA.mjs.map