@code-yeongyu/senpi-ai 2026.10.13 → 2026.10.15
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/providers/anthropic.js +3 -7
- package/dist/providers/data/.manifest.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/google.json +1 -1
- package/dist/providers/data/meta.json +1 -1
- package/dist/providers/data/nvidia.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/opencode-go.d.ts +3 -1
- package/dist/providers/opencode-go.js +2 -0
- package/dist/utils/provider-failure-description.js +17 -1
- package/dist/utils/retry.d.ts +8 -0
- package/dist/utils/retry.js +12 -0
- package/dist/utils/tool-schema-compat.js +10 -10
- package/package.json +2 -2
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import { type Provider } from "../models.ts";
|
|
2
|
-
|
|
2
|
+
type OpenCodeGoApi = "anthropic-messages" | "google-generative-ai" | "openai-completions" | "openai-responses";
|
|
3
|
+
export declare function opencodeGoProvider(): Provider<OpenCodeGoApi>;
|
|
4
|
+
export {};
|
|
3
5
|
//# sourceMappingURL=opencode-go.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { anthropicMessagesApi } from "../api/anthropic-messages.lazy.js";
|
|
2
|
+
import { googleGenerativeAIApi } from "../api/google-generative-ai.lazy.js";
|
|
2
3
|
import { openAICompletionsApi } from "../api/openai-completions.lazy.js";
|
|
3
4
|
import { openAIResponsesApi } from "../api/openai-responses.lazy.js";
|
|
4
5
|
import { envApiKeyAuth } from "../auth/helpers.js";
|
|
@@ -13,6 +14,7 @@ export function opencodeGoProvider() {
|
|
|
13
14
|
models: Object.values(OPENCODE_GO_MODELS),
|
|
14
15
|
api: {
|
|
15
16
|
"anthropic-messages": withOpenCodeSessionHeader(anthropicMessagesApi()),
|
|
17
|
+
"google-generative-ai": withOpenCodeSessionHeader(googleGenerativeAIApi()),
|
|
16
18
|
"openai-completions": withOpenCodeSessionHeader(openAICompletionsApi()),
|
|
17
19
|
"openai-responses": withOpenCodeSessionHeader(openAIResponsesApi()),
|
|
18
20
|
},
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
import { describeProviderStallForUser, formatStallDuration } from "./retry.js";
|
|
1
|
+
import { describeProviderStallForUser, formatStallDuration, isAnthropicThirdPartyBillingMessage, } from "./retry.js";
|
|
2
|
+
const CLAUDE_EXTRA_USAGE_URL = "https://claude.ai/settings/usage";
|
|
3
|
+
/**
|
|
4
|
+
* senpi#3259: the billing path is the whole story. On the direct `anthropic` provider a Claude
|
|
5
|
+
* subscription token is billed as a third-party app, so the fix is the Agent SDK lane; on the
|
|
6
|
+
* `anthropic-subscription` lane the request already ran through Claude Code, so only extra usage or
|
|
7
|
+
* another model can help.
|
|
8
|
+
*/
|
|
9
|
+
function describeAnthropicThirdPartyBilling(provider) {
|
|
10
|
+
if (provider === "anthropic-subscription") {
|
|
11
|
+
return `Anthropic billed this Claude Code request to your extra usage instead of your plan limits, and your extra usage is used up. Add extra usage at ${CLAUDE_EXTRA_USAGE_URL}, or switch to another model with /model.`;
|
|
12
|
+
}
|
|
13
|
+
return `Anthropic billed this request to your extra usage instead of your Claude plan limits, because a Claude subscription used directly through the Anthropic API counts as a third-party app. Run /login and choose "Anthropic Subscription (Claude Pro/Max)" so requests run through Claude Code and use your plan limits, add extra usage at ${CLAUDE_EXTRA_USAGE_URL}, or switch to another model with /model.`;
|
|
14
|
+
}
|
|
2
15
|
/**
|
|
3
16
|
* Session-internal replay-suppression signal, read off the error text by the
|
|
4
17
|
* retry predicates. A failover lane stamps it when replaying the turn could
|
|
@@ -44,6 +57,9 @@ export function describeProviderFailureForUser(errorMessage, options = {}) {
|
|
|
44
57
|
const message = stripTurnRetrySuppressionPrefix(errorMessage).trim();
|
|
45
58
|
if (!message)
|
|
46
59
|
return undefined;
|
|
60
|
+
if (isAnthropicThirdPartyBillingMessage(message)) {
|
|
61
|
+
return describeAnthropicThirdPartyBilling(options.model?.split("/")[0]);
|
|
62
|
+
}
|
|
47
63
|
if (options.usageLimit === "account") {
|
|
48
64
|
const provider = options.model?.split("/")[0];
|
|
49
65
|
const reset = /\bresets?\s+([^"}\n]+)/i
|
package/dist/utils/retry.d.ts
CHANGED
|
@@ -24,6 +24,14 @@ export declare const USAGE_LIMIT_EXHAUSTION: {
|
|
|
24
24
|
/** Message markers: the codes appear verbatim in bodies, plus the sentence. */
|
|
25
25
|
readonly markers: readonly ["usage_limit_reached", "usage_not_included", "usage limit has been reached"];
|
|
26
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* Anthropic's rejection of a Claude subscription token used outside Claude Code (senpi#3259): a
|
|
29
|
+
* 400 invalid_request_error reading "Third-party apps now draw from your extra usage, not your plan
|
|
30
|
+
* limits. Add more at claude.ai/settings/usage and keep going." The request was billed to extra
|
|
31
|
+
* usage instead of the plan, so every model on the same credential is refused the same way.
|
|
32
|
+
*/
|
|
33
|
+
export declare const ANTHROPIC_THIRD_PARTY_BILLING_MARKER = "Third-party apps now draw from your extra usage";
|
|
34
|
+
export declare function isAnthropicThirdPartyBillingMessage(errorMessage: string | undefined): boolean;
|
|
27
35
|
export declare function isQuotaExhaustionMessage(errorMessage: string | undefined): boolean;
|
|
28
36
|
/**
|
|
29
37
|
* Retry policy: bounded attempts with exponential backoff (`baseDelayMs * 2^(attempt-1)`).
|
package/dist/utils/retry.js
CHANGED
|
@@ -38,6 +38,16 @@ export const USAGE_LIMIT_EXHAUSTION = {
|
|
|
38
38
|
/** Message markers: the codes appear verbatim in bodies, plus the sentence. */
|
|
39
39
|
markers: ["usage_limit_reached", "usage_not_included", "usage limit has been reached"],
|
|
40
40
|
};
|
|
41
|
+
/**
|
|
42
|
+
* Anthropic's rejection of a Claude subscription token used outside Claude Code (senpi#3259): a
|
|
43
|
+
* 400 invalid_request_error reading "Third-party apps now draw from your extra usage, not your plan
|
|
44
|
+
* limits. Add more at claude.ai/settings/usage and keep going." The request was billed to extra
|
|
45
|
+
* usage instead of the plan, so every model on the same credential is refused the same way.
|
|
46
|
+
*/
|
|
47
|
+
export const ANTHROPIC_THIRD_PARTY_BILLING_MARKER = "Third-party apps now draw from your extra usage";
|
|
48
|
+
export function isAnthropicThirdPartyBillingMessage(errorMessage) {
|
|
49
|
+
return errorMessage?.toLowerCase().includes(ANTHROPIC_THIRD_PARTY_BILLING_MARKER.toLowerCase()) === true;
|
|
50
|
+
}
|
|
41
51
|
/**
|
|
42
52
|
* Account quota, budget, credit, and billing exhaustion: the account cannot
|
|
43
53
|
* serve more requests until the user pays or the quota resets. Shared by the
|
|
@@ -70,6 +80,8 @@ const QUOTA_EXHAUSTION_PATTERNS = [
|
|
|
70
80
|
// stays dead until its quota resets — every same-account retry is guaranteed
|
|
71
81
|
// to fail, so the failure is terminal, not rate-limited.
|
|
72
82
|
...USAGE_LIMIT_EXHAUSTION.markers,
|
|
83
|
+
// Anthropic billing a subscription token as a third-party app (senpi#3259).
|
|
84
|
+
escapeRegExp(ANTHROPIC_THIRD_PARTY_BILLING_MARKER),
|
|
73
85
|
];
|
|
74
86
|
const QUOTA_EXHAUSTION_PATTERN = buildProviderErrorPattern(QUOTA_EXHAUSTION_PATTERNS);
|
|
75
87
|
export function isQuotaExhaustionMessage(errorMessage) {
|
|
@@ -202,12 +202,17 @@ export function normalizeToolParametersForOpenAICompat(schema) {
|
|
|
202
202
|
/**
|
|
203
203
|
* Guarantee the wire shape every OpenAI-compatible backend requires for tool
|
|
204
204
|
* parameters: a root object schema. A root union of object shapes is merged into
|
|
205
|
-
* one object schema
|
|
205
|
+
* one object schema and a root that already declares a `type` is returned as-is.
|
|
206
|
+
* Every other root without a `type` gets `type: "object"`: a `$ref` root, a root
|
|
207
|
+
* `allOf`, and an `anyOf`/`oneOf` union that cannot merge because a branch is not
|
|
208
|
+
* an object shape. Any combiner is kept beside the added type.
|
|
206
209
|
*
|
|
207
|
-
*
|
|
208
|
-
* `
|
|
209
|
-
*
|
|
210
|
-
*
|
|
210
|
+
* DeepSeek and gateways forwarding to it reject a typeless root outright
|
|
211
|
+
* (`schema must be a JSON Schema of 'type: "object"', got 'type: null'`, #3245).
|
|
212
|
+
* The cost: for a union with no object branch the wire schema becomes stricter
|
|
213
|
+
* than the source (`type: "object"` beside `anyOf: [string, number]` is
|
|
214
|
+
* unsatisfiable). That is accepted because tool arguments are always JSON objects
|
|
215
|
+
* and are validated locally against the original, unnormalized schema.
|
|
211
216
|
*/
|
|
212
217
|
function ensureRootObjectSchema(schema) {
|
|
213
218
|
const merged = mergeRootObjectUnion(schema);
|
|
@@ -215,11 +220,6 @@ function ensureRootObjectSchema(schema) {
|
|
|
215
220
|
return merged;
|
|
216
221
|
if (schema.type !== undefined)
|
|
217
222
|
return schema;
|
|
218
|
-
// DeepSeek (and gateways forwarding to it) reject any root parameters schema
|
|
219
|
-
// without `type: "object"` (`got 'type: null'`). When the root union cannot
|
|
220
|
-
// merge, keep the combiner and claim the object type rather than send a
|
|
221
|
-
// typeless root: tool parameters are objects in practice, and the wire
|
|
222
|
-
// shape stays valid for validators that enforce the root type.
|
|
223
223
|
return { ...schema, type: "object" };
|
|
224
224
|
}
|
|
225
225
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi-ai",
|
|
3
|
-
"version": "2026.10.
|
|
3
|
+
"version": "2026.10.15",
|
|
4
4
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"dependencies": {
|
|
89
89
|
"@anthropic-ai/sdk": "0.127.0",
|
|
90
90
|
"@aws-sdk/client-bedrock-runtime": "3.1136.0",
|
|
91
|
-
"@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.10.
|
|
91
|
+
"@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.10.15",
|
|
92
92
|
"@google/genai": "2.23.0",
|
|
93
93
|
"@smithy/node-http-handler": "4.12.1",
|
|
94
94
|
"http-proxy-agent": "9.1.0",
|