@code-yeongyu/senpi-ai 2026.9.30 → 2026.10.1-3

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 (274) hide show
  1. package/README.md +189 -19
  2. package/dist/api/anthropic-messages.js +168 -32
  3. package/dist/api/azure-openai-responses.js +23 -10
  4. package/dist/api/bedrock-converse-stream.js +12 -4
  5. package/dist/api/cloudflare-workers-ai-system-one.d.ts +4 -0
  6. package/dist/api/cloudflare-workers-ai-system-one.js +43 -0
  7. package/dist/api/cloudflare-workers-ai-system-one.lazy.d.ts +3 -0
  8. package/dist/api/cloudflare-workers-ai-system-one.lazy.js +4 -0
  9. package/dist/api/cloudflare.d.ts +2 -0
  10. package/dist/api/cloudflare.js +2 -0
  11. package/dist/api/context-room.d.ts +2 -2
  12. package/dist/api/cursor-agent.js +19 -10
  13. package/dist/api/devin-agent/request.d.ts +9 -9
  14. package/dist/api/devin-agent/request.js +14 -10
  15. package/dist/api/google-generative-ai.js +20 -80
  16. package/dist/api/google-shared.d.ts +13 -4
  17. package/dist/api/google-shared.js +54 -4
  18. package/dist/api/google-vertex.js +19 -62
  19. package/dist/api/llama-cpp-classify.d.ts +33 -0
  20. package/dist/api/llama-cpp-classify.js +365 -0
  21. package/dist/api/llama-cpp-classify.lazy.d.ts +3 -0
  22. package/dist/api/llama-cpp-classify.lazy.js +4 -0
  23. package/dist/api/mistral-conversations.d.ts +1 -1
  24. package/dist/api/mistral-conversations.js +36 -29
  25. package/dist/api/openai-codex-responses.d.ts +1 -1
  26. package/dist/api/openai-codex-responses.js +72 -37
  27. package/dist/api/openai-completions.d.ts +3 -2
  28. package/dist/api/openai-completions.js +76 -50
  29. package/dist/api/openai-images-params.d.ts +2 -2
  30. package/dist/api/openai-images.d.ts +1 -1
  31. package/dist/api/openai-responses-shared.d.ts +31 -8
  32. package/dist/api/openai-responses-shared.js +115 -27
  33. package/dist/api/openai-responses.d.ts +1 -1
  34. package/dist/api/openai-responses.js +38 -33
  35. package/dist/api/openrouter-images.d.ts +2 -1
  36. package/dist/api/openrouter-images.js +1 -0
  37. package/dist/api/pi-messages.d.ts +3 -3
  38. package/dist/api/pi-messages.js +3 -2
  39. package/dist/api/simple-options.d.ts +2 -2
  40. package/dist/api/simple-options.js +1 -0
  41. package/dist/api/system-one-shared.d.ts +23 -0
  42. package/dist/api/system-one-shared.js +183 -0
  43. package/dist/api/transform-messages.js +5 -2
  44. package/dist/api/typesafe-system-one.d.ts +4 -0
  45. package/dist/api/typesafe-system-one.js +19 -0
  46. package/dist/api/typesafe-system-one.lazy.d.ts +3 -0
  47. package/dist/api/typesafe-system-one.lazy.js +4 -0
  48. package/dist/api-registry.d.ts +3 -3
  49. package/dist/auth/helpers.js +1 -1
  50. package/dist/auth/oauth/anthropic-callback-listener.js +1 -1
  51. package/dist/auth/oauth/callback-server.d.ts +55 -0
  52. package/dist/auth/oauth/callback-server.js +146 -0
  53. package/dist/auth/oauth/chatgpt-subscription.d.ts +1 -1
  54. package/dist/auth/oauth/chatgpt-subscription.js +20 -124
  55. package/dist/auth/oauth/devin-callback.js +1 -1
  56. package/dist/auth/oauth/load.d.ts +4 -0
  57. package/dist/auth/oauth/load.js +10 -0
  58. package/dist/auth/oauth/meta.d.ts +17 -0
  59. package/dist/auth/oauth/meta.js +190 -0
  60. package/dist/auth/oauth/openai-chatgpt.d.ts +9 -0
  61. package/dist/auth/oauth/openai-chatgpt.js +266 -0
  62. package/dist/auth/oauth/openrouter.d.ts +1 -1
  63. package/dist/auth/oauth/openrouter.js +19 -138
  64. package/dist/auth/oauth/radius.d.ts +1 -1
  65. package/dist/auth/oauth/radius.js +21 -89
  66. package/dist/auth/resolve.d.ts +3 -8
  67. package/dist/auth/resolve.js +3 -18
  68. package/dist/auth/types.d.ts +10 -1
  69. package/dist/bun-oauth.js +4 -0
  70. package/dist/cli.js +3 -1
  71. package/dist/compat.js +17 -14
  72. package/dist/env-api-keys.js +2 -0
  73. package/dist/image-models.d.ts +19 -8
  74. package/dist/image-models.js +14 -13
  75. package/dist/images-api-registry.d.ts +8 -8
  76. package/dist/images.d.ts +7 -2
  77. package/dist/images.js +5 -0
  78. package/dist/index.d.ts +2 -2
  79. package/dist/index.js +2 -2
  80. package/dist/model-catalog.d.ts +27 -9
  81. package/dist/model-catalog.js +24 -2
  82. package/dist/model.d.ts +13 -13
  83. package/dist/models-store.d.ts +3 -2
  84. package/dist/models.d.ts +106 -36
  85. package/dist/models.generated.d.ts +142 -42
  86. package/dist/models.generated.js +142 -42
  87. package/dist/models.js +174 -36
  88. package/dist/providers/alibaba-token-plan.models.d.ts +4 -2
  89. package/dist/providers/alibaba-token-plan.models.js +4 -2
  90. package/dist/providers/all.d.ts +72 -19
  91. package/dist/providers/all.js +28 -22
  92. package/dist/providers/amazon-bedrock.models.d.ts +4 -2
  93. package/dist/providers/amazon-bedrock.models.js +4 -2
  94. package/dist/providers/ant-ling.models.d.ts +4 -2
  95. package/dist/providers/ant-ling.models.js +4 -2
  96. package/dist/providers/anthropic.models.d.ts +4 -2
  97. package/dist/providers/anthropic.models.js +4 -2
  98. package/dist/providers/azure-openai-responses.models.d.ts +4 -2
  99. package/dist/providers/azure-openai-responses.models.js +4 -2
  100. package/dist/providers/bai.models.d.ts +4 -2
  101. package/dist/providers/bai.models.js +4 -2
  102. package/dist/providers/baseten.models.d.ts +4 -2
  103. package/dist/providers/baseten.models.js +4 -2
  104. package/dist/providers/cerebras.models.d.ts +4 -2
  105. package/dist/providers/cerebras.models.js +4 -2
  106. package/dist/providers/chatgpt-subscription.models.d.ts +4 -2
  107. package/dist/providers/chatgpt-subscription.models.js +4 -2
  108. package/dist/providers/cloudflare-ai-gateway.models.d.ts +4 -2
  109. package/dist/providers/cloudflare-ai-gateway.models.js +4 -2
  110. package/dist/providers/cloudflare-stream.d.ts +6 -2
  111. package/dist/providers/cloudflare-stream.js +6 -0
  112. package/dist/providers/cloudflare-workers-ai.js +10 -3
  113. package/dist/providers/cloudflare-workers-ai.models.d.ts +4 -2
  114. package/dist/providers/cloudflare-workers-ai.models.js +4 -2
  115. package/dist/providers/data/.manifest.json +1 -1
  116. package/dist/providers/data/alibaba-token-plan.json +1 -1
  117. package/dist/providers/data/amazon-bedrock.json +1 -1
  118. package/dist/providers/data/ant-ling.json +1 -1
  119. package/dist/providers/data/anthropic.json +1 -1
  120. package/dist/providers/data/azure-openai-responses.json +1 -1
  121. package/dist/providers/data/bai.json +1 -1
  122. package/dist/providers/data/baseten.json +1 -1
  123. package/dist/providers/data/cerebras.json +1 -1
  124. package/dist/providers/data/chatgpt-subscription.json +1 -1
  125. package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  126. package/dist/providers/data/cloudflare-workers-ai.json +1 -1
  127. package/dist/providers/data/deepseek.json +1 -1
  128. package/dist/providers/data/fireworks.json +1 -1
  129. package/dist/providers/data/github-copilot.json +1 -1
  130. package/dist/providers/data/google-vertex.json +1 -1
  131. package/dist/providers/data/google.json +1 -1
  132. package/dist/providers/data/groq.json +1 -1
  133. package/dist/providers/data/huggingface.json +1 -1
  134. package/dist/providers/data/meta.json +1 -0
  135. package/dist/providers/data/minimax-cn.json +1 -1
  136. package/dist/providers/data/minimax.json +1 -1
  137. package/dist/providers/data/mistral.json +1 -1
  138. package/dist/providers/data/moonshotai-cn.json +1 -1
  139. package/dist/providers/data/moonshotai.json +1 -1
  140. package/dist/providers/data/nvidia.json +1 -1
  141. package/dist/providers/data/openai.json +1 -1
  142. package/dist/providers/data/opencode-go.json +1 -1
  143. package/dist/providers/data/opencode.json +1 -1
  144. package/dist/providers/data/opengateway.json +1 -1
  145. package/dist/providers/data/openrouter.json +1 -1
  146. package/dist/providers/data/qwen-token-plan-cn.json +1 -1
  147. package/dist/providers/data/qwen-token-plan-individual.json +1 -1
  148. package/dist/providers/data/qwen-token-plan.json +1 -1
  149. package/dist/providers/data/radius.json +1 -0
  150. package/dist/providers/data/together.json +1 -1
  151. package/dist/providers/data/typesafe.json +1 -0
  152. package/dist/providers/data/venice.json +1 -1
  153. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  154. package/dist/providers/data/xai.json +1 -1
  155. package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
  156. package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
  157. package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
  158. package/dist/providers/data/xiaomi.json +1 -1
  159. package/dist/providers/data/zai-coding-cn.json +1 -1
  160. package/dist/providers/data/zai.json +1 -1
  161. package/dist/providers/deepseek.models.d.ts +4 -2
  162. package/dist/providers/deepseek.models.js +4 -2
  163. package/dist/providers/faux.d.ts +7 -2
  164. package/dist/providers/faux.js +31 -22
  165. package/dist/providers/fireworks.models.d.ts +4 -2
  166. package/dist/providers/fireworks.models.js +4 -2
  167. package/dist/providers/github-copilot.models.d.ts +4 -2
  168. package/dist/providers/github-copilot.models.js +4 -2
  169. package/dist/providers/google-vertex.models.d.ts +4 -2
  170. package/dist/providers/google-vertex.models.js +4 -2
  171. package/dist/providers/google.models.d.ts +4 -2
  172. package/dist/providers/google.models.js +4 -2
  173. package/dist/providers/groq.models.d.ts +4 -2
  174. package/dist/providers/groq.models.js +4 -2
  175. package/dist/providers/huggingface.models.d.ts +4 -2
  176. package/dist/providers/huggingface.models.js +4 -2
  177. package/dist/providers/images/register-builtins.d.ts +2 -2
  178. package/dist/providers/kimi-coding.models.d.ts +54 -7
  179. package/dist/providers/kimi-coding.models.js +34 -7
  180. package/dist/providers/meta.d.ts +3 -0
  181. package/dist/providers/meta.js +24 -0
  182. package/dist/providers/meta.models.d.ts +6 -0
  183. package/dist/providers/meta.models.js +8 -0
  184. package/dist/providers/minimax-cn.models.d.ts +4 -2
  185. package/dist/providers/minimax-cn.models.js +4 -2
  186. package/dist/providers/minimax.models.d.ts +4 -2
  187. package/dist/providers/minimax.models.js +4 -2
  188. package/dist/providers/mistral.models.d.ts +4 -2
  189. package/dist/providers/mistral.models.js +4 -2
  190. package/dist/providers/moonshotai-cn.models.d.ts +4 -2
  191. package/dist/providers/moonshotai-cn.models.js +4 -2
  192. package/dist/providers/moonshotai.models.d.ts +4 -2
  193. package/dist/providers/moonshotai.models.js +4 -2
  194. package/dist/providers/nvidia.models.d.ts +4 -2
  195. package/dist/providers/nvidia.models.js +4 -2
  196. package/dist/providers/openai.js +4 -2
  197. package/dist/providers/openai.models.d.ts +4 -2
  198. package/dist/providers/openai.models.js +4 -2
  199. package/dist/providers/opencode-go.models.d.ts +4 -2
  200. package/dist/providers/opencode-go.models.js +4 -2
  201. package/dist/providers/opencode.d.ts +3 -1
  202. package/dist/providers/opencode.js +5 -2
  203. package/dist/providers/opencode.models.d.ts +4 -2
  204. package/dist/providers/opencode.models.js +4 -2
  205. package/dist/providers/opengateway.models.d.ts +4 -2
  206. package/dist/providers/opengateway.models.js +4 -2
  207. package/dist/providers/openrouter.js +11 -2
  208. package/dist/providers/openrouter.models.d.ts +4 -2
  209. package/dist/providers/openrouter.models.js +4 -2
  210. package/dist/providers/qwen-token-plan-cn.models.d.ts +4 -2
  211. package/dist/providers/qwen-token-plan-cn.models.js +4 -2
  212. package/dist/providers/qwen-token-plan-individual.models.d.ts +4 -2
  213. package/dist/providers/qwen-token-plan-individual.models.js +4 -2
  214. package/dist/providers/qwen-token-plan.models.d.ts +4 -2
  215. package/dist/providers/qwen-token-plan.models.js +4 -2
  216. package/dist/providers/radius.js +19 -5
  217. package/dist/providers/radius.models.d.ts +6 -0
  218. package/dist/providers/radius.models.js +8 -0
  219. package/dist/providers/together.models.d.ts +4 -2
  220. package/dist/providers/together.models.js +4 -2
  221. package/dist/providers/typesafe.d.ts +3 -0
  222. package/dist/providers/typesafe.js +18 -0
  223. package/dist/providers/typesafe.models.d.ts +6 -0
  224. package/dist/providers/typesafe.models.js +8 -0
  225. package/dist/providers/venice.models.d.ts +4 -2
  226. package/dist/providers/venice.models.js +4 -2
  227. package/dist/providers/vercel-ai-gateway.js +5 -2
  228. package/dist/providers/vercel-ai-gateway.models.d.ts +4 -2
  229. package/dist/providers/vercel-ai-gateway.models.js +4 -2
  230. package/dist/providers/xai.models.d.ts +4 -2
  231. package/dist/providers/xai.models.js +4 -2
  232. package/dist/providers/xiaomi-token-plan-ams.models.d.ts +4 -2
  233. package/dist/providers/xiaomi-token-plan-ams.models.js +4 -2
  234. package/dist/providers/xiaomi-token-plan-cn.models.d.ts +4 -2
  235. package/dist/providers/xiaomi-token-plan-cn.models.js +4 -2
  236. package/dist/providers/xiaomi-token-plan-sgp.models.d.ts +4 -2
  237. package/dist/providers/xiaomi-token-plan-sgp.models.js +4 -2
  238. package/dist/providers/xiaomi.models.d.ts +4 -2
  239. package/dist/providers/xiaomi.models.js +4 -2
  240. package/dist/providers/zai-coding-cn.models.d.ts +4 -2
  241. package/dist/providers/zai-coding-cn.models.js +4 -2
  242. package/dist/providers/zai.models.d.ts +4 -2
  243. package/dist/providers/zai.models.js +4 -2
  244. package/dist/tool-call-middleware/context-transformer.d.ts +8 -5
  245. package/dist/tool-call-middleware/context-transformer.js +44 -15
  246. package/dist/types.d.ts +262 -28
  247. package/dist/utils/diagnostics.d.ts +3 -2
  248. package/dist/utils/estimate.d.ts +2 -2
  249. package/dist/utils/estimate.js +22 -30
  250. package/dist/utils/headers.d.ts +1 -1
  251. package/dist/utils/headers.js +10 -8
  252. package/dist/utils/model-operations.d.ts +11 -0
  253. package/dist/utils/model-operations.js +47 -0
  254. package/dist/utils/models-error.d.ts +8 -0
  255. package/dist/utils/models-error.js +18 -0
  256. package/dist/utils/overflow.d.ts +1 -0
  257. package/dist/utils/overflow.js +11 -5
  258. package/dist/utils/prompt-cache-ttl.js +10 -3
  259. package/dist/utils/retry.js +9 -0
  260. package/dist/utils/text.d.ts +9 -1
  261. package/dist/utils/text.js +26 -0
  262. package/dist/utils/transcript.d.ts +85 -0
  263. package/dist/utils/transcript.js +205 -0
  264. package/package.json +3 -4
  265. package/dist/image-models.generated.d.ts +0 -925
  266. package/dist/image-models.generated.js +0 -927
  267. package/dist/images-models.d.ts +0 -95
  268. package/dist/images-models.js +0 -141
  269. package/dist/providers/openai-images.d.ts +0 -3
  270. package/dist/providers/openai-images.js +0 -16
  271. package/dist/providers/openrouter-images.d.ts +0 -3
  272. package/dist/providers/openrouter-images.js +0 -22
  273. /package/dist/{auth/oauth → utils}/oauth-page.d.ts +0 -0
  274. /package/dist/{auth/oauth → utils}/oauth-page.js +0 -0
