@terminai/core 0.21.0 → 0.25.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 (303) hide show
  1. package/dist/docs/assets/image.png +0 -0
  2. package/dist/src/audit/redaction.js +24 -0
  3. package/dist/src/audit/redaction.js.map +1 -1
  4. package/dist/src/audit/redaction.test.d.ts +7 -0
  5. package/dist/src/audit/redaction.test.js +45 -0
  6. package/dist/src/audit/redaction.test.js.map +1 -0
  7. package/dist/src/auth/geminiAuthStatus.js +1 -1
  8. package/dist/src/auth/geminiAuthStatus.js.map +1 -1
  9. package/dist/src/auth/geminiAuthStatus.test.js +1 -1
  10. package/dist/src/auth/geminiAuthStatus.test.js.map +1 -1
  11. package/dist/src/auth/providerRegistry.js +20 -0
  12. package/dist/src/auth/providerRegistry.js.map +1 -1
  13. package/dist/src/auth/wizardSettings.d.ts +5 -0
  14. package/dist/src/auth/wizardSettings.js +48 -1
  15. package/dist/src/auth/wizardSettings.js.map +1 -1
  16. package/dist/src/auth/wizardSettings.test.js +30 -2
  17. package/dist/src/auth/wizardSettings.test.js.map +1 -1
  18. package/dist/src/auth/wizardState.d.ts +1 -1
  19. package/dist/src/auth/wizardState.js +6 -0
  20. package/dist/src/auth/wizardState.js.map +1 -1
  21. package/dist/src/auth/wizardState.test.js +23 -0
  22. package/dist/src/auth/wizardState.test.js.map +1 -1
  23. package/dist/src/availability/policyHelpers.test.js +4 -4
  24. package/dist/src/availability/policyHelpers.test.js.map +1 -1
  25. package/dist/src/brain/__tests__/environmentDetector.test.js +2 -1
  26. package/dist/src/brain/__tests__/environmentDetector.test.js.map +1 -1
  27. package/dist/src/brain/__tests__/orchestratorFlag.test.d.ts +7 -0
  28. package/dist/src/brain/__tests__/orchestratorFlag.test.js +47 -0
  29. package/dist/src/brain/__tests__/orchestratorFlag.test.js.map +1 -0
  30. package/dist/src/brain/__tests__/orchestratorRecovery.test.d.ts +7 -0
  31. package/dist/src/brain/__tests__/orchestratorRecovery.test.js +85 -0
  32. package/dist/src/brain/__tests__/orchestratorRecovery.test.js.map +1 -0
  33. package/dist/src/brain/__tests__/thinkingOrchestrator.test.js +1 -0
  34. package/dist/src/brain/__tests__/thinkingOrchestrator.test.js.map +1 -1
  35. package/dist/src/brain/__tests__/toolIntegration.test.d.ts +7 -0
  36. package/dist/src/brain/__tests__/toolIntegration.test.js +154 -0
  37. package/dist/src/brain/__tests__/toolIntegration.test.js.map +1 -0
  38. package/dist/src/brain/advisors/codeGenerator.js +3 -2
  39. package/dist/src/brain/advisors/codeGenerator.js.map +1 -1
  40. package/dist/src/brain/advisors/depScanner.js +3 -2
  41. package/dist/src/brain/advisors/depScanner.js.map +1 -1
  42. package/dist/src/brain/advisors/enumerator.js +3 -2
  43. package/dist/src/brain/advisors/enumerator.js.map +1 -1
  44. package/dist/src/brain/advisors/fallbackChain.js +3 -2
  45. package/dist/src/brain/advisors/fallbackChain.js.map +1 -1
  46. package/dist/src/brain/advisors/patternMatcher.js +3 -2
  47. package/dist/src/brain/advisors/patternMatcher.js.map +1 -1
  48. package/dist/src/brain/consensus.js +2 -1
  49. package/dist/src/brain/consensus.js.map +1 -1
  50. package/dist/src/brain/environmentDetector.d.ts +1 -0
  51. package/dist/src/brain/environmentDetector.js +35 -20
  52. package/dist/src/brain/environmentDetector.js.map +1 -1
  53. package/dist/src/brain/pacLoop.js +2 -1
  54. package/dist/src/brain/pacLoop.js.map +1 -1
  55. package/dist/src/brain/riskAssessor.d.ts +6 -2
  56. package/dist/src/brain/riskAssessor.js +7 -4
  57. package/dist/src/brain/riskAssessor.js.map +1 -1
  58. package/dist/src/brain/stepBackEvaluator.js +2 -1
  59. package/dist/src/brain/stepBackEvaluator.js.map +1 -1
  60. package/dist/src/brain/thinkingOrchestrator.d.ts +9 -1
  61. package/dist/src/brain/thinkingOrchestrator.js +47 -1
  62. package/dist/src/brain/thinkingOrchestrator.js.map +1 -1
  63. package/dist/src/brain/toolIntegration.d.ts +51 -0
  64. package/dist/src/brain/toolIntegration.js +202 -0
  65. package/dist/src/brain/toolIntegration.js.map +1 -0
  66. package/dist/src/code_assist/oauth2.begin.test.js +4 -1
  67. package/dist/src/code_assist/oauth2.begin.test.js.map +1 -1
  68. package/dist/src/code_assist/oauth2.d.ts +1 -1
  69. package/dist/src/code_assist/oauth2.js +9 -13
  70. package/dist/src/code_assist/oauth2.js.map +1 -1
  71. package/dist/src/code_assist/oauth2.test.js +69 -41
  72. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  73. package/dist/src/commands/init.js +5 -0
  74. package/dist/src/commands/init.js.map +1 -1
  75. package/dist/src/computer/PersistentShell.d.ts +5 -0
  76. package/dist/src/computer/PersistentShell.js +21 -2
  77. package/dist/src/computer/PersistentShell.js.map +1 -1
  78. package/dist/src/computer/PersistentShell.test.js +23 -11
  79. package/dist/src/computer/PersistentShell.test.js.map +1 -1
  80. package/dist/src/config/builder.js +78 -5
  81. package/dist/src/config/builder.js.map +1 -1
  82. package/dist/src/config/config.d.ts +12 -1
  83. package/dist/src/config/config.js +64 -0
  84. package/dist/src/config/config.js.map +1 -1
  85. package/dist/src/config/config.test.js +39 -0
  86. package/dist/src/config/config.test.js.map +1 -1
  87. package/dist/src/config/models.d.ts +2 -2
  88. package/dist/src/config/models.js +2 -2
  89. package/dist/src/config/models.js.map +1 -1
  90. package/dist/src/config/models.test.js +1 -1
  91. package/dist/src/config/settings/schema.d.ts +78 -4
  92. package/dist/src/config/settings/schema.js +76 -4
  93. package/dist/src/config/settings/schema.js.map +1 -1
  94. package/dist/src/core/chatGptCodexContentGenerator.d.ts +35 -0
  95. package/dist/src/core/chatGptCodexContentGenerator.js +605 -0
  96. package/dist/src/core/chatGptCodexContentGenerator.js.map +1 -0
  97. package/dist/src/core/chatGptCodexContentGenerator.test.d.ts +7 -0
  98. package/dist/src/core/chatGptCodexContentGenerator.test.js +250 -0
  99. package/dist/src/core/chatGptCodexContentGenerator.test.js.map +1 -0
  100. package/dist/src/core/contentGenerator.d.ts +2 -1
  101. package/dist/src/core/contentGenerator.js +10 -0
  102. package/dist/src/core/contentGenerator.js.map +1 -1
  103. package/dist/src/core/contentGenerator.test.js +36 -0
  104. package/dist/src/core/contentGenerator.test.js.map +1 -1
  105. package/dist/src/core/loggingContentGenerator.d.ts +1 -0
  106. package/dist/src/core/loggingContentGenerator.js +30 -0
  107. package/dist/src/core/loggingContentGenerator.js.map +1 -1
  108. package/dist/src/core/loggingContentGenerator.test.js +62 -0
  109. package/dist/src/core/loggingContentGenerator.test.js.map +1 -1
  110. package/dist/src/core/openaiContentGenerator.d.ts +16 -0
  111. package/dist/src/core/openaiContentGenerator.js +252 -53
  112. package/dist/src/core/openaiContentGenerator.js.map +1 -1
  113. package/dist/src/core/openaiContentGenerator.test.js +176 -7
  114. package/dist/src/core/openaiContentGenerator.test.js.map +1 -1
  115. package/dist/src/core/providerTypes.d.ts +17 -1
  116. package/dist/src/core/providerTypes.js +10 -0
  117. package/dist/src/core/providerTypes.js.map +1 -1
  118. package/dist/src/core/providerTypes.test.js +11 -0
  119. package/dist/src/core/providerTypes.test.js.map +1 -1
  120. package/dist/src/core/turn.js +1 -1
  121. package/dist/src/core/turn.js.map +1 -1
  122. package/dist/src/core/turn.test.js +1 -1
  123. package/dist/src/core/turn.test.js.map +1 -1
  124. package/dist/src/fallback/handler.test.js +14 -14
  125. package/dist/src/fallback/handler.test.js.map +1 -1
  126. package/dist/src/generated/git-commit.d.ts +2 -2
  127. package/dist/src/generated/git-commit.js +2 -2
  128. package/dist/src/generated/git-commit.js.map +1 -1
  129. package/dist/src/gui/drivers/__tests__/sidecarContract.test.d.ts +2 -2
  130. package/dist/src/gui/drivers/__tests__/sidecarContract.test.js +3 -4
  131. package/dist/src/gui/drivers/__tests__/sidecarContract.test.js.map +1 -1
  132. package/dist/src/gui/drivers/linuxAtspiDriver.js +11 -4
  133. package/dist/src/gui/drivers/linuxAtspiDriver.js.map +1 -1
  134. package/dist/src/gui/drivers/windowsUiaDriver.js +2 -2
  135. package/dist/src/gui/drivers/windowsUiaDriver.js.map +1 -1
  136. package/dist/src/gui/selectors/__tests__/selectors.test.js +5 -2
  137. package/dist/src/gui/selectors/__tests__/selectors.test.js.map +1 -1
  138. package/dist/src/gui/selectors/parser.d.ts +1 -1
  139. package/dist/src/gui/selectors/parser.js +1 -1
  140. package/dist/src/gui/selectors/parser.js.map +1 -1
  141. package/dist/src/gui/service/DesktopAutomationService.d.ts +1 -1
  142. package/dist/src/gui/service/DesktopAutomationService.js +8 -9
  143. package/dist/src/gui/service/DesktopAutomationService.js.map +1 -1
  144. package/dist/src/gui/service/__tests__/capabilities.test.d.ts +6 -0
  145. package/dist/src/gui/service/__tests__/capabilities.test.js +32 -4
  146. package/dist/src/gui/service/__tests__/capabilities.test.js.map +1 -1
  147. package/dist/src/gui/service/__tests__/diagnose.test.d.ts +6 -0
  148. package/dist/src/gui/service/__tests__/diagnose.test.js +6 -0
  149. package/dist/src/gui/service/__tests__/diagnose.test.js.map +1 -1
  150. package/dist/src/gui/service/__tests__/progressive.test.d.ts +6 -0
  151. package/dist/src/gui/service/__tests__/progressive.test.js +30 -3
  152. package/dist/src/gui/service/__tests__/progressive.test.js.map +1 -1
  153. package/dist/src/gui/service/__tests__/redaction.test.d.ts +6 -0
  154. package/dist/src/gui/service/__tests__/redaction.test.js +27 -1
  155. package/dist/src/gui/service/__tests__/redaction.test.js.map +1 -1
  156. package/dist/src/gui/service/__tests__/selectorError.test.d.ts +6 -0
  157. package/dist/src/gui/service/__tests__/selectorError.test.js +6 -0
  158. package/dist/src/gui/service/__tests__/selectorError.test.js.map +1 -1
  159. package/dist/src/gui/service/__tests__/wait.test.d.ts +6 -0
  160. package/dist/src/gui/service/__tests__/wait.test.js +29 -2
  161. package/dist/src/gui/service/__tests__/wait.test.js.map +1 -1
  162. package/dist/src/index.d.ts +6 -0
  163. package/dist/src/index.js +7 -0
  164. package/dist/src/index.js.map +1 -1
  165. package/dist/src/openai_chatgpt/constants.d.ts +23 -0
  166. package/dist/src/openai_chatgpt/constants.js +35 -0
  167. package/dist/src/openai_chatgpt/constants.js.map +1 -0
  168. package/dist/src/openai_chatgpt/credentialStorage.d.ts +18 -0
  169. package/dist/src/openai_chatgpt/credentialStorage.js +116 -0
  170. package/dist/src/openai_chatgpt/credentialStorage.js.map +1 -0
  171. package/dist/src/openai_chatgpt/credentialStorage.test.d.ts +7 -0
  172. package/dist/src/openai_chatgpt/credentialStorage.test.js +73 -0
  173. package/dist/src/openai_chatgpt/credentialStorage.test.js.map +1 -0
  174. package/dist/src/openai_chatgpt/imports.d.ts +10 -0
  175. package/dist/src/openai_chatgpt/imports.js +164 -0
  176. package/dist/src/openai_chatgpt/imports.js.map +1 -0
  177. package/dist/src/openai_chatgpt/imports.test.d.ts +7 -0
  178. package/dist/src/openai_chatgpt/imports.test.js +82 -0
  179. package/dist/src/openai_chatgpt/imports.test.js.map +1 -0
  180. package/dist/src/openai_chatgpt/jwt.d.ts +7 -0
  181. package/dist/src/openai_chatgpt/jwt.js +31 -0
  182. package/dist/src/openai_chatgpt/jwt.js.map +1 -0
  183. package/dist/src/openai_chatgpt/jwt.test.d.ts +7 -0
  184. package/dist/src/openai_chatgpt/jwt.test.js +23 -0
  185. package/dist/src/openai_chatgpt/jwt.test.js.map +1 -0
  186. package/dist/src/openai_chatgpt/oauthClient.d.ts +52 -0
  187. package/dist/src/openai_chatgpt/oauthClient.js +196 -0
  188. package/dist/src/openai_chatgpt/oauthClient.js.map +1 -0
  189. package/dist/src/openai_chatgpt/oauthClient.test.d.ts +7 -0
  190. package/dist/src/openai_chatgpt/oauthClient.test.js +138 -0
  191. package/dist/src/openai_chatgpt/oauthClient.test.js.map +1 -0
  192. package/dist/src/openai_chatgpt/types.d.ts +34 -0
  193. package/dist/src/openai_chatgpt/types.js +8 -0
  194. package/dist/src/openai_chatgpt/types.js.map +1 -0
  195. package/dist/src/policy/shell-safety.test.js +2 -1
  196. package/dist/src/policy/shell-safety.test.js.map +1 -1
  197. package/dist/src/safety/approval-ladder/__tests__/buildShellActionProfile.test.js +2 -1
  198. package/dist/src/safety/approval-ladder/__tests__/buildShellActionProfile.test.js.map +1 -1
  199. package/dist/src/safety/built-in.test.js +4 -2
  200. package/dist/src/safety/built-in.test.js.map +1 -1
  201. package/dist/src/telemetry/metrics.test.js +95 -75
  202. package/dist/src/telemetry/metrics.test.js.map +1 -1
  203. package/dist/src/tools/__tests__/edit.risk.test.d.ts +7 -0
  204. package/dist/src/tools/__tests__/edit.risk.test.js +138 -0
  205. package/dist/src/tools/__tests__/edit.risk.test.js.map +1 -0
  206. package/dist/src/tools/__tests__/memoryToolDedupe.test.d.ts +7 -0
  207. package/dist/src/tools/__tests__/memoryToolDedupe.test.js +53 -0
  208. package/dist/src/tools/__tests__/memoryToolDedupe.test.js.map +1 -0
  209. package/dist/src/tools/__tests__/shell.pin.test.js +2 -1
  210. package/dist/src/tools/__tests__/shell.pin.test.js.map +1 -1
  211. package/dist/src/tools/__tests__/shell.provenance.test.js +14 -4
  212. package/dist/src/tools/__tests__/shell.provenance.test.js.map +1 -1
  213. package/dist/src/tools/__tests__/ui-diagnose.test.d.ts +6 -0
  214. package/dist/src/tools/__tests__/ui-diagnose.test.js +11 -8
  215. package/dist/src/tools/__tests__/ui-diagnose.test.js.map +1 -1
  216. package/dist/src/tools/__tests__/ui-health.test.d.ts +6 -0
  217. package/dist/src/tools/__tests__/ui-health.test.js +37 -37
  218. package/dist/src/tools/__tests__/ui-health.test.js.map +1 -1
  219. package/dist/src/tools/agent-control.js +6 -18
  220. package/dist/src/tools/agent-control.js.map +1 -1
  221. package/dist/src/tools/agent-control.test.js +2 -28
  222. package/dist/src/tools/agent-control.test.js.map +1 -1
  223. package/dist/src/tools/edit.js +49 -9
  224. package/dist/src/tools/edit.js.map +1 -1
  225. package/dist/src/tools/edit.test.js +1 -19
  226. package/dist/src/tools/edit.test.js.map +1 -1
  227. package/dist/src/tools/file-ops.js +9 -10
  228. package/dist/src/tools/file-ops.js.map +1 -1
  229. package/dist/src/tools/file-ops.test.js +0 -7
  230. package/dist/src/tools/file-ops.test.js.map +1 -1
  231. package/dist/src/tools/glob.js +4 -16
  232. package/dist/src/tools/glob.js.map +1 -1
  233. package/dist/src/tools/glob.test.js +0 -29
  234. package/dist/src/tools/glob.test.js.map +1 -1
  235. package/dist/src/tools/grep.js +4 -10
  236. package/dist/src/tools/grep.js.map +1 -1
  237. package/dist/src/tools/ls.d.ts +1 -1
  238. package/dist/src/tools/ls.js +8 -7
  239. package/dist/src/tools/ls.js.map +1 -1
  240. package/dist/src/tools/ls.test.js +0 -7
  241. package/dist/src/tools/ls.test.js.map +1 -1
  242. package/dist/src/tools/mcp-client-manager.js +8 -6
  243. package/dist/src/tools/mcp-client-manager.js.map +1 -1
  244. package/dist/src/tools/mcp-client-manager.test.js +0 -10
  245. package/dist/src/tools/mcp-client-manager.test.js.map +1 -1
  246. package/dist/src/tools/memoryTool.js +10 -0
  247. package/dist/src/tools/memoryTool.js.map +1 -1
  248. package/dist/src/tools/process-manager.js +6 -5
  249. package/dist/src/tools/process-manager.js.map +1 -1
  250. package/dist/src/tools/process-manager.test.js +2 -1
  251. package/dist/src/tools/process-manager.test.js.map +1 -1
  252. package/dist/src/tools/read-file.js +8 -10
  253. package/dist/src/tools/read-file.js.map +1 -1
  254. package/dist/src/tools/read-file.test.js +0 -12
  255. package/dist/src/tools/read-file.test.js.map +1 -1
  256. package/dist/src/tools/read-many-files.js +2 -7
  257. package/dist/src/tools/read-many-files.js.map +1 -1
  258. package/dist/src/tools/repl.js +10 -45
  259. package/dist/src/tools/repl.js.map +1 -1
  260. package/dist/src/tools/ripGrep.js +2 -5
  261. package/dist/src/tools/ripGrep.js.map +1 -1
  262. package/dist/src/tools/ripGrep.test.js +1 -1
  263. package/dist/src/tools/ripGrep.test.js.map +1 -1
  264. package/dist/src/tools/shell.d.ts +2 -11
  265. package/dist/src/tools/shell.js +31 -191
  266. package/dist/src/tools/shell.js.map +1 -1
  267. package/dist/src/tools/shell.test.js +2 -1
  268. package/dist/src/tools/shell.test.js.map +1 -1
  269. package/dist/src/tools/smart-edit.js +2 -5
  270. package/dist/src/tools/smart-edit.js.map +1 -1
  271. package/dist/src/tools/smart-edit.test.js +0 -9
  272. package/dist/src/tools/smart-edit.test.js.map +1 -1
  273. package/dist/src/tools/tools.d.ts +5 -0
  274. package/dist/src/tools/tools.js.map +1 -1
  275. package/dist/src/tools/ui-click.js +69 -3
  276. package/dist/src/tools/ui-click.js.map +1 -1
  277. package/dist/src/tools/write-file.js +34 -8
  278. package/dist/src/tools/write-file.js.map +1 -1
  279. package/dist/src/tools/write-file.test.js +1 -15
  280. package/dist/src/tools/write-file.test.js.map +1 -1
  281. package/dist/src/utils/errorReporting.js +1 -1
  282. package/dist/src/utils/errorReporting.js.map +1 -1
  283. package/dist/src/utils/errorReporting.test.js +1 -1
  284. package/dist/src/utils/errorReporting.test.js.map +1 -1
  285. package/dist/src/utils/events.d.ts +11 -0
  286. package/dist/src/utils/events.js +1 -0
  287. package/dist/src/utils/events.js.map +1 -1
  288. package/dist/src/utils/fileUtils.js +2 -7
  289. package/dist/src/utils/fileUtils.js.map +1 -1
  290. package/dist/src/utils/processUtils.d.ts +43 -0
  291. package/dist/src/utils/processUtils.js +134 -0
  292. package/dist/src/utils/processUtils.js.map +1 -0
  293. package/dist/src/utils/shell-permissions.d.ts +1 -0
  294. package/dist/src/utils/shell-permissions.js +3 -3
  295. package/dist/src/utils/shell-permissions.js.map +1 -1
  296. package/dist/src/utils/shell-permissions.test.js +11 -5
  297. package/dist/src/utils/shell-permissions.test.js.map +1 -1
  298. package/dist/src/utils/shell-utils.js +6 -3
  299. package/dist/src/utils/shell-utils.js.map +1 -1
  300. package/dist/src/utils/shell-utils.test.js +6 -2
  301. package/dist/src/utils/shell-utils.test.js.map +1 -1
  302. package/dist/tsconfig.tsbuildinfo +1 -1
  303. package/package.json +5 -3
