@boostecom/provider 0.0.1

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 (70) hide show
  1. package/README.md +90 -0
  2. package/dist/index.cjs +2522 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +848 -0
  5. package/dist/index.d.ts +848 -0
  6. package/dist/index.js +2484 -0
  7. package/dist/index.js.map +1 -0
  8. package/docs/content/README.md +337 -0
  9. package/docs/content/agent-teams.mdx +324 -0
  10. package/docs/content/api.mdx +757 -0
  11. package/docs/content/best-practices.mdx +624 -0
  12. package/docs/content/examples.mdx +675 -0
  13. package/docs/content/guide.mdx +516 -0
  14. package/docs/content/index.mdx +99 -0
  15. package/docs/content/installation.mdx +246 -0
  16. package/docs/content/skills.mdx +548 -0
  17. package/docs/content/troubleshooting.mdx +588 -0
  18. package/docs/examples/README.md +499 -0
  19. package/docs/examples/abort-signal.ts +125 -0
  20. package/docs/examples/agent-teams.ts +122 -0
  21. package/docs/examples/basic-usage.ts +73 -0
  22. package/docs/examples/check-cli.ts +51 -0
  23. package/docs/examples/conversation-history.ts +69 -0
  24. package/docs/examples/custom-config.ts +90 -0
  25. package/docs/examples/generate-object-constraints.ts +209 -0
  26. package/docs/examples/generate-object.ts +211 -0
  27. package/docs/examples/hooks-callbacks.ts +63 -0
  28. package/docs/examples/images.ts +76 -0
  29. package/docs/examples/integration-test.ts +241 -0
  30. package/docs/examples/limitations.ts +150 -0
  31. package/docs/examples/logging-custom-logger.ts +99 -0
  32. package/docs/examples/logging-default.ts +55 -0
  33. package/docs/examples/logging-disabled.ts +74 -0
  34. package/docs/examples/logging-verbose.ts +64 -0
  35. package/docs/examples/long-running-tasks.ts +179 -0
  36. package/docs/examples/message-injection.ts +210 -0
  37. package/docs/examples/mid-stream-injection.ts +126 -0
  38. package/docs/examples/run-all-examples.sh +48 -0
  39. package/docs/examples/sdk-tools-callbacks.ts +49 -0
  40. package/docs/examples/skills-discovery.ts +144 -0
  41. package/docs/examples/skills-management.ts +140 -0
  42. package/docs/examples/stream-object.ts +80 -0
  43. package/docs/examples/streaming.ts +52 -0
  44. package/docs/examples/structured-output-repro.ts +227 -0
  45. package/docs/examples/tool-management.ts +215 -0
  46. package/docs/examples/tool-streaming.ts +132 -0
  47. package/docs/examples/zod4-compatibility-test.ts +290 -0
  48. package/docs/src/claude-code-language-model.test.ts +3883 -0
  49. package/docs/src/claude-code-language-model.ts +2586 -0
  50. package/docs/src/claude-code-provider.test.ts +97 -0
  51. package/docs/src/claude-code-provider.ts +179 -0
  52. package/docs/src/convert-to-claude-code-messages.images.test.ts +104 -0
  53. package/docs/src/convert-to-claude-code-messages.test.ts +193 -0
  54. package/docs/src/convert-to-claude-code-messages.ts +419 -0
  55. package/docs/src/errors.test.ts +213 -0
  56. package/docs/src/errors.ts +216 -0
  57. package/docs/src/index.test.ts +49 -0
  58. package/docs/src/index.ts +98 -0
  59. package/docs/src/logger.integration.test.ts +164 -0
  60. package/docs/src/logger.test.ts +184 -0
  61. package/docs/src/logger.ts +65 -0
  62. package/docs/src/map-claude-code-finish-reason.test.ts +120 -0
  63. package/docs/src/map-claude-code-finish-reason.ts +60 -0
  64. package/docs/src/mcp-helpers.test.ts +71 -0
  65. package/docs/src/mcp-helpers.ts +123 -0
  66. package/docs/src/message-injection.test.ts +460 -0
  67. package/docs/src/types.ts +447 -0
  68. package/docs/src/validation.test.ts +558 -0
  69. package/docs/src/validation.ts +360 -0
  70. package/package.json +124 -0