@@ -1,7 +1,6 @@
1
1
  import OpenAI from "openai";
2
2
  import { calculateCost, clampThinkingLevel, inferOpenAIThinkingLevelMap, supportsMax, supportsXhigh, } from "../models.js";
3
3
  import { readProviderDiagnostic } from "../provider-diagnostic.js";
4
- import { splitDeferredTools } from "../utils/deferred-tools.js";
5
4
  import { formatProviderError, normalizeProviderError } from "../utils/error-body.js";
6
5
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
7
6
  import { formatGitHubCopilotToolLimitError, limitGitHubCopilotTools, recordGitHubCopilotToolLimit, } from "../utils/github-copilot-tool-limit.js";
@@ -11,6 +10,7 @@ import { awaitProviderTransport, openAICompatibleProviderDiagnosticFromError, }
11
10
  import { getProviderEnvValue } from "../utils/provider-env.js";
12
11
  import { retryProviderRequest } from "../utils/provider-retry.js";
13
12
  import { sendWithForcedToolChoiceFallback } from "../utils/tool-choice-fallback.js";
13
+ import { getCurrentTools, getDeclaredTools, normalizeContext, resolveTranscript } from "../utils/transcript.js";
14
14
  import { isCloudflareProvider, resolveCloudflareBaseUrl } from "./cloudflare.js";
15
15
  import { createGrammarToolInputProperties } from "./constrained-sampling.js";
16
16
  import { withGitHubCopilotFailureNote } from "./github-copilot-errors.js";
@@ -18,7 +18,7 @@ import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "./github-copi
18
18
  import { resolveOpenAIClientAuth } from "./openai-client-auth.js";
19
19
  import { clampOpenAIPromptCacheKey } from "./openai-prompt-cache.js";
20
20
  import { findPromptCacheComparisonResponseId, withPromptCacheComparison, } from "./openai-responses-prompt-cache.js";
21
- import { convertResponsesMessages, convertResponsesTools, processResponsesStream } from "./openai-responses-shared.js";
21
+ import { convertResponsesMessages, convertResponsesTools, processResponsesStream, resolveResponsesDeferredToolsMode, resolveResponsesToolPlacement, } from "./openai-responses-shared.js";
22
22
  import { buildBaseOptions, clampMaxForOpenAI, OPENAI_RESPONSES_RESERVED_BODY_KEYS } from "./simple-options.js";
23
23
  import { startWebSocketLiveness } from "./websocket-liveness.js";
24
24
  import { createWebSocketTransportFailure } from "./websocket-transport-failure.js";
@@ -29,6 +29,7 @@ const SESSION_WEBSOCKET_CACHE_TTL_MS = 5 * 60 * 1000;
29
29
  // OpenAI Responses rejects max_output_tokens below 16: https://github.com/earendil-works/pi/issues/6265
30
30
  const OPENAI_RESPONSES_MIN_OUTPUT_TOKENS = 16;
31
31
  const PROMPT_CACHE_PREWARM_TIMEOUT_MS = 30_000;
32
+ const CHATGPT_USAGE_URL = "https://chatgpt.com/settings/usage";
32
33
  const websocketSessionCache = new Map();
33
34
  /** True when tool_choice forces a specific tool or mode (anything but "auto"/"none"). */
34
35
  function isForcedOpenAIResponsesToolChoice(toolChoice) {
@@ -54,6 +55,7 @@ function getCompat(model, env) {
54
55
  const isNativeEndpoint = isOpenAIResponsesNativeEndpoint(model, env);
55
56
  return {
56
57
  supportsDeveloperRole: model.compat?.supportsDeveloperRole ?? true,
58
+ supportsMidConvoSystemMessages: model.compat?.supportsMidConvoSystemMessages ?? false,
57
59
  sessionAffinityFormat: model.compat?.sessionAffinityFormat ?? detectSessionAffinityFormat(model),
58
60
  supportsLongCacheRetention: model.compat?.supportsLongCacheRetention ?? true,
59
61
  supportsWebSocket: model.compat?.supportsWebSocket ?? isNativeEndpoint,
@@ -146,11 +148,6 @@ function allowedReference(tool) {
146
148
  return { type: "mcp", server_label: tool.server_label };
147
149
  return { type: tool.type };
148
150
  }
149
- function resolveDeferredToolsMode(compat) {
150
- if (compat.supportsAdditionalTools)
151
- return "additional-tools";
152
- return compat.supportsToolSearch ? "tool-search" : undefined;
153
- }
154
151
  /**
155
152
  * senpi#2095: `tools` carries every tool declared this session, so removing a tool never rewrites the
156
153
  * cached prefix; the callable subset rides `tool_choice: allowed_tools` instead. Hosted tools a payload
@@ -158,13 +155,12 @@ function resolveDeferredToolsMode(compat) {
158
155
  * referenced by name. A declared tool a payload hook removed from `tools` is not referenced (senpi#2234).
159
156
  * An empty subset forbids tool calls. An explicit `tool_choice` always wins.
160
157
  */
161
- function applyAllowedToolsChoice(params, context, compat) {
162
- const activeToolNames = context.activeToolNames;
158
+ function applyAllowedToolsChoice(params, context, activeToolNames, compat) {
163
159
  if (!compat.supportsAllowedTools || activeToolNames === undefined || params.tool_choice !== undefined) {
164
160
  return params;
165
161
  }
166
162
  const active = new Set(activeToolNames);
167
- const declaredTools = context.tools ?? [];
163
+ const declaredTools = getCurrentTools(context.messages);
168
164
  if (declaredTools.every((tool) => active.has(tool.name)))
169
165
  return params;
170
166
  const allowed = [];
@@ -177,9 +173,10 @@ function applyAllowedToolsChoice(params, context, compat) {
177
173
  }
178
174
  allowed.push(allowedReference(tool));
179
175
  }
180
- const deferredTools = splitDeferredTools(context, resolveDeferredToolsMode(compat) !== undefined).deferred;
176
+ // Tools missing from the request-level `tools` placement are the ones transcript items load in place.
177
+ const requestToolNames = new Set(resolveResponsesToolPlacement(context.messages, resolveResponsesDeferredToolsMode(compat) !== undefined).requestTools.map((tool) => tool.name));
181
178
  for (const tool of declaredTools) {
182
- if (namedInTools.has(tool.name) || !active.has(tool.name) || !deferredTools.has(tool.name))
179
+ if (namedInTools.has(tool.name) || !active.has(tool.name) || requestToolNames.has(tool.name))
183
180
  continue;
184
181
  const [converted] = convertResponsesTools([tool], {
185
182
  supportsStrictMode: compat.supportsStrictMode,
@@ -191,14 +188,19 @@ function applyAllowedToolsChoice(params, context, compat) {
191
188
  const toolChoice = allowed.length > 0 ? { type: "allowed_tools", mode: "auto", tools: allowed } : "none";
192
189
  return { ...params, tool_choice: toolChoice };
193
190
  }
194
- function formatOpenAIResponsesError(error) {
195
- return formatProviderError(normalizeProviderError(error), "OpenAI API error");
191
+ function formatOpenAIResponsesError(error, provider) {
192
+ const errorMessage = formatProviderError(normalizeProviderError(error), `${provider === "openai" ? "OpenAI" : provider} API error`);
193
+ // Sign in with ChatGPT shares the subscription's usage limit with other apps.
194
+ return errorMessage.includes("subscription_sharing_usage_limit_exceeded")
195
+ ? `${errorMessage}\nCheck your ChatGPT usage: ${CHATGPT_USAGE_URL}`
196
+ : errorMessage;
196
197
  }
197
198
  /**
198
199
  * Generate function for OpenAI Responses API
199
200
  */
200
201
  export const stream = (model, context, options) => {
201
202
  const stream = new AssistantMessageEventStream();
203
+ const normalizedContext = resolveTranscript(context, getCompat(model).supportsMidConvoSystemMessages);
202
204
  // Start async processing
203
205
  (async () => {
204
206
  const output = {
@@ -223,15 +225,15 @@ export const stream = (model, context, options) => {
223
225
  const cacheRetention = resolveCacheRetention(options?.cacheRetention, options?.env);
224
226
  const cacheSessionId = cacheRetention === "none" ? undefined : options?.sessionId;
225
227
  const compat = getCompat(model, options?.env);
226
- const grammarToolInputProperties = createGrammarToolInputProperties(context.tools, compat.supportsOpenAIGrammarTools);
227
- const client = createClient(model, context, clientAuth.apiKey, clientAuth.headers, options?.fetch, cacheSessionId, options?.env);
228
- let params = buildParams(model, context, options, compat, grammarToolInputProperties);
228
+ const grammarToolInputProperties = createGrammarToolInputProperties(getDeclaredTools(normalizedContext.messages), compat.supportsOpenAIGrammarTools);
229
+ const client = createClient(model, normalizedContext, clientAuth.apiKey, clientAuth.headers, options?.fetch, cacheSessionId, options?.env);
230
+ let params = buildParams(model, normalizedContext, options, compat, grammarToolInputProperties);
229
231
  const nextParams = await options?.onPayload?.(params, model);
230
232
  if (nextParams !== undefined) {
231
233
  params = nextParams;
232
234
  }
233
235
  params = sanitizeUnsupportedNativeTools(params, compat);
234
- params = applyAllowedToolsChoice(params, context, compat);
236
+ params = applyAllowedToolsChoice(params, normalizedContext, context.activeToolNames, compat);
235
237
  const limitedTools = limitGitHubCopilotTools(model.provider, params.tools, params.tool_choice);
236
238
  if (limitedTools.omittedCount > 0) {
237
239
  params = { ...params, tools: limitedTools.tools };
@@ -241,7 +243,7 @@ export const stream = (model, context, options) => {
241
243
  if (transport !== "sse" && compat.supportsWebSocket) {
242
244
  let websocketStarted = false;
243
245
  try {
244
- await processWebSocketStream(resolveOpenAIResponsesWebSocketUrl(model, options?.env), params, buildWebSocketHeaders(model, context, clientAuth.apiKey, clientAuth.headers, cacheSessionId, options?.env), output, stream, model, () => {
246
+ await processWebSocketStream(resolveOpenAIResponsesWebSocketUrl(model, options?.env), params, buildWebSocketHeaders(model, normalizedContext, clientAuth.apiKey, clientAuth.headers, cacheSessionId, options?.env), output, stream, model, () => {
245
247
  websocketStarted = true;
246
248
  }, cacheSessionId, grammarToolInputProperties, options);
247
249
  if (options?.signal?.aborted) {
@@ -283,6 +285,7 @@ export const stream = (model, context, options) => {
283
285
  await options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
284
286
  stream.push({ type: "start", partial: output });
285
287
  await processResponsesStream(openaiStream, output, stream, model, {
288
+ onProviderStreamEvent: options?.onProviderStreamEvent,
286
289
  serviceTier: options?.serviceTier,
287
290
  grammarToolInputProperties,
288
291
  applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model),
@@ -312,7 +315,7 @@ export const stream = (model, context, options) => {
312
315
  : undefined;
313
316
  if (providerDiagnostic !== undefined)
314
317
  output.providerDiagnostic = providerDiagnostic;
315
- output.errorMessage = withGitHubCopilotFailureNote(formatGitHubCopilotToolLimitError(output, formatOpenAIResponsesError(error)), model.provider, error);
318
+ output.errorMessage = withGitHubCopilotFailureNote(formatGitHubCopilotToolLimitError(output, formatOpenAIResponsesError(error, model.provider)), model.provider, error);
316
319
  stream.push({ type: "error", reason: output.stopReason, error: output });
317
320
  stream.end();
318
321
  }
@@ -344,12 +347,12 @@ function resolveSimpleOptions(model, context, options) {
344
347
  * prefix matches the reasoning effort, service tier, and cache options that turn sends.
345
348
  */
346
349
  export async function warmOpenAIResponsesPromptCache(model, context, options) {
347
- const prefix = { ...context, messages: [] };
350
+ const prefix = normalizeContext({ ...context, messages: [] });
348
351
  const resolved = resolveSimpleOptions(model, prefix, options);
349
352
  const clientAuth = resolveOpenAIClientAuth(model.provider, resolved.apiKey, resolved.headers);
350
353
  const cacheRetention = resolveCacheRetention(resolved.cacheRetention, resolved.env);
351
354
  const compat = getCompat(model, resolved.env);
352
- const grammarToolInputProperties = createGrammarToolInputProperties(prefix.tools, compat.supportsOpenAIGrammarTools);
355
+ const grammarToolInputProperties = createGrammarToolInputProperties(getDeclaredTools(prefix.messages), compat.supportsOpenAIGrammarTools);
353
356
  const client = createClient(model, prefix, clientAuth.apiKey, clientAuth.headers, resolved.fetch, cacheRetention === "none" ? undefined : resolved.sessionId, resolved.env);
354
357
  let params = buildParams(model, prefix, resolved, compat, grammarToolInputProperties);
355
358
  const nextParams = await resolved.onPayload?.(params, model);
@@ -419,9 +422,8 @@ function createClient(model, context, apiKey, optionsHeaders, fetch, sessionId,
419
422
  defaultHeaders: headers,
420
423
  });
421
424
  }
422
- function buildParams(model, context, options, compat = getCompat(model, options?.env), grammarToolInputProperties = createGrammarToolInputProperties(context.tools, compat.supportsOpenAIGrammarTools)) {
423
- const deferredToolsMode = resolveDeferredToolsMode(compat);
424
- const toolPlacement = splitDeferredTools(context, deferredToolsMode !== undefined);
425
+ function buildParams(model, context, options, compat = getCompat(model, options?.env), grammarToolInputProperties = createGrammarToolInputProperties(getDeclaredTools(context.messages), compat.supportsOpenAIGrammarTools)) {
426
+ const toolPlacement = resolveResponsesToolPlacement(context.messages, resolveResponsesDeferredToolsMode(compat) !== undefined);
425
427
  const requestedReasoningEffort = options?.reasoningEffort ?? (options?.reasoningSummary ? "medium" : undefined);
426
428
  const thinkingLevelMap = inferOpenAIThinkingLevelMap(model);
427
429
  const mappedReasoningEffort = requestedReasoningEffort === undefined ? undefined : thinkingLevelMap?.[requestedReasoningEffort];
@@ -435,8 +437,9 @@ function buildParams(model, context, options, compat = getCompat(model, options?
435
437
  // nor a previous prefix unless the system prompt carries an explicit breakpoint.
436
438
  systemPromptCacheBreakpoint: compat.supportsExplicitPromptCacheMode && cacheRetention !== "none",
437
439
  grammarToolInputProperties,
438
- deferredTools: toolPlacement.deferred,
439
- deferredToolsMode,
440
+ supportsMidConvoSystemMessages: compat.supportsMidConvoSystemMessages,
441
+ supportsAdditionalTools: compat.supportsAdditionalTools,
442
+ supportsToolSearch: compat.supportsToolSearch,
440
443
  toolOptions: {
441
444
  supportsStrictMode: compat.supportsStrictMode,
442
445
  supportsOpenAIGrammarTools: compat.supportsOpenAIGrammarTools,
@@ -468,8 +471,8 @@ function buildParams(model, context, options, compat = getCompat(model, options?
468
471
  if (options?.serviceTier !== undefined) {
469
472
  params.service_tier = options.serviceTier;
470
473
  }
471
- if (toolPlacement.immediate.length > 0) {
472
- params.tools = convertResponsesTools(toolPlacement.immediate, {
474
+ if (toolPlacement.requestTools.length > 0) {
475
+ params.tools = convertResponsesTools(toolPlacement.requestTools, {
473
476
  supportsStrictMode: compat.supportsStrictMode,
474
477
  supportsOpenAIGrammarTools: compat.supportsOpenAIGrammarTools,
475
478
  });
@@ -494,10 +497,8 @@ function buildParams(model, context, options, compat = getCompat(model, options?
494
497
  params.include = ["reasoning.encrypted_content"];
495
498
  }
496
499
  applyExtraBodyToResponsesParams(params, options?.extraBody);
497
- // Last so custom keys override the named request fields.
498
- if (options?.samplingParams) {
499
- Object.assign(params, options.samplingParams);
500
- }
500
+ // Last so custom keys override the named request fields. Per-request keys override model defaults.
501
+ Object.assign(params, model.samplingParams, options?.samplingParams);
501
502
  return params;
502
503
  }
503
504
  function applyExtraBodyToResponsesParams(params, extraBody) {
@@ -859,6 +860,10 @@ function buildWebSocketHeaders(model, context, apiKey, optionsHeaders, sessionId
859
860
  }
860
861
  function getServiceTierCostMultiplier(model, serviceTier) {
861
862
  switch (serviceTier) {
863
+ case "ultrafast":
864
+ // OpenAI publishes an Ultrafast price for GPT-6 Astra only: 6x Standard on every
865
+ // token class and context tier. Any other model keeps its base rate.
866
+ return model.id === "gpt-6-astra" ? 6 : 1;
862
867
  case "flex":
863
868
  return 0.5;
864
869
  case "priority":
@@ -1,3 +1,4 @@
1
1
  import type { ImagesFunction, ImagesOptions } from "../types.ts";
2
- export declare const generateImages: ImagesFunction<"openrouter-images", ImagesOptions>;
2
+ /** Image generation over OpenRouter's chat completions endpoint. */
3
+ export declare const generateImages: ImagesFunction<ImagesOptions>;
3
4
  //# sourceMappingURL=openrouter-images.d.ts.map
@@ -3,6 +3,7 @@ import { formatProviderError, normalizeProviderError } from "../utils/error-body
3
3
  import { headersToRecord, providerHeadersToRecord } from "../utils/headers.js";
4
4
  import { retryProviderRequest } from "../utils/provider-retry.js";
5
5
  import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
6
+ /** Image generation over OpenRouter's chat completions endpoint. */
6
7
  export const generateImages = async (model, context, options) => {
7
8
  const output = {
8
9
  api: model.api,
@@ -8,7 +8,7 @@
8
8
  * Radius gateway, but any backend implementing it can be used, e.g. via a
9
9
  * models.json custom provider with `"api": "pi-messages"`.
10
10
  */
11
- import type { AssistantMessage, SimpleStreamOptions, StreamFunction, StreamOptions, ThinkingLevel, ToolCall } from "../types.ts";
11
+ import type { AssistantMessage, JsonObject, SimpleStreamOptions, StreamFunction, StreamOptions, ThinkingLevel, ToolCall } from "../types.ts";
12
12
  export interface PiMessagesOptions extends StreamOptions {
13
13
  reasoning?: ThinkingLevel;
14
14
  toolChoice?: "auto" | "none" | "required" | {
@@ -90,8 +90,8 @@ export type PiMessagesEvent = {
90
90
  };
91
91
  export declare class PiMessagesResponseError extends Error {
92
92
  code?: string;
93
- readonly diagnosticDetails: Record<string, unknown>;
94
- constructor(message: string, code: string | undefined, diagnosticDetails: Record<string, unknown>);
93
+ readonly diagnosticDetails: JsonObject;
94
+ constructor(message: string, code: string | undefined, diagnosticDetails: JsonObject);
95
95
  }
96
96
  export declare const stream: StreamFunction<"pi-messages", PiMessagesOptions>;
97
97
  export declare const streamSimple: StreamFunction<"pi-messages", SimpleStreamOptions>;
@@ -52,8 +52,8 @@ function createPiMessagesResponseError(model, url, response, body) {
52
52
  url: url.toString(),
53
53
  status: response.status,
54
54
  statusText: response.statusText,
55
- error: errorBody?.error,
56
- body: errorBody ? undefined : truncateDiagnosticString(body),
55
+ ...(errorBody?.error === undefined ? {} : { error: errorBody.error }),
56
+ ...(errorBody ? {} : { body: truncateDiagnosticString(body) }),
57
57
  timestampMs: Date.now(),
58
58
  });
59
59
  }
@@ -301,6 +301,7 @@ export const stream = (model, context, options) => {
301
301
  throw new Error(`${model.provider} response has no body`);
302
302
  }
303
303
  for await (const piEvent of readPiMessagesEvents(response.body)) {
304
+ await options?.onProviderStreamEvent?.(piEvent, model);
304
305
  const event = convertEvent(piEvent);
305
306
  eventStream.push(event);
306
307
  if (event.type === "done" || event.type === "error") {
@@ -1,4 +1,4 @@
1
- import type { Api, Context, Model, SimpleStreamOptions, StreamOptions, ThinkingBudgets, ThinkingLevel } from "../types.ts";
1
+ import type { Api, Model, SimpleStreamOptions, StreamOptions, ThinkingBudgets, ThinkingLevel, TranscriptContext } from "../types.ts";
2
2
  export { CONTEXT_GUARD_MIN_WINDOW, CONTEXT_SAFETY_TOKENS, ContextWindowExhaustedError, clampMaxTokensToContext, MIN_ANSWER_TOKENS, } from "./context-room.ts";
3
3
  /**
4
4
  * Merge user-supplied extraBody fields into a provider request payload, skipping
@@ -17,7 +17,7 @@ export declare const GOOGLE_RESERVED_BODY_KEYS: ReadonlySet<string>;
17
17
  export declare const ANTHROPIC_RESERVED_BODY_KEYS: ReadonlySet<string>;
18
18
  export declare const MISTRAL_RESERVED_BODY_KEYS: ReadonlySet<string>;
19
19
  export declare const BEDROCK_RESERVED_BODY_KEYS: ReadonlySet<string>;
20
- export declare function buildBaseOptions(model: Model<Api>, context: Context, options?: SimpleStreamOptions, apiKey?: string): StreamOptions;
20
+ export declare function buildBaseOptions(model: Model<Api>, context: TranscriptContext, options?: SimpleStreamOptions, apiKey?: string): StreamOptions;
21
21
  export declare const DEFAULT_THINKING_BUDGETS: ThinkingBudgets;
22
22
  export declare function clampReasoning(effort: ThinkingLevel | undefined): Exclude<ThinkingLevel, "xhigh" | "max"> | undefined;
23
23
  /**
@@ -122,6 +122,7 @@ export function buildBaseOptions(model, context, options, apiKey) {
122
122
  extraBody: options?.extraBody,
123
123
  onPayload: options?.onPayload,
124
124
  onResponse: options?.onResponse,
125
+ onProviderStreamEvent: options?.onProviderStreamEvent,
125
126
  timeoutMs: options?.timeoutMs,
126
127
  websocketConnectTimeoutMs: options?.websocketConnectTimeoutMs,
127
128
  maxRetries: options?.maxRetries,
@@ -0,0 +1,23 @@
1
+ import type { ClassifierApi, ClassifierContext, ClassifierModel, ClassifierOptions, ClassifierResult } from "../types.ts";
2
+ /** TypeSafe System One request body without the transport-specific envelope. */
3
+ export interface SystemOneWireRequest {
4
+ state: ClassifierContext["state"];
5
+ questions: Record<string, unknown>;
6
+ }
7
+ /** Differences between services that serve System One models. */
8
+ export interface SystemOneTransport {
9
+ /** Classifier API implemented by this transport. */
10
+ api: ClassifierApi;
11
+ /** Service name used in error messages. */
12
+ label: string;
13
+ /** Absolute request URL. */
14
+ url(model: ClassifierModel<ClassifierApi>): URL;
15
+ /** Wraps the System One request in the service's request envelope. */
16
+ payload(model: ClassifierModel<ClassifierApi>, request: SystemOneWireRequest): unknown;
17
+ /** Extracts the System One output (`{ answers, usage }`) from the service's response envelope. */
18
+ output(body: unknown): Record<string, unknown>;
19
+ }
20
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
21
+ /** Runs one System One classification over the given transport. */
22
+ export declare function classifySystemOne(transport: SystemOneTransport, model: ClassifierModel<ClassifierApi>, context: ClassifierContext, options: ClassifierOptions | undefined): Promise<ClassifierResult>;
23
+ //# sourceMappingURL=system-one-shared.d.ts.map
@@ -0,0 +1,183 @@
1
+ import { calculateCost } from "../models.js";
2
+ import { formatProviderError, normalizeProviderError } from "../utils/error-body.js";
3
+ import { headersToRecord, providerHeadersToRecord } from "../utils/headers.js";
4
+ import { retryProviderRequest } from "../utils/provider-retry.js";
5
+ function httpError(label, response, body) {
6
+ const error = new Error(`${label} returned ${response.status}`);
7
+ error.status = response.status;
8
+ error.headers = response.headers;
9
+ error.body = body;
10
+ return error;
11
+ }
12
+ function timeoutError(timeoutMs) {
13
+ const error = new Error(`Request timed out after ${timeoutMs}ms`);
14
+ error.name = "TimeoutError";
15
+ error.status = undefined;
16
+ error.headers = undefined;
17
+ error.body = "";
18
+ return error;
19
+ }
20
+ export function isRecord(value) {
21
+ return typeof value === "object" && value !== null && !Array.isArray(value);
22
+ }
23
+ function requiredNumber(label, value, field) {
24
+ if (typeof value !== "number" || !Number.isFinite(value)) {
25
+ throw new Error(`${label} returned an invalid ${field}`);
26
+ }
27
+ return value;
28
+ }
29
+ function probabilities(label, value, id) {
30
+ if (!isRecord(value))
31
+ throw new Error(`${label} returned invalid probabilities for ${id}`);
32
+ return Object.fromEntries(Object.entries(value).map(([key, probability]) => [
33
+ key,
34
+ requiredNumber(label, probability, `probability for ${id}.${key}`),
35
+ ]));
36
+ }
37
+ function parseAnswers(label, value, context) {
38
+ if (!isRecord(value))
39
+ throw new Error(`${label} returned an unexpected response`);
40
+ const answers = [];
41
+ for (const [id, question] of Object.entries(context.questions)) {
42
+ const answer = value[id];
43
+ if (!isRecord(answer))
44
+ throw new Error(`${label} did not return an answer for ${id}`);
45
+ if (question.type === "choice") {
46
+ if (answer.type !== "choice" || typeof answer.choice !== "string") {
47
+ throw new Error(`${label} did not return a choice answer for ${id}`);
48
+ }
49
+ answers.push([
50
+ id,
51
+ {
52
+ type: "choice",
53
+ choice: answer.choice,
54
+ probabilities: probabilities(label, answer.probabilities, id),
55
+ confidence: requiredNumber(label, answer.confidence, `confidence for ${id}`),
56
+ },
57
+ ]);
58
+ }
59
+ else if (question.type === "score") {
60
+ if (answer.type !== "score")
61
+ throw new Error(`${label} did not return a score answer for ${id}`);
62
+ answers.push([
63
+ id,
64
+ {
65
+ type: "score",
66
+ score: requiredNumber(label, answer.score, `score for ${id}`),
67
+ confidence: requiredNumber(label, answer.confidence, `confidence for ${id}`),
68
+ },
69
+ ]);
70
+ }
71
+ else {
72
+ if (answer.type !== "noul")
73
+ throw new Error(`${label} did not return a bool answer for ${id}`);
74
+ answers.push([
75
+ id,
76
+ {
77
+ type: "bool",
78
+ probability: requiredNumber(label, answer.noul, `probability for ${id}`),
79
+ },
80
+ ]);
81
+ }
82
+ }
83
+ return Object.fromEntries(answers);
84
+ }
85
+ function tokenCount(value) {
86
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
87
+ }
88
+ /**
89
+ * Usage from System One's `{ input_tokens, output_tokens }`, priced from the model catalog like chat
90
+ * usage. A missing or malformed usage object leaves the result without usage instead of failing it.
91
+ */
92
+ function parseUsage(value, model) {
93
+ if (!isRecord(value) || (value.input_tokens === undefined && value.output_tokens === undefined))
94
+ return undefined;
95
+ const input = tokenCount(value.input_tokens);
96
+ const output = tokenCount(value.output_tokens);
97
+ const usage = {
98
+ input,
99
+ output,
100
+ cacheRead: 0,
101
+ cacheWrite: 0,
102
+ totalTokens: input + output,
103
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
104
+ };
105
+ calculateCost(model, usage);
106
+ return usage;
107
+ }
108
+ /** Maps public `bool` questions to TypeSafe's wire-level `noul` type. */
109
+ function wireRequest(context) {
110
+ return {
111
+ state: context.state,
112
+ questions: Object.fromEntries(Object.entries(context.questions).map(([id, question]) => [
113
+ id,
114
+ question.type === "bool" ? { ...question, type: "noul" } : question,
115
+ ])),
116
+ };
117
+ }
118
+ function requestHeaders(model, apiKey, optionsHeaders) {
119
+ return (providerHeadersToRecord({ authorization: `Bearer ${apiKey}`, "content-type": "application/json" }, model.headers, optionsHeaders) ?? {});
120
+ }
121
+ /** Runs one System One classification over the given transport. */
122
+ export async function classifySystemOne(transport, model, context, options) {
123
+ const output = {
124
+ api: model.api,
125
+ provider: model.provider,
126
+ model: model.id,
127
+ answers: {},
128
+ stopReason: "stop",
129
+ timestamp: Date.now(),
130
+ };
131
+ try {
132
+ if (model.api !== transport.api)
133
+ throw new Error(`Unsupported classifier API: ${model.api}`);
134
+ if (!options?.apiKey)
135
+ throw new Error(`No API key for provider: ${model.provider}`);
136
+ const apiKey = options.apiKey;
137
+ let payload = transport.payload(model, wireRequest(context));
138
+ const transformed = await options.onPayload?.(payload, model);
139
+ if (transformed !== undefined)
140
+ payload = transformed;
141
+ const requestFetch = options.fetch ?? globalThis.fetch;
142
+ const { response, body } = await retryProviderRequest(async () => {
143
+ const timeoutSignal = options.timeoutMs !== undefined ? AbortSignal.timeout(options.timeoutMs) : undefined;
144
+ const signal = options.signal && timeoutSignal
145
+ ? AbortSignal.any([options.signal, timeoutSignal])
146
+ : (options.signal ?? timeoutSignal);
147
+ try {
148
+ const next = await requestFetch(transport.url(model), {
149
+ method: "POST",
150
+ headers: requestHeaders(model, apiKey, options.headers),
151
+ body: JSON.stringify(payload),
152
+ signal,
153
+ });
154
+ if (!next.ok)
155
+ throw httpError(transport.label, next, await next.text());
156
+ return { response: next, body: (await next.json()) };
157
+ }
158
+ catch (error) {
159
+ if (timeoutSignal?.aborted && !options.signal?.aborted)
160
+ throw timeoutError(options.timeoutMs);
161
+ throw error;
162
+ }
163
+ }, {
164
+ maxRetries: options.maxRetries ?? 2,
165
+ maxRetryDelayMs: options.maxRetryDelayMs,
166
+ signal: options.signal,
167
+ });
168
+ await options.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
169
+ const result = transport.output(body);
170
+ // Set before parsing answers: a request with malformed answers was still billed.
171
+ const usage = parseUsage(result.usage, model);
172
+ if (usage)
173
+ output.usage = usage;
174
+ output.answers = parseAnswers(transport.label, result.answers, context);
175
+ return output;
176
+ }
177
+ catch (error) {
178
+ output.stopReason = options?.signal?.aborted ? "aborted" : "error";
179
+ output.errorMessage = formatProviderError(normalizeProviderError(error), `${transport.label} error`);
180
+ return output;
181
+ }
182
+ }
183
+ //# sourceMappingURL=system-one-shared.js.map
@@ -88,8 +88,8 @@ export function transformMessages(messages, model, normalizeToolCallId, options
88
88
  const normalizeSameModelToolCallIds = options.normalizeSameModelToolCallIds ?? false;
89
89
  // First pass: transform messages (unsupported image downgrade, thinking blocks, tool call ID normalization)
90
90
  const transformed = imageAwareMessages.map((msg) => {
91
- // User messages pass through unchanged
92
- if (msg.role === "user") {
91
+ // System and user messages pass through unchanged
92
+ if (msg.role === "system" || msg.role === "user") {
93
93
  return msg;
94
94
  }
95
95
  // Handle toolResult messages - normalize toolCallId if we have a mapping
@@ -168,6 +168,9 @@ export function transformMessages(messages, model, normalizeToolCallId, options
168
168
  // result after its declaring assistant. Results are emitted adjacent to their
169
169
  // calls because Anthropic requires that ordering. A reused ID starts a new
170
170
  // pairing window, so a later result cannot repair an earlier call.
171
+ // System messages are transparent to this accounting: they keep their source
172
+ // position, and because matched results are pulled forward, one that lands
173
+ // between a call and its result is emitted after that result.
171
174
  const result = [];
172
175
  const toolResultsById = new Map();
173
176
  const nextToolCallIndexById = new Map();
@@ -0,0 +1,4 @@
1
+ import type { ClassifierFunction, ClassifierOptions } from "../types.ts";
2
+ /** TypeSafe System One classification with public `bool` values mapped to wire-level `noul`. */
3
+ export declare const classify: ClassifierFunction<ClassifierOptions>;
4
+ //# sourceMappingURL=typesafe-system-one.d.ts.map
@@ -0,0 +1,19 @@
1
+ import { classifySystemOne, isRecord } from "./system-one-shared.js";
2
+ /**
3
+ * TypeSafe's native System One protocol. OpenRouter serves the same protocol,
4
+ * so both providers use this API with different base URLs.
5
+ */
6
+ const transport = {
7
+ api: "typesafe-system-one",
8
+ label: "System One API",
9
+ url: (model) => new URL("systemone", `${model.baseUrl.replace(/\/+$/u, "")}/`),
10
+ payload: (model, request) => ({ model: model.id, ...request }),
11
+ output: (body) => {
12
+ if (!isRecord(body))
13
+ throw new Error("System One API returned an unexpected response");
14
+ return body;
15
+ },
16
+ };
17
+ /** TypeSafe System One classification with public `bool` values mapped to wire-level `noul`. */
18
+ export const classify = (model, context, options) => classifySystemOne(transport, model, context, options);
19
+ //# sourceMappingURL=typesafe-system-one.js.map
@@ -0,0 +1,3 @@
1
+ import type { ProviderClassifier } from "../types.ts";
2
+ export declare const typesafeSystemOneApi: () => ProviderClassifier;
3
+ //# sourceMappingURL=typesafe-system-one.lazy.d.ts.map
@@ -0,0 +1,4 @@
1
+ export const typesafeSystemOneApi = () => ({
2
+ classify: async (model, context, options) => (await import("./typesafe-system-one.js")).classify(model, context, options),
3
+ });
4
+ //# sourceMappingURL=typesafe-system-one.lazy.js.map
@@ -1,6 +1,6 @@
1
- import type { Api, AssistantMessageEventStream, Context, Model, SimpleStreamOptions, StreamFunction, StreamOptions } from "./types.ts";
2
- export type ApiStreamFunction = (model: Model<Api>, context: Context, options?: StreamOptions) => AssistantMessageEventStream;
3
- export type ApiStreamSimpleFunction = (model: Model<Api>, context: Context, options?: SimpleStreamOptions) => AssistantMessageEventStream;
1
+ import type { Api, AssistantMessageEventStream, Model, SimpleStreamOptions, StreamFunction, StreamOptions, TranscriptContext } from "./types.ts";
2
+ export type ApiStreamFunction = (model: Model<Api>, context: TranscriptContext, options?: StreamOptions) => AssistantMessageEventStream;
3
+ export type ApiStreamSimpleFunction = (model: Model<Api>, context: TranscriptContext, options?: SimpleStreamOptions) => AssistantMessageEventStream;
4
4
  export interface ApiProvider<TApi extends Api = Api, TOptions extends StreamOptions = StreamOptions> {
5
5
  api: TApi;
6
6
  stream: StreamFunction<TApi, TOptions>;
@@ -46,7 +46,7 @@ export function lazyOAuth(input) {
46
46
  isSubscription: input.isSubscription,
47
47
  loginLabel: input.loginLabel,
48
48
  ...(input.rejectedTokenStatuses === undefined ? {} : { rejectedTokenStatuses: input.rejectedTokenStatuses }),
49
- login: async (interaction) => (await loaded()).login(interaction),
49
+ login: async (interaction, options) => (await loaded()).login(interaction, options),
50
50
  refresh: async (credential, signal) => (await loaded()).refresh(credential, signal),
51
51
  toAuth: async (credential) => (await loaded()).toAuth(credential),
52
52
  };
@@ -10,8 +10,8 @@
10
10
  *
11
11
  * NOTE: Node-only (http.createServer); reached through the lazy OAuth loader.
12
12
  */
13
+ import { oauthErrorHtml, oauthSuccessHtml } from "../../utils/oauth-page.js";
13
14
  import { formatErrorDetails } from "./error-details.js";
14
- import { oauthErrorHtml, oauthSuccessHtml } from "./oauth-page.js";
15
15
  export const PREFERRED_CALLBACK_PORT = 53692;
16
16
  export const CALLBACK_PATH = "/callback";
17
17
  const BIND_FAILURE_CODES = new Set(["EACCES", "EADDRINUSE", "EPERM"]);