whale-code 6.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. package/README.md +95 -0
  2. package/bin/swag-agent.js +9 -0
  3. package/bin/swagmanager-mcp.js +321 -0
  4. package/dist/cli/app.d.ts +26 -0
  5. package/dist/cli/app.js +64 -0
  6. package/dist/cli/chat/AgentSelector.d.ts +14 -0
  7. package/dist/cli/chat/AgentSelector.js +14 -0
  8. package/dist/cli/chat/ChatApp.d.ts +9 -0
  9. package/dist/cli/chat/ChatApp.js +267 -0
  10. package/dist/cli/chat/ChatInput.d.ts +39 -0
  11. package/dist/cli/chat/ChatInput.js +509 -0
  12. package/dist/cli/chat/MarkdownText.d.ts +10 -0
  13. package/dist/cli/chat/MarkdownText.js +20 -0
  14. package/dist/cli/chat/MessageList.d.ts +37 -0
  15. package/dist/cli/chat/MessageList.js +80 -0
  16. package/dist/cli/chat/ModelSelector.d.ts +20 -0
  17. package/dist/cli/chat/ModelSelector.js +73 -0
  18. package/dist/cli/chat/RewindViewer.d.ts +26 -0
  19. package/dist/cli/chat/RewindViewer.js +185 -0
  20. package/dist/cli/chat/StoreSelector.d.ts +14 -0
  21. package/dist/cli/chat/StoreSelector.js +24 -0
  22. package/dist/cli/chat/StreamingText.d.ts +12 -0
  23. package/dist/cli/chat/StreamingText.js +12 -0
  24. package/dist/cli/chat/SubagentPanel.d.ts +45 -0
  25. package/dist/cli/chat/SubagentPanel.js +110 -0
  26. package/dist/cli/chat/TeamPanel.d.ts +21 -0
  27. package/dist/cli/chat/TeamPanel.js +42 -0
  28. package/dist/cli/chat/ToolIndicator.d.ts +25 -0
  29. package/dist/cli/chat/ToolIndicator.js +436 -0
  30. package/dist/cli/chat/hooks/useAgentLoop.d.ts +39 -0
  31. package/dist/cli/chat/hooks/useAgentLoop.js +382 -0
  32. package/dist/cli/chat/hooks/useSlashCommands.d.ts +37 -0
  33. package/dist/cli/chat/hooks/useSlashCommands.js +387 -0
  34. package/dist/cli/commands/config-cmd.d.ts +10 -0
  35. package/dist/cli/commands/config-cmd.js +99 -0
  36. package/dist/cli/commands/doctor.d.ts +14 -0
  37. package/dist/cli/commands/doctor.js +172 -0
  38. package/dist/cli/commands/init.d.ts +16 -0
  39. package/dist/cli/commands/init.js +278 -0
  40. package/dist/cli/commands/mcp.d.ts +12 -0
  41. package/dist/cli/commands/mcp.js +162 -0
  42. package/dist/cli/login/LoginApp.d.ts +7 -0
  43. package/dist/cli/login/LoginApp.js +157 -0
  44. package/dist/cli/print-mode.d.ts +31 -0
  45. package/dist/cli/print-mode.js +202 -0
  46. package/dist/cli/serve-mode.d.ts +37 -0
  47. package/dist/cli/serve-mode.js +636 -0
  48. package/dist/cli/services/agent-definitions.d.ts +25 -0
  49. package/dist/cli/services/agent-definitions.js +91 -0
  50. package/dist/cli/services/agent-events.d.ts +178 -0
  51. package/dist/cli/services/agent-events.js +175 -0
  52. package/dist/cli/services/agent-loop.d.ts +90 -0
  53. package/dist/cli/services/agent-loop.js +762 -0
  54. package/dist/cli/services/agent-worker-base.d.ts +97 -0
  55. package/dist/cli/services/agent-worker-base.js +220 -0
  56. package/dist/cli/services/auth-service.d.ts +30 -0
  57. package/dist/cli/services/auth-service.js +160 -0
  58. package/dist/cli/services/background-processes.d.ts +126 -0
  59. package/dist/cli/services/background-processes.js +318 -0
  60. package/dist/cli/services/browser-auth.d.ts +24 -0
  61. package/dist/cli/services/browser-auth.js +180 -0
  62. package/dist/cli/services/claude-md-loader.d.ts +16 -0
  63. package/dist/cli/services/claude-md-loader.js +58 -0
  64. package/dist/cli/services/config-store.d.ts +47 -0
  65. package/dist/cli/services/config-store.js +79 -0
  66. package/dist/cli/services/debug-log.d.ts +10 -0
  67. package/dist/cli/services/debug-log.js +52 -0
  68. package/dist/cli/services/error-logger.d.ts +58 -0
  69. package/dist/cli/services/error-logger.js +269 -0
  70. package/dist/cli/services/file-history.d.ts +21 -0
  71. package/dist/cli/services/file-history.js +83 -0
  72. package/dist/cli/services/format-server-response.d.ts +16 -0
  73. package/dist/cli/services/format-server-response.js +440 -0
  74. package/dist/cli/services/git-context.d.ts +11 -0
  75. package/dist/cli/services/git-context.js +66 -0
  76. package/dist/cli/services/hooks.d.ts +85 -0
  77. package/dist/cli/services/hooks.js +258 -0
  78. package/dist/cli/services/interactive-tools.d.ts +125 -0
  79. package/dist/cli/services/interactive-tools.js +260 -0
  80. package/dist/cli/services/keybinding-manager.d.ts +52 -0
  81. package/dist/cli/services/keybinding-manager.js +115 -0
  82. package/dist/cli/services/local-tools.d.ts +22 -0
  83. package/dist/cli/services/local-tools.js +697 -0
  84. package/dist/cli/services/lsp-manager.d.ts +18 -0
  85. package/dist/cli/services/lsp-manager.js +717 -0
  86. package/dist/cli/services/mcp-client.d.ts +48 -0
  87. package/dist/cli/services/mcp-client.js +157 -0
  88. package/dist/cli/services/memory-manager.d.ts +16 -0
  89. package/dist/cli/services/memory-manager.js +57 -0
  90. package/dist/cli/services/model-manager.d.ts +18 -0
  91. package/dist/cli/services/model-manager.js +71 -0
  92. package/dist/cli/services/model-router.d.ts +26 -0
  93. package/dist/cli/services/model-router.js +149 -0
  94. package/dist/cli/services/permission-modes.d.ts +13 -0
  95. package/dist/cli/services/permission-modes.js +43 -0
  96. package/dist/cli/services/rewind.d.ts +84 -0
  97. package/dist/cli/services/rewind.js +194 -0
  98. package/dist/cli/services/ripgrep.d.ts +28 -0
  99. package/dist/cli/services/ripgrep.js +138 -0
  100. package/dist/cli/services/sandbox.d.ts +29 -0
  101. package/dist/cli/services/sandbox.js +97 -0
  102. package/dist/cli/services/server-tools.d.ts +61 -0
  103. package/dist/cli/services/server-tools.js +543 -0
  104. package/dist/cli/services/session-persistence.d.ts +23 -0
  105. package/dist/cli/services/session-persistence.js +99 -0
  106. package/dist/cli/services/subagent-worker.d.ts +19 -0
  107. package/dist/cli/services/subagent-worker.js +41 -0
  108. package/dist/cli/services/subagent.d.ts +47 -0
  109. package/dist/cli/services/subagent.js +647 -0
  110. package/dist/cli/services/system-prompt.d.ts +7 -0
  111. package/dist/cli/services/system-prompt.js +198 -0
  112. package/dist/cli/services/team-lead.d.ts +73 -0
  113. package/dist/cli/services/team-lead.js +512 -0
  114. package/dist/cli/services/team-state.d.ts +77 -0
  115. package/dist/cli/services/team-state.js +398 -0
  116. package/dist/cli/services/teammate.d.ts +31 -0
  117. package/dist/cli/services/teammate.js +689 -0
  118. package/dist/cli/services/telemetry.d.ts +61 -0
  119. package/dist/cli/services/telemetry.js +209 -0
  120. package/dist/cli/services/tools/agent-tools.d.ts +14 -0
  121. package/dist/cli/services/tools/agent-tools.js +347 -0
  122. package/dist/cli/services/tools/file-ops.d.ts +15 -0
  123. package/dist/cli/services/tools/file-ops.js +487 -0
  124. package/dist/cli/services/tools/search-tools.d.ts +8 -0
  125. package/dist/cli/services/tools/search-tools.js +186 -0
  126. package/dist/cli/services/tools/shell-exec.d.ts +10 -0
  127. package/dist/cli/services/tools/shell-exec.js +168 -0
  128. package/dist/cli/services/tools/task-manager.d.ts +28 -0
  129. package/dist/cli/services/tools/task-manager.js +209 -0
  130. package/dist/cli/services/tools/web-tools.d.ts +11 -0
  131. package/dist/cli/services/tools/web-tools.js +395 -0
  132. package/dist/cli/setup/SetupApp.d.ts +9 -0
  133. package/dist/cli/setup/SetupApp.js +191 -0
  134. package/dist/cli/shared/MatrixIntro.d.ts +4 -0
  135. package/dist/cli/shared/MatrixIntro.js +83 -0
  136. package/dist/cli/shared/Theme.d.ts +74 -0
  137. package/dist/cli/shared/Theme.js +127 -0
  138. package/dist/cli/shared/WhaleBanner.d.ts +10 -0
  139. package/dist/cli/shared/WhaleBanner.js +12 -0
  140. package/dist/cli/shared/markdown.d.ts +21 -0
  141. package/dist/cli/shared/markdown.js +756 -0
  142. package/dist/cli/status/StatusApp.d.ts +4 -0
  143. package/dist/cli/status/StatusApp.js +105 -0
  144. package/dist/cli/stores/StoreApp.d.ts +7 -0
  145. package/dist/cli/stores/StoreApp.js +81 -0
  146. package/dist/index.d.ts +15 -0
  147. package/dist/index.js +538 -0
  148. package/dist/local-agent/connection.d.ts +48 -0
  149. package/dist/local-agent/connection.js +332 -0
  150. package/dist/local-agent/discovery.d.ts +18 -0
  151. package/dist/local-agent/discovery.js +146 -0
  152. package/dist/local-agent/executor.d.ts +34 -0
  153. package/dist/local-agent/executor.js +241 -0
  154. package/dist/local-agent/index.d.ts +14 -0
  155. package/dist/local-agent/index.js +198 -0
  156. package/dist/node/adapters/base.d.ts +35 -0
  157. package/dist/node/adapters/base.js +10 -0
  158. package/dist/node/adapters/discord.d.ts +29 -0
  159. package/dist/node/adapters/discord.js +299 -0
  160. package/dist/node/adapters/email.d.ts +23 -0
  161. package/dist/node/adapters/email.js +218 -0
  162. package/dist/node/adapters/imessage.d.ts +17 -0
  163. package/dist/node/adapters/imessage.js +118 -0
  164. package/dist/node/adapters/slack.d.ts +26 -0
  165. package/dist/node/adapters/slack.js +259 -0
  166. package/dist/node/adapters/sms.d.ts +23 -0
  167. package/dist/node/adapters/sms.js +161 -0
  168. package/dist/node/adapters/telegram.d.ts +17 -0
  169. package/dist/node/adapters/telegram.js +101 -0
  170. package/dist/node/adapters/webchat.d.ts +27 -0
  171. package/dist/node/adapters/webchat.js +160 -0
  172. package/dist/node/adapters/whatsapp.d.ts +28 -0
  173. package/dist/node/adapters/whatsapp.js +230 -0
  174. package/dist/node/cli.d.ts +2 -0
  175. package/dist/node/cli.js +325 -0
  176. package/dist/node/config.d.ts +17 -0
  177. package/dist/node/config.js +31 -0
  178. package/dist/node/runtime.d.ts +50 -0
  179. package/dist/node/runtime.js +351 -0
  180. package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +11 -0
  181. package/dist/server/handlers/__test-utils__/mock-supabase.js +393 -0
  182. package/dist/server/handlers/analytics.d.ts +17 -0
  183. package/dist/server/handlers/analytics.js +266 -0
  184. package/dist/server/handlers/api-keys.d.ts +6 -0
  185. package/dist/server/handlers/api-keys.js +221 -0
  186. package/dist/server/handlers/billing.d.ts +33 -0
  187. package/dist/server/handlers/billing.js +272 -0
  188. package/dist/server/handlers/browser.d.ts +10 -0
  189. package/dist/server/handlers/browser.js +517 -0
  190. package/dist/server/handlers/catalog.d.ts +99 -0
  191. package/dist/server/handlers/catalog.js +976 -0
  192. package/dist/server/handlers/comms.d.ts +254 -0
  193. package/dist/server/handlers/comms.js +588 -0
  194. package/dist/server/handlers/creations.d.ts +6 -0
  195. package/dist/server/handlers/creations.js +479 -0
  196. package/dist/server/handlers/crm.d.ts +89 -0
  197. package/dist/server/handlers/crm.js +538 -0
  198. package/dist/server/handlers/discovery.d.ts +6 -0
  199. package/dist/server/handlers/discovery.js +288 -0
  200. package/dist/server/handlers/embeddings.d.ts +92 -0
  201. package/dist/server/handlers/embeddings.js +197 -0
  202. package/dist/server/handlers/enrichment.d.ts +8 -0
  203. package/dist/server/handlers/enrichment.js +768 -0
  204. package/dist/server/handlers/image-gen.d.ts +6 -0
  205. package/dist/server/handlers/image-gen.js +409 -0
  206. package/dist/server/handlers/inventory.d.ts +319 -0
  207. package/dist/server/handlers/inventory.js +447 -0
  208. package/dist/server/handlers/kali.d.ts +10 -0
  209. package/dist/server/handlers/kali.js +210 -0
  210. package/dist/server/handlers/llm-providers.d.ts +6 -0
  211. package/dist/server/handlers/llm-providers.js +673 -0
  212. package/dist/server/handlers/local-agent.d.ts +6 -0
  213. package/dist/server/handlers/local-agent.js +118 -0
  214. package/dist/server/handlers/meta-ads.d.ts +111 -0
  215. package/dist/server/handlers/meta-ads.js +2279 -0
  216. package/dist/server/handlers/nodes.d.ts +33 -0
  217. package/dist/server/handlers/nodes.js +699 -0
  218. package/dist/server/handlers/operations.d.ts +138 -0
  219. package/dist/server/handlers/operations.js +131 -0
  220. package/dist/server/handlers/platform.d.ts +23 -0
  221. package/dist/server/handlers/platform.js +227 -0
  222. package/dist/server/handlers/supply-chain.d.ts +19 -0
  223. package/dist/server/handlers/supply-chain.js +327 -0
  224. package/dist/server/handlers/transcription.d.ts +17 -0
  225. package/dist/server/handlers/transcription.js +121 -0
  226. package/dist/server/handlers/video-gen.d.ts +6 -0
  227. package/dist/server/handlers/video-gen.js +466 -0
  228. package/dist/server/handlers/voice.d.ts +8 -0
  229. package/dist/server/handlers/voice.js +1146 -0
  230. package/dist/server/handlers/workflow-steps.d.ts +86 -0
  231. package/dist/server/handlers/workflow-steps.js +2349 -0
  232. package/dist/server/handlers/workflows.d.ts +7 -0
  233. package/dist/server/handlers/workflows.js +989 -0
  234. package/dist/server/index.d.ts +1 -0
  235. package/dist/server/index.js +2427 -0
  236. package/dist/server/lib/batch-client.d.ts +80 -0
  237. package/dist/server/lib/batch-client.js +467 -0
  238. package/dist/server/lib/code-worker-pool.d.ts +31 -0
  239. package/dist/server/lib/code-worker-pool.js +224 -0
  240. package/dist/server/lib/code-worker.d.ts +1 -0
  241. package/dist/server/lib/code-worker.js +188 -0
  242. package/dist/server/lib/compaction-service.d.ts +32 -0
  243. package/dist/server/lib/compaction-service.js +162 -0
  244. package/dist/server/lib/logger.d.ts +19 -0
  245. package/dist/server/lib/logger.js +46 -0
  246. package/dist/server/lib/otel.d.ts +38 -0
  247. package/dist/server/lib/otel.js +126 -0
  248. package/dist/server/lib/pg-rate-limiter.d.ts +21 -0
  249. package/dist/server/lib/pg-rate-limiter.js +86 -0
  250. package/dist/server/lib/prompt-sanitizer.d.ts +37 -0
  251. package/dist/server/lib/prompt-sanitizer.js +177 -0
  252. package/dist/server/lib/provider-capabilities.d.ts +85 -0
  253. package/dist/server/lib/provider-capabilities.js +190 -0
  254. package/dist/server/lib/provider-failover.d.ts +74 -0
  255. package/dist/server/lib/provider-failover.js +210 -0
  256. package/dist/server/lib/rate-limiter.d.ts +39 -0
  257. package/dist/server/lib/rate-limiter.js +147 -0
  258. package/dist/server/lib/server-agent-loop.d.ts +107 -0
  259. package/dist/server/lib/server-agent-loop.js +667 -0
  260. package/dist/server/lib/server-subagent.d.ts +78 -0
  261. package/dist/server/lib/server-subagent.js +203 -0
  262. package/dist/server/lib/session-checkpoint.d.ts +51 -0
  263. package/dist/server/lib/session-checkpoint.js +145 -0
  264. package/dist/server/lib/ssrf-guard.d.ts +13 -0
  265. package/dist/server/lib/ssrf-guard.js +240 -0
  266. package/dist/server/lib/supabase-client.d.ts +7 -0
  267. package/dist/server/lib/supabase-client.js +78 -0
  268. package/dist/server/lib/template-resolver.d.ts +31 -0
  269. package/dist/server/lib/template-resolver.js +215 -0
  270. package/dist/server/lib/utils.d.ts +16 -0
  271. package/dist/server/lib/utils.js +147 -0
  272. package/dist/server/local-agent-gateway.d.ts +82 -0
  273. package/dist/server/local-agent-gateway.js +426 -0
  274. package/dist/server/providers/anthropic.d.ts +20 -0
  275. package/dist/server/providers/anthropic.js +199 -0
  276. package/dist/server/providers/bedrock.d.ts +20 -0
  277. package/dist/server/providers/bedrock.js +194 -0
  278. package/dist/server/providers/gemini.d.ts +24 -0
  279. package/dist/server/providers/gemini.js +486 -0
  280. package/dist/server/providers/openai.d.ts +24 -0
  281. package/dist/server/providers/openai.js +522 -0
  282. package/dist/server/providers/registry.d.ts +32 -0
  283. package/dist/server/providers/registry.js +58 -0
  284. package/dist/server/providers/shared.d.ts +32 -0
  285. package/dist/server/providers/shared.js +124 -0
  286. package/dist/server/providers/types.d.ts +92 -0
  287. package/dist/server/providers/types.js +12 -0
  288. package/dist/server/proxy-handlers.d.ts +6 -0
  289. package/dist/server/proxy-handlers.js +89 -0
  290. package/dist/server/tool-router.d.ts +149 -0
  291. package/dist/server/tool-router.js +803 -0
  292. package/dist/server/validation.d.ts +24 -0
  293. package/dist/server/validation.js +301 -0
  294. package/dist/server/worker.d.ts +19 -0
  295. package/dist/server/worker.js +201 -0
  296. package/dist/setup.d.ts +8 -0
  297. package/dist/setup.js +181 -0
  298. package/dist/shared/agent-core.d.ts +157 -0
  299. package/dist/shared/agent-core.js +534 -0
  300. package/dist/shared/anthropic-types.d.ts +105 -0
  301. package/dist/shared/anthropic-types.js +7 -0
  302. package/dist/shared/api-client.d.ts +90 -0
  303. package/dist/shared/api-client.js +379 -0
  304. package/dist/shared/constants.d.ts +33 -0
  305. package/dist/shared/constants.js +80 -0
  306. package/dist/shared/sse-parser.d.ts +26 -0
  307. package/dist/shared/sse-parser.js +259 -0
  308. package/dist/shared/tool-dispatch.d.ts +52 -0
  309. package/dist/shared/tool-dispatch.js +191 -0
  310. package/dist/shared/types.d.ts +72 -0
  311. package/dist/shared/types.js +7 -0
  312. package/dist/updater.d.ts +25 -0
  313. package/dist/updater.js +140 -0
  314. package/dist/webchat/widget.d.ts +0 -0
  315. package/dist/webchat/widget.js +397 -0
  316. package/package.json +95 -0
  317. package/src/cli/services/builtin-skills/commit.md +19 -0
  318. package/src/cli/services/builtin-skills/review-pr.md +21 -0
  319. package/src/cli/services/builtin-skills/review.md +18 -0
@@ -0,0 +1,486 @@
1
+ /**
2
+ * Gemini Provider Adapter — full format conversion to/from Anthropic SSE.
3
+ *
4
+ * Contains:
5
+ * - anthropicToGeminiMessages() — message format conversion
6
+ * - sanitizeSchemaForGemini() — type uppercase, enum stringification, key stripping
7
+ * - anthropicToGeminiFunctions() — tool conversion
8
+ * - Streaming event loop with thinking/text/function call detection
9
+ * - seenFunctionCalls deduplication
10
+ *
11
+ * Phase 7.1: Also encapsulates thinking config, context management,
12
+ * output token limits, and compaction config for Gemini models.
13
+ */
14
+ import { randomUUID } from "node:crypto";
15
+ import { GoogleGenAI } from "@google/genai";
16
+ import { sanitizeError } from "../../shared/agent-core.js";
17
+ import { getCapabilities } from "../lib/provider-capabilities.js";
18
+ import { registerProvider } from "./registry.js";
19
+ import { jsonResponse, writeSSEHeaders, emitError, resolveProviderCredentials } from "./shared.js";
20
+ // ============================================================================
21
+ // CONSTANTS — Gemini-specific model config
22
+ // ============================================================================
23
+ const GEMINI_MAX_TOKENS = 65536;
24
+ const DEFAULT_OUTPUT_TOKENS = 16384;
25
+ const MODEL_MAX_OUTPUT_TOKENS = {
26
+ "gemini-3-pro-preview": 65536,
27
+ "gemini-3-flash-preview": 65536,
28
+ "gemini-2.5-pro": 65536,
29
+ "gemini-2.5-flash": 65536,
30
+ "gemini-2.5-flash-lite": 65536,
31
+ };
32
+ // ============================================================================
33
+ // MESSAGE CONVERSION
34
+ // ============================================================================
35
+ /** Convert Anthropic messages to Gemini Content format */
36
+ function anthropicToGeminiMessages(messages) {
37
+ const contents = [];
38
+ // Track tool_use_id → name for tool_result conversion
39
+ const toolIdMap = new Map();
40
+ for (const msg of messages) {
41
+ const role = msg.role === "assistant" ? "model" : "user";
42
+ const parts = [];
43
+ if (typeof msg.content === "string") {
44
+ parts.push({ text: msg.content });
45
+ }
46
+ else if (Array.isArray(msg.content)) {
47
+ for (const block of msg.content) {
48
+ if (block.type === "text" && block.text) {
49
+ parts.push({ text: block.text });
50
+ }
51
+ else if (block.type === "tool_use") {
52
+ toolIdMap.set(block.id, block.name);
53
+ parts.push({
54
+ functionCall: { name: block.name, args: block.input || {} },
55
+ // Preserve Gemini thought signature for function call round-trips
56
+ ...(block.thought_signature ? { thoughtSignature: block.thought_signature } : {}),
57
+ });
58
+ }
59
+ else if (block.type === "image" && block.source?.data) {
60
+ parts.push({
61
+ inlineData: {
62
+ mimeType: block.source.media_type,
63
+ data: block.source.data,
64
+ },
65
+ });
66
+ }
67
+ else if (block.type === "tool_result") {
68
+ const toolName = toolIdMap.get(block.tool_use_id) || block.tool_use_id;
69
+ let resultContent = "";
70
+ if (typeof block.content === "string") {
71
+ resultContent = block.content;
72
+ }
73
+ else if (Array.isArray(block.content)) {
74
+ resultContent = block.content
75
+ .filter((c) => c.type === "text")
76
+ .map((c) => c.text)
77
+ .join("\n");
78
+ }
79
+ parts.push({
80
+ functionResponse: {
81
+ name: toolName,
82
+ response: { result: resultContent },
83
+ },
84
+ });
85
+ }
86
+ }
87
+ }
88
+ if (parts.length > 0) {
89
+ contents.push({ role, parts });
90
+ }
91
+ }
92
+ return contents;
93
+ }
94
+ // ============================================================================
95
+ // SCHEMA SANITIZATION
96
+ // ============================================================================
97
+ /** Gemini uses uppercase OpenAPI schema types, not lowercase JSON Schema types. */
98
+ const GEMINI_TYPE_MAP = {
99
+ string: "STRING",
100
+ number: "NUMBER",
101
+ integer: "INTEGER",
102
+ boolean: "BOOLEAN",
103
+ array: "ARRAY",
104
+ object: "OBJECT",
105
+ };
106
+ /**
107
+ * Keys that are JSON Schema-only and not supported by Gemini's OpenAPI Schema.
108
+ * IMPORTANT: Only strip keys that can't collide with property names in our tools.
109
+ */
110
+ const GEMINI_SCHEMA_ONLY_KEYS = new Set([
111
+ "$schema", "additionalProperties", "propertyNames",
112
+ "minProperties", "maxProperties",
113
+ "anyOf", "oneOf", "allOf", "not", "const",
114
+ ]);
115
+ /** Keys to strip only from schema definition objects (objects with a "type" field) */
116
+ const GEMINI_SCHEMA_VALIDATION_KEYS = new Set([
117
+ "exclusiveMinimum", "exclusiveMaximum",
118
+ "minItems", "maxItems", "minLength", "maxLength",
119
+ "minimum", "maximum", "pattern",
120
+ "default",
121
+ ]);
122
+ /**
123
+ * Sanitize JSON Schema → Gemini OpenAPI Schema:
124
+ * - Convert type values to uppercase (string → STRING)
125
+ * - Stringify all enum values
126
+ * - Remove unsupported JSON Schema keys (carefully — don't strip property names)
127
+ * - Ensure arrays have items, objects have properties
128
+ * - Filter required to only include defined properties
129
+ */
130
+ function sanitizeSchemaForGemini(schema) {
131
+ if (!schema || typeof schema !== "object")
132
+ return schema;
133
+ if (Array.isArray(schema))
134
+ return schema.map((s) => sanitizeSchemaForGemini(s));
135
+ const result = {};
136
+ const isSchemaNode = "type" in schema;
137
+ for (const [key, value] of Object.entries(schema)) {
138
+ // Always strip structural JSON Schema keys
139
+ if (GEMINI_SCHEMA_ONLY_KEYS.has(key))
140
+ continue;
141
+ // Strip validation keys only from schema nodes (not property name maps)
142
+ if (isSchemaNode && GEMINI_SCHEMA_VALIDATION_KEYS.has(key))
143
+ continue;
144
+ if (key === "type" && typeof value === "string") {
145
+ result[key] = GEMINI_TYPE_MAP[value] || value.toUpperCase();
146
+ }
147
+ else if (key === "enum" && Array.isArray(value)) {
148
+ result[key] = value.map((v) => String(v));
149
+ }
150
+ else if (key === "properties" && typeof value === "object" && value !== null && !Array.isArray(value)) {
151
+ // Properties map: keys are property names, values are schema definitions
152
+ const props = {};
153
+ for (const [propName, propSchema] of Object.entries(value)) {
154
+ props[propName] = sanitizeSchemaForGemini(propSchema);
155
+ }
156
+ result[key] = props;
157
+ }
158
+ else {
159
+ result[key] = sanitizeSchemaForGemini(value);
160
+ }
161
+ }
162
+ // Gemini requires array items to have a type
163
+ if (result.type === "ARRAY") {
164
+ if (!result.items || (typeof result.items === "object" && Object.keys(result.items).length === 0)) {
165
+ result.items = { type: "STRING" };
166
+ }
167
+ }
168
+ // Gemini requires object types to have properties
169
+ if (result.type === "OBJECT" && !result.properties) {
170
+ result.properties = {};
171
+ }
172
+ // Gemini only allows enum on STRING type — force type to STRING if enum is present
173
+ if (result.enum && Array.isArray(result.enum) && result.type && result.type !== "STRING") {
174
+ result.type = "STRING";
175
+ }
176
+ // Filter required to only include properties that actually exist
177
+ if (result.required && Array.isArray(result.required) && result.properties) {
178
+ const validProps = new Set(Object.keys(result.properties));
179
+ result.required = result.required.filter((r) => validProps.has(r));
180
+ if (result.required.length === 0)
181
+ delete result.required;
182
+ }
183
+ return result;
184
+ }
185
+ function anthropicToGeminiFunctions(tools) {
186
+ return tools.map((t) => ({
187
+ name: t.name,
188
+ description: typeof t.description === "string" ? t.description.slice(0, 4096) : "",
189
+ parameters: sanitizeSchemaForGemini(t.input_schema || {}),
190
+ }));
191
+ }
192
+ // ============================================================================
193
+ // ADAPTER
194
+ // ============================================================================
195
+ export class GeminiAdapter {
196
+ name = "gemini";
197
+ async handleStream(res, config, corsHeaders) {
198
+ const { model, messages, system, tools, max_tokens, temperature, storeId, tool_choice } = config;
199
+ const creds = await resolveProviderCredentials("google", storeId);
200
+ if (!creds.google) {
201
+ jsonResponse(res, 422, { error: "Google AI credentials not configured. Add google_ai_api_key to platform_secrets." }, corsHeaders);
202
+ return;
203
+ }
204
+ const client = new GoogleGenAI({ apiKey: creds.google.apiKey });
205
+ // Extract system prompt text
206
+ let systemText = "";
207
+ if (typeof system === "string") {
208
+ systemText = system;
209
+ }
210
+ else if (Array.isArray(system)) {
211
+ systemText = system.map((s) => s.text || "").join("\n");
212
+ }
213
+ // Build generation config
214
+ const genConfig = { maxOutputTokens: max_tokens };
215
+ if (temperature !== undefined)
216
+ genConfig.temperature = temperature;
217
+ // Map tool_choice to Gemini functionCallingConfig
218
+ let geminiFunctionCallingConfig = { mode: "AUTO" };
219
+ let omitGeminiTools = false;
220
+ if (tool_choice) {
221
+ if (tool_choice === "auto") {
222
+ geminiFunctionCallingConfig = { mode: "AUTO" };
223
+ }
224
+ else if (tool_choice === "any") {
225
+ geminiFunctionCallingConfig = { mode: "ANY" };
226
+ }
227
+ else if (tool_choice === "none") {
228
+ geminiFunctionCallingConfig = { mode: "NONE" };
229
+ omitGeminiTools = true;
230
+ }
231
+ else if (typeof tool_choice === "object" && tool_choice.type === "tool") {
232
+ geminiFunctionCallingConfig = { mode: "ANY", allowedFunctionNames: [tool_choice.name] };
233
+ }
234
+ }
235
+ // Build request config for the new SDK — model is passed per-request
236
+ const requestConfig = {
237
+ model,
238
+ config: {
239
+ ...genConfig,
240
+ ...(systemText ? { systemInstruction: systemText } : {}),
241
+ ...(!omitGeminiTools && tools?.length ? {
242
+ tools: [{ functionDeclarations: anthropicToGeminiFunctions(tools) }],
243
+ toolConfig: { functionCallingConfig: geminiFunctionCallingConfig },
244
+ } : {}),
245
+ },
246
+ };
247
+ const genModel = client.models;
248
+ // Convert messages
249
+ const contents = anthropicToGeminiMessages(messages);
250
+ // Track tokens outside try so catch block can reference them
251
+ let totalOutputTokens = 0;
252
+ let totalInputTokens = 0;
253
+ let totalThinkingTokens = 0;
254
+ let totalCacheReadTokens = 0;
255
+ // Defer SSE headers until stream is successfully created
256
+ // so we can return proper HTTP errors if the initial API call fails.
257
+ let heartbeat;
258
+ try {
259
+ // Request with includeThoughts so we stream Gemini's thinking process
260
+ const streamResult = await genModel.generateContentStream({
261
+ ...requestConfig,
262
+ contents,
263
+ config: {
264
+ ...requestConfig.config,
265
+ thinkingConfig: { includeThoughts: true },
266
+ },
267
+ });
268
+ // Stream created successfully — now safe to commit to SSE
269
+ writeSSEHeaders(res, corsHeaders);
270
+ // Heartbeat keeps the connection alive and helps detect dead clients
271
+ heartbeat = setInterval(() => {
272
+ if (!res.writableEnded)
273
+ res.write(":ping\n\n");
274
+ }, 15_000);
275
+ // Emit Anthropic-format message_start
276
+ res.write(`data: ${JSON.stringify({
277
+ type: "message_start",
278
+ message: {
279
+ id: `msg_gemini_${randomUUID().slice(0, 8)}`,
280
+ type: "message",
281
+ role: "assistant",
282
+ content: [],
283
+ model,
284
+ stop_reason: null,
285
+ usage: { input_tokens: 0, output_tokens: 0 },
286
+ },
287
+ })}\n\n`);
288
+ let blockIndex = 0;
289
+ let hasOpenTextBlock = false;
290
+ let hasOpenThinkingBlock = false;
291
+ let hasToolUse = false;
292
+ let hasEmittedContent = false;
293
+ const seenFunctionCalls = new Set();
294
+ for await (const chunk of streamResult) {
295
+ const candidates = chunk.candidates || [];
296
+ const usage = chunk.usageMetadata;
297
+ if (usage) {
298
+ totalOutputTokens = usage.candidatesTokenCount || usage.totalTokenCount || 0;
299
+ totalInputTokens = usage.promptTokenCount || 0;
300
+ totalThinkingTokens = usage.thoughtsTokenCount || 0;
301
+ if (usage.cachedContentTokenCount) {
302
+ totalCacheReadTokens = usage.cachedContentTokenCount;
303
+ }
304
+ }
305
+ for (const candidate of candidates) {
306
+ const allParts = candidate.content?.parts || [];
307
+ for (const part of allParts) {
308
+ const isThought = !!part.thought;
309
+ // Thinking text delta
310
+ if (isThought && part.text) {
311
+ if (hasOpenTextBlock) {
312
+ res.write(`data: ${JSON.stringify({ type: "content_block_stop", index: blockIndex })}\n\n`);
313
+ blockIndex++;
314
+ hasOpenTextBlock = false;
315
+ }
316
+ if (!hasOpenThinkingBlock) {
317
+ res.write(`data: ${JSON.stringify({
318
+ type: "content_block_start",
319
+ index: blockIndex,
320
+ content_block: { type: "thinking", thinking: "" },
321
+ })}\n\n`);
322
+ hasOpenThinkingBlock = true;
323
+ hasEmittedContent = true;
324
+ }
325
+ res.write(`data: ${JSON.stringify({
326
+ type: "content_block_delta",
327
+ index: blockIndex,
328
+ delta: { type: "thinking_delta", thinking: part.text },
329
+ })}\n\n`);
330
+ continue;
331
+ }
332
+ // Response text delta
333
+ if (part.text && !isThought) {
334
+ if (hasOpenThinkingBlock) {
335
+ res.write(`data: ${JSON.stringify({ type: "content_block_stop", index: blockIndex })}\n\n`);
336
+ blockIndex++;
337
+ hasOpenThinkingBlock = false;
338
+ }
339
+ if (!hasOpenTextBlock) {
340
+ res.write(`data: ${JSON.stringify({
341
+ type: "content_block_start",
342
+ index: blockIndex,
343
+ content_block: { type: "text", text: "" },
344
+ })}\n\n`);
345
+ hasOpenTextBlock = true;
346
+ hasEmittedContent = true;
347
+ }
348
+ res.write(`data: ${JSON.stringify({
349
+ type: "content_block_delta",
350
+ index: blockIndex,
351
+ delta: { type: "text_delta", text: part.text },
352
+ })}\n\n`);
353
+ continue;
354
+ }
355
+ // Function call
356
+ if (part.functionCall) {
357
+ const fc = {
358
+ name: part.functionCall.name || "",
359
+ args: part.functionCall.args || {},
360
+ thoughtSignature: part.thoughtSignature,
361
+ };
362
+ const fcKey = `${fc.name}:${JSON.stringify(fc.args)}`;
363
+ if (seenFunctionCalls.has(fcKey))
364
+ continue;
365
+ seenFunctionCalls.add(fcKey);
366
+ // Close any open thinking block
367
+ if (hasOpenThinkingBlock) {
368
+ res.write(`data: ${JSON.stringify({ type: "content_block_stop", index: blockIndex })}\n\n`);
369
+ blockIndex++;
370
+ hasOpenThinkingBlock = false;
371
+ }
372
+ // Close any open text block
373
+ if (hasOpenTextBlock) {
374
+ res.write(`data: ${JSON.stringify({ type: "content_block_stop", index: blockIndex })}\n\n`);
375
+ blockIndex++;
376
+ hasOpenTextBlock = false;
377
+ }
378
+ hasToolUse = true;
379
+ hasEmittedContent = true;
380
+ const toolUseId = `toolu_gemini_${randomUUID().slice(0, 12)}`;
381
+ res.write(`data: ${JSON.stringify({
382
+ type: "content_block_start",
383
+ index: blockIndex,
384
+ content_block: {
385
+ type: "tool_use",
386
+ id: toolUseId,
387
+ name: fc.name,
388
+ input: {},
389
+ // Preserve Gemini thought_signature — required for tool call round-trips with thinking
390
+ ...(fc.thoughtSignature ? { thought_signature: fc.thoughtSignature } : {}),
391
+ },
392
+ })}\n\n`);
393
+ res.write(`data: ${JSON.stringify({
394
+ type: "content_block_delta",
395
+ index: blockIndex,
396
+ delta: {
397
+ type: "input_json_delta",
398
+ partial_json: JSON.stringify(fc.args || {}),
399
+ },
400
+ })}\n\n`);
401
+ res.write(`data: ${JSON.stringify({ type: "content_block_stop", index: blockIndex })}\n\n`);
402
+ blockIndex++;
403
+ }
404
+ }
405
+ }
406
+ }
407
+ // Close any open blocks — no signature_delta for Gemini thinking
408
+ if (hasOpenThinkingBlock) {
409
+ res.write(`data: ${JSON.stringify({ type: "content_block_stop", index: blockIndex })}\n\n`);
410
+ }
411
+ if (hasOpenTextBlock) {
412
+ res.write(`data: ${JSON.stringify({ type: "content_block_stop", index: blockIndex })}\n\n`);
413
+ }
414
+ // Handle empty response — Gemini sometimes returns finishReason: STOP with no content
415
+ if (!hasEmittedContent) {
416
+ res.write(`data: ${JSON.stringify({
417
+ type: "content_block_start",
418
+ index: 0,
419
+ content_block: { type: "text", text: "" },
420
+ })}\n\n`);
421
+ res.write(`data: ${JSON.stringify({
422
+ type: "content_block_delta",
423
+ index: 0,
424
+ delta: { type: "text_delta", text: "[Gemini returned an empty response. Please try again.]" },
425
+ })}\n\n`);
426
+ res.write(`data: ${JSON.stringify({ type: "content_block_stop", index: 0 })}\n\n`);
427
+ }
428
+ // Determine stop reason
429
+ const stopReason = hasToolUse ? "tool_use" : "end_turn";
430
+ // Emit message_delta with stop reason + full usage
431
+ res.write(`data: ${JSON.stringify({
432
+ type: "message_delta",
433
+ delta: { stop_reason: stopReason, stop_sequence: null },
434
+ usage: {
435
+ input_tokens: totalInputTokens,
436
+ output_tokens: totalOutputTokens,
437
+ ...(totalThinkingTokens > 0 ? { thinking_tokens: totalThinkingTokens } : {}),
438
+ ...(totalCacheReadTokens > 0 ? { cache_read_input_tokens: totalCacheReadTokens } : {}),
439
+ },
440
+ })}\n\n`);
441
+ // Emit message_stop
442
+ res.write(`data: ${JSON.stringify({ type: "message_stop" })}\n\n`);
443
+ res.write("data: [DONE]\n\n");
444
+ }
445
+ catch (err) {
446
+ emitError(res, err, corsHeaders, sanitizeError, {
447
+ provider: "gemini",
448
+ inputTokens: totalInputTokens,
449
+ outputTokens: totalOutputTokens,
450
+ thinkingTokens: totalThinkingTokens,
451
+ cacheReadTokens: totalCacheReadTokens,
452
+ });
453
+ }
454
+ finally {
455
+ if (heartbeat)
456
+ clearInterval(heartbeat);
457
+ }
458
+ res.end();
459
+ }
460
+ // ---- Provider-specific config methods ----
461
+ getThinkingConfig(_model, enabled) {
462
+ if (!enabled) {
463
+ return { thinking: { type: "disabled" }, beta: "" };
464
+ }
465
+ // Gemini models: thinking is always-on for 2.5+/3.x — signal pass-through
466
+ return { thinking: { type: "enabled" }, beta: "" };
467
+ }
468
+ getMaxOutputTokens(model, agentMax) {
469
+ const modelMax = MODEL_MAX_OUTPUT_TOKENS[model] ?? DEFAULT_OUTPUT_TOKENS;
470
+ if (agentMax)
471
+ return Math.min(agentMax, modelMax);
472
+ return Math.min(DEFAULT_OUTPUT_TOKENS, modelMax);
473
+ }
474
+ getContextManagement(_model) {
475
+ // Gemini doesn't use Anthropic betas or context management
476
+ return { betas: [], config: { edits: [] } };
477
+ }
478
+ getCompactionConfig(_model) {
479
+ return { triggerTokens: 700_000, totalBudget: 4_000_000, isNative: false };
480
+ }
481
+ getCapabilities() {
482
+ return getCapabilities("gemini");
483
+ }
484
+ }
485
+ // Auto-register on import
486
+ registerProvider("gemini", new GeminiAdapter());
@@ -0,0 +1,24 @@
1
+ /**
2
+ * OpenAI Provider Adapter — Responses API (2026) with format conversion to Anthropic SSE.
3
+ *
4
+ * Contains:
5
+ * - anthropicToResponsesInput() — messages → Responses API format
6
+ * - sanitizeSchemaForOpenAI() — schema cleanup
7
+ * - anthropicToResponsesTools() — tool format conversion
8
+ * - Streaming with 12 event type handlers
9
+ * - pendingFunctionCalls tracking (per-request, not shared)
10
+ *
11
+ * Phase 7.1: Also encapsulates thinking config, context management,
12
+ * output token limits, and compaction config for OpenAI models.
13
+ */
14
+ import type { ProviderAdapter, ProviderStreamConfig, ProviderCapabilities, ThinkingConfigResult, ContextManagementConfig, CompactionConfig } from "./types.js";
15
+ import type http from "node:http";
16
+ export declare class OpenAIAdapter implements ProviderAdapter {
17
+ name: string;
18
+ handleStream(res: http.ServerResponse, config: ProviderStreamConfig, corsHeaders: Record<string, string>): Promise<void>;
19
+ getThinkingConfig(model: string, enabled: boolean): ThinkingConfigResult;
20
+ getMaxOutputTokens(model: string, agentMax?: number): number;
21
+ getContextManagement(_model: string): ContextManagementConfig;
22
+ getCompactionConfig(_model: string): CompactionConfig;
23
+ getCapabilities(): ProviderCapabilities;
24
+ }