@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.
- package/README.md +90 -0
- package/dist/index.cjs +2522 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +848 -0
- package/dist/index.d.ts +848 -0
- package/dist/index.js +2484 -0
- package/dist/index.js.map +1 -0
- package/docs/content/README.md +337 -0
- package/docs/content/agent-teams.mdx +324 -0
- package/docs/content/api.mdx +757 -0
- package/docs/content/best-practices.mdx +624 -0
- package/docs/content/examples.mdx +675 -0
- package/docs/content/guide.mdx +516 -0
- package/docs/content/index.mdx +99 -0
- package/docs/content/installation.mdx +246 -0
- package/docs/content/skills.mdx +548 -0
- package/docs/content/troubleshooting.mdx +588 -0
- package/docs/examples/README.md +499 -0
- package/docs/examples/abort-signal.ts +125 -0
- package/docs/examples/agent-teams.ts +122 -0
- package/docs/examples/basic-usage.ts +73 -0
- package/docs/examples/check-cli.ts +51 -0
- package/docs/examples/conversation-history.ts +69 -0
- package/docs/examples/custom-config.ts +90 -0
- package/docs/examples/generate-object-constraints.ts +209 -0
- package/docs/examples/generate-object.ts +211 -0
- package/docs/examples/hooks-callbacks.ts +63 -0
- package/docs/examples/images.ts +76 -0
- package/docs/examples/integration-test.ts +241 -0
- package/docs/examples/limitations.ts +150 -0
- package/docs/examples/logging-custom-logger.ts +99 -0
- package/docs/examples/logging-default.ts +55 -0
- package/docs/examples/logging-disabled.ts +74 -0
- package/docs/examples/logging-verbose.ts +64 -0
- package/docs/examples/long-running-tasks.ts +179 -0
- package/docs/examples/message-injection.ts +210 -0
- package/docs/examples/mid-stream-injection.ts +126 -0
- package/docs/examples/run-all-examples.sh +48 -0
- package/docs/examples/sdk-tools-callbacks.ts +49 -0
- package/docs/examples/skills-discovery.ts +144 -0
- package/docs/examples/skills-management.ts +140 -0
- package/docs/examples/stream-object.ts +80 -0
- package/docs/examples/streaming.ts +52 -0
- package/docs/examples/structured-output-repro.ts +227 -0
- package/docs/examples/tool-management.ts +215 -0
- package/docs/examples/tool-streaming.ts +132 -0
- package/docs/examples/zod4-compatibility-test.ts +290 -0
- package/docs/src/claude-code-language-model.test.ts +3883 -0
- package/docs/src/claude-code-language-model.ts +2586 -0
- package/docs/src/claude-code-provider.test.ts +97 -0
- package/docs/src/claude-code-provider.ts +179 -0
- package/docs/src/convert-to-claude-code-messages.images.test.ts +104 -0
- package/docs/src/convert-to-claude-code-messages.test.ts +193 -0
- package/docs/src/convert-to-claude-code-messages.ts +419 -0
- package/docs/src/errors.test.ts +213 -0
- package/docs/src/errors.ts +216 -0
- package/docs/src/index.test.ts +49 -0
- package/docs/src/index.ts +98 -0
- package/docs/src/logger.integration.test.ts +164 -0
- package/docs/src/logger.test.ts +184 -0
- package/docs/src/logger.ts +65 -0
- package/docs/src/map-claude-code-finish-reason.test.ts +120 -0
- package/docs/src/map-claude-code-finish-reason.ts +60 -0
- package/docs/src/mcp-helpers.test.ts +71 -0
- package/docs/src/mcp-helpers.ts +123 -0
- package/docs/src/message-injection.test.ts +460 -0
- package/docs/src/types.ts +447 -0
- package/docs/src/validation.test.ts +558 -0
- package/docs/src/validation.ts +360 -0
- package/package.json +124 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { APICallError, LoadAPIKeyError } from '@ai-sdk/provider';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Metadata associated with Claude Code SDK errors.
|
|
5
|
+
* Provides additional context about command execution failures.
|
|
6
|
+
*/
|
|
7
|
+
export interface ClaudeCodeErrorMetadata {
|
|
8
|
+
/**
|
|
9
|
+
* Error code from the CLI process (e.g., 'ENOENT', 'ETIMEDOUT').
|
|
10
|
+
*/
|
|
11
|
+
code?: string;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Exit code from the Claude Code SDK process.
|
|
15
|
+
* Common codes:
|
|
16
|
+
* - 401: Authentication error
|
|
17
|
+
* - 1: General error
|
|
18
|
+
*/
|
|
19
|
+
exitCode?: number;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Standard error output from the CLI process.
|
|
23
|
+
*/
|
|
24
|
+
stderr?: string;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Excerpt from the prompt that caused the error.
|
|
28
|
+
* Limited to first 200 characters for debugging.
|
|
29
|
+
*/
|
|
30
|
+
promptExcerpt?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Creates an APICallError with Claude Code specific metadata.
|
|
35
|
+
* Used for general CLI execution errors.
|
|
36
|
+
*
|
|
37
|
+
* @param options - Error details and metadata
|
|
38
|
+
* @param options.message - Human-readable error message
|
|
39
|
+
* @param options.code - Error code from the CLI process
|
|
40
|
+
* @param options.exitCode - Exit code from the CLI
|
|
41
|
+
* @param options.stderr - Standard error output
|
|
42
|
+
* @param options.promptExcerpt - Excerpt of the prompt that caused the error
|
|
43
|
+
* @param options.isRetryable - Whether the error is potentially retryable
|
|
44
|
+
* @returns An APICallError instance with Claude Code metadata
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* throw createAPICallError({
|
|
49
|
+
* message: 'Claude Code SDK failed',
|
|
50
|
+
* code: 'ENOENT',
|
|
51
|
+
* isRetryable: true
|
|
52
|
+
* });
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export function createAPICallError({
|
|
56
|
+
message,
|
|
57
|
+
code,
|
|
58
|
+
exitCode,
|
|
59
|
+
stderr,
|
|
60
|
+
promptExcerpt,
|
|
61
|
+
isRetryable = false,
|
|
62
|
+
}: ClaudeCodeErrorMetadata & {
|
|
63
|
+
message: string;
|
|
64
|
+
isRetryable?: boolean;
|
|
65
|
+
}): APICallError {
|
|
66
|
+
const metadata: ClaudeCodeErrorMetadata = {
|
|
67
|
+
code,
|
|
68
|
+
exitCode,
|
|
69
|
+
stderr,
|
|
70
|
+
promptExcerpt,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
return new APICallError({
|
|
74
|
+
message,
|
|
75
|
+
isRetryable,
|
|
76
|
+
url: 'claude-code-cli://command',
|
|
77
|
+
requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : undefined,
|
|
78
|
+
data: metadata,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Creates an authentication error for Claude Code SDK login failures.
|
|
84
|
+
*
|
|
85
|
+
* @param options - Error configuration
|
|
86
|
+
* @param options.message - Error message describing the authentication failure
|
|
87
|
+
* @returns A LoadAPIKeyError instance
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* throw createAuthenticationError({
|
|
92
|
+
* message: 'Please run "claude login" to authenticate'
|
|
93
|
+
* });
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export function createAuthenticationError({ message }: { message: string }): LoadAPIKeyError {
|
|
97
|
+
return new LoadAPIKeyError({
|
|
98
|
+
message:
|
|
99
|
+
message || 'Authentication failed. Please ensure Claude Code SDK is properly authenticated.',
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Creates a timeout error for Claude Code SDK operations.
|
|
105
|
+
*
|
|
106
|
+
* @param options - Timeout error details
|
|
107
|
+
* @param options.message - Error message describing the timeout
|
|
108
|
+
* @param options.promptExcerpt - Excerpt of the prompt that timed out
|
|
109
|
+
* @param options.timeoutMs - Timeout duration in milliseconds
|
|
110
|
+
* @returns An APICallError instance configured as a timeout error
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* throw createTimeoutError({
|
|
115
|
+
* message: 'Request timed out after 2 minutes',
|
|
116
|
+
* timeoutMs: 120000
|
|
117
|
+
* });
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export function createTimeoutError({
|
|
121
|
+
message,
|
|
122
|
+
promptExcerpt,
|
|
123
|
+
timeoutMs,
|
|
124
|
+
}: {
|
|
125
|
+
message: string;
|
|
126
|
+
promptExcerpt?: string;
|
|
127
|
+
timeoutMs?: number;
|
|
128
|
+
}): APICallError {
|
|
129
|
+
// Store timeoutMs in metadata for potential use by error handlers
|
|
130
|
+
const metadata: ClaudeCodeErrorMetadata = {
|
|
131
|
+
code: 'TIMEOUT',
|
|
132
|
+
promptExcerpt,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
return new APICallError({
|
|
136
|
+
message,
|
|
137
|
+
isRetryable: true,
|
|
138
|
+
url: 'claude-code-cli://command',
|
|
139
|
+
requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : undefined,
|
|
140
|
+
data: timeoutMs !== undefined ? { ...metadata, timeoutMs } : metadata,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Checks if an error is an authentication error.
|
|
146
|
+
* Returns true for LoadAPIKeyError instances or APICallError with exit code 401.
|
|
147
|
+
*
|
|
148
|
+
* @param error - The error to check
|
|
149
|
+
* @returns True if the error is an authentication error
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```typescript
|
|
153
|
+
* try {
|
|
154
|
+
* await model.generate(...);
|
|
155
|
+
* } catch (error) {
|
|
156
|
+
* if (isAuthenticationError(error)) {
|
|
157
|
+
* console.log('Please authenticate with Claude Code SDK');
|
|
158
|
+
* }
|
|
159
|
+
* }
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
export function isAuthenticationError(error: unknown): boolean {
|
|
163
|
+
if (error instanceof LoadAPIKeyError) return true;
|
|
164
|
+
if (error instanceof APICallError && (error.data as ClaudeCodeErrorMetadata)?.exitCode === 401)
|
|
165
|
+
return true;
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Checks if an error is a timeout error.
|
|
171
|
+
* Returns true for APICallError instances with code 'TIMEOUT'.
|
|
172
|
+
*
|
|
173
|
+
* @param error - The error to check
|
|
174
|
+
* @returns True if the error is a timeout error
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```typescript
|
|
178
|
+
* try {
|
|
179
|
+
* await model.generate(...);
|
|
180
|
+
* } catch (error) {
|
|
181
|
+
* if (isTimeoutError(error)) {
|
|
182
|
+
* console.log('Request timed out, consider retrying');
|
|
183
|
+
* }
|
|
184
|
+
* }
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
export function isTimeoutError(error: unknown): boolean {
|
|
188
|
+
if (error instanceof APICallError && (error.data as ClaudeCodeErrorMetadata)?.code === 'TIMEOUT')
|
|
189
|
+
return true;
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Extracts Claude Code error metadata from an error object.
|
|
195
|
+
*
|
|
196
|
+
* @param error - The error to extract metadata from
|
|
197
|
+
* @returns The error metadata if available, undefined otherwise
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```typescript
|
|
201
|
+
* try {
|
|
202
|
+
* await model.generate(...);
|
|
203
|
+
* } catch (error) {
|
|
204
|
+
* const metadata = getErrorMetadata(error);
|
|
205
|
+
* if (metadata?.exitCode === 401) {
|
|
206
|
+
* console.log('Authentication required');
|
|
207
|
+
* }
|
|
208
|
+
* }
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
export function getErrorMetadata(error: unknown): ClaudeCodeErrorMetadata | undefined {
|
|
212
|
+
if (error instanceof APICallError && error.data) {
|
|
213
|
+
return error.data as ClaudeCodeErrorMetadata;
|
|
214
|
+
}
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
|
|
3
|
+
describe('index exports', () => {
|
|
4
|
+
it('should export all expected functions and types', async () => {
|
|
5
|
+
const exports = await import('./index.js');
|
|
6
|
+
|
|
7
|
+
// Provider exports
|
|
8
|
+
expect(exports.createClaudeCode).toBeDefined();
|
|
9
|
+
expect(typeof exports.createClaudeCode).toBe('function');
|
|
10
|
+
expect(exports.claudeCode).toBeDefined();
|
|
11
|
+
expect(typeof exports.claudeCode).toBe('function');
|
|
12
|
+
|
|
13
|
+
// Language model exports
|
|
14
|
+
expect(exports.ClaudeCodeLanguageModel).toBeDefined();
|
|
15
|
+
expect(typeof exports.ClaudeCodeLanguageModel).toBe('function');
|
|
16
|
+
|
|
17
|
+
// Error handling exports
|
|
18
|
+
expect(exports.isAuthenticationError).toBeDefined();
|
|
19
|
+
expect(typeof exports.isAuthenticationError).toBe('function');
|
|
20
|
+
expect(exports.isTimeoutError).toBeDefined();
|
|
21
|
+
expect(typeof exports.isTimeoutError).toBe('function');
|
|
22
|
+
expect(exports.getErrorMetadata).toBeDefined();
|
|
23
|
+
expect(typeof exports.getErrorMetadata).toBe('function');
|
|
24
|
+
expect(exports.createAPICallError).toBeDefined();
|
|
25
|
+
expect(typeof exports.createAPICallError).toBe('function');
|
|
26
|
+
expect(exports.createAuthenticationError).toBeDefined();
|
|
27
|
+
expect(typeof exports.createAuthenticationError).toBe('function');
|
|
28
|
+
expect(exports.createTimeoutError).toBeDefined();
|
|
29
|
+
expect(typeof exports.createTimeoutError).toBe('function');
|
|
30
|
+
|
|
31
|
+
// SDK passthroughs
|
|
32
|
+
expect(exports.createSdkMcpServer).toBeDefined();
|
|
33
|
+
expect(typeof exports.createSdkMcpServer).toBe('function');
|
|
34
|
+
expect(exports.tool).toBeDefined();
|
|
35
|
+
expect(typeof exports.tool).toBe('function');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should export correct modules', async () => {
|
|
39
|
+
const indexExports = await import('./index.js');
|
|
40
|
+
const providerExports = await import('./claude-code-provider.js');
|
|
41
|
+
const errorExports = await import('./errors.js');
|
|
42
|
+
|
|
43
|
+
// Check that exported functions are the same references
|
|
44
|
+
expect(indexExports.createClaudeCode).toBe(providerExports.createClaudeCode);
|
|
45
|
+
expect(indexExports.claudeCode).toBe(providerExports.claudeCode);
|
|
46
|
+
expect(indexExports.isAuthenticationError).toBe(errorExports.isAuthenticationError);
|
|
47
|
+
expect(indexExports.isTimeoutError).toBe(errorExports.isTimeoutError);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider exports for creating and configuring Claude Code instances.
|
|
3
|
+
* @module claude-code
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates a new Claude Code provider instance and the default provider instance.
|
|
8
|
+
* @see {@link createClaudeCode} for creating custom provider instances
|
|
9
|
+
* @see {@link claudeCode} for the default provider instance
|
|
10
|
+
*/
|
|
11
|
+
export { createClaudeCode, claudeCode } from './claude-code-provider.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Type definitions for the Claude Code provider.
|
|
15
|
+
* @see {@link ClaudeCodeProvider} for the provider interface
|
|
16
|
+
* @see {@link ClaudeCodeProviderSettings} for provider configuration options
|
|
17
|
+
*/
|
|
18
|
+
export type { ClaudeCodeProvider, ClaudeCodeProviderSettings } from './claude-code-provider.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Language model implementation for Claude Code.
|
|
22
|
+
* This class implements the AI SDK's LanguageModelV3 interface.
|
|
23
|
+
*/
|
|
24
|
+
export { ClaudeCodeLanguageModel } from './claude-code-language-model.js';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Type definitions for Claude Code language models.
|
|
28
|
+
* @see {@link ClaudeCodeModelId} for supported model identifiers
|
|
29
|
+
* @see {@link ClaudeCodeLanguageModelOptions} for model configuration options
|
|
30
|
+
*/
|
|
31
|
+
export type {
|
|
32
|
+
ClaudeCodeModelId,
|
|
33
|
+
ClaudeCodeLanguageModelOptions,
|
|
34
|
+
} from './claude-code-language-model.js';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Settings for configuring Claude Code behavior.
|
|
38
|
+
* Includes options for customizing the CLI execution, permissions, and tool usage.
|
|
39
|
+
*/
|
|
40
|
+
export type { ClaudeCodeSettings, Logger, MessageInjector } from './types.js';
|
|
41
|
+
|
|
42
|
+
// Convenience re-exports from the SDK for custom tools and hooks
|
|
43
|
+
export { createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk';
|
|
44
|
+
export { createCustomMcpServer } from './mcp-helpers.js';
|
|
45
|
+
export type { ToolAnnotations, MinimalCallToolResult } from './mcp-helpers.js';
|
|
46
|
+
export type {
|
|
47
|
+
HookEvent,
|
|
48
|
+
HookCallback,
|
|
49
|
+
HookCallbackMatcher,
|
|
50
|
+
HookInput,
|
|
51
|
+
HookJSONOutput,
|
|
52
|
+
PreToolUseHookInput,
|
|
53
|
+
PostToolUseHookInput,
|
|
54
|
+
UserPromptSubmitHookInput,
|
|
55
|
+
SessionStartHookInput,
|
|
56
|
+
SessionEndHookInput,
|
|
57
|
+
TeammateIdleHookInput,
|
|
58
|
+
TaskCompletedHookInput,
|
|
59
|
+
CanUseTool,
|
|
60
|
+
PermissionResult,
|
|
61
|
+
PermissionUpdate,
|
|
62
|
+
PermissionBehavior,
|
|
63
|
+
PermissionRuleValue,
|
|
64
|
+
McpServerConfig,
|
|
65
|
+
McpSdkServerConfigWithInstance,
|
|
66
|
+
OutputFormat,
|
|
67
|
+
SpawnedProcess,
|
|
68
|
+
SpawnOptions,
|
|
69
|
+
AgentMcpServerSpec,
|
|
70
|
+
// Query interface for mid-stream message injection via streamInput()
|
|
71
|
+
Query,
|
|
72
|
+
} from '@anthropic-ai/claude-agent-sdk';
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Error handling utilities for Claude Code.
|
|
76
|
+
* These functions help create and identify specific error types.
|
|
77
|
+
*
|
|
78
|
+
* @see {@link isAuthenticationError} to check for authentication failures
|
|
79
|
+
* @see {@link isTimeoutError} to check for timeout errors
|
|
80
|
+
* @see {@link getErrorMetadata} to extract error metadata
|
|
81
|
+
* @see {@link createAPICallError} to create general API errors
|
|
82
|
+
* @see {@link createAuthenticationError} to create authentication errors
|
|
83
|
+
* @see {@link createTimeoutError} to create timeout errors
|
|
84
|
+
*/
|
|
85
|
+
export {
|
|
86
|
+
isAuthenticationError,
|
|
87
|
+
isTimeoutError,
|
|
88
|
+
getErrorMetadata,
|
|
89
|
+
createAPICallError,
|
|
90
|
+
createAuthenticationError,
|
|
91
|
+
createTimeoutError,
|
|
92
|
+
} from './errors.js';
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Metadata associated with Claude Code errors.
|
|
96
|
+
* Contains additional context about CLI execution failures.
|
|
97
|
+
*/
|
|
98
|
+
export type { ClaudeCodeErrorMetadata } from './errors.js';
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { createClaudeCode } from '../src/index.js';
|
|
3
|
+
import type { Logger } from '../src/types.js';
|
|
4
|
+
|
|
5
|
+
describe('logger integration', () => {
|
|
6
|
+
let originalConsoleWarn: typeof console.warn;
|
|
7
|
+
let originalConsoleError: typeof console.error;
|
|
8
|
+
let consoleWarnSpy: any;
|
|
9
|
+
let consoleErrorSpy: any;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
originalConsoleWarn = console.warn;
|
|
13
|
+
originalConsoleError = console.error;
|
|
14
|
+
consoleWarnSpy = vi.fn();
|
|
15
|
+
consoleErrorSpy = vi.fn();
|
|
16
|
+
console.warn = consoleWarnSpy;
|
|
17
|
+
console.error = consoleErrorSpy;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
console.warn = originalConsoleWarn;
|
|
22
|
+
console.error = originalConsoleError;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('createClaudeCode with logger settings', () => {
|
|
26
|
+
it('should use console logger by default', () => {
|
|
27
|
+
createClaudeCode({
|
|
28
|
+
defaultSettings: {
|
|
29
|
+
maxTurns: 99, // High value to trigger warning
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining('High maxTurns value'));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should respect logger: false setting', () => {
|
|
37
|
+
createClaudeCode({
|
|
38
|
+
defaultSettings: {
|
|
39
|
+
logger: false,
|
|
40
|
+
maxTurns: 99, // High value that would normally trigger warning
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should use custom logger when provided', () => {
|
|
48
|
+
const customLogger: Logger = {
|
|
49
|
+
debug: vi.fn(),
|
|
50
|
+
info: vi.fn(),
|
|
51
|
+
warn: vi.fn(),
|
|
52
|
+
error: vi.fn(),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
createClaudeCode({
|
|
56
|
+
defaultSettings: {
|
|
57
|
+
logger: customLogger,
|
|
58
|
+
maxTurns: 99, // High value to trigger warning
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
expect(customLogger.warn).toHaveBeenCalledWith(
|
|
63
|
+
expect.stringContaining('High maxTurns value')
|
|
64
|
+
);
|
|
65
|
+
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should pass logger to created models', () => {
|
|
69
|
+
const customLogger: Logger = {
|
|
70
|
+
debug: vi.fn(),
|
|
71
|
+
info: vi.fn(),
|
|
72
|
+
warn: vi.fn(),
|
|
73
|
+
error: vi.fn(),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const provider = createClaudeCode({
|
|
77
|
+
defaultSettings: {
|
|
78
|
+
logger: customLogger,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Create a model with an unknown ID to trigger warning
|
|
83
|
+
provider('unknown-model-id');
|
|
84
|
+
|
|
85
|
+
expect(customLogger.warn).toHaveBeenCalledWith(expect.stringContaining('Unknown model ID'));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should allow model-specific logger override', () => {
|
|
89
|
+
const providerLogger: Logger = {
|
|
90
|
+
debug: vi.fn(),
|
|
91
|
+
info: vi.fn(),
|
|
92
|
+
warn: vi.fn(),
|
|
93
|
+
error: vi.fn(),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const modelLogger: Logger = {
|
|
97
|
+
debug: vi.fn(),
|
|
98
|
+
info: vi.fn(),
|
|
99
|
+
warn: vi.fn(),
|
|
100
|
+
error: vi.fn(),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const provider = createClaudeCode({
|
|
104
|
+
defaultSettings: {
|
|
105
|
+
logger: providerLogger,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Create a model with its own logger
|
|
110
|
+
provider('unknown-model', {
|
|
111
|
+
logger: modelLogger,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
expect(modelLogger.warn).toHaveBeenCalledWith(expect.stringContaining('Unknown model ID'));
|
|
115
|
+
expect(providerLogger.warn).not.toHaveBeenCalled();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should handle image input warnings', async () => {
|
|
119
|
+
const customLogger: Logger = {
|
|
120
|
+
debug: vi.fn(),
|
|
121
|
+
info: vi.fn(),
|
|
122
|
+
warn: vi.fn(),
|
|
123
|
+
error: vi.fn(),
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const provider = createClaudeCode({
|
|
127
|
+
defaultSettings: {
|
|
128
|
+
logger: customLogger,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
provider('opus');
|
|
133
|
+
|
|
134
|
+
// Mock the query function to prevent actual API calls
|
|
135
|
+
const { ClaudeCodeLanguageModel } = await import('../src/claude-code-language-model.js');
|
|
136
|
+
const proto = ClaudeCodeLanguageModel.prototype as any;
|
|
137
|
+
|
|
138
|
+
// Access the private method through prototype
|
|
139
|
+
const result = proto.generateAllWarnings.call(
|
|
140
|
+
{
|
|
141
|
+
modelValidationWarning: undefined,
|
|
142
|
+
settingsValidationWarnings: [],
|
|
143
|
+
logger: customLogger,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
prompt: [
|
|
147
|
+
{
|
|
148
|
+
role: 'user',
|
|
149
|
+
content: [
|
|
150
|
+
{ type: 'text', text: 'Hello' },
|
|
151
|
+
{ type: 'image', image: new Uint8Array([]) },
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
'test prompt'
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
// The warning should be in the warnings array, not logged directly
|
|
160
|
+
expect(customLogger.warn).not.toHaveBeenCalled();
|
|
161
|
+
expect(result.some((w: any) => w.message?.includes('image inputs'))).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
});
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { getLogger, createVerboseLogger } from './logger.js';
|
|
3
|
+
import type { Logger } from './types.js';
|
|
4
|
+
|
|
5
|
+
describe('logger', () => {
|
|
6
|
+
describe('getLogger', () => {
|
|
7
|
+
it('should return default logger when undefined', () => {
|
|
8
|
+
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
9
|
+
const logger = getLogger(undefined);
|
|
10
|
+
|
|
11
|
+
logger.warn('test warning');
|
|
12
|
+
|
|
13
|
+
expect(consoleSpy).toHaveBeenCalledWith('[WARN] test warning');
|
|
14
|
+
consoleSpy.mockRestore();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should support all log levels with default logger', () => {
|
|
18
|
+
const debugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {});
|
|
19
|
+
const infoSpy = vi.spyOn(console, 'info').mockImplementation(() => {});
|
|
20
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
21
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
22
|
+
|
|
23
|
+
const logger = getLogger(undefined);
|
|
24
|
+
|
|
25
|
+
logger.debug('test debug');
|
|
26
|
+
logger.info('test info');
|
|
27
|
+
logger.warn('test warning');
|
|
28
|
+
logger.error('test error');
|
|
29
|
+
|
|
30
|
+
expect(debugSpy).toHaveBeenCalledWith('[DEBUG] test debug');
|
|
31
|
+
expect(infoSpy).toHaveBeenCalledWith('[INFO] test info');
|
|
32
|
+
expect(warnSpy).toHaveBeenCalledWith('[WARN] test warning');
|
|
33
|
+
expect(errorSpy).toHaveBeenCalledWith('[ERROR] test error');
|
|
34
|
+
|
|
35
|
+
debugSpy.mockRestore();
|
|
36
|
+
infoSpy.mockRestore();
|
|
37
|
+
warnSpy.mockRestore();
|
|
38
|
+
errorSpy.mockRestore();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should return noop logger when false', () => {
|
|
42
|
+
const logger = getLogger(false);
|
|
43
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
44
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
45
|
+
|
|
46
|
+
logger.debug('test debug');
|
|
47
|
+
logger.info('test info');
|
|
48
|
+
logger.warn('test warning');
|
|
49
|
+
logger.error('test error');
|
|
50
|
+
|
|
51
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
52
|
+
expect(errorSpy).not.toHaveBeenCalled();
|
|
53
|
+
warnSpy.mockRestore();
|
|
54
|
+
errorSpy.mockRestore();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should return custom logger when provided', () => {
|
|
58
|
+
const customLogger: Logger = {
|
|
59
|
+
debug: vi.fn(),
|
|
60
|
+
info: vi.fn(),
|
|
61
|
+
warn: vi.fn(),
|
|
62
|
+
error: vi.fn(),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const logger = getLogger(customLogger);
|
|
66
|
+
|
|
67
|
+
expect(logger).toBe(customLogger);
|
|
68
|
+
|
|
69
|
+
logger.debug('custom debug');
|
|
70
|
+
logger.info('custom info');
|
|
71
|
+
logger.warn('custom warning');
|
|
72
|
+
logger.error('custom error');
|
|
73
|
+
|
|
74
|
+
expect(customLogger.debug).toHaveBeenCalledWith('custom debug');
|
|
75
|
+
expect(customLogger.info).toHaveBeenCalledWith('custom info');
|
|
76
|
+
expect(customLogger.warn).toHaveBeenCalledWith('custom warning');
|
|
77
|
+
expect(customLogger.error).toHaveBeenCalledWith('custom error');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should handle error logging with default logger', () => {
|
|
81
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
82
|
+
const logger = getLogger(undefined);
|
|
83
|
+
|
|
84
|
+
logger.error('test error');
|
|
85
|
+
|
|
86
|
+
expect(consoleSpy).toHaveBeenCalledWith('[ERROR] test error');
|
|
87
|
+
consoleSpy.mockRestore();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('createVerboseLogger', () => {
|
|
92
|
+
it('should allow all log levels when verbose is true', () => {
|
|
93
|
+
const mockLogger: Logger = {
|
|
94
|
+
debug: vi.fn(),
|
|
95
|
+
info: vi.fn(),
|
|
96
|
+
warn: vi.fn(),
|
|
97
|
+
error: vi.fn(),
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const logger = createVerboseLogger(mockLogger, true);
|
|
101
|
+
|
|
102
|
+
logger.debug('test debug');
|
|
103
|
+
logger.info('test info');
|
|
104
|
+
logger.warn('test warning');
|
|
105
|
+
logger.error('test error');
|
|
106
|
+
|
|
107
|
+
expect(mockLogger.debug).toHaveBeenCalledWith('test debug');
|
|
108
|
+
expect(mockLogger.info).toHaveBeenCalledWith('test info');
|
|
109
|
+
expect(mockLogger.warn).toHaveBeenCalledWith('test warning');
|
|
110
|
+
expect(mockLogger.error).toHaveBeenCalledWith('test error');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should suppress debug and info when verbose is false', () => {
|
|
114
|
+
const mockLogger: Logger = {
|
|
115
|
+
debug: vi.fn(),
|
|
116
|
+
info: vi.fn(),
|
|
117
|
+
warn: vi.fn(),
|
|
118
|
+
error: vi.fn(),
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const logger = createVerboseLogger(mockLogger, false);
|
|
122
|
+
|
|
123
|
+
logger.debug('test debug');
|
|
124
|
+
logger.info('test info');
|
|
125
|
+
logger.warn('test warning');
|
|
126
|
+
logger.error('test error');
|
|
127
|
+
|
|
128
|
+
expect(mockLogger.debug).not.toHaveBeenCalled();
|
|
129
|
+
expect(mockLogger.info).not.toHaveBeenCalled();
|
|
130
|
+
expect(mockLogger.warn).toHaveBeenCalledWith('test warning');
|
|
131
|
+
expect(mockLogger.error).toHaveBeenCalledWith('test error');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should default to verbose false when not specified', () => {
|
|
135
|
+
const mockLogger: Logger = {
|
|
136
|
+
debug: vi.fn(),
|
|
137
|
+
info: vi.fn(),
|
|
138
|
+
warn: vi.fn(),
|
|
139
|
+
error: vi.fn(),
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const logger = createVerboseLogger(mockLogger);
|
|
143
|
+
|
|
144
|
+
logger.debug('test debug');
|
|
145
|
+
logger.info('test info');
|
|
146
|
+
|
|
147
|
+
expect(mockLogger.debug).not.toHaveBeenCalled();
|
|
148
|
+
expect(mockLogger.info).not.toHaveBeenCalled();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should preserve this binding for custom logger instances', () => {
|
|
152
|
+
// Create a logger that relies on instance state
|
|
153
|
+
class CustomLogger implements Logger {
|
|
154
|
+
private prefix = '[CUSTOM]';
|
|
155
|
+
|
|
156
|
+
debug(message: string) {
|
|
157
|
+
return `${this.prefix} DEBUG: ${message}`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
info(message: string) {
|
|
161
|
+
return `${this.prefix} INFO: ${message}`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
warn(message: string) {
|
|
165
|
+
return `${this.prefix} WARN: ${message}`;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
error(message: string) {
|
|
169
|
+
return `${this.prefix} ERROR: ${message}`;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const customLogger = new CustomLogger();
|
|
174
|
+
const logger = createVerboseLogger(customLogger, false);
|
|
175
|
+
|
|
176
|
+
// These should not throw and should preserve 'this' binding
|
|
177
|
+
const warnResult = logger.warn('test warning');
|
|
178
|
+
const errorResult = logger.error('test error');
|
|
179
|
+
|
|
180
|
+
expect(warnResult).toBe('[CUSTOM] WARN: test warning');
|
|
181
|
+
expect(errorResult).toBe('[CUSTOM] ERROR: test error');
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
});
|