@code-yeongyu/senpi-ai 2026.10.9 → 2026.10.10-10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/anthropic-messages.js +101 -38
- package/dist/api/anthropic-tool-references.js +24 -18
- package/dist/api/bedrock-converse-stream.js +5 -1
- package/dist/api/lazy.js +13 -0
- package/dist/api/mistral-conversations.js +18 -3
- package/dist/api/openai-codex-responses.js +2 -2
- package/dist/api/openai-completions.js +26 -2
- package/dist/api/openai-responses.js +2 -2
- package/dist/auth/oauth/anthropic.js +4 -3
- package/dist/auth/oauth/chatgpt-subscription.js +3 -2
- package/dist/auth/oauth/cursor.js +3 -2
- package/dist/auth/oauth/devin-token.js +2 -1
- package/dist/auth/oauth/github-copilot.js +3 -2
- package/dist/auth/oauth/kimi-coding.js +5 -4
- package/dist/auth/oauth/openai-chatgpt.js +2 -1
- package/dist/auth/oauth/openrouter.js +2 -1
- package/dist/auth/oauth/radius.js +5 -5
- package/dist/auth/oauth/xai.js +7 -4
- package/dist/auth/oauth-refresh.js +10 -0
- package/dist/auth/resolve.js +5 -0
- package/dist/providers/all.d.ts +1 -1
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/providers/data/amazon-bedrock.json +1 -1
- package/dist/providers/data/anthropic.json +1 -1
- package/dist/providers/data/baseten.json +1 -1
- package/dist/providers/data/chatgpt-subscription.json +1 -1
- package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/dist/providers/data/cloudflare-workers-ai.json +1 -1
- package/dist/providers/data/github-copilot.json +1 -1
- package/dist/providers/data/google-vertex.json +1 -1
- package/dist/providers/data/mistral.json +1 -1
- package/dist/providers/data/moonshotai-cn.json +1 -1
- package/dist/providers/data/moonshotai.json +1 -1
- package/dist/providers/data/opencode-go.json +1 -1
- package/dist/providers/data/opencode.json +1 -1
- package/dist/providers/data/opengateway.json +1 -1
- package/dist/providers/data/openrouter.json +1 -1
- package/dist/providers/data/radius.json +1 -1
- package/dist/providers/data/venice.json +1 -1
- package/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/dist/providers/kimi-coding.models.d.ts +1 -1
- package/dist/providers/kimi-coding.models.js +1 -1
- package/dist/providers/opengateway-refresh.js +2 -1
- package/dist/utils/oauth-refresh-error.d.ts +16 -0
- package/dist/utils/oauth-refresh-error.js +67 -0
- package/dist/utils/prompt-cache-ttl.js +3 -1
- package/dist/utils/retry.d.ts +6 -0
- package/dist/utils/retry.js +10 -0
- package/dist/utils/tool-choice-fallback.d.ts +6 -0
- package/dist/utils/tool-choice-fallback.js +69 -2
- package/package.json +2 -2
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* access token authenticates requests to that region's coding API as an
|
|
7
7
|
* `Authorization: Bearer` header; see kimi-region.ts for the host table.
|
|
8
8
|
*/
|
|
9
|
+
import { OAuthTokenEndpointError } from "../../utils/oauth-refresh-error.js";
|
|
9
10
|
import { sleep } from "../../utils/sleep.js";
|
|
10
11
|
import { pollOAuthDeviceCodeFlow } from "./device-code.js";
|
|
11
12
|
import { kimiCodeIdentityHeaders } from "./kimi-identity.js";
|
|
@@ -178,7 +179,7 @@ async function refreshToken(oauthHost, refreshTokenValue, signal) {
|
|
|
178
179
|
await sleep(1000 * 2 ** (attempt - 1), signal);
|
|
179
180
|
}
|
|
180
181
|
if (signal.aborted) {
|
|
181
|
-
throw new Error("Kimi Code token refresh aborted");
|
|
182
|
+
throw new Error("Kimi Code token refresh aborted", { cause: signal.reason });
|
|
182
183
|
}
|
|
183
184
|
let response;
|
|
184
185
|
try {
|
|
@@ -208,14 +209,14 @@ async function refreshToken(oauthHost, refreshTokenValue, signal) {
|
|
|
208
209
|
// Unauthorized: the stored credential is dead; Models clears it and prompts re-login.
|
|
209
210
|
if (response.status === 401 || response.status === 403 || json?.error === "invalid_grant") {
|
|
210
211
|
const description = typeof json?.error_description === "string" ? `: ${json.error_description}` : "";
|
|
211
|
-
throw new
|
|
212
|
+
throw new OAuthTokenEndpointError(`Kimi Code token refresh unauthorized (status ${response.status})${description}`, response.status);
|
|
212
213
|
}
|
|
213
214
|
if (isRetryableRefreshFailure(response) && attempt < REFRESH_MAX_RETRIES) {
|
|
214
|
-
lastError = new
|
|
215
|
+
lastError = new OAuthTokenEndpointError(`Kimi Code token refresh failed with status ${response.status}`, response.status);
|
|
215
216
|
continue;
|
|
216
217
|
}
|
|
217
218
|
const text = JSON.stringify(json);
|
|
218
|
-
throw new
|
|
219
|
+
throw new OAuthTokenEndpointError(`Kimi Code token refresh failed with status ${response.status}${text ? `: ${text}` : ""}`, response.status);
|
|
219
220
|
}
|
|
220
221
|
throw lastError ?? new Error("Kimi Code token refresh failed");
|
|
221
222
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { randomBytes } from "node:crypto";
|
|
8
8
|
import { createServer } from "node:http";
|
|
9
9
|
import { oauthErrorHtml, oauthSuccessHtml } from "../../utils/oauth-page.js";
|
|
10
|
+
import { OAuthTokenEndpointError } from "../../utils/oauth-refresh-error.js";
|
|
10
11
|
import { getProviderEnvValue } from "../../utils/provider-env.js";
|
|
11
12
|
import { generatePKCE } from "./pkce.js";
|
|
12
13
|
// every login registers a new client with this ID; OpenAI returns the issued client ID in the callback
|
|
@@ -119,7 +120,7 @@ async function requestToken(body, signal) {
|
|
|
119
120
|
});
|
|
120
121
|
if (!response.ok) {
|
|
121
122
|
const responseBody = await response.text().catch(() => "");
|
|
122
|
-
throw new
|
|
123
|
+
throw new OAuthTokenEndpointError(`OpenAI OAuth token request failed (${response.status}): ${responseBody || response.statusText}`, response.status);
|
|
123
124
|
}
|
|
124
125
|
const data = await response.json();
|
|
125
126
|
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* NOTE: This module uses node:http (via callback-server.ts) for the OAuth callback server.
|
|
11
11
|
* It is only intended for CLI use, not browser environments.
|
|
12
12
|
*/
|
|
13
|
+
import { OAuthTokenEndpointError } from "../../utils/oauth-refresh-error.js";
|
|
13
14
|
import { getProviderEnvValue } from "../../utils/provider-env.js";
|
|
14
15
|
import { startOAuthCallbackServer, waitForCallbackOrManualInput } from "./callback-server.js";
|
|
15
16
|
import { generatePKCE } from "./pkce.js";
|
|
@@ -88,7 +89,7 @@ async function exchangeAuthorizationCode(code, verifier, signal) {
|
|
|
88
89
|
}
|
|
89
90
|
if (!response.ok) {
|
|
90
91
|
const detail = errorDetail(body);
|
|
91
|
-
throw new
|
|
92
|
+
throw new OAuthTokenEndpointError(`OpenRouter OAuth key exchange failed (HTTP ${response.status})${detail ? `: ${detail}` : ""}`, response.status);
|
|
92
93
|
}
|
|
93
94
|
if (typeof body.key !== "string" || body.key.length === 0) {
|
|
94
95
|
throw new Error('OpenRouter OAuth response carries no "key"');
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* It is only intended for CLI use, not browser environments.
|
|
10
10
|
*/
|
|
11
11
|
import { normalizeRadiusGatewayUrl } from "../../providers/radius-config.js";
|
|
12
|
+
import { OAuthTokenEndpointError } from "../../utils/oauth-refresh-error.js";
|
|
12
13
|
import { startOAuthCallbackServer } from "./callback-server.js";
|
|
13
14
|
import { pollOAuthDeviceCodeFlow } from "./device-code.js";
|
|
14
15
|
import { generatePKCE } from "./pkce.js";
|
|
@@ -36,15 +37,14 @@ async function loadRadiusOAuthDiscovery(gateway, signal) {
|
|
|
36
37
|
}
|
|
37
38
|
return { authorizationEndpoint: discovery.authorizationEndpoint };
|
|
38
39
|
}
|
|
39
|
-
class OAuthResponseError extends
|
|
40
|
+
class OAuthResponseError extends OAuthTokenEndpointError {
|
|
40
41
|
constructor(status, oauthError, description, message) {
|
|
41
42
|
const detail = oauthError
|
|
42
43
|
? description
|
|
43
44
|
? `${oauthError}: ${description}`
|
|
44
45
|
: oauthError
|
|
45
46
|
: description || String(status);
|
|
46
|
-
super(`${message}: ${detail}
|
|
47
|
-
this.status = status;
|
|
47
|
+
super(`${message}: ${detail}`, status);
|
|
48
48
|
this.oauthError = oauthError;
|
|
49
49
|
}
|
|
50
50
|
}
|
|
@@ -76,7 +76,7 @@ async function requestOAuthToken(gateway, body, signal) {
|
|
|
76
76
|
}
|
|
77
77
|
catch (error) {
|
|
78
78
|
if (signal.aborted) {
|
|
79
|
-
throw new Error("Login cancelled");
|
|
79
|
+
throw new Error("Login cancelled", { cause: signal.reason });
|
|
80
80
|
}
|
|
81
81
|
throw error;
|
|
82
82
|
}
|
|
@@ -149,7 +149,7 @@ async function requestDeviceAuthorization(gateway, signal) {
|
|
|
149
149
|
}
|
|
150
150
|
catch (error) {
|
|
151
151
|
if (signal.aborted) {
|
|
152
|
-
throw new Error("Login cancelled");
|
|
152
|
+
throw new Error("Login cancelled", { cause: signal.reason });
|
|
153
153
|
}
|
|
154
154
|
throw error;
|
|
155
155
|
}
|
package/dist/auth/oauth/xai.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* xAI OAuth device-code flow.
|
|
3
3
|
*/
|
|
4
|
+
import { OAuthTokenEndpointError } from "../../utils/oauth-refresh-error.js";
|
|
4
5
|
import { pollOAuthDeviceCodeFlow } from "./device-code.js";
|
|
5
6
|
const XAI_CLIENT_ID = "b1a00492-073a-47ea-816f-4c329264a828";
|
|
6
7
|
const XAI_SCOPE = "openid profile email offline_access grok-cli:access api:access";
|
|
@@ -53,7 +54,7 @@ async function postForm(url, fields, signal) {
|
|
|
53
54
|
}
|
|
54
55
|
catch (error) {
|
|
55
56
|
if (signal.aborted) {
|
|
56
|
-
throw new Error("Login cancelled");
|
|
57
|
+
throw new Error("Login cancelled", { cause: signal.reason });
|
|
57
58
|
}
|
|
58
59
|
throw error;
|
|
59
60
|
}
|
|
@@ -64,9 +65,11 @@ async function postForm(url, fields, signal) {
|
|
|
64
65
|
}
|
|
65
66
|
catch {
|
|
66
67
|
if (signal.aborted) {
|
|
67
|
-
throw new Error("Login cancelled");
|
|
68
|
+
throw new Error("Login cancelled", { cause: signal.reason });
|
|
68
69
|
}
|
|
69
|
-
|
|
70
|
+
const message = `xAI OAuth returned invalid JSON (HTTP ${response.status})`;
|
|
71
|
+
// An error page (an HTML 503 from a proxy, say) still carries the status the retry decision needs.
|
|
72
|
+
throw response.ok ? new Error(message) : new OAuthTokenEndpointError(message, response.status);
|
|
70
73
|
}
|
|
71
74
|
return {
|
|
72
75
|
ok: response.ok,
|
|
@@ -78,7 +81,7 @@ function requestFailure(action, response) {
|
|
|
78
81
|
const error = typeof response.body.error === "string" ? response.body.error : undefined;
|
|
79
82
|
const description = typeof response.body.error_description === "string" ? response.body.error_description : undefined;
|
|
80
83
|
const detail = [error, description].filter(Boolean).join(": ");
|
|
81
|
-
return new
|
|
84
|
+
return new OAuthTokenEndpointError(`xAI OAuth ${action} failed (HTTP ${response.status})${detail ? `: ${detail}` : ""}`, response.status);
|
|
82
85
|
}
|
|
83
86
|
function parseDeviceCode(body) {
|
|
84
87
|
// RFC 8628 allows interval 0 (no minimum wait); fall back to the poller's
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* superseded catalog refresh stops waiting but never aborts the exchange.
|
|
16
16
|
*/
|
|
17
17
|
import { raceWithAbortSignal } from "../utils/abort.js";
|
|
18
|
+
import { oauthRefreshFailureCause } from "../utils/oauth-refresh-error.js";
|
|
18
19
|
import { mergeRefreshed, mergeRefreshedSlot, projectSlot } from "./pool/slots.js";
|
|
19
20
|
export const DEFAULT_OAUTH_REFRESH_TIMEOUT_MS = 15_000;
|
|
20
21
|
/** The provider rejected or timed out the token exchange. */
|
|
@@ -103,6 +104,15 @@ async function exchangeAndStore(request, signal) {
|
|
|
103
104
|
signal.throwIfAborted();
|
|
104
105
|
}
|
|
105
106
|
catch (error) {
|
|
107
|
+
signal.throwIfAborted();
|
|
108
|
+
const causeClass = oauthRefreshFailureCause(error);
|
|
109
|
+
if (causeClass !== undefined) {
|
|
110
|
+
console.warn("OAuth refresh unavailable", JSON.stringify({
|
|
111
|
+
provider: providerId,
|
|
112
|
+
...(slotName ? { slot: slotName } : {}),
|
|
113
|
+
cause: causeClass,
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
106
116
|
throw new OAuthRefreshExchangeError(error);
|
|
107
117
|
}
|
|
108
118
|
try {
|
package/dist/auth/resolve.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { operationSignal, raceWithAbortSignal } from "../utils/abort.js";
|
|
2
2
|
import { ModelsError } from "../utils/models-error.js";
|
|
3
|
+
import { classifyOAuthRefreshFailure, OAuthRefreshUnavailableError } from "../utils/oauth-refresh-error.js";
|
|
3
4
|
import { OAuthRefreshExchangeError, OAuthRefreshStoreError, projectOAuthSlot, refreshOAuthCredential, } from "./oauth-refresh.js";
|
|
4
5
|
import { projectSlot } from "./pool/slots.js";
|
|
5
6
|
export { ModelsError } from "../utils/models-error.js";
|
|
@@ -82,6 +83,9 @@ export function oauthRefreshModelsError(error, providerId) {
|
|
|
82
83
|
if (error instanceof ModelsError)
|
|
83
84
|
return error;
|
|
84
85
|
if (error instanceof OAuthRefreshExchangeError) {
|
|
86
|
+
if (classifyOAuthRefreshFailure(error.cause) === "transient") {
|
|
87
|
+
return new OAuthRefreshUnavailableError(providerId, error.cause);
|
|
88
|
+
}
|
|
85
89
|
return new ModelsError("oauth", `OAuth refresh failed for ${providerId}`, { cause: error.cause });
|
|
86
90
|
}
|
|
87
91
|
const cause = error instanceof OAuthRefreshStoreError ? error.cause : error;
|
|
@@ -112,6 +116,7 @@ async function resolveStoredOAuth(credentials, providerId, oauth, stored, authCo
|
|
|
112
116
|
});
|
|
113
117
|
}
|
|
114
118
|
catch (error) {
|
|
119
|
+
signal.throwIfAborted();
|
|
115
120
|
throw oauthRefreshModelsError(error, providerId);
|
|
116
121
|
}
|
|
117
122
|
if (post?.type !== "oauth")
|
package/dist/providers/all.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"schemaVersion":6,"generatedAt":"2026-10-
|
|
1
|
+
{"schemaVersion":6,"generatedAt":"2026-10-09T02:02:14.857Z","structureHash":"3663f1f5cad5c9cc1deecb3db21134e5d250b29f8ea2b5082d0783f3abdea0fe","files":{"alibaba-token-plan.json":"16b3c6c78d72c73ca34a1e79ce71c0cf36ba409d02e83a17396ce0949a1d5363","amazon-bedrock.json":"4a95e6bf974e5ca967d5c050df580262e13573e42b3186edb448fa11a302f09f","ant-ling.json":"628feab3ef6c7d80ce96f75999804e1d9f05fec3851e5302e30fff3b69db90f6","anthropic.json":"111384e9f5845e0247bd34edf2400ef7720fcbdac140b944cf8878192d8bd6ab","azure-openai-responses.json":"a3e52bd78f0220aa32bce519e13955b8e6e567860416343844cbe19902f4a682","bai.json":"7e6861aaf27d8af34e1771c6092602194fcec0a0046e6adfab9c3ee54d32b99f","baseten.json":"1fcdab8daf2881bd099eb08f9416f7df5bf39975937d2a63b841542bd032f2e6","cerebras.json":"3eccb58c5de3d0dc7ba49dc6e477ba29f00cb3150ce0a35cc90d59e6b5ab368e","chatgpt-subscription.json":"2fbeb0141b29c340542798bce53422b93bef026a13420986e87fca89353e0aa2","cloudflare-ai-gateway.json":"5c8782fdbf2477f620b238da4344b0bcc0948b8c177461e63e7e5ba256f769c7","cloudflare-workers-ai.json":"f351e80493dea7b7a2c31dceb086ee804a88d67c5f8a3c823ea907991fe5d659","deepseek.json":"10a296fb3e898f7715c80890c8af0af4f5fd58f22dbcd6612fdc5d762157ccdc","fireworks.json":"c3dc21dc2e95428fc9a38e3840c05bbf90f94e4c25dee0eedf0b385b49ff65a4","github-copilot.json":"2c5a84983477a7c4f6c001088a1e043ee959ce58c99fea6eb843e0286228664b","google-vertex.json":"96ecc19bf20a706f99ae658b021a0ee5438146119e1e99de5fd824a01fcfd81b","google.json":"c0f5a633e5e5a5044432db3e537f4bc674d4de18df09325c6349547fcc13cabe","groq.json":"910952ad9dad07856983459d63fce0148dba1203edd7b10d801a77c5824f3405","huggingface.json":"1b8604bd31a5e05fe43a40ec1ec6dfc1a51a28b0b020151ef057fab43f57c923","meta.json":"0c7e9a370a7005c5a89dc94f0f93bc67b8935c3f802de324f11af6ff56fff429","minimax-cn.json":"f962c20ee6a6a342820f778e9dc363a18a524bf11998f6e2c57f2989915d62e5","minimax.json":"17a432f80d0ea6dd26eade80a4314250d3a855678601728a5f70dd8fa0d21dd2","mistral.json":"27aa66c71a0504b53dc87ec194286fa7e70b99747fc8df9b709b23b3548afe50","moonshotai-cn.json":"18954b55dc5edecc174d8a07a2c5993416dd9d3ce52e411438035c16ee982e8e","moonshotai.json":"62d08fd5beead025a56d5f2e4227d8516b9c5034275b1b36d761a8625918f2b9","nvidia.json":"a75e963dc81779e3eec01c051b12277c15ec37c5b94ab51ece9e9ccb3b16407c","openai.json":"9104538118053eed57f90852332b6ad274a4e4792389bdbc9cfca1ae400c705a","opencode-go.json":"48ff011f3e171f977cfed2928272667e9361f13a508e5994f00f2241f6a23419","opencode.json":"882b039f64ef266da7b02d30aee9255c191e16f0600ef5494022bc1ef746f749","opengateway.json":"dd4259885ea6045d723b3abaae06657dfcce07b9ce3c2b0300220ac661432859","openrouter.json":"66421942de8fffa71bb4f60d9616408a0af5b52970bc4250753f57293fac3ec2","qwen-token-plan-cn.json":"bd36a6d9cc2048deafacc85f7f0f2e51868c8b7aa09502e3e4e60e3e8b76f690","qwen-token-plan-individual.json":"35ffb0ba1a1ce3f68fc73262dd5e07fbcb5bf5911ad1e6e17bc0621e2894676a","qwen-token-plan.json":"ca27e49e62965221badead8e867b2007035c6b6a15a97c47219bd23d2c83c2b1","radius.json":"8214122385e8e4c4bc2e1d2a7d032f9e4c654da3bfd3ac8c5d4396e1b522e53a","together.json":"bbd7fad86cacfefe3d3d61f6638f22e9b6f5d575722fd1112a16aafe0485c46d","typesafe.json":"a4429a2f696d5cc5273969af61a7310bda07d6bdcd27bf9ac9c76c534cc43716","venice.json":"052c57c962adb3e2460a3ffbe052f376099cafe943e0b26279ece64265a613e8","vercel-ai-gateway.json":"8fe5af15a8fcb727009923271fa288d24a655252b2dc90fbdd749fed59ecda95","xai.json":"d7771a1450b21c1f6aee887ca6fea61b6e7b3fc13b4dee5cf72a71134fe623bc","xiaomi-token-plan-ams.json":"4dd8f838e21d7aa7caf3122d0b0b1e5a02a33bc012b96e6a75bd119334152d7a","xiaomi-token-plan-cn.json":"46414ff49973b95a0c14bd33e8992cc1938a50ce2c7cea9e2e5cc02de8f1df31","xiaomi-token-plan-sgp.json":"f5b3f66bf7bb7238592d4a9667cad983a008d7539a66e9896b02422622800935","xiaomi.json":"8b62c158d55d5a165f35cd72c1fc82ab89ff8e73f08960105fc41b822f236f69","zai-coding-cn.json":"2a7f59d15f588a97d218997883f2506dfc52d3ee320697d39f14213a050fc912","zai.json":"e9cc408e1756de8abc70148d63d89d11a63c65b7ef06ad0661cd1328e4209ba3"}}
|