@nanocollective/nanocoder 1.17.2 → 1.18.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 (238) hide show
  1. package/README.md +3 -5
  2. package/assets/nanocoder-vscode.vsix +0 -0
  3. package/dist/ai-sdk-client-empty-message.spec.d.ts +2 -0
  4. package/dist/ai-sdk-client-empty-message.spec.d.ts.map +1 -0
  5. package/dist/ai-sdk-client-empty-message.spec.js +124 -0
  6. package/dist/ai-sdk-client-empty-message.spec.js.map +1 -0
  7. package/dist/ai-sdk-client-error-handling.spec.d.ts +2 -0
  8. package/dist/ai-sdk-client-error-handling.spec.d.ts.map +1 -0
  9. package/dist/ai-sdk-client-error-handling.spec.js +108 -0
  10. package/dist/ai-sdk-client-error-handling.spec.js.map +1 -0
  11. package/dist/ai-sdk-client-maxretries.spec.d.ts +2 -0
  12. package/dist/ai-sdk-client-maxretries.spec.d.ts.map +1 -0
  13. package/dist/ai-sdk-client-maxretries.spec.js +96 -0
  14. package/dist/ai-sdk-client-maxretries.spec.js.map +1 -0
  15. package/dist/ai-sdk-client.d.ts +25 -2
  16. package/dist/ai-sdk-client.d.ts.map +1 -1
  17. package/dist/ai-sdk-client.js +187 -124
  18. package/dist/ai-sdk-client.js.map +1 -1
  19. package/dist/app/utils/appUtils.js +3 -3
  20. package/dist/app/utils/appUtils.js.map +1 -1
  21. package/dist/app.d.ts.map +1 -1
  22. package/dist/app.js +22 -11
  23. package/dist/app.js.map +1 -1
  24. package/dist/commands/debugging.d.ts +3 -0
  25. package/dist/commands/debugging.d.ts.map +1 -0
  26. package/dist/commands/debugging.js +23 -0
  27. package/dist/commands/debugging.js.map +1 -0
  28. package/dist/commands/index.d.ts +3 -2
  29. package/dist/commands/index.d.ts.map +1 -1
  30. package/dist/commands/index.js +3 -2
  31. package/dist/commands/index.js.map +1 -1
  32. package/dist/commands/lsp.d.ts +15 -0
  33. package/dist/commands/lsp.d.ts.map +1 -0
  34. package/dist/commands/lsp.js +45 -0
  35. package/dist/commands/lsp.js.map +1 -0
  36. package/dist/commands/model-database.d.ts +8 -0
  37. package/dist/commands/model-database.d.ts.map +1 -0
  38. package/dist/commands/model-database.js +193 -0
  39. package/dist/commands/model-database.js.map +1 -0
  40. package/dist/commands/usage.d.ts.map +1 -1
  41. package/dist/commands/usage.js +1 -1
  42. package/dist/commands/usage.js.map +1 -1
  43. package/dist/components/assistant-message.d.ts.map +1 -1
  44. package/dist/components/assistant-message.js +3 -2
  45. package/dist/components/assistant-message.js.map +1 -1
  46. package/dist/components/status.d.ts +3 -0
  47. package/dist/components/status.d.ts.map +1 -1
  48. package/dist/components/status.js +24 -3
  49. package/dist/components/status.js.map +1 -1
  50. package/dist/components/user-input.d.ts.map +1 -1
  51. package/dist/components/user-input.js +2 -1
  52. package/dist/components/user-input.js.map +1 -1
  53. package/dist/config/preferences.d.ts +2 -2
  54. package/dist/config/preferences.js +5 -5
  55. package/dist/config/preferences.js.map +1 -1
  56. package/dist/context/mode-context.d.ts +12 -0
  57. package/dist/context/mode-context.d.ts.map +1 -0
  58. package/dist/context/mode-context.js +21 -0
  59. package/dist/context/mode-context.js.map +1 -0
  60. package/dist/context/mode-context.spec.d.ts +2 -0
  61. package/dist/context/mode-context.spec.d.ts.map +1 -0
  62. package/dist/context/mode-context.spec.js +61 -0
  63. package/dist/context/mode-context.spec.js.map +1 -0
  64. package/dist/hooks/useAppInitialization.d.ts +4 -2
  65. package/dist/hooks/useAppInitialization.d.ts.map +1 -1
  66. package/dist/hooks/useAppInitialization.js +102 -26
  67. package/dist/hooks/useAppInitialization.js.map +1 -1
  68. package/dist/hooks/useAppState.d.ts +7 -5
  69. package/dist/hooks/useAppState.d.ts.map +1 -1
  70. package/dist/hooks/useAppState.js +11 -7
  71. package/dist/hooks/useAppState.js.map +1 -1
  72. package/dist/hooks/useChatHandler.d.ts +2 -2
  73. package/dist/hooks/useChatHandler.d.ts.map +1 -1
  74. package/dist/hooks/useChatHandler.js +186 -111
  75. package/dist/hooks/useChatHandler.js.map +1 -1
  76. package/dist/hooks/useModeHandlers.d.ts +4 -4
  77. package/dist/hooks/useModeHandlers.d.ts.map +1 -1
  78. package/dist/hooks/useModeHandlers.js +9 -9
  79. package/dist/hooks/useModeHandlers.js.map +1 -1
  80. package/dist/mcp/mcp-client.d.ts.map +1 -1
  81. package/dist/mcp/mcp-client.js +16 -6
  82. package/dist/mcp/mcp-client.js.map +1 -1
  83. package/dist/message-handler.js +1 -1
  84. package/dist/message-handler.js.map +1 -1
  85. package/dist/model-database/database-engine.d.ts +25 -0
  86. package/dist/model-database/database-engine.d.ts.map +1 -0
  87. package/dist/model-database/database-engine.js +37 -0
  88. package/dist/model-database/database-engine.js.map +1 -0
  89. package/dist/model-database/database-engine.spec.d.ts +2 -0
  90. package/dist/model-database/database-engine.spec.d.ts.map +1 -0
  91. package/dist/model-database/database-engine.spec.js +413 -0
  92. package/dist/model-database/database-engine.spec.js.map +1 -0
  93. package/dist/model-database/model-database.d.ts +30 -0
  94. package/dist/model-database/model-database.d.ts.map +1 -0
  95. package/dist/model-database/model-database.js +82 -0
  96. package/dist/model-database/model-database.js.map +1 -0
  97. package/dist/model-database/model-database.spec.d.ts +2 -0
  98. package/dist/model-database/model-database.spec.d.ts.map +1 -0
  99. package/dist/model-database/model-database.spec.js +322 -0
  100. package/dist/model-database/model-database.spec.js.map +1 -0
  101. package/dist/model-database/model-engine.d.ts +25 -0
  102. package/dist/model-database/model-engine.d.ts.map +1 -0
  103. package/dist/model-database/model-engine.js +49 -0
  104. package/dist/model-database/model-engine.js.map +1 -0
  105. package/dist/model-database/model-engine.spec.d.ts +2 -0
  106. package/dist/model-database/model-engine.spec.d.ts.map +1 -0
  107. package/dist/model-database/model-engine.spec.js +381 -0
  108. package/dist/model-database/model-engine.spec.js.map +1 -0
  109. package/dist/model-database/model-fetcher.d.ts +14 -0
  110. package/dist/model-database/model-fetcher.d.ts.map +1 -0
  111. package/dist/model-database/model-fetcher.js +248 -0
  112. package/dist/model-database/model-fetcher.js.map +1 -0
  113. package/dist/model-database/model-fetcher.spec.d.ts +2 -0
  114. package/dist/model-database/model-fetcher.spec.d.ts.map +1 -0
  115. package/dist/model-database/model-fetcher.spec.js +498 -0
  116. package/dist/model-database/model-fetcher.spec.js.map +1 -0
  117. package/dist/models/models-dev-client.d.ts.map +1 -1
  118. package/dist/models/models-dev-client.js +8 -0
  119. package/dist/models/models-dev-client.js.map +1 -1
  120. package/dist/tool-calling/json-parser.d.ts.map +1 -1
  121. package/dist/tool-calling/json-parser.js +10 -4
  122. package/dist/tool-calling/json-parser.js.map +1 -1
  123. package/dist/tool-calling/tool-parser.d.ts.map +1 -1
  124. package/dist/tool-calling/tool-parser.js +37 -9
  125. package/dist/tool-calling/tool-parser.js.map +1 -1
  126. package/dist/tool-calling/tool-parser.spec.js +79 -1
  127. package/dist/tool-calling/tool-parser.spec.js.map +1 -1
  128. package/dist/tool-calling/xml-parser.d.ts.map +1 -1
  129. package/dist/tool-calling/xml-parser.js +21 -23
  130. package/dist/tool-calling/xml-parser.js.map +1 -1
  131. package/dist/tool-calling/xml-parser.spec.js +23 -3
  132. package/dist/tool-calling/xml-parser.spec.js.map +1 -1
  133. package/dist/tools/create-file.d.ts +24 -2
  134. package/dist/tools/create-file.d.ts.map +1 -1
  135. package/dist/tools/create-file.js +14 -10
  136. package/dist/tools/create-file.js.map +1 -1
  137. package/dist/tools/delete-lines.d.ts +18 -2
  138. package/dist/tools/delete-lines.d.ts.map +1 -1
  139. package/dist/tools/delete-lines.js +14 -9
  140. package/dist/tools/delete-lines.js.map +1 -1
  141. package/dist/tools/execute-bash.d.ts +2 -2
  142. package/dist/tools/execute-bash.d.ts.map +1 -1
  143. package/dist/tools/execute-bash.js +10 -9
  144. package/dist/tools/execute-bash.js.map +1 -1
  145. package/dist/tools/execute-function.spec.d.ts +2 -0
  146. package/dist/tools/execute-function.spec.d.ts.map +1 -0
  147. package/dist/tools/execute-function.spec.js +141 -0
  148. package/dist/tools/execute-function.spec.js.map +1 -0
  149. package/dist/tools/fetch-url.d.ts +16 -2
  150. package/dist/tools/fetch-url.d.ts.map +1 -1
  151. package/dist/tools/fetch-url.js +10 -10
  152. package/dist/tools/fetch-url.js.map +1 -1
  153. package/dist/tools/find-files.d.ts +18 -2
  154. package/dist/tools/find-files.d.ts.map +1 -1
  155. package/dist/tools/find-files.js +7 -5
  156. package/dist/tools/find-files.js.map +1 -1
  157. package/dist/tools/index.d.ts +2 -3
  158. package/dist/tools/index.d.ts.map +1 -1
  159. package/dist/tools/index.js +30 -20
  160. package/dist/tools/index.js.map +1 -1
  161. package/dist/tools/insert-lines.d.ts +18 -2
  162. package/dist/tools/insert-lines.d.ts.map +1 -1
  163. package/dist/tools/insert-lines.js +14 -9
  164. package/dist/tools/insert-lines.js.map +1 -1
  165. package/dist/tools/lsp-get-diagnostics.d.ts +10 -2
  166. package/dist/tools/lsp-get-diagnostics.d.ts.map +1 -1
  167. package/dist/tools/lsp-get-diagnostics.js +8 -6
  168. package/dist/tools/lsp-get-diagnostics.js.map +1 -1
  169. package/dist/tools/needs-approval.spec.d.ts +2 -0
  170. package/dist/tools/needs-approval.spec.d.ts.map +1 -0
  171. package/dist/tools/needs-approval.spec.js +308 -0
  172. package/dist/tools/needs-approval.spec.js.map +1 -0
  173. package/dist/tools/read-file.d.ts +2 -2
  174. package/dist/tools/read-file.d.ts.map +1 -1
  175. package/dist/tools/read-file.js +10 -12
  176. package/dist/tools/read-file.js.map +1 -1
  177. package/dist/tools/replace-lines.d.ts +19 -2
  178. package/dist/tools/replace-lines.d.ts.map +1 -1
  179. package/dist/tools/replace-lines.js +14 -9
  180. package/dist/tools/replace-lines.js.map +1 -1
  181. package/dist/tools/search-file-contents.d.ts +20 -2
  182. package/dist/tools/search-file-contents.d.ts.map +1 -1
  183. package/dist/tools/search-file-contents.js +11 -8
  184. package/dist/tools/search-file-contents.js.map +1 -1
  185. package/dist/tools/web-search.d.ts +2 -2
  186. package/dist/tools/web-search.d.ts.map +1 -1
  187. package/dist/tools/web-search.js +10 -10
  188. package/dist/tools/web-search.js.map +1 -1
  189. package/dist/types/app.d.ts +1 -1
  190. package/dist/types/app.d.ts.map +1 -1
  191. package/dist/types/config.d.ts +4 -1
  192. package/dist/types/config.d.ts.map +1 -1
  193. package/dist/types/core-connection-status.spec.d.ts +2 -0
  194. package/dist/types/core-connection-status.spec.d.ts.map +1 -0
  195. package/dist/types/core-connection-status.spec.js +52 -0
  196. package/dist/types/core-connection-status.spec.js.map +1 -0
  197. package/dist/types/core.d.ts +33 -28
  198. package/dist/types/core.d.ts.map +1 -1
  199. package/dist/types/core.js.map +1 -1
  200. package/dist/types/system.d.ts +5 -30
  201. package/dist/types/system.d.ts.map +1 -1
  202. package/dist/utils/prompt-processor.d.ts +2 -3
  203. package/dist/utils/prompt-processor.d.ts.map +1 -1
  204. package/dist/utils/prompt-processor.js +2 -50
  205. package/dist/utils/prompt-processor.js.map +1 -1
  206. package/dist/wizard/templates/provider-templates.d.ts.map +1 -1
  207. package/dist/wizard/templates/provider-templates.js +34 -2
  208. package/dist/wizard/templates/provider-templates.js.map +1 -1
  209. package/package.json +6 -5
  210. package/source/app/prompts/main-prompt.md +29 -96
  211. package/dist/commands/recommendations.d.ts +0 -8
  212. package/dist/commands/recommendations.d.ts.map +0 -1
  213. package/dist/commands/recommendations.js +0 -311
  214. package/dist/commands/recommendations.js.map +0 -1
  215. package/dist/commands/streaming.d.ts +0 -3
  216. package/dist/commands/streaming.d.ts.map +0 -1
  217. package/dist/commands/streaming.js +0 -23
  218. package/dist/commands/streaming.js.map +0 -1
  219. package/dist/components/thinking-indicator.d.ts +0 -3
  220. package/dist/components/thinking-indicator.d.ts.map +0 -1
  221. package/dist/components/thinking-indicator.js +0 -69
  222. package/dist/components/thinking-indicator.js.map +0 -1
  223. package/dist/recommendations/model-database.d.ts +0 -8
  224. package/dist/recommendations/model-database.d.ts.map +0 -1
  225. package/dist/recommendations/model-database.js +0 -514
  226. package/dist/recommendations/model-database.js.map +0 -1
  227. package/dist/recommendations/model-engine.d.ts +0 -16
  228. package/dist/recommendations/model-engine.d.ts.map +0 -1
  229. package/dist/recommendations/model-engine.js +0 -185
  230. package/dist/recommendations/model-engine.js.map +0 -1
  231. package/dist/recommendations/recommendation-engine.d.ts +0 -39
  232. package/dist/recommendations/recommendation-engine.d.ts.map +0 -1
  233. package/dist/recommendations/recommendation-engine.js +0 -144
  234. package/dist/recommendations/recommendation-engine.js.map +0 -1
  235. package/dist/system/detector.d.ts +0 -18
  236. package/dist/system/detector.d.ts.map +0 -1
  237. package/dist/system/detector.js +0 -140
  238. package/dist/system/detector.js.map +0 -1