@@ -0,0 +1,97 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { createClaudeCode } from './claude-code-provider.js';
3
+ import { ClaudeCodeLanguageModel } from './claude-code-language-model.js';
4
+
5
+ describe('createClaudeCode', () => {
6
+ it('should create a provider with default settings', () => {
7
+ const provider = createClaudeCode();
8
+ expect(provider).toBeDefined();
9
+ expect(typeof provider).toBe('function');
10
+ });
11
+
12
+ it('should create a provider with custom settings', () => {
13
+ const customSettings = {
14
+ pathToClaudeCodeExecutable: '/custom/path/claude',
15
+ customSystemPrompt: 'Custom prompt',
16
+ maxTurns: 10,
17
+ };
18
+
19
+ const provider = createClaudeCode({ defaultSettings: customSettings });
20
+ expect(provider).toBeDefined();
21
+ });
22
+
23
+ it('should return a language model when called with opus', () => {
24
+ const provider = createClaudeCode();
25
+ const model = provider('opus');
26
+
27
+ expect(model).toBeInstanceOf(ClaudeCodeLanguageModel);
28
+ expect(model.modelId).toBe('opus');
29
+ });
30
+
31
+ it('should return a language model when called with sonnet', () => {
32
+ const provider = createClaudeCode();
33
+ const model = provider('sonnet');
34
+
35
+ expect(model).toBeInstanceOf(ClaudeCodeLanguageModel);
36
+ expect(model.modelId).toBe('sonnet');
37
+ });
38
+
39
+ it('should allow custom model IDs', () => {
40
+ const provider = createClaudeCode();
41
+ const model = provider('custom-model-id');
42
+
43
+ expect(model).toBeInstanceOf(ClaudeCodeLanguageModel);
44
+ expect(model.modelId).toBe('custom-model-id');
45
+ });
46
+
47
+ it('should merge provider settings with model settings', () => {
48
+ const providerSettings = {
49
+ pathToClaudeCodeExecutable: '/provider/path',
50
+ maxTurns: 5,
51
+ };
52
+
53
+ const modelSettings = {
54
+ maxTurns: 10,
55
+ customSystemPrompt: 'Model prompt',
56
+ };
57
+
58
+ const provider = createClaudeCode({ defaultSettings: providerSettings });
59
+ const model = provider('opus', modelSettings);
60
+
61
+ expect(model).toBeInstanceOf(ClaudeCodeLanguageModel);
62
+ // Model settings should override provider settings
63
+ expect((model as ClaudeCodeLanguageModel).settings.maxTurns).toBe(10);
64
+ expect((model as ClaudeCodeLanguageModel).settings.customSystemPrompt).toBe('Model prompt');
65
+ expect((model as ClaudeCodeLanguageModel).settings.pathToClaudeCodeExecutable).toBe(
66
+ '/provider/path'
67
+ );
68
+ });
69
+
70
+ it('should create model with custom settings', () => {
71
+ const provider = createClaudeCode();
72
+ const model = provider('sonnet', { resume: 'test-session-123' });
73
+
74
+ expect(model).toBeInstanceOf(ClaudeCodeLanguageModel);
75
+ expect((model as ClaudeCodeLanguageModel).settings.resume).toBe('test-session-123');
76
+ });
77
+
78
+ it('should work with destructured usage', () => {
79
+ const { claudeCode } = { claudeCode: createClaudeCode() };
80
+ const model = claudeCode('opus');
81
+
82
+ expect(model).toBeInstanceOf(ClaudeCodeLanguageModel);
83
+ expect(model.modelId).toBe('opus');
84
+ });
85
+ });
86
+
87
+ describe('claudeCode export', () => {
88
+ it('should export a default provider instance', async () => {
89
+ const { claudeCode } = await import('./claude-code-provider.js');
90
+
91
+ expect(claudeCode).toBeDefined();
92
+ expect(typeof claudeCode).toBe('function');
93
+
94
+ const model = claudeCode('sonnet');
95
+ expect(model).toBeInstanceOf(ClaudeCodeLanguageModel);
96
+ });
97
+ });
@@ -0,0 +1,179 @@
1
+ import type { LanguageModelV3, ProviderV3 } from '@ai-sdk/provider';
2
+ import { NoSuchModelError } from '@ai-sdk/provider';
3
+ import { ClaudeCodeLanguageModel, type ClaudeCodeModelId } from './claude-code-language-model.js';
4
+ import type { ClaudeCodeSettings } from './types.js';
5
+ import { validateSettings } from './validation.js';
6
+ import { getLogger } from './logger.js';
7
+
8
+ /**
9
+ * Claude Code provider interface that extends the AI SDK's ProviderV3.
10
+ * Provides methods to create language models for interacting with Claude via the CLI.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { claudeCode } from '@boostecom/provider';
15
+ *
16
+ * // Create a model instance
17
+ * const model = claudeCode('opus');
18
+ *
19
+ * // Or use the explicit methods
20
+ * const chatModel = claudeCode.chat('sonnet');
21
+ * const languageModel = claudeCode.languageModel('opus', { maxTurns: 10 });
22
+ * ```
23
+ */
24
+ export interface ClaudeCodeProvider extends ProviderV3 {
25
+ /**
26
+ * Creates a language model instance for the specified model ID.
27
+ * This is a shorthand for calling `languageModel()`.
28
+ *
29
+ * @param modelId - The Claude model to use ('opus' or 'sonnet')
30
+ * @param settings - Optional settings to configure the model
31
+ * @returns A language model instance
32
+ */
33
+ (modelId: ClaudeCodeModelId, settings?: ClaudeCodeSettings): LanguageModelV3;
34
+
35
+ /**
36
+ * Creates a language model instance for text generation.
37
+ *
38
+ * @param modelId - The Claude model to use ('opus' or 'sonnet')
39
+ * @param settings - Optional settings to configure the model
40
+ * @returns A language model instance
41
+ */
42
+ languageModel(modelId: ClaudeCodeModelId, settings?: ClaudeCodeSettings): LanguageModelV3;
43
+
44
+ /**
45
+ * Alias for `languageModel()` to maintain compatibility with AI SDK patterns.
46
+ *
47
+ * @param modelId - The Claude model to use ('opus' or 'sonnet')
48
+ * @param settings - Optional settings to configure the model
49
+ * @returns A language model instance
50
+ */
51
+ chat(modelId: ClaudeCodeModelId, settings?: ClaudeCodeSettings): LanguageModelV3;
52
+
53
+ imageModel(modelId: string): never;
54
+ }
55
+
56
+ /**
57
+ * Configuration options for creating a Claude Code provider instance.
58
+ * These settings will be applied as defaults to all models created by the provider.
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * const provider = createClaudeCode({
63
+ * defaultSettings: {
64
+ * maxTurns: 5,
65
+ * cwd: '/path/to/project'
66
+ * }
67
+ * });
68
+ * ```
69
+ */
70
+ export interface ClaudeCodeProviderSettings {
71
+ /**
72
+ * Default settings to use for all models created by this provider.
73
+ * Individual model settings will override these defaults.
74
+ */
75
+ defaultSettings?: ClaudeCodeSettings;
76
+ }
77
+
78
+ /**
79
+ * Creates a Claude Code provider instance with the specified configuration.
80
+ * The provider can be used to create language models for interacting with Claude 4 models.
81
+ *
82
+ * @param options - Provider configuration options
83
+ * @returns Claude Code provider instance
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * const provider = createClaudeCode({
88
+ * defaultSettings: {
89
+ * permissionMode: 'bypassPermissions',
90
+ * maxTurns: 10
91
+ * }
92
+ * });
93
+ *
94
+ * const model = provider('opus');
95
+ * ```
96
+ */
97
+ export function createClaudeCode(options: ClaudeCodeProviderSettings = {}): ClaudeCodeProvider {
98
+ // Get logger from default settings if provided
99
+ const logger = getLogger(options.defaultSettings?.logger);
100
+
101
+ // Validate default settings if provided
102
+ if (options.defaultSettings) {
103
+ const validation = validateSettings(options.defaultSettings);
104
+ if (!validation.valid) {
105
+ throw new Error(`Invalid default settings: ${validation.errors.join(', ')}`);
106
+ }
107
+ if (validation.warnings.length > 0) {
108
+ validation.warnings.forEach((warning) => logger.warn(`Claude Code Provider: ${warning}`));
109
+ }
110
+ }
111
+
112
+ const createModel = (
113
+ modelId: ClaudeCodeModelId,
114
+ settings: ClaudeCodeSettings = {}
115
+ ): LanguageModelV3 => {
116
+ const mergedSettings = {
117
+ ...options.defaultSettings,
118
+ ...settings,
119
+ };
120
+
121
+ // Validate merged settings
122
+ const validation = validateSettings(mergedSettings);
123
+ if (!validation.valid) {
124
+ throw new Error(`Invalid settings: ${validation.errors.join(', ')}`);
125
+ }
126
+
127
+ return new ClaudeCodeLanguageModel({
128
+ id: modelId,
129
+ settings: mergedSettings,
130
+ settingsValidationWarnings: validation.warnings,
131
+ });
132
+ };
133
+
134
+ const provider = function (modelId: ClaudeCodeModelId, settings?: ClaudeCodeSettings) {
135
+ if (new.target) {
136
+ throw new Error('The Claude Code model function cannot be called with the new keyword.');
137
+ }
138
+
139
+ return createModel(modelId, settings);
140
+ };
141
+
142
+ provider.languageModel = createModel;
143
+ provider.chat = createModel; // Alias for languageModel
144
+ provider.specificationVersion = 'v3' as const;
145
+
146
+ // Add embeddingModel method that throws NoSuchModelError
147
+ provider.embeddingModel = (modelId: string) => {
148
+ throw new NoSuchModelError({
149
+ modelId,
150
+ modelType: 'embeddingModel',
151
+ });
152
+ };
153
+
154
+ provider.imageModel = (modelId: string) => {
155
+ throw new NoSuchModelError({
156
+ modelId,
157
+ modelType: 'imageModel',
158
+ });
159
+ };
160
+
161
+ return provider as ClaudeCodeProvider;
162
+ }
163
+
164
+ /**
165
+ * Default Claude Code provider instance.
166
+ * Pre-configured provider for quick usage without custom settings.
167
+ *
168
+ * @example
169
+ * ```typescript
170
+ * import { claudeCode } from '@boostecom/provider';
171
+ * import { generateText } from 'ai';
172
+ *
173
+ * const { text } = await generateText({
174
+ * model: claudeCode('sonnet'),
175
+ * prompt: 'Hello, Claude!'
176
+ * });
177
+ * ```
178
+ */
179
+ export const claudeCode = createClaudeCode();
@@ -0,0 +1,104 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import type { ModelMessage } from 'ai';
3
+ import { convertToClaudeCodeMessages } from './convert-to-claude-code-messages.js';
4
+
5
+ describe('convertToClaudeCodeMessages (images)', () => {
6
+ it('includes data URL images in streaming content', () => {
7
+ const prompt = [
8
+ {
9
+ role: 'user',
10
+ content: [
11
+ { type: 'text', text: 'Here is a sample image.' },
12
+ { type: 'image', image: 'data:image/png;base64,aGVsbG8=' },
13
+ ],
14
+ },
15
+ ] as ModelMessage[];
16
+
17
+ const result = convertToClaudeCodeMessages(prompt);
18
+
19
+ expect(result.warnings).toBeUndefined();
20
+ expect(result.hasImageParts).toBe(true);
21
+ expect(result.streamingContentParts).toHaveLength(2);
22
+ expect(result.streamingContentParts[0]).toEqual({
23
+ type: 'text',
24
+ text: 'Human: Here is a sample image.',
25
+ });
26
+ expect(result.streamingContentParts[1]).toEqual({
27
+ type: 'image',
28
+ source: {
29
+ type: 'base64',
30
+ media_type: 'image/png',
31
+ data: 'aGVsbG8=',
32
+ },
33
+ });
34
+ });
35
+
36
+ it('includes base64 images when mimeType is provided', () => {
37
+ const prompt = [
38
+ {
39
+ role: 'user',
40
+ content: [
41
+ { type: 'text', text: 'Inline base64 image.' },
42
+ { type: 'image', image: { data: 'AQID', mimeType: 'image/jpeg' } },
43
+ ],
44
+ },
45
+ ] as ModelMessage[];
46
+
47
+ const result = convertToClaudeCodeMessages(prompt);
48
+
49
+ expect(result.warnings).toBeUndefined();
50
+ expect(result.hasImageParts).toBe(true);
51
+ expect(result.streamingContentParts[1]).toEqual({
52
+ type: 'image',
53
+ source: {
54
+ type: 'base64',
55
+ media_type: 'image/jpeg',
56
+ data: 'AQID',
57
+ },
58
+ });
59
+ });
60
+
61
+ it('warns and skips HTTP image URLs', () => {
62
+ const prompt = [
63
+ {
64
+ role: 'user',
65
+ content: [
66
+ { type: 'text', text: 'Remote image' },
67
+ { type: 'image', image: 'https://example.com/image.png' },
68
+ ],
69
+ },
70
+ ] as ModelMessage[];
71
+
72
+ const result = convertToClaudeCodeMessages(prompt);
73
+
74
+ expect(result.hasImageParts).toBe(false);
75
+ expect(result.warnings).toContain(
76
+ 'Image URLs are not supported by this provider; supply base64/data URLs.'
77
+ );
78
+ expect(result.streamingContentParts).toHaveLength(1);
79
+ });
80
+
81
+ it('accepts file parts with image media type', () => {
82
+ const prompt = [
83
+ {
84
+ role: 'user',
85
+ content: [
86
+ { type: 'text', text: 'File part image.' },
87
+ { type: 'file', mediaType: 'image/png', data: 'aGVsbG8=' },
88
+ ],
89
+ },
90
+ ] as ModelMessage[];
91
+
92
+ const result = convertToClaudeCodeMessages(prompt);
93
+
94
+ expect(result.hasImageParts).toBe(true);
95
+ expect(result.streamingContentParts[1]).toEqual({
96
+ type: 'image',
97
+ source: {
98
+ type: 'base64',
99
+ media_type: 'image/png',
100
+ data: 'aGVsbG8=',
101
+ },
102
+ });
103
+ });
104
+ });
@@ -0,0 +1,193 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { convertToClaudeCodeMessages } from './convert-to-claude-code-messages.js';
3
+ import type { ModelMessage } from 'ai';
4
+
5
+ describe('convertToClaudeCodeMessages', () => {
6
+ it('should convert a simple user message', () => {
7
+ const result = convertToClaudeCodeMessages([
8
+ { role: 'user', content: 'Hello, Claude!' },
9
+ ] as ModelMessage[]);
10
+
11
+ expect(result.messagesPrompt).toBe('Human: Hello, Claude!');
12
+ expect(result.systemPrompt).toBeUndefined();
13
+ });
14
+
15
+ it('should convert a simple assistant message', () => {
16
+ const result = convertToClaudeCodeMessages([
17
+ { role: 'assistant', content: 'Hello! How can I help you?' },
18
+ ] as ModelMessage[]);
19
+
20
+ expect(result.messagesPrompt).toBe('Assistant: Hello! How can I help you?');
21
+ expect(result.systemPrompt).toBeUndefined();
22
+ });
23
+
24
+ it('should handle system message', () => {
25
+ const result = convertToClaudeCodeMessages([
26
+ { role: 'system', content: 'You are a helpful assistant.' },
27
+ ] as ModelMessage[]);
28
+
29
+ expect(result.messagesPrompt).toBe('You are a helpful assistant.');
30
+ expect(result.systemPrompt).toBe('You are a helpful assistant.');
31
+ });
32
+
33
+ it('should handle a conversation with multiple messages', () => {
34
+ const result = convertToClaudeCodeMessages([
35
+ { role: 'system', content: 'Be helpful.' },
36
+ { role: 'user', content: 'What is 2+2?' },
37
+ { role: 'assistant', content: '2+2 equals 4.' },
38
+ { role: 'user', content: 'Thanks!' },
39
+ ] as ModelMessage[]);
40
+
41
+ expect(result.systemPrompt).toBe('Be helpful.');
42
+ expect(result.messagesPrompt).toBe(
43
+ 'Be helpful.\n\nHuman: What is 2+2?\n\nAssistant: 2+2 equals 4.\n\nHuman: Thanks!'
44
+ );
45
+ });
46
+
47
+ it('should handle multi-part text messages', () => {
48
+ const result = convertToClaudeCodeMessages([
49
+ {
50
+ role: 'user',
51
+ content: [
52
+ { type: 'text', text: 'Hello' },
53
+ { type: 'text', text: ', ' },
54
+ { type: 'text', text: 'world!' },
55
+ ],
56
+ },
57
+ ] as ModelMessage[]);
58
+
59
+ expect(result.messagesPrompt).toBe('Human: Hello\n, \nworld!');
60
+ });
61
+
62
+ it('should return warning when image content cannot be converted', () => {
63
+ const result = convertToClaudeCodeMessages([
64
+ {
65
+ role: 'user',
66
+ content: [
67
+ { type: 'text', text: 'Look at this:' },
68
+ { type: 'image', image: new Uint8Array([1, 2, 3]) },
69
+ ],
70
+ },
71
+ ] as ModelMessage[]);
72
+
73
+ expect(result.warnings).toBeDefined();
74
+ expect(result.warnings).toContain('Unable to convert image content; supply base64/data URLs.');
75
+ expect(result.messagesPrompt).toBe('Human: Look at this:');
76
+ expect(result.hasImageParts).toBe(false);
77
+ });
78
+
79
+ it('should handle unknown content types gracefully', () => {
80
+ const result = convertToClaudeCodeMessages([
81
+ {
82
+ role: 'user',
83
+ content: [
84
+ { type: 'text', text: 'Check this file:' },
85
+ { type: 'file', data: 'data:text/plain;base64,AQID', mimeType: 'text/plain' },
86
+ ],
87
+ },
88
+ ] as any);
89
+
90
+ // Unknown content types are filtered out
91
+ expect(result.messagesPrompt).toBe('Human: Check this file:');
92
+ });
93
+
94
+ it('should convert tool messages', () => {
95
+ const result = convertToClaudeCodeMessages([
96
+ {
97
+ role: 'tool',
98
+ content: [
99
+ {
100
+ type: 'tool-result',
101
+ toolCallId: 'call-123',
102
+ toolName: 'calculator',
103
+ output: { type: 'json', value: { answer: 42 } },
104
+ },
105
+ ],
106
+ },
107
+ ] as any);
108
+
109
+ expect(result.messagesPrompt).toBe('Tool Result (calculator): {"answer":42}');
110
+ });
111
+
112
+ it('should handle tool error messages', () => {
113
+ const result = convertToClaudeCodeMessages([
114
+ {
115
+ role: 'tool',
116
+ content: [
117
+ {
118
+ type: 'tool-result',
119
+ toolCallId: 'call-456',
120
+ toolName: 'search',
121
+ output: { type: 'text', value: 'Network error' },
122
+ },
123
+ ],
124
+ },
125
+ ] as any);
126
+
127
+ expect(result.messagesPrompt).toBe('Tool Result (search): Network error');
128
+ });
129
+
130
+ it('should handle empty content array', () => {
131
+ const result = convertToClaudeCodeMessages([
132
+ {
133
+ role: 'user',
134
+ content: [],
135
+ },
136
+ ] as ModelMessage[]);
137
+
138
+ expect(result.messagesPrompt).toBe('');
139
+ });
140
+
141
+ it('should handle undefined content gracefully', () => {
142
+ const result = convertToClaudeCodeMessages([
143
+ {
144
+ role: 'user',
145
+ content: [{ type: 'text', text: undefined as any }],
146
+ },
147
+ ] as ModelMessage[]);
148
+
149
+ expect(result.messagesPrompt).toBe('');
150
+ });
151
+
152
+ it('should handle complex tool results', () => {
153
+ const result = convertToClaudeCodeMessages([
154
+ {
155
+ role: 'tool',
156
+ content: [
157
+ {
158
+ type: 'tool-result',
159
+ toolCallId: 'call-789',
160
+ toolName: 'database',
161
+ output: {
162
+ type: 'json',
163
+ value: {
164
+ users: [
165
+ { id: 1, name: 'Alice' },
166
+ { id: 2, name: 'Bob' },
167
+ ],
168
+ count: 2,
169
+ },
170
+ },
171
+ },
172
+ ],
173
+ },
174
+ ] as any);
175
+
176
+ expect(result.messagesPrompt).toBe(
177
+ 'Tool Result (database): {"users":[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}],"count":2}'
178
+ );
179
+ });
180
+
181
+ it('should handle consecutive messages properly', () => {
182
+ const result = convertToClaudeCodeMessages([
183
+ { role: 'user', content: 'First message' },
184
+ { role: 'user', content: 'Second message' },
185
+ { role: 'assistant', content: 'Response' },
186
+ { role: 'user', content: 'Third message' },
187
+ ] as ModelMessage[]);
188
+
189
+ expect(result.messagesPrompt).toBe(
190
+ 'Human: First message\n\nHuman: Second message\n\nAssistant: Response\n\nHuman: Third message'
191
+ );
192
+ });
193
+ });