@@ -19,6 +19,7 @@ vi.mock('../telemetry/trace.js', () => ({
19
19
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
20
20
  import { LoggingContentGenerator } from './loggingContentGenerator.js';
21
21
  import { ApiRequestEvent } from '../telemetry/types.js';
22
+ import { LlmProviderId } from './providerTypes.js';
22
23
  describe('LoggingContentGenerator', () => {
23
24
  let wrapped;
24
25
  let config;
@@ -33,6 +34,9 @@ describe('LoggingContentGenerator', () => {
33
34
  config = {
34
35
  getGoogleAIConfig: vi.fn(),
35
36
  getVertexAIConfig: vi.fn(),
37
+ getProviderConfig: vi
38
+ .fn()
39
+ .mockReturnValue({ provider: LlmProviderId.GEMINI }),
36
40
  getContentGeneratorConfig: vi.fn().mockReturnValue({
37
41
  authType: 'API_KEY',
38
42
  }),
@@ -75,6 +79,35 @@ describe('LoggingContentGenerator', () => {
75
79
  const responseEvent = vi.mocked(logApiResponse).mock.calls[0][1];
76
80
  expect(responseEvent.duration_ms).toBe(1000);
77
81
  });
82
+ it('should attribute server details for OpenAI-compatible providers', async () => {
83
+ vi.mocked(config.getProviderConfig).mockReturnValue({
84
+ provider: LlmProviderId.OPENAI_COMPATIBLE,
85
+ baseUrl: 'https://openrouter.ai/api/v1',
86
+ model: 'gpt-4o-mini',
87
+ });
88
+ const req = {
89
+ contents: [{ role: 'user', parts: [{ text: 'hello' }] }],
90
+ model: 'gpt-4o-mini',
91
+ };
92
+ const userPromptId = 'prompt-123';
93
+ const response = {
94
+ candidates: [],
95
+ usageMetadata: undefined,
96
+ text: undefined,
97
+ functionCalls: undefined,
98
+ executableCode: undefined,
99
+ codeExecutionResult: undefined,
100
+ data: undefined,
101
+ };
102
+ vi.mocked(wrapped.generateContent).mockResolvedValue(response);
103
+ await loggingContentGenerator.generateContent(req, userPromptId);
104
+ const requestEvent = vi.mocked(logApiRequest).mock.calls[0][1];
105
+ expect(requestEvent).toBeInstanceOf(ApiRequestEvent);
106
+ expect(requestEvent.prompt.server).toEqual({
107
+ address: 'openrouter.ai',
108
+ port: 443,
109
+ });
110
+ });
78
111
  it('should log error on failure', async () => {
79
112
  const req = {
80
113
  contents: [{ role: 'user', parts: [{ text: 'hello' }] }],
@@ -124,6 +157,35 @@ describe('LoggingContentGenerator', () => {
124
157
  const responseEvent = vi.mocked(logApiResponse).mock.calls[0][1];
125
158
  expect(responseEvent.duration_ms).toBe(1000);
126
159
  });
160
+ it('should attribute server details for OpenAI-compatible streaming calls', async () => {
161
+ vi.mocked(config.getProviderConfig).mockReturnValue({
162
+ provider: LlmProviderId.OPENAI_COMPATIBLE,
163
+ baseUrl: 'http://localhost:1234/v1',
164
+ model: 'gpt-4o-mini',
165
+ });
166
+ const req = {
167
+ contents: [{ role: 'user', parts: [{ text: 'hello' }] }],
168
+ model: 'gpt-4o-mini',
169
+ };
170
+ const userPromptId = 'prompt-123';
171
+ const response = {
172
+ candidates: [],
173
+ usageMetadata: undefined,
174
+ };
175
+ async function* createAsyncGenerator() {
176
+ yield response;
177
+ }
178
+ vi.mocked(wrapped.generateContentStream).mockResolvedValue(createAsyncGenerator());
179
+ const stream = await loggingContentGenerator.generateContentStream(req, userPromptId);
180
+ for await (const _ of stream) {
181
+ // consume stream
182
+ }
183
+ const requestEvent = vi.mocked(logApiRequest).mock.calls[0][1];
184
+ expect(requestEvent.prompt.server).toEqual({
185
+ address: 'localhost',
186
+ port: 1234,
187
+ });
188
+ });
127
189
  it('should log error on failure', async () => {
128
190
  const req = {
129
191
  contents: [{ role: 'user', parts: [{ text: 'hello' }] }],
@@ -1 +1 @@
1
- {"version":3,"file":"loggingContentGenerator.test.js","sourceRoot":"","sources":["../../../src/core/loggingContentGenerator.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"loggingContentGenerator.test.js","sourceRoot":"","sources":["../../../src/core/loggingContentGenerator.test.ts"],"names":[],"mappings":"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"}
@@ -11,9 +11,13 @@ import type { Config } from '../config/config.js';
11
11
  export declare class OpenAIContentGenerator implements ContentGenerator {
12
12
  private readonly config;
13
13
  private readonly globalConfig;
14
+ private toolNameMap;
14
15
  constructor(config: OpenAICompatibleConfig, globalConfig: Config);
15
16
  private hasUnsupportedModalities;
16
17
  private partUnionToText;
18
+ private isPlainObject;
19
+ private getParametersJsonSchema;
20
+ private parseToolArguments;
17
21
  private contentUnionToText;
18
22
  private normalizeTools;
19
23
  generateContent(request: GenerateContentParameters, _userPromptId: string): Promise<GenerateContentResponse>;
@@ -22,6 +26,18 @@ export declare class OpenAIContentGenerator implements ContentGenerator {
22
26
  embedContent(_request: EmbedContentParameters): Promise<EmbedContentResponse>;
23
27
  private fetchOpenAI;
24
28
  private convertTools;
29
+ /**
30
+ * Sanitizes tool names for OpenAI function calling compatibility.
31
+ * OpenAI spec only allows: ^[a-zA-Z0-9_-]+$ (no dots, must not start with number)
32
+ * This converts names like "ui.click" → "ui_click" at the adapter level,
33
+ * keeping Gemini tool names unchanged in the core codebase.
34
+ * Records the mapping so we can reverse it when parsing model responses.
35
+ */
36
+ private sanitizeToolName;
37
+ /**
38
+ * Reverses tool name sanitization to get original Gemini name.
39
+ */
40
+ private unsanitizeToolName;
25
41
  private convertSchemaToOpenAI;
26
42
  private convertContentsToOpenAIMessages;
27
43
  private convertOpenAIResponseToGemini;
@@ -11,6 +11,9 @@ import { estimateTokenCountSync } from '../utils/tokenCalculation.js';
11
11
  export class OpenAIContentGenerator {
12
12
  config;
13
13
  globalConfig;
14
+ // Maps sanitized OpenAI tool names back to original Gemini names
15
+ // e.g., "ui_click" → "ui.click"
16
+ toolNameMap = new Map();
14
17
  constructor(config, globalConfig) {
15
18
  this.config = config;
16
19
  this.globalConfig = globalConfig;
@@ -41,6 +44,32 @@ export class OpenAIContentGenerator {
41
44
  }
42
45
  return '';
43
46
  }
47
+ isPlainObject(value) {
48
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
49
+ }
50
+ getParametersJsonSchema(fn) {
51
+ if (!this.isPlainObject(fn)) {
52
+ return null;
53
+ }
54
+ const parametersJsonSchema = fn['parametersJsonSchema'];
55
+ return this.isPlainObject(parametersJsonSchema)
56
+ ? parametersJsonSchema
57
+ : null;
58
+ }
59
+ parseToolArguments(value) {
60
+ if (value === undefined || value === null) {
61
+ return null;
62
+ }
63
+ if (typeof value === 'string') {
64
+ const trimmed = value.trim();
65
+ if (!trimmed) {
66
+ return null;
67
+ }
68
+ const parsed = JSON.parse(trimmed);
69
+ return this.isPlainObject(parsed) ? parsed : {};
70
+ }
71
+ return this.isPlainObject(value) ? value : null;
72
+ }
44
73
  contentUnionToText(content) {
45
74
  if (!content) {
46
75
  return '';
@@ -69,7 +98,7 @@ export class OpenAIContentGenerator {
69
98
  async generateContent(request, _userPromptId) {
70
99
  const contents = toContents(request.contents);
71
100
  if (this.hasUnsupportedModalities(contents)) {
72
- throw new Error('This provider is configured for text-only. Images and files are not supported.');
101
+ throw new Error('The OpenAI provider currently only supports text. To use images or other files, please switch to the Gemini provider.');
73
102
  }
74
103
  const messages = this.convertContentsToOpenAIMessages(contents);
75
104
  const systemText = this.contentUnionToText(request.config?.systemInstruction);
@@ -92,6 +121,21 @@ export class OpenAIContentGenerator {
92
121
  if (request.config?.temperature !== undefined) {
93
122
  body['temperature'] = request.config.temperature;
94
123
  }
124
+ if (request.config?.topP !== undefined) {
125
+ body['top_p'] = request.config.topP;
126
+ }
127
+ if (request.config?.presencePenalty !== undefined) {
128
+ body['presence_penalty'] = request.config.presencePenalty;
129
+ }
130
+ if (request.config?.frequencyPenalty !== undefined) {
131
+ body['frequency_penalty'] = request.config.frequencyPenalty;
132
+ }
133
+ if (request.config?.seed !== undefined) {
134
+ body['seed'] = request.config.seed;
135
+ }
136
+ if (request.config?.stopSequences) {
137
+ body['stop'] = request.config.stopSequences;
138
+ }
95
139
  const response = await this.fetchOpenAI('/chat/completions', body, request.config?.abortSignal);
96
140
  const data = (await response.json());
97
141
  return this.convertOpenAIResponseToGemini(data);
@@ -99,7 +143,7 @@ export class OpenAIContentGenerator {
99
143
  async generateContentStream(request, _userPromptId) {
100
144
  const contents = toContents(request.contents);
101
145
  if (this.hasUnsupportedModalities(contents)) {
102
- throw new Error('This provider is configured for text-only. Images and files are not supported.');
146
+ throw new Error('The OpenAI provider currently only supports text. To use images or other files, please switch to the Gemini provider.');
103
147
  }
104
148
  const messages = this.convertContentsToOpenAIMessages(contents);
105
149
  const systemText = this.contentUnionToText(request.config?.systemInstruction);
@@ -121,15 +165,30 @@ export class OpenAIContentGenerator {
121
165
  body['max_tokens'] = request.config.maxOutputTokens;
122
166
  if (request.config?.temperature !== undefined)
123
167
  body['temperature'] = request.config.temperature;
168
+ if (request.config?.topP !== undefined)
169
+ body['top_p'] = request.config.topP;
170
+ if (request.config?.presencePenalty !== undefined)
171
+ body['presence_penalty'] = request.config.presencePenalty;
172
+ if (request.config?.frequencyPenalty !== undefined)
173
+ body['frequency_penalty'] = request.config.frequencyPenalty;
174
+ if (request.config?.seed !== undefined)
175
+ body['seed'] = request.config.seed;
176
+ if (request.config?.stopSequences)
177
+ body['stop'] = request.config.stopSequences;
178
+ // Request usage implementation for streaming
179
+ body['stream_options'] = { include_usage: true };
124
180
  const response = await this.fetchOpenAI('/chat/completions', body, request.config?.abortSignal);
125
181
  if (!response.body) {
126
182
  throw new Error('No response body from OpenAI stream');
127
183
  }
128
184
  const debugMode = this.globalConfig.getDebugMode();
185
+ const toolNameMap = this.toolNameMap;
186
+ const parseToolArguments = (value) => this.parseToolArguments(value);
129
187
  return (async function* () {
130
188
  const reader = response.body.getReader();
131
189
  const decoder = new TextDecoder();
132
190
  let buffer = '';
191
+ let hasYieldedAnyText = false;
133
192
  // Buffer for streaming tool calls
134
193
  const pendingToolCalls = {};
135
194
  try {
@@ -147,20 +206,23 @@ export class OpenAIContentGenerator {
147
206
  if (trimmed.startsWith('data: ')) {
148
207
  try {
149
208
  const data = JSON.parse(trimmed.slice(6));
150
- const delta = data.choices?.[0]?.delta;
151
- const finishReason = data.choices?.[0]?.finish_reason?.toUpperCase();
209
+ const choice = data.choices?.[0];
210
+ const delta = choice?.delta;
211
+ const message = choice?.message;
212
+ const finishReason = choice?.finish_reason?.toUpperCase();
152
213
  // 1. Handle Text Content
153
214
  if (delta?.content) {
154
- yield {
155
- candidates: [
156
- {
157
- content: {
158
- role: 'model',
159
- parts: [{ text: delta.content }],
160
- },
215
+ hasYieldedAnyText = true;
216
+ const resp = new GenerateContentResponse();
217
+ resp.candidates = [
218
+ {
219
+ content: {
220
+ role: 'model',
221
+ parts: [{ text: delta.content }],
161
222
  },
162
- ],
163
- };
223
+ },
224
+ ];
225
+ yield resp;
164
226
  }
165
227
  // 2. Accumulate Tool Calls
166
228
  if (delta?.tool_calls) {
@@ -172,14 +234,21 @@ export class OpenAIContentGenerator {
172
234
  type: 'function',
173
235
  function: {
174
236
  name: tc.function?.name || '',
175
- arguments: tc.function?.arguments || '',
237
+ arguments: typeof tc.function?.arguments === 'string'
238
+ ? tc.function.arguments
239
+ : '',
176
240
  },
177
241
  };
178
242
  }
179
243
  else {
244
+ if (tc.id && !pendingToolCalls[index].id) {
245
+ pendingToolCalls[index].id = tc.id;
246
+ }
180
247
  if (tc.function?.arguments) {
181
- pendingToolCalls[index].function.arguments +=
182
- tc.function.arguments;
248
+ if (typeof tc.function.arguments === 'string') {
249
+ pendingToolCalls[index].function.arguments +=
250
+ tc.function.arguments;
251
+ }
183
252
  }
184
253
  if (tc.function?.name) {
185
254
  pendingToolCalls[index].function.name +=
@@ -188,10 +257,28 @@ export class OpenAIContentGenerator {
188
257
  }
189
258
  }
190
259
  }
191
- // 3. Handle Finish
260
+ // 3. Handle Usage Metadata (streaming)
261
+ if (data.usage) {
262
+ const usage = data.usage;
263
+ const resp = new GenerateContentResponse();
264
+ resp.usageMetadata = {
265
+ promptTokenCount: usage.prompt_tokens,
266
+ candidatesTokenCount: usage.completion_tokens,
267
+ totalTokenCount: usage.total_tokens,
268
+ };
269
+ // If usage comes in a separate chunk without content, we must yield it
270
+ // Often it's the last chunk.
271
+ yield resp;
272
+ }
273
+ // 4. Handle Finish
192
274
  if (finishReason) {
193
275
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
194
276
  const parts = [];
277
+ if (!hasYieldedAnyText &&
278
+ typeof message?.content === 'string') {
279
+ parts.push({ text: message.content });
280
+ }
281
+ let hasEmittedToolCall = false;
195
282
  // If we have buffered tool calls, finalize them now
196
283
  const toolIndices = Object.keys(pendingToolCalls)
197
284
  .map(Number)
@@ -199,13 +286,18 @@ export class OpenAIContentGenerator {
199
286
  for (const index of toolIndices) {
200
287
  const tc = pendingToolCalls[index];
201
288
  try {
202
- const args = JSON.parse(tc.function.arguments);
289
+ const parsedArgs = parseToolArguments(tc.function.arguments);
290
+ const args = parsedArgs ?? {};
291
+ // Unsanitize tool name back to original Gemini format
292
+ const originalName = toolNameMap.get(tc.function.name) || tc.function.name;
203
293
  parts.push({
204
294
  functionCall: {
205
- name: tc.function.name,
295
+ ...(tc.id ? { id: tc.id } : {}),
296
+ name: originalName,
206
297
  args,
207
298
  },
208
299
  });
300
+ hasEmittedToolCall = true;
209
301
  }
210
302
  catch (e) {
211
303
  if (debugMode) {
@@ -213,17 +305,39 @@ export class OpenAIContentGenerator {
213
305
  }
214
306
  }
215
307
  }
216
- yield {
217
- candidates: [
218
- {
219
- content: {
220
- role: 'model',
221
- parts,
222
- },
223
- finishReason: finishReason,
308
+ if (!hasEmittedToolCall && message?.tool_calls) {
309
+ for (const tc of message.tool_calls) {
310
+ try {
311
+ const parsedArgs = parseToolArguments(tc.function.arguments);
312
+ const args = parsedArgs ?? {};
313
+ parts.push({
314
+ functionCall: {
315
+ ...(tc.id ? { id: tc.id } : {}),
316
+ name: toolNameMap.get(tc.function.name || '') ||
317
+ tc.function.name ||
318
+ '',
319
+ args,
320
+ },
321
+ });
322
+ }
323
+ catch (e) {
324
+ if (debugMode) {
325
+ console.error('[OpenAI Provider] Failed to parse message tool arguments:', e);
326
+ }
327
+ }
328
+ }
329
+ }
330
+ const resp = new GenerateContentResponse();
331
+ resp.candidates = [
332
+ {
333
+ content: {
334
+ role: 'model',
335
+ parts,
224
336
  },
225
- ],
226
- };
337
+ finishReason: finishReason,
338
+ },
339
+ ];
340
+ yield resp;
227
341
  }
228
342
  }
229
343
  catch (e) {
@@ -258,7 +372,14 @@ export class OpenAIContentGenerator {
258
372
  throw new Error('Embeddings not supported for OpenAI-compatible provider yet.');
259
373
  }
260
374
  async fetchOpenAI(endpoint, body, signal) {
261
- const url = `${this.config.baseUrl.replace(/\/$/, '')}${endpoint}`;
375
+ const baseUrl = this.config.baseUrl.trim();
376
+ if (baseUrl.length === 0) {
377
+ throw new Error('OpenAI-compatible baseUrl is missing. Set llm.openaiCompatible.baseUrl (or run /auth wizard) and try again.');
378
+ }
379
+ if (!/^https?:\/\//i.test(baseUrl)) {
380
+ throw new Error(`OpenAI-compatible baseUrl must start with http:// or https:// (got "${this.config.baseUrl}").`);
381
+ }
382
+ const url = `${baseUrl.replace(/\/+$/, '')}${endpoint}`;
262
383
  const headers = {
263
384
  'Content-Type': 'application/json',
264
385
  ...this.config.headers,
@@ -287,26 +408,55 @@ export class OpenAIContentGenerator {
287
408
  if (proxy) {
288
409
  options.dispatcher = new ProxyAgent(proxy);
289
410
  }
290
- const response = await fetch(url, options);
291
- if (!response.ok) {
292
- const text = await response.text();
293
- throw new Error(`OpenAI Provider Error (${response.status}): ${text}`);
411
+ let attempt = 0;
412
+ while (true) {
413
+ attempt++;
414
+ try {
415
+ const response = await fetch(url, options);
416
+ if (response.ok) {
417
+ return response;
418
+ }
419
+ // Check for retryable errors (429, 503)
420
+ if (attempt < 3 &&
421
+ (response.status === 429 || response.status >= 500)) {
422
+ const delay = Math.pow(2, attempt) * 1000 + Math.random() * 500;
423
+ if (this.globalConfig.getDebugMode()) {
424
+ console.warn(`[OpenAI] Request failed with ${response.status}. Retrying in ${Math.round(delay)}ms (attempt ${attempt}/3)`);
425
+ }
426
+ await new Promise((resolve) => setTimeout(resolve, delay));
427
+ continue;
428
+ }
429
+ const text = await response.text().catch(() => '');
430
+ throw new Error(`OpenAI compatible backend error (${response.status}): ${text}`);
431
+ }
432
+ catch (error) {
433
+ if (attempt < 3) {
434
+ const delay = Math.pow(2, attempt) * 1000 + Math.random() * 500;
435
+ if (this.globalConfig.getDebugMode()) {
436
+ console.warn(`[OpenAI] Network error. Retrying in ${Math.round(delay)}ms (attempt ${attempt}/3)`, error);
437
+ }
438
+ await new Promise((resolve) => setTimeout(resolve, delay));
439
+ continue;
440
+ }
441
+ throw error;
442
+ }
294
443
  }
295
- return response;
296
444
  }
297
445
  convertTools(tools) {
298
446
  const openAITools = [];
299
447
  for (const tool of tools) {
300
448
  if (tool.functionDeclarations) {
301
449
  for (const fn of tool.functionDeclarations) {
450
+ const parametersFromJsonSchema = this.getParametersJsonSchema(fn);
302
451
  openAITools.push({
303
452
  type: 'function',
304
453
  function: {
305
- name: fn.name || 'unknown_tool',
454
+ name: this.sanitizeToolName(fn.name || 'unknown_tool'),
306
455
  description: fn.description,
307
- parameters: fn.parameters
308
- ? this.convertSchemaToOpenAI(fn.parameters)
309
- : undefined,
456
+ parameters: parametersFromJsonSchema ??
457
+ (fn.parameters
458
+ ? this.convertSchemaToOpenAI(fn.parameters)
459
+ : undefined),
310
460
  },
311
461
  });
312
462
  }
@@ -314,6 +464,32 @@ export class OpenAIContentGenerator {
314
464
  }
315
465
  return openAITools;
316
466
  }
467
+ /**
468
+ * Sanitizes tool names for OpenAI function calling compatibility.
469
+ * OpenAI spec only allows: ^[a-zA-Z0-9_-]+$ (no dots, must not start with number)
470
+ * This converts names like "ui.click" → "ui_click" at the adapter level,
471
+ * keeping Gemini tool names unchanged in the core codebase.
472
+ * Records the mapping so we can reverse it when parsing model responses.
473
+ */
474
+ sanitizeToolName(name) {
475
+ // Replace dots and other invalid characters with underscores
476
+ let sanitized = name.replace(/[^a-zA-Z0-9_-]/g, '_');
477
+ // Ensure name doesn't start with a number
478
+ if (/^[0-9]/.test(sanitized)) {
479
+ sanitized = '_' + sanitized;
480
+ }
481
+ // Record mapping for reverse lookup
482
+ if (sanitized !== name) {
483
+ this.toolNameMap.set(sanitized, name);
484
+ }
485
+ return sanitized;
486
+ }
487
+ /**
488
+ * Reverses tool name sanitization to get original Gemini name.
489
+ */
490
+ unsanitizeToolName(sanitizedName) {
491
+ return this.toolNameMap.get(sanitizedName) || sanitizedName;
492
+ }
317
493
  convertSchemaToOpenAI(schema) {
318
494
  // Convert Gemini Schema to JSON Schema format
319
495
  // Gemini uses Type enum (OBJECT, STRING, etc.) while JSON Schema uses lowercase strings
@@ -424,9 +600,10 @@ export class OpenAIContentGenerator {
424
600
  if (part.functionCall) {
425
601
  const name = part.functionCall.name;
426
602
  if (name) {
427
- const id = `call_${name}_${Date.now()}_${Math.random()
428
- .toString(36)
429
- .slice(2, 7)}`;
603
+ const id = part.functionCall.id ||
604
+ `call_${name}_${Date.now()}_${Math.random()
605
+ .toString(36)
606
+ .slice(2, 7)}`;
430
607
  if (!toolCallIdStack[name])
431
608
  toolCallIdStack[name] = [];
432
609
  toolCallIdStack[name].push(id);
@@ -434,7 +611,7 @@ export class OpenAIContentGenerator {
434
611
  id,
435
612
  type: 'function',
436
613
  function: {
437
- name,
614
+ name: this.sanitizeToolName(name),
438
615
  arguments: JSON.stringify(part.functionCall.args),
439
616
  },
440
617
  });
@@ -452,8 +629,15 @@ export class OpenAIContentGenerator {
452
629
  }
453
630
  // Standard text message
454
631
  const textParts = (content.parts || [])
455
- .filter((p) => p.text)
456
- .map((p) => p.text)
632
+ .map((p) => {
633
+ if ('thought' in p && p.thought) {
634
+ return `[Thought: ${p.thought}]\n`;
635
+ }
636
+ if ('text' in p && p.text) {
637
+ return p.text;
638
+ }
639
+ return '';
640
+ })
457
641
  .join('');
458
642
  messages.push({
459
643
  role,
@@ -473,21 +657,29 @@ export class OpenAIContentGenerator {
473
657
  if (message?.tool_calls) {
474
658
  for (const toolCall of message.tool_calls) {
475
659
  if (toolCall.type === 'function') {
476
- let args = {};
477
660
  try {
478
- args = JSON.parse(toolCall.function.arguments || '{}');
661
+ const parsedArgs = this.parseToolArguments(toolCall.function.arguments);
662
+ const args = parsedArgs ?? {};
663
+ parts.push({
664
+ functionCall: {
665
+ id: toolCall.id,
666
+ name: this.unsanitizeToolName(toolCall.function.name || ''),
667
+ args,
668
+ },
669
+ });
479
670
  }
480
671
  catch (e) {
481
672
  if (this.globalConfig.getDebugMode()) {
482
673
  console.error('[OpenAI Provider] Failed to parse function arguments:', e);
483
674
  }
675
+ parts.push({
676
+ functionCall: {
677
+ id: toolCall.id,
678
+ name: this.unsanitizeToolName(toolCall.function.name || ''),
679
+ args: {},
680
+ },
681
+ });
484
682
  }
485
- parts.push({
486
- functionCall: {
487
- name: toolCall.function.name || '',
488
- args,
489
- },
490
- });
491
683
  }
492
684
  }
493
685
  }
@@ -501,6 +693,13 @@ export class OpenAIContentGenerator {
501
693
  };
502
694
  const response = new GenerateContentResponse();
503
695
  response.candidates = [candidate];
696
+ if (data.usage) {
697
+ response.usageMetadata = {
698
+ promptTokenCount: data.usage.prompt_tokens,
699
+ candidatesTokenCount: data.usage.completion_tokens,
700
+ totalTokenCount: data.usage.total_tokens,
701
+ };
702
+ }
504
703
  return response;
505
704
  }
506
705
  }