package/README.md CHANGED
@@ -423,7 +423,6 @@ Preferences follow the same location hierarchy as configuration files:
423
423
  - **Last model per provider**: Your preferred model for each provider
424
424
  - **Session continuity**: Automatically switches back to your preferred provider/model when restarting
425
425
  - **Last theme used**: The theme you last selected
426
- - **Streaming preference**: Whether response streaming is enabled (toggle with `/streaming` command)
427
426
 
428
427
  **Manual management:**
429
428
 
@@ -451,8 +450,7 @@ You can override this directory using `NANOCODER_DATA_DIR`.
451
450
  - `/model` - Switch between available models
452
451
  - `/provider` - Switch between configured AI providers
453
452
  - `/status` - Display current status (CWD, provider, model, theme, available updates, AGENTS setup)
454
- - `/streaming` - Toggle response streaming on/off (streams tokens in real-time when enabled)
455
- - `/recommendations` - Get AI model recommendations based on your system capabilities (RAM, GPU, network)
453
+ - `/model-database` - Browse coding models from OpenRouter (searchable, filterable by open/proprietary)
456
454
  - `/mcp` - Show connected MCP servers and their tools
457
455
  - `/custom-commands` - List all custom commands
458
456
  - `/exit` - Exit the application
@@ -460,6 +458,7 @@ You can override this directory using `NANOCODER_DATA_DIR`.
460
458
  - `/theme` - Select a theme for the Nanocoder CLI
