@code-yeongyu/senpi-ai 2026.10.13 → 2026.10.16-2

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 (46) hide show
  1. package/dist/api/google-shared.js +3 -0
  2. package/dist/google-thinking-generation.d.ts +5 -0
  3. package/dist/google-thinking-generation.js +29 -0
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +1 -0
  6. package/dist/model-cost.d.ts +4 -0
  7. package/dist/model-cost.js +14 -0
  8. package/dist/models.d.ts +3 -1
  9. package/dist/models.js +11 -4
  10. package/dist/provider-diagnostic.d.ts +2 -2
  11. package/dist/provider-diagnostic.js +8 -1
  12. package/dist/providers/all.js +3 -1
  13. package/dist/providers/anthropic.js +3 -7
  14. package/dist/providers/data/.manifest.json +1 -1
  15. package/dist/providers/data/fireworks.json +1 -1
  16. package/dist/providers/data/github-copilot.json +1 -1
  17. package/dist/providers/data/google-vertex.json +1 -1
  18. package/dist/providers/data/google.json +1 -1
  19. package/dist/providers/data/meta.json +1 -1
  20. package/dist/providers/data/nvidia.json +1 -1
  21. package/dist/providers/data/opencode-go.json +1 -1
  22. package/dist/providers/data/opencode.json +1 -1
  23. package/dist/providers/data/opengateway.json +1 -1
  24. package/dist/providers/data/openrouter.json +1 -1
  25. package/dist/providers/data/radius.json +1 -1
  26. package/dist/providers/data/venice.json +1 -1
  27. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  28. package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
  29. package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
  30. package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
  31. package/dist/providers/data/xiaomi.json +1 -1
  32. package/dist/providers/faux.d.ts +2 -0
  33. package/dist/providers/faux.js +4 -3
  34. package/dist/providers/opencode-go.d.ts +3 -1
  35. package/dist/providers/opencode-go.js +2 -0
  36. package/dist/types.d.ts +9 -1
  37. package/dist/utils/provider-diagnostic-sources.d.ts +1 -0
  38. package/dist/utils/provider-diagnostic-sources.js +41 -3
  39. package/dist/utils/provider-diagnostic-vocabulary.d.ts +6 -0
  40. package/dist/utils/provider-diagnostic-vocabulary.js +26 -0
  41. package/dist/utils/provider-failure-description.js +17 -1
  42. package/dist/utils/retry.d.ts +8 -0
  43. package/dist/utils/retry.js +12 -0
  44. package/dist/utils/tool-choice-fallback.js +4 -1
  45. package/dist/utils/tool-schema-compat.js +10 -10
  46. package/package.json +2 -2