461
459
  - `/update` - Update Nanocoder to the latest version
462
460
  - `/usage` – Get current model context usage visually
461
+ - `/lsp` – List connected LSP servers
463
462
  - `!command` - Execute bash commands directly without leaving Nanocoder (output becomes context for the LLM)
464
463
  - `@file` - Include file contents in messages automatically via fuzzy search as you type
465
464
 
@@ -535,7 +534,7 @@ Generate comprehensive unit tests for {{component}}. Include:
535
534
  - **Smart autocomplete**: Tab completion for commands with real-time suggestions
536
535
  - **Colorized output**: Syntax highlighting and structured display
537
536
  - **Session persistence**: Maintains context and preferences across sessions
538
- - **Real-time streaming**: Live token-by-token streaming of AI responses (toggle with `/streaming`)
537
+ - **Real-time streaming**: Live token-by-token streaming of AI responses
539
538
  - **Real-time indicators**: Shows token usage, timing, and processing status
540
539
  - **First-time directory security disclaimer**: Prompts on first run and stores a per-project trust decision to prevent accidental exposure of local code or secrets.
541
540
  - **Development modes**: Three modes to control tool execution behavior (toggle with Shift+Tab)
@@ -587,6 +586,5 @@ Nanocoder could benefit from help all across the board. Such as:
587
586
  - Reporting bugs or suggesting features