@@ -1 +1 @@
1
- {"openai-completions":{"chat:mimo-v2.5":{"id":"mimo-v2.5","name":"MiMo-V2.5","api":"openai-completions","provider":"xiaomi-token-plan-ams","baseUrl":"https://token-plan-ams.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:mimo-v2.5-pro":{"id":"mimo-v2.5-pro","name":"MiMo-V2.5-Pro","api":"openai-completions","provider":"xiaomi-token-plan-ams","baseUrl":"https://token-plan-ams.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"type":"chat"},"chat:mimo-v2.6-flash":{"id":"mimo-v2.6-flash","name":"MiMo-V2.6-Flash","api":"openai-completions","provider":"xiaomi-token-plan-ams","baseUrl":"https://token-plan-ams.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:mimo-v2.6-pro":{"id":"mimo-v2.6-pro","name":"MiMo-V2.6-Pro","api":"openai-completions","provider":"xiaomi-token-plan-ams","baseUrl":"https://token-plan-ams.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"}}}
1
+ {"openai-completions":{"chat:mimo-v2.6-flash":{"id":"mimo-v2.6-flash","name":"MiMo-V2.6-Flash","api":"openai-completions","provider":"xiaomi-token-plan-ams","baseUrl":"https://token-plan-ams.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:mimo-v2.6-pro":{"id":"mimo-v2.6-pro","name":"MiMo-V2.6-Pro","api":"openai-completions","provider":"xiaomi-token-plan-ams","baseUrl":"https://token-plan-ams.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"}}}
@@ -1 +1 @@
1
- {"openai-completions":{"chat:mimo-v2.5":{"id":"mimo-v2.5","name":"MiMo-V2.5","api":"openai-completions","provider":"xiaomi-token-plan-cn","baseUrl":"https://token-plan-cn.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:mimo-v2.5-pro":{"id":"mimo-v2.5-pro","name":"MiMo-V2.5-Pro","api":"openai-completions","provider":"xiaomi-token-plan-cn","baseUrl":"https://token-plan-cn.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"type":"chat"},"chat:mimo-v2.6-flash":{"id":"mimo-v2.6-flash","name":"MiMo-V2.6-Flash","api":"openai-completions","provider":"xiaomi-token-plan-cn","baseUrl":"https://token-plan-cn.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:mimo-v2.6-pro":{"id":"mimo-v2.6-pro","name":"MiMo-V2.6-Pro","api":"openai-completions","provider":"xiaomi-token-plan-cn","baseUrl":"https://token-plan-cn.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"}}}
1
+ {"openai-completions":{"chat:mimo-v2.6-flash":{"id":"mimo-v2.6-flash","name":"MiMo-V2.6-Flash","api":"openai-completions","provider":"xiaomi-token-plan-cn","baseUrl":"https://token-plan-cn.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:mimo-v2.6-pro":{"id":"mimo-v2.6-pro","name":"MiMo-V2.6-Pro","api":"openai-completions","provider":"xiaomi-token-plan-cn","baseUrl":"https://token-plan-cn.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"}}}
@@ -1 +1 @@
1
- {"openai-completions":{"chat:mimo-v2.5":{"id":"mimo-v2.5","name":"MiMo-V2.5","api":"openai-completions","provider":"xiaomi-token-plan-sgp","baseUrl":"https://token-plan-sgp.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:mimo-v2.5-pro":{"id":"mimo-v2.5-pro","name":"MiMo-V2.5-Pro","api":"openai-completions","provider":"xiaomi-token-plan-sgp","baseUrl":"https://token-plan-sgp.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"type":"chat"},"chat:mimo-v2.6-flash":{"id":"mimo-v2.6-flash","name":"MiMo-V2.6-Flash","api":"openai-completions","provider":"xiaomi-token-plan-sgp","baseUrl":"https://token-plan-sgp.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:mimo-v2.6-pro":{"id":"mimo-v2.6-pro","name":"MiMo-V2.6-Pro","api":"openai-completions","provider":"xiaomi-token-plan-sgp","baseUrl":"https://token-plan-sgp.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"}}}
1
+ {"openai-completions":{"chat:mimo-v2.6-flash":{"id":"mimo-v2.6-flash","name":"MiMo-V2.6-Flash","api":"openai-completions","provider":"xiaomi-token-plan-sgp","baseUrl":"https://token-plan-sgp.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:mimo-v2.6-pro":{"id":"mimo-v2.6-pro","name":"MiMo-V2.6-Pro","api":"openai-completions","provider":"xiaomi-token-plan-sgp","baseUrl":"https://token-plan-sgp.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"}}}
@@ -1 +1 @@
1
- {"openai-completions":{"chat:mimo-v2.5":{"id":"mimo-v2.5","name":"MiMo-V2.5","api":"openai-completions","provider":"xiaomi","baseUrl":"https://api.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0.14,"output":0.28,"cacheRead":0.0028,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:mimo-v2.5-pro":{"id":"mimo-v2.5-pro","name":"MiMo-V2.5-Pro","api":"openai-completions","provider":"xiaomi","baseUrl":"https://api.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text"],"cost":{"input":0.435,"output":0.87,"cacheRead":0.0036,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"type":"chat"},"chat:mimo-v2.5-pro-ultraspeed":{"id":"mimo-v2.5-pro-ultraspeed","name":"MiMo-V2.5-Pro-UltraSpeed","api":"openai-completions","provider":"xiaomi","baseUrl":"https://api.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text"],"cost":{"input":1.305,"output":2.61,"cacheRead":0.0108,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"type":"chat"},"chat:mimo-v2.6-flash":{"id":"mimo-v2.6-flash","name":"MiMo-V2.6-Flash","api":"openai-completions","provider":"xiaomi","baseUrl":"https://api.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0.14,"output":0.28,"cacheRead":0.0028,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:mimo-v2.6-pro":{"id":"mimo-v2.6-pro","name":"MiMo-V2.6-Pro","api":"openai-completions","provider":"xiaomi","baseUrl":"https://api.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0.435,"output":0.87,"cacheRead":0.0036,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:mimo-v2.6-pro-ultraspeed":{"id":"mimo-v2.6-pro-ultraspeed","name":"MiMo-V2.6-Pro-UltraSpeed","api":"openai-completions","provider":"xiaomi","baseUrl":"https://api.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":4.35,"output":8.7,"cacheRead":0.036,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"}}}
1
+ {"openai-completions":{"chat:mimo-v2.6-flash":{"id":"mimo-v2.6-flash","name":"MiMo-V2.6-Flash","api":"openai-completions","provider":"xiaomi","baseUrl":"https://api.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0.14,"output":0.28,"cacheRead":0.0028,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:mimo-v2.6-pro":{"id":"mimo-v2.6-pro","name":"MiMo-V2.6-Pro","api":"openai-completions","provider":"xiaomi","baseUrl":"https://api.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":0.435,"output":0.87,"cacheRead":0.0036,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:mimo-v2.6-pro-ultraspeed":{"id":"mimo-v2.6-pro-ultraspeed","name":"MiMo-V2.6-Pro-UltraSpeed","api":"openai-completions","provider":"xiaomi","baseUrl":"https://api.xiaomimimo.com/v1","compat":{"supportsStrictMode":true,"requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek","supportsDisabledThinking":false},"reasoning":true,"input":["text","image"],"cost":{"input":4.35,"output":8.7,"cacheRead":0.036,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"}}}
@@ -47,6 +47,8 @@ export interface RegisterFauxProviderOptions {
47
47
  pollAfterMs?: number;
48
48
  };
49
49
  tokensPerSecond?: number;
50
+ /** Headers every scripted response reports through `onResponse` (default none). */
51
+ responseHeaders?: Record<string, string>;
50
52
  schedulerHook?: () => void | Promise<void>;
51
53
  tokenSize?: {
52
54
  min?: number;
@@ -334,6 +334,7 @@ async function streamWithDeltas(stream, message, minTokenSize, maxTokenSize, tok
334
334
  export function createFauxCore(options) {
335
335
  const api = options.api ?? randomId(DEFAULT_API);
336
336
  const provider = options.provider ?? DEFAULT_PROVIDER;
337
+ const responseHeaders = options.responseHeaders ?? {};
337
338
  const minTokenSize = Math.max(1, Math.min(options.tokenSize?.min ?? DEFAULT_MIN_TOKEN_SIZE, options.tokenSize?.max ?? DEFAULT_MAX_TOKEN_SIZE));
338
339
  const maxTokenSize = Math.max(minTokenSize, options.tokenSize?.max ?? DEFAULT_MAX_TOKEN_SIZE);
339
340
  let pendingResponses = [];
@@ -385,7 +386,7 @@ export function createFauxCore(options) {
385
386
  });
386
387
  queueMicrotask(async () => {
387
388
  try {
388
- await streamOptions?.onResponse?.({ status: 200, headers: {} }, requestModel);
389
+ await streamOptions?.onResponse?.({ status: 200, headers: { ...responseHeaders } }, requestModel);
389
390
  if (!step) {
390
391
  let message = createErrorMessage(new Error("No more faux responses queued"), api, provider, requestModel.id);
391
392
  message = withUsageEstimate(message, context, streamOptions, promptCache);
@@ -430,7 +431,7 @@ export function createFauxCore(options) {
430
431
  state.deferredFetchCount++;
431
432
  queueMicrotask(async () => {
432
433
  try {
433
- await fetchOptions?.onResponse?.({ status: 200, headers: {} }, requestModel);
434
+ await fetchOptions?.onResponse?.({ status: 200, headers: { ...responseHeaders } }, requestModel);
434
435
  const entry = deferredResponses.get(handle.id);
435
436
  if (!entry ||
436
437
  entry.handle.provider !== handle.provider ||
@@ -469,7 +470,7 @@ export function createFauxCore(options) {
469
470
  const entry = deferredResponses.get(handle.id);
470
471
  if (entry)
471
472
  entry.cancelled = true;
472
- await cancelOptions?.onResponse?.({ status: 200, headers: {} }, requestModel);
473
+ await cancelOptions?.onResponse?.({ status: 200, headers: { ...responseHeaders } }, requestModel);
473
474
  };
474
475
  function getModel(requestedModelId) {
475
476
  if (!requestedModelId) {
@@ -1,3 +1,5 @@
1
1
  import { type Provider } from "../models.ts";
2
- export declare function opencodeGoProvider(): Provider<"anthropic-messages" | "openai-completions" | "openai-responses">;
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
  },
package/dist/types.d.ts CHANGED
@@ -1108,10 +1108,18 @@ export interface ModelCostTier extends ModelCostRates {
1108
1108
  /** Use this tier for requests whose total input usage exceeds this token count. */
1109
1109
  inputTokensAbove: number;
1110
1110
  }
1111
- export interface ModelCost extends ModelCostRates {
1111
+ export interface ModelCostCard extends ModelCostRates {
1112
1112
  /** Request-wide pricing tiers. The highest matching input threshold applies to the full request. */
1113
1113
  tiers?: ModelCostTier[];
1114
1114
  }
1115
+ export interface ModelCostEffectiveRate extends ModelCostCard {
1116
+ /** ISO 8601 UTC instant from which this card replaces the base card and every earlier dated card. */
1117
+ effectiveFrom: string;
1118
+ }
1119
+ export interface ModelCost extends ModelCostCard {
1120
+ /** Scheduled list-price changes, each billed for requests at or after its `effectiveFrom`. */
1121
+ effectiveRates?: ModelCostEffectiveRate[];
1122
+ }
1115
1123
  export interface ModelImageResizeOptions {
1116
1124
  maxWidth?: number;
1117
1125
  maxHeight?: number;
@@ -14,6 +14,7 @@ export declare function anthropicProviderDiagnosticFromSseData(data: string): Pr
14
14
  * servers reuse it for coarse buckets that can contradict the precise code.
15
15
  */
16
16
  export declare function openAICompatibleProviderDiagnosticFromError(error: unknown): ProviderDiagnostic | undefined;
17
+ export declare function transportProviderDiagnosticFromError(error: unknown): ProviderDiagnostic | undefined;
17
18
  /**
18
19
  * Await one provider transport call and, if it rejects, attach the classifier's
19
20
  * verdict to the rejection. Only errors raised by the wrapped call are
@@ -1,5 +1,5 @@
1
1
  import { attachProviderDiagnostic } from "./provider-diagnostic-carrier.js";
2
- import { classifyProviderFailure } from "./provider-diagnostic-vocabulary.js";
2
+ import { buildProviderDiagnostic, categoryForTransportCode, classifyProviderFailure, } from "./provider-diagnostic-vocabulary.js";
3
3
  /** Largest SSE error envelope parsed for classification at all. */
4
4
  const SSE_ERROR_ENVELOPE_MAX_BYTES = 8_192;
5
5
  function isRecord(value) {
@@ -74,6 +74,44 @@ export function openAICompatibleProviderDiagnosticFromError(error) {
74
74
  return undefined;
75
75
  }
76
76
  }
77
+ /**
78
+ * A connection failure carries no HTTP status; it proves only that the request got
79
+ * no provider answer. Reads the error's own `code`, then `cause.code` (fetch
80
+ * wraps the socket error), against a closed allowlist. Messages are never read.
81
+ */
82
+ /** How many `cause` links a transport code is looked for through (the error itself counts as one). */
83
+ const TRANSPORT_CAUSE_DEPTH = 4;
84
+ export function transportProviderDiagnosticFromError(error) {
85
+ try {
86
+ if (!(error instanceof Error))
87
+ return undefined;
88
+ // SDK errors wrap the runtime's error: Bun puts the code one cause down, Node's fetch two
89
+ // (SDK error -> TypeError "fetch failed" -> { code }). Walk a bounded chain, reading only `code`;
90
+ // the SDK's own connection error class name is the fallback when no link carries a code.
91
+ let current = error;
92
+ let sdkConnectionError;
93
+ for (let depth = 0; depth < TRANSPORT_CAUSE_DEPTH && (isRecord(current) || current instanceof Error); depth++) {
94
+ const code = Reflect.get(current, "code");
95
+ if (typeof code === "string" && categoryForTransportCode(code) !== undefined)
96
+ return buildProviderDiagnostic({ category: "network", code, evidence: "structured_transport" });
97
+ // The SDKs keep `name` as "Error"; their class is what says connection error.
98
+ for (const name of current instanceof Error ? [current.name, current.constructor.name] : []) {
99
+ if (sdkConnectionError === undefined && categoryForTransportCode(name) !== undefined)
100
+ sdkConnectionError = name;
101
+ }
102
+ current = Reflect.get(current, "cause");
103
+ }
104
+ return sdkConnectionError === undefined
105
+ ? undefined
106
+ : buildProviderDiagnostic({ category: "network", code: sdkConnectionError, evidence: "structured_transport" });
107
+ }
108
+ catch {
109
+ return undefined;
110
+ }
111
+ }
112
+ function classifyWithTransport(classify, error) {
113
+ return classify(error) ?? transportProviderDiagnosticFromError(error);
114
+ }
77
115
  /**
78
116
  * Await one provider transport call and, if it rejects, attach the classifier's
79
117
  * verdict to the rejection. Only errors raised by the wrapped call are
@@ -85,7 +123,7 @@ export async function awaitProviderTransport(request, classify) {
85
123
  }
86
124
  catch (error) {
87
125
  if (typeof error === "object" && error !== null)
88
- attachProviderDiagnostic(error, classify(error));
126
+ attachProviderDiagnostic(error, classifyWithTransport(classify, error));
89
127
  throw error;
90
128
  }
91
129
  }
@@ -96,7 +134,7 @@ export async function* iterateProviderTransport(source, classify) {
96
134
  }
97
135
  catch (error) {
98
136
  if (typeof error === "object" && error !== null)
99
- attachProviderDiagnostic(error, classify(error));
137
+ attachProviderDiagnostic(error, classifyWithTransport(classify, error));
100
138
  throw error;
101
139
  }
102
140
  }
@@ -8,6 +8,12 @@ export declare const PROVIDER_DIAGNOSTIC_MAX_BYTES = 512;
8
8
  * exhaustion, so it stays `unknown`.
9
9
  */
10
10
  export declare function categoryForProviderCode(code: string): ProviderDiagnosticCategory | undefined;
11
+ /**
12
+ * Closed allowlist of transport failures: Node `errno` / undici `code` names, and the provider SDKs'
13
+ * connection error class names (`APIConnectionError`, `APIConnectionTimeoutError`). Read only from the
14
+ * error chain's `code` and `name` fields, never from message text.
15
+ */
16
+ export declare function categoryForTransportCode(code: string): ProviderDiagnosticCategory | undefined;
11
17
  /** The HTTP status a recognized token may arrive with; anything else is contradictory evidence. */
12
18
  export declare function isProviderCodeStatusCompatible(code: string, status: number): boolean;
13
19
  /** Status-only family. 402/403/429 alone cannot tell quota, auth and rate limit apart. */
@@ -32,6 +32,31 @@ export function categoryForProviderCode(code) {
32
32
  return undefined;
33
33
  }
34
34
  }
35
+ /**
36
+ * Closed allowlist of transport failures: Node `errno` / undici `code` names, and the provider SDKs'
37
+ * connection error class names (`APIConnectionError`, `APIConnectionTimeoutError`). Read only from the
38
+ * error chain's `code` and `name` fields, never from message text.
39
+ */
40
+ export function categoryForTransportCode(code) {
41
+ switch (code) {
42
+ case "ECONNRESET":
43
+ case "ECONNREFUSED":
44
+ case "ETIMEDOUT":
45
+ case "ENOTFOUND":
46
+ case "EAI_AGAIN":
47
+ case "EPIPE":
48
+ case "UND_ERR_SOCKET":
49
+ case "UND_ERR_CONNECT_TIMEOUT":
50
+ case "UND_ERR_HEADERS_TIMEOUT":
51
+ case "UND_ERR_BODY_TIMEOUT":
52
+ case "UND_ERR_CLOSED":
53
+ case "APIConnectionError":
54
+ case "APIConnectionTimeoutError":
55
+ return "network";
56
+ default:
57
+ return undefined;
58
+ }
59
+ }
35
60
  /** The HTTP status a recognized token may arrive with; anything else is contradictory evidence. */
36
61
  export function isProviderCodeStatusCompatible(code, status) {
37
62
  switch (code) {
@@ -80,6 +105,7 @@ export function isProviderDiagnosticCategory(value) {
80
105
  case "context_limit":
81
106
  case "invalid_request":
82
107
  case "provider_unavailable":
108
+ case "network":
83
109
  case "unknown":
84
110
  return true;
85
111
  default:
@@ -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
@@ -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)`).
@@ -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) {
@@ -104,7 +104,10 @@ export function isForcedToolChoiceUnsupportedError(error, sentForcedToolChoice)
104
104
  const message = errorMessage(error);
105
105
  return (/tool[_\s-]?choices?\b.*?(not\s+(?:currently\s+)?compatible|incompatible|not\s+(?:currently\s+)?supported|unsupported)/is.test(message) ||
106
106
  /forces?\s+tool\s+use.*?(not\s+(?:currently\s+)?compatible|incompatible|not\s+(?:currently\s+)?supported|unsupported)/is.test(message) ||
107
- /does\s+not\s+support\s+forced\s+tool[_\s-]?choices?/is.test(message) ||
107
+ // "does not support forced tool_choice", and opencode-go's thinking models (senpi#3324): "Thinking
108
+ // mode does not support this tool_choice". Only these determiners, so a refusal of some other
109
+ // parameter that merely sits next to tool_choice never matches.
110
+ /does\s+not\s+support\s+(?:forced|this|the)\s+tool[_\s-]?choices?/is.test(message) ||
108
111
  // Auto-only tool-choice upstreams. OmniRoute serving opencode-go/muse-spark-1.3-contributor
109
112
  // (2026-09-27): "only `\"auto\"` is supported for `tool_choice`. `\"none\"`, `\"required\"`, and named
110
113
  // function choices are not currently supported". Kiro behind an OpenAI-compatible proxy
@@ -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; a root that merely lost its `type` gets it restored.
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
- * A root whose branches are not object shapes is left alone: forcing
208
- * `type: "object"` onto a scalar union would assert something the schema
209
- * contradicts, which is worse than the missing keyword. Tool parameters are
210
- * objects in practice, so this only guards against corrupting an exotic schema.
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.13",
3
+ "version": "2026.10.16-2",
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.13",
91
+ "@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.10.16-2",
92
92
  "@google/genai": "2.23.0",
93
93
  "@smithy/node-http-handler": "4.12.1",
94
94
  "http-proxy-agent": "9.1.0",