588
587
  - Marketing and getting the word out
589
588
  - Design and building more great software
590
- - Model cards for our recommendations database
591
589
 
592
590
  All contributions and community participation are welcome!
Binary file
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ai-sdk-client-empty-message.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-sdk-client-empty-message.spec.d.ts","sourceRoot":"","sources":["../source/ai-sdk-client-empty-message.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,124 @@
1
+ import test from 'ava';
2
+ import { isEmptyAssistantMessage } from './ai-sdk-client.js';
3
+ // Tests for isEmptyAssistantMessage function
4
+ // This function is used to filter out empty assistant messages that would cause API errors:
5
+ // "400 Bad Request: Assistant message must have either content or tool_calls, but not none."
6
+ // ============================================================================
7
+ // Non-assistant messages should never be considered empty
8
+ // ============================================================================
9
+ test('isEmptyAssistantMessage - user message is not empty', t => {
10
+ const message = {
11
+ role: 'user',
12
+ content: '',
13
+ };
14
+ t.false(isEmptyAssistantMessage(message));
15
+ });
16
+ test('isEmptyAssistantMessage - system message is not empty', t => {
17
+ const message = {
18
+ role: 'system',
19
+ content: '',
20
+ };
21
+ t.false(isEmptyAssistantMessage(message));
22
+ });
23
+ test('isEmptyAssistantMessage - tool message is not empty', t => {
24
+ const message = {
25
+ role: 'tool',
26
+ content: '',
27
+ };
28
+ t.false(isEmptyAssistantMessage(message));
29
+ });
30
+ // ============================================================================
31
+ // Empty assistant messages (should be filtered out)
32
+ // ============================================================================
33
+ test('isEmptyAssistantMessage - assistant with empty string content is empty', t => {
34
+ const message = {
35
+ role: 'assistant',
36
+ content: '',
37
+ };
38
+ t.true(isEmptyAssistantMessage(message));
39
+ });
40
+ test('isEmptyAssistantMessage - assistant with whitespace-only content is empty', t => {
41
+ const message = {
42
+ role: 'assistant',
43
+ content: ' \n\t ',
44
+ };
45
+ t.true(isEmptyAssistantMessage(message));
46
+ });
47
+ test('isEmptyAssistantMessage - assistant with empty array content is empty', t => {
48
+ const message = {
49
+ role: 'assistant',
50
+ content: [],
51
+ };
52
+ t.true(isEmptyAssistantMessage(message));
53
+ });
54
+ test('isEmptyAssistantMessage - assistant with empty content and empty toolCalls is empty', t => {
55
+ const message = {
56
+ role: 'assistant',
57
+ content: '',
58
+ toolCalls: [],
59
+ };
60
+ t.true(isEmptyAssistantMessage(message));
61
+ });
62
+ test('isEmptyAssistantMessage - assistant with undefined toolCalls is empty', t => {
63
+ const message = {
64
+ role: 'assistant',
65
+ content: '',
66
+ toolCalls: undefined,
67
+ };
68
+ t.true(isEmptyAssistantMessage(message));
69
+ });
70
+ // ============================================================================
71
+ // Valid assistant messages (should NOT be filtered out)
72
+ // ============================================================================
73
+ test('isEmptyAssistantMessage - assistant with text content is not empty', t => {
74
+ const message = {
75
+ role: 'assistant',
76
+ content: 'Hello, how can I help you?',
77
+ };
78
+ t.false(isEmptyAssistantMessage(message));
79
+ });
80
+ test('isEmptyAssistantMessage - assistant with single character content is not empty', t => {
81
+ const message = {
82
+ role: 'assistant',
83
+ content: 'a',
84
+ };
85
+ t.false(isEmptyAssistantMessage(message));
86
+ });
87
+ test('isEmptyAssistantMessage - assistant with array content is not empty', t => {
88
+ const message = {
89
+ role: 'assistant',
90
+ content: [{ type: 'text', text: 'Hello' }],
91
+ };
92
+ t.false(isEmptyAssistantMessage(message));
93
+ });
94
+ test('isEmptyAssistantMessage - assistant with toolCalls is not empty', t => {
95
+ const message = {
96
+ role: 'assistant',
97
+ content: '',
98
+ toolCalls: [
99
+ { toolName: 'read_file', toolCallId: '123', input: { path: 'test.txt' } },
100
+ ],
101
+ };
102
+ t.false(isEmptyAssistantMessage(message));
103
+ });
104
+ test('isEmptyAssistantMessage - assistant with content and toolCalls is not empty', t => {
105
+ const message = {
106
+ role: 'assistant',
107
+ content: 'Let me read that file for you.',
108
+ toolCalls: [
109
+ { toolName: 'read_file', toolCallId: '123', input: { path: 'test.txt' } },
110
+ ],
111
+ };
112
+ t.false(isEmptyAssistantMessage(message));
113
+ });
114
+ test('isEmptyAssistantMessage - assistant with whitespace content but toolCalls is not empty', t => {
115
+ const message = {
116
+ role: 'assistant',
117
+ content: ' ',
118
+ toolCalls: [
119
+ { toolName: 'read_file', toolCallId: '123', input: { path: 'test.txt' } },
120
+ ],
121
+ };
122
+ t.false(isEmptyAssistantMessage(message));
123
+ });
124
+ //# sourceMappingURL=ai-sdk-client-empty-message.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-sdk-client-empty-message.spec.js","sourceRoot":"","sources":["../source/ai-sdk-client-empty-message.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ai-sdk-client-error-handling.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-sdk-client-error-handling.spec.d.ts","sourceRoot":"","sources":["../source/ai-sdk-client-error-handling.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,108 @@
1
+ import test from 'ava';
2
+ import { parseAPIError } from './ai-sdk-client.js';
3
+ // Tests for parseAPIError function
4
+ // Now using the actual exported function instead of a duplicated copy
5
+ test('parseAPIError - handles Ollama unmarshal error from issue #87', t => {
6
+ const error = new Error("RetryError [AI_RetryError]: Failed after 3 attempts. Last error: unmarshal: invalid character '{' after top-level value");
7
+ const result = parseAPIError(error);
8
+ t.true(result.includes('Ollama server error'));
9
+ t.true(result.includes('malformed JSON'));
10
+ t.true(result.includes('Restart Ollama'));
11
+ t.true(result.includes('Re-pull the model'));
12
+ t.true(result.includes('Check Ollama logs'));
13
+ t.true(result.includes('Try a different model'));
14
+ t.true(result.includes('Original error:'));
15
+ });
16
+ test('parseAPIError - handles unmarshal error without retry wrapper', t => {
17
+ const error = new Error("unmarshal: invalid character '{' after top-level value");
18
+ const result = parseAPIError(error);
19
+ t.true(result.includes('Ollama server error'));
20
+ t.true(result.includes('malformed JSON'));
21
+ });
22
+ test('parseAPIError - handles 500 error with invalid character (status code takes precedence)', t => {
23
+ // This test verifies that HTTP status codes are parsed FIRST,
24
+ // so a 500 error with "invalid character" in the message is treated
25
+ // as a server error, not an Ollama-specific error
26
+ const error = new Error("500 Internal Server Error: invalid character 'x' after top-level value");
27
+ const result = parseAPIError(error);
28
+ // Status code parsing takes precedence over Ollama-specific pattern matching
29
+ t.is(result, "Server error: invalid character 'x' after top-level value");
30
+ });
31
+ test('parseAPIError - handles 500 error without JSON parsing issue', t => {
32
+ const error = new Error('500 Internal Server Error: database connection failed');
33
+ const result = parseAPIError(error);
34
+ t.is(result, 'Server error: database connection failed');
35
+ });
36
+ test('parseAPIError - handles 404 error', t => {
37
+ const error = new Error('404 Not Found: model not available');
38
+ const result = parseAPIError(error);
39
+ t.is(result, 'Model not found: The requested model may not exist or is unavailable');
40
+ });
41
+ test('parseAPIError - handles connection refused', t => {
42
+ const error = new Error('ECONNREFUSED: Connection refused');
43
+ const result = parseAPIError(error);
44
+ t.is(result, 'Connection failed: Unable to reach the model server');
45
+ });
46
+ test('parseAPIError - handles timeout error', t => {
47
+ const error = new Error('Request timeout: ETIMEDOUT');
48
+ const result = parseAPIError(error);
49
+ t.is(result, 'Request timed out: The model took too long to respond');
50
+ });
51
+ test('parseAPIError - handles non-Error objects', t => {
52
+ const result = parseAPIError('string error');
53
+ t.is(result, 'An unknown error occurred while communicating with the model');
54
+ });
55
+ test('parseAPIError - handles context length errors', t => {
56
+ const error = new Error('context length exceeded');
57
+ const result = parseAPIError(error);
58
+ // Use exact assertion instead of OR condition
59
+ t.is(result, 'Context too large: Please reduce the conversation length or message size');
60
+ });
61
+ test('parseAPIError - handles too many tokens errors', t => {
62
+ const error = new Error('too many tokens in the request');
63
+ const result = parseAPIError(error);
64
+ t.is(result, 'Context too large: Please reduce the conversation length or message size');
65
+ });
66
+ test('parseAPIError - handles 400 with context length in message', t => {
67
+ const error = new Error('400 Bad Request: context length exceeded');
68
+ const result = parseAPIError(error);
69
+ // The 400 status code pattern matches first, so we get the full message
70
+ t.is(result, 'Bad request: context length exceeded');
71
+ });
72
+ test('parseAPIError - handles 401 authentication error', t => {
73
+ const error = new Error('401 Unauthorized: Invalid API key');
74
+ const result = parseAPIError(error);
75
+ t.is(result, 'Authentication failed: Invalid API key or credentials');
76
+ });
77
+ test('parseAPIError - handles 403 forbidden error', t => {
78
+ const error = new Error('403 Forbidden: Access denied');
79
+ const result = parseAPIError(error);
80
+ t.is(result, 'Access forbidden: Check your API permissions');
81
+ });
82
+ test('parseAPIError - handles 429 rate limit error', t => {
83
+ const error = new Error('429 Too Many Requests: Rate limit exceeded');
84
+ const result = parseAPIError(error);
85
+ t.is(result, 'Rate limit exceeded: Too many requests. Please wait and try again');
86
+ });
87
+ test('parseAPIError - handles 502 bad gateway error', t => {
88
+ const error = new Error('502 Bad Gateway: upstream error');
89
+ const result = parseAPIError(error);
90
+ t.is(result, 'Server error: upstream error');
91
+ });
92
+ test('parseAPIError - handles 503 service unavailable error', t => {
93
+ const error = new Error('503 Service Unavailable: server overloaded');
94
+ const result = parseAPIError(error);
95
+ t.is(result, 'Server error: server overloaded');
96
+ });
97
+ test('parseAPIError - handles reduce tokens message', t => {
98
+ const error = new Error('Please reduce the number of tokens in your request');
99
+ const result = parseAPIError(error);
100
+ t.is(result, 'Too many tokens: Please shorten your message or clear conversation history');
101
+ });
102
+ test('parseAPIError - cleans up unknown errors', t => {
103
+ const error = new Error('Error: Something unexpected happened\nWith more details');
104
+ const result = parseAPIError(error);
105
+ // Should strip "Error: " prefix and only return first line
106
+ t.is(result, 'Something unexpected happened');
107
+ });
108
+ //# sourceMappingURL=ai-sdk-client-error-handling.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-sdk-client-error-handling.spec.js","sourceRoot":"","sources":["../source/ai-sdk-client-error-handling.spec.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,KAAK,CAAC;AACvB,OAAO,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAC;AAEjD,mCAAmC;AACnC,sEAAsE;AAEtE,IAAI,CAAC,+DAA+D,EAAE,CAAC,CAAC,EAAE;IACzE,MAAM,KAAK,GAAG,IAAI,KAAK,CACtB,yHAAyH,CACzH,CAAC;IAEF,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAAC;IAC/C,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC1C,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC1C,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC7C,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC7C,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAC5C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+DAA+D,EAAE,CAAC,CAAC,EAAE;IACzE,MAAM,KAAK,GAAG,IAAI,KAAK,CACtB,wDAAwD,CACxD,CAAC;IAEF,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAAC;IAC/C,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAC3C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,yFAAyF,EAAE,CAAC,CAAC,EAAE;IACnG,8DAA8D;IAC9D,oEAAoE;IACpE,kDAAkD;IAClD,MAAM,KAAK,GAAG,IAAI,KAAK,CACtB,wEAAwE,CACxE,CAAC;IAEF,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,6EAA6E;IAC7E,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,2DAA2D,CAAC,CAAC;AAC3E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8DAA8D,EAAE,CAAC,CAAC,EAAE;IACxE,MAAM,KAAK,GAAG,IAAI,KAAK,CACtB,uDAAuD,CACvD,CAAC;IAEF,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,0CAA0C,CAAC,CAAC;AAC1D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,mCAAmC,EAAE,CAAC,CAAC,EAAE;IAC7C,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IAE9D,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,CAAC,CAAC,EAAE,CACH,MAAM,EACN,sEAAsE,CACtE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4CAA4C,EAAE,CAAC,CAAC,EAAE;IACtD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAE5D,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,qDAAqD,CAAC,CAAC;AACrE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,uCAAuC,EAAE,CAAC,CAAC,EAAE;IACjD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAEtD,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,uDAAuD,CAAC,CAAC;AACvE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2CAA2C,EAAE,CAAC,CAAC,EAAE;IACrD,MAAM,MAAM,GAAG,aAAa,CAAC,cAAc,CAAC,CAAC;IAE7C,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,8DAA8D,CAAC,CAAC;AAC9E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+CAA+C,EAAE,CAAC,CAAC,EAAE;IACzD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAEnD,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,8CAA8C;IAC9C,CAAC,CAAC,EAAE,CACH,MAAM,EACN,0EAA0E,CAC1E,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gDAAgD,EAAE,CAAC,CAAC,EAAE;IAC1D,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IAE1D,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,CAAC,CAAC,EAAE,CACH,MAAM,EACN,0EAA0E,CAC1E,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4DAA4D,EAAE,CAAC,CAAC,EAAE;IACtE,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAEpE,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,wEAAwE;IACxE,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,sCAAsC,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,kDAAkD,EAAE,CAAC,CAAC,EAAE;IAC5D,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IAE7D,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,uDAAuD,CAAC,CAAC;AACvE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6CAA6C,EAAE,CAAC,CAAC,EAAE;IACvD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAExD,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,8CAA8C,CAAC,CAAC;AAC9D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8CAA8C,EAAE,CAAC,CAAC,EAAE;IACxD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAEtE,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,CAAC,CAAC,EAAE,CACH,MAAM,EACN,mEAAmE,CACnE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+CAA+C,EAAE,CAAC,CAAC,EAAE;IACzD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IAE3D,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,8BAA8B,CAAC,CAAC;AAC9C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,uDAAuD,EAAE,CAAC,CAAC,EAAE;IACjE,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAEtE,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,iCAAiC,CAAC,CAAC;AACjD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+CAA+C,EAAE,CAAC,CAAC,EAAE;IACzD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IAE9E,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,CAAC,CAAC,EAAE,CACH,MAAM,EACN,4EAA4E,CAC5E,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0CAA0C,EAAE,CAAC,CAAC,EAAE;IACpD,MAAM,KAAK,GAAG,IAAI,KAAK,CACtB,yDAAyD,CACzD,CAAC;IAEF,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,2DAA2D;IAC3D,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,+BAA+B,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ai-sdk-client-maxretries.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-sdk-client-maxretries.spec.d.ts","sourceRoot":"","sources":["../source/ai-sdk-client-maxretries.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,96 @@
1
+ import test from 'ava';
2
+ import { AISDKClient } from './ai-sdk-client.js';
3
+ // Tests for maxRetries configuration
4
+ // Now tests actual AISDKClient instantiation and behavior
5
+ test('AISDKClient - maxRetries defaults to 2 when not specified', t => {
6
+ const config = {
7
+ name: 'TestProvider',
8
+ type: 'openai-compatible',
9
+ models: ['test-model'],
10
+ config: {
11
+ baseURL: 'http://localhost:11434/v1',
12
+ apiKey: 'test-key',
13
+ },
14
+ };
15
+ const client = new AISDKClient(config);
16
+ // Verify the client's internal maxRetries is set to default of 2
17
+ t.is(client.getMaxRetries(), 2);
18
+ });
19
+ test('AISDKClient - maxRetries respects custom value', t => {
20
+ const config = {
21
+ name: 'TestProvider',
22
+ type: 'openai-compatible',
23
+ models: ['test-model'],
24
+ maxRetries: 5,
25
+ config: {
26
+ baseURL: 'http://localhost:11434/v1',
27
+ apiKey: 'test-key',
28
+ },
29
+ };
30
+ const client = new AISDKClient(config);
31
+ // Verify the client uses the custom maxRetries value
32
+ t.is(client.getMaxRetries(), 5);
33
+ });
34
+ test('AISDKClient - maxRetries can be set to 0 to disable retries', t => {
35
+ // Important: This test verifies that 0 is treated as a valid value,
36
+ // not as falsy (which would incorrectly default to 2)
37
+ const config = {
38
+ name: 'TestProvider',
39
+ type: 'openai-compatible',
40
+ models: ['test-model'],
41
+ maxRetries: 0,
42
+ config: {
43
+ baseURL: 'http://localhost:11434/v1',
44
+ apiKey: 'test-key',
45
+ },
46
+ };
47
+ const client = new AISDKClient(config);
48
+ // Verify that 0 is respected (nullish coalescing handles this correctly)
49
+ t.is(client.getMaxRetries(), 0);
50
+ });
51
+ test('AISDKClient - maxRetries handles value of 1', t => {
52
+ const config = {
53
+ name: 'TestProvider',
54
+ type: 'openai-compatible',
55
+ models: ['test-model'],
56
+ maxRetries: 1,
57
+ config: {
58
+ baseURL: 'http://localhost:11434/v1',
59
+ apiKey: 'test-key',
60
+ },
61
+ };
62
+ const client = new AISDKClient(config);
63
+ t.is(client.getMaxRetries(), 1);
64
+ });
65
+ test('AIProviderConfig type - includes maxRetries in interface', t => {
66
+ // Compile-time test that maxRetries is part of the interface
67
+ const config = {
68
+ name: 'TestProvider',
69
+ type: 'openai-compatible',
70
+ models: ['test-model'],
71
+ maxRetries: 3,
72
+ config: {
73
+ baseURL: 'http://localhost:11434/v1',
74
+ },
75
+ };
76
+ // TypeScript should not complain about maxRetries property
77
+ t.is(typeof config.maxRetries, 'number');
78
+ t.true('maxRetries' in config);
79
+ });
80
+ test('AISDKClient - undefined maxRetries uses default', t => {
81
+ // Explicitly set to undefined to test fallback behavior
82
+ const config = {
83
+ name: 'TestProvider',
84
+ type: 'openai-compatible',
85
+ models: ['test-model'],
86
+ maxRetries: undefined,
87
+ config: {
88
+ baseURL: 'http://localhost:11434/v1',
89
+ apiKey: 'test-key',
90
+ },
91
+ };
92
+ const client = new AISDKClient(config);
93
+ // Verify undefined falls back to default of 2
94
+ t.is(client.getMaxRetries(), 2);
95
+ });
96
+ //# sourceMappingURL=ai-sdk-client-maxretries.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-sdk-client-maxretries.spec.js","sourceRoot":"","sources":["../source/ai-sdk-client-maxretries.spec.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,26 @@
1
1
  import type { AIProviderConfig, LLMChatResponse, LLMClient, Message, AISDKCoreTool, StreamCallbacks } from './types/index.js';
2
+ /**
3
+ * Message type used for testing the empty assistant message filter.
4
+ * This is a simplified version of the AI SDK's internal message format.
5
+ */
6
+ export interface TestableMessage {
7
+ role: 'system' | 'user' | 'assistant' | 'tool';
8
+ content: string | unknown[];
9
+ toolCalls?: unknown[];
10
+ }
11
+ /**
12
+ * Checks if an assistant message is empty (no content and no tool calls).
13
+ * Empty assistant messages cause API errors:
14
+ * "400 Bad Request: Assistant message must have either content or tool_calls, but not none."
15
+ *
16
+ * Exported for testing purposes.
17
+ */
18
+ export declare function isEmptyAssistantMessage(message: TestableMessage): boolean;
19
+ /**
20
+ * Parses API errors into user-friendly messages.
21
+ * Exported for testing purposes.
22
+ */
23
+ export declare function parseAPIError(error: unknown): string;
2
24
  export declare class AISDKClient implements LLMClient {
3
25
  private provider;
4
26
  private currentModel;
@@ -6,6 +28,7 @@ export declare class AISDKClient implements LLMClient {
6
28
  private providerConfig;
7
29
  private undiciAgent;
8
30
  private cachedContextSize;
31
+ private maxRetries;
9
32
  constructor(providerConfig: AIProviderConfig);
10
33
  /**
11
34
  * Fetch and cache context size from models.dev
@@ -16,12 +39,12 @@ export declare class AISDKClient implements LLMClient {
16
39
  setModel(model: string): void;
17
40
  getCurrentModel(): string;
18
41
  getContextSize(): number;
42
+ getMaxRetries(): number;
19
43
  getAvailableModels(): Promise<string[]>;
20
- chat(messages: Message[], tools: Record<string, AISDKCoreTool>, signal?: AbortSignal): Promise<LLMChatResponse>;
21
44
  /**
22
45
  * Stream chat with real-time token updates
23
46
  */
24
- chatStream(messages: Message[], tools: Record<string, AISDKCoreTool>, callbacks: StreamCallbacks, signal?: AbortSignal): Promise<LLMChatResponse>;
47
+ chat(messages: Message[], tools: Record<string, AISDKCoreTool>, callbacks: StreamCallbacks, signal?: AbortSignal): Promise<LLMChatResponse>;
25
48
  clearContext(): Promise<void>;
26
49
  }
27
50
  //# sourceMappingURL=ai-sdk-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-sdk-client.d.ts","sourceRoot":"","sources":["../source/ai-sdk-client.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACX,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,OAAO,EAEP,aAAa,EACb,eAAe,EACf,MAAM,eAAe,CAAC;AAyHvB,qBAAa,WAAY,YAAW,SAAS;IAC5C,OAAO,CAAC,QAAQ,CAA4C;IAC5D,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,eAAe,CAAW;IAClC,OAAO,CAAC,cAAc,CAAmB;IACzC,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,iBAAiB,CAAS;gBAEtB,cAAc,EAAE,gBAAgB;IA8B5C;;OAEG;YACW,iBAAiB;IAU/B,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC;IAKrE,OAAO,CAAC,cAAc;IAgCtB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAM7B,eAAe,IAAI,MAAM;IAIzB,cAAc,IAAI,MAAM;IAIxB,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIjC,IAAI,CACT,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EACpC,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,eAAe,CAAC;IAiH3B;;OAEG;IACG,UAAU,CACf,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EACpC,SAAS,EAAE,eAAe,EAC1B,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,eAAe,CAAC;IAoIrB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;CAGnC"}
1
+ {"version":3,"file":"ai-sdk-client.d.ts","sourceRoot":"","sources":["../source/ai-sdk-client.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACX,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,OAAO,EAEP,aAAa,EACb,eAAe,EACf,MAAM,eAAe,CAAC;AAIvB;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IAC/C,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAczE;AAgBD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAyJpD;AAmDD,qBAAa,WAAY,YAAW,SAAS;IAC5C,OAAO,CAAC,QAAQ,CAA4C;IAC5D,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,eAAe,CAAW;IAClC,OAAO,CAAC,cAAc,CAAmB;IACzC,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,UAAU,CAAS;gBAEf,cAAc,EAAE,gBAAgB;IAgC5C;;OAEG;YACW,iBAAiB;IAU/B,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC;IAKrE,OAAO,CAAC,cAAc;IAgCtB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAM7B,eAAe,IAAI,MAAM;IAIzB,cAAc,IAAI,MAAM;IAIxB,aAAa,IAAI,MAAM;IAIvB,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIvC;;OAEG;IACG,IAAI,CACT,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EACpC,SAAS,EAAE,eAAe,EAC1B,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,eAAe,CAAC;IAyLrB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;CAGnC"}