@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,65 @@
1
+ import type { Logger } from './types.js';
2
+
3
+ /**
4
+ * Default logger that uses console with level tags.
5
+ */
6
+ const defaultLogger: Logger = {
7
+ // eslint-disable-next-line no-console
8
+ debug: (message: string) => console.debug(`[DEBUG] ${message}`),
9
+ // eslint-disable-next-line no-console
10
+ info: (message: string) => console.info(`[INFO] ${message}`),
11
+ warn: (message: string) => console.warn(`[WARN] ${message}`),
12
+ error: (message: string) => console.error(`[ERROR] ${message}`),
13
+ };
14
+
15
+ /**
16
+ * No-op logger that discards all messages.
17
+ */
18
+ const noopLogger: Logger = {
19
+ debug: () => {},
20
+ info: () => {},
21
+ warn: () => {},
22
+ error: () => {},
23
+ };
24
+
25
+ /**
26
+ * Gets the appropriate logger based on configuration.
27
+ *
28
+ * @param logger - Logger configuration from settings
29
+ * @returns The logger to use
30
+ */
31
+ export function getLogger(logger: Logger | false | undefined): Logger {
32
+ if (logger === false) {
33
+ return noopLogger;
34
+ }
35
+
36
+ if (logger === undefined) {
37
+ return defaultLogger;
38
+ }
39
+
40
+ return logger;
41
+ }
42
+
43
+ /**
44
+ * Creates a verbose-aware logger that only logs debug/info when verbose is enabled.
45
+ * Warn and error are always logged regardless of verbose setting.
46
+ *
47
+ * @param logger - Base logger to wrap
48
+ * @param verbose - Whether to enable verbose (debug/info) logging
49
+ * @returns Logger with verbose-aware behavior
50
+ */
51
+ export function createVerboseLogger(logger: Logger, verbose: boolean = false): Logger {
52
+ if (verbose) {
53
+ // When verbose is enabled, use all log levels
54
+ return logger;
55
+ }
56
+
57
+ // When verbose is disabled, only allow warn/error
58
+ // Bind methods to preserve 'this' context for custom loggers
59
+ return {
60
+ debug: () => {}, // No-op when not verbose
61
+ info: () => {}, // No-op when not verbose
62
+ warn: logger.warn.bind(logger),
63
+ error: logger.error.bind(logger),
64
+ };
65
+ }
@@ -0,0 +1,120 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { mapClaudeCodeFinishReason } from './map-claude-code-finish-reason.js';
3
+
4
+ describe('mapClaudeCodeFinishReason', () => {
5
+ it('should map success to stop with raw value', () => {
6
+ expect(mapClaudeCodeFinishReason('success')).toEqual({
7
+ unified: 'stop',
8
+ raw: 'success',
9
+ });
10
+ });
11
+
12
+ it('should map error_max_turns to length with raw value', () => {
13
+ expect(mapClaudeCodeFinishReason('error_max_turns')).toEqual({
14
+ unified: 'length',
15
+ raw: 'error_max_turns',
16
+ });
17
+ });
18
+
19
+ it('should map error_during_execution to error with raw value', () => {
20
+ expect(mapClaudeCodeFinishReason('error_during_execution')).toEqual({
21
+ unified: 'error',
22
+ raw: 'error_during_execution',
23
+ });
24
+ });
25
+
26
+ it('should map unknown subtypes to other with raw value preserved', () => {
27
+ expect(mapClaudeCodeFinishReason('unknown_subtype')).toEqual({
28
+ unified: 'other',
29
+ raw: 'unknown_subtype',
30
+ });
31
+ expect(mapClaudeCodeFinishReason('custom')).toEqual({
32
+ unified: 'other',
33
+ raw: 'custom',
34
+ });
35
+ expect(mapClaudeCodeFinishReason('')).toEqual({
36
+ unified: 'other',
37
+ raw: '',
38
+ });
39
+ });
40
+
41
+ it('should handle undefined subtype', () => {
42
+ expect(mapClaudeCodeFinishReason(undefined)).toEqual({
43
+ unified: 'stop',
44
+ raw: undefined,
45
+ });
46
+ });
47
+
48
+ it('should handle null subtype as unknown', () => {
49
+ expect(mapClaudeCodeFinishReason(null as unknown as string | undefined)).toEqual({
50
+ unified: 'other',
51
+ raw: null,
52
+ });
53
+ });
54
+
55
+ it('should be case sensitive - non-matching cases map to other', () => {
56
+ expect(mapClaudeCodeFinishReason('Success')).toEqual({
57
+ unified: 'other',
58
+ raw: 'Success',
59
+ });
60
+ expect(mapClaudeCodeFinishReason('ERROR_MAX_TURNS')).toEqual({
61
+ unified: 'other',
62
+ raw: 'ERROR_MAX_TURNS',
63
+ });
64
+ expect(mapClaudeCodeFinishReason('Error_During_Execution')).toEqual({
65
+ unified: 'other',
66
+ raw: 'Error_During_Execution',
67
+ });
68
+ });
69
+
70
+ describe('stop_reason support', () => {
71
+ it('should map end_turn stop_reason to stop', () => {
72
+ expect(mapClaudeCodeFinishReason('success', 'end_turn')).toEqual({
73
+ unified: 'stop',
74
+ raw: 'end_turn',
75
+ });
76
+ });
77
+
78
+ it('should map max_tokens stop_reason to length', () => {
79
+ expect(mapClaudeCodeFinishReason('success', 'max_tokens')).toEqual({
80
+ unified: 'length',
81
+ raw: 'max_tokens',
82
+ });
83
+ });
84
+
85
+ it('should map tool_use stop_reason to tool-calls', () => {
86
+ expect(mapClaudeCodeFinishReason('success', 'tool_use')).toEqual({
87
+ unified: 'tool-calls',
88
+ raw: 'tool_use',
89
+ });
90
+ });
91
+
92
+ it('should map stop_sequence stop_reason to stop', () => {
93
+ expect(mapClaudeCodeFinishReason('success', 'stop_sequence')).toEqual({
94
+ unified: 'stop',
95
+ raw: 'stop_sequence',
96
+ });
97
+ });
98
+
99
+ it('should fall back to subtype mapping when stop_reason is null', () => {
100
+ expect(mapClaudeCodeFinishReason('success', null)).toEqual({
101
+ unified: 'stop',
102
+ raw: 'success',
103
+ });
104
+ });
105
+
106
+ it('should fall back to subtype mapping when stop_reason is undefined (backward compat)', () => {
107
+ expect(mapClaudeCodeFinishReason('error_max_turns', undefined)).toEqual({
108
+ unified: 'length',
109
+ raw: 'error_max_turns',
110
+ });
111
+ });
112
+
113
+ it('should use unknown stop_reason as raw when falling back to subtype mapping', () => {
114
+ expect(mapClaudeCodeFinishReason('success', 'unknown_reason')).toEqual({
115
+ unified: 'stop',
116
+ raw: 'unknown_reason',
117
+ });
118
+ });
119
+ });
120
+ });
@@ -0,0 +1,60 @@
1
+ import type { LanguageModelV3FinishReason } from '@ai-sdk/provider';
2
+
3
+ /**
4
+ * Maps Claude Code SDK result subtypes to AI SDK finish reasons.
5
+ *
6
+ * When `stopReason` is provided (from the SDK's `stop_reason` field), it takes
7
+ * priority for mapping well-known Anthropic API stop reasons. Otherwise, falls
8
+ * back to the existing subtype-based mapping.
9
+ *
10
+ * @param subtype - The result subtype from Claude Code SDK
11
+ * @param stopReason - The optional stop_reason from SDKResultSuccess/SDKResultError (v0.2.31+)
12
+ * @returns The corresponding AI SDK finish reason with unified and raw values
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * // With stop_reason (preferred when available)
17
+ * mapClaudeCodeFinishReason('success', 'end_turn');
18
+ * // Returns: { unified: 'stop', raw: 'end_turn' }
19
+ *
20
+ * // Without stop_reason (backward compatible)
21
+ * mapClaudeCodeFinishReason('error_max_turns');
22
+ * // Returns: { unified: 'length', raw: 'error_max_turns' }
23
+ * ```
24
+ */
25
+ export function mapClaudeCodeFinishReason(
26
+ subtype?: string,
27
+ stopReason?: string | null
28
+ ): LanguageModelV3FinishReason {
29
+ // When stop_reason is present and non-null, map known Anthropic API stop reasons
30
+ if (stopReason != null) {
31
+ switch (stopReason) {
32
+ case 'end_turn':
33
+ return { unified: 'stop', raw: 'end_turn' };
34
+ case 'max_tokens':
35
+ return { unified: 'length', raw: 'max_tokens' };
36
+ case 'stop_sequence':
37
+ return { unified: 'stop', raw: 'stop_sequence' };
38
+ case 'tool_use':
39
+ return { unified: 'tool-calls', raw: 'tool_use' };
40
+ default:
41
+ // Unknown stop_reason: fall back to subtype mapping but preserve stop_reason as raw
42
+ break;
43
+ }
44
+ }
45
+
46
+ // Fall back to subtype-based mapping
47
+ const raw = stopReason ?? subtype;
48
+ switch (subtype) {
49
+ case 'success':
50
+ return { unified: 'stop', raw };
51
+ case 'error_max_turns':
52
+ return { unified: 'length', raw };
53
+ case 'error_during_execution':
54
+ return { unified: 'error', raw };
55
+ case undefined:
56
+ return { unified: 'stop', raw };
57
+ default:
58
+ return { unified: 'other', raw };
59
+ }
60
+ }
@@ -0,0 +1,71 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { z } from 'zod';
3
+
4
+ // Mock the SDK module
5
+ vi.mock('@anthropic-ai/claude-agent-sdk', () => {
6
+ return {
7
+ tool: vi.fn((_name, _description, _inputSchema, _handler, _extras) => ({
8
+ name: _name,
9
+ description: _description,
10
+ inputSchema: _inputSchema,
11
+ handler: _handler,
12
+ annotations: _extras?.annotations,
13
+ })),
14
+ createSdkMcpServer: vi.fn((_options) => ({
15
+ type: 'sdk' as const,
16
+ name: _options.name,
17
+ instance: { tools: _options.tools },
18
+ })),
19
+ };
20
+ });
21
+
22
+ import { tool as mockTool } from '@anthropic-ai/claude-agent-sdk';
23
+ import { createCustomMcpServer } from './mcp-helpers.js';
24
+ import type { ToolAnnotations } from './mcp-helpers.js';
25
+
26
+ describe('createCustomMcpServer', () => {
27
+ beforeEach(() => {
28
+ vi.clearAllMocks();
29
+ });
30
+
31
+ it('should forward annotations to tool() when provided', () => {
32
+ const annotations: ToolAnnotations = {
33
+ readOnlyHint: true,
34
+ destructiveHint: false,
35
+ };
36
+
37
+ createCustomMcpServer({
38
+ name: 'test-server',
39
+ tools: {
40
+ myTool: {
41
+ description: 'A test tool',
42
+ inputSchema: z.object({ query: z.string() }),
43
+ handler: async () => ({ content: [{ type: 'text' as const, text: 'ok' }] }),
44
+ annotations,
45
+ },
46
+ },
47
+ });
48
+
49
+ expect(vi.mocked(mockTool)).toHaveBeenCalledOnce();
50
+ const callArgs = vi.mocked(mockTool).mock.calls[0];
51
+ expect(callArgs?.[0]).toBe('myTool');
52
+ expect(callArgs?.[4]).toEqual({ annotations });
53
+ });
54
+
55
+ it('should pass undefined as 5th arg when annotations are not provided', () => {
56
+ createCustomMcpServer({
57
+ name: 'test-server',
58
+ tools: {
59
+ plainTool: {
60
+ description: 'No annotations',
61
+ inputSchema: z.object({ x: z.number() }),
62
+ handler: async () => ({ content: [{ type: 'text' as const, text: 'ok' }] }),
63
+ },
64
+ },
65
+ });
66
+
67
+ expect(vi.mocked(mockTool)).toHaveBeenCalledOnce();
68
+ const callArgs = vi.mocked(mockTool).mock.calls[0];
69
+ expect(callArgs?.[4]).toBeUndefined();
70
+ });
71
+ });
@@ -0,0 +1,123 @@
1
+ import { createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk';
2
+ import type {
3
+ McpSdkServerConfigWithInstance,
4
+ SdkMcpToolDefinition,
5
+ } from '@anthropic-ai/claude-agent-sdk';
6
+ import { type ZodRawShape, type ZodObject } from 'zod';
7
+
8
+ /**
9
+ * Optional annotations for content items, per MCP specification.
10
+ * Validated against MCP SDK schema version 2025-06-18.
11
+ */
12
+ type ContentAnnotations = {
13
+ /** Intended audience(s) for this content */
14
+ audience?: ('user' | 'assistant')[];
15
+ /** Priority hint (0 = least important, 1 = most important) */
16
+ priority?: number;
17
+ /** ISO 8601 timestamp of last modification */
18
+ lastModified?: string;
19
+ };
20
+
21
+ /**
22
+ * MCP tool annotations for hinting tool behavior to the model.
23
+ * Derived from the SDK's SdkMcpToolDefinition type to stay in sync
24
+ * with the upstream MCP ToolAnnotations definition.
25
+ */
26
+ export type ToolAnnotations = NonNullable<SdkMcpToolDefinition['annotations']>;
27
+
28
+ /**
29
+ * Convenience helper to create an SDK MCP server from a simple tool map.
30
+ * Each tool provides a description, a Zod object schema, and a handler.
31
+ *
32
+ * Type definition validated against MCP SDK specification version 2025-06-18.
33
+ * See: https://modelcontextprotocol.io/specification/2025-06-18/server/tools
34
+ */
35
+ export type MinimalCallToolResult = {
36
+ content: Array<
37
+ | {
38
+ /** Text content */
39
+ type: 'text';
40
+ /** The text content (plain text or structured format like JSON) */
41
+ text: string;
42
+ annotations?: ContentAnnotations;
43
+ _meta?: Record<string, unknown>;
44
+ [key: string]: unknown;
45
+ }
46
+ | {
47
+ /** Image content (base64-encoded) */
48
+ type: 'image';
49
+ /** Base64-encoded image data */
50
+ data: string;
51
+ /** MIME type of the image (e.g., image/png, image/jpeg) */
52
+ mimeType: string;
53
+ annotations?: ContentAnnotations;
54
+ _meta?: Record<string, unknown>;
55
+ [key: string]: unknown;
56
+ }
57
+ | {
58
+ /** Audio content (base64-encoded) */
59
+ type: 'audio';
60
+ /** Base64-encoded audio data */
61
+ data: string;
62
+ /** MIME type of the audio (e.g., audio/wav, audio/mp3) */
63
+ mimeType: string;
64
+ annotations?: ContentAnnotations;
65
+ _meta?: Record<string, unknown>;
66
+ [key: string]: unknown;
67
+ }
68
+ | {
69
+ /** Embedded resource with full content (text or blob) */
70
+ type: 'resource';
71
+ /** Resource contents - either text or blob variant */
72
+ resource: { uri: string; _meta?: Record<string, unknown>; [key: string]: unknown } & (
73
+ | { text: string; mimeType?: string }
74
+ | { blob: string; mimeType: string }
75
+ );
76
+ annotations?: ContentAnnotations;
77
+ _meta?: Record<string, unknown>;
78
+ [key: string]: unknown;
79
+ }
80
+ | {
81
+ /** Resource link (reference only - no embedded content) */
82
+ type: 'resource_link';
83
+ /** URI of the resource */
84
+ uri: string;
85
+ /** Human-readable name (required per MCP spec) */
86
+ name: string;
87
+ /** Optional description of what this resource represents */
88
+ description?: string;
89
+ /** MIME type of the resource, if known */
90
+ mimeType?: string;
91
+ annotations?: ContentAnnotations;
92
+ _meta?: Record<string, unknown>;
93
+ [key: string]: unknown;
94
+ }
95
+ >;
96
+ isError?: boolean;
97
+ structuredContent?: Record<string, unknown>;
98
+ _meta?: Record<string, unknown>;
99
+ [key: string]: unknown;
100
+ };
101
+
102
+ export function createCustomMcpServer<
103
+ Tools extends Record<
104
+ string,
105
+ {
106
+ description: string;
107
+ inputSchema: ZodObject<ZodRawShape>;
108
+ handler: (args: Record<string, unknown>, extra: unknown) => Promise<MinimalCallToolResult>;
109
+ annotations?: ToolAnnotations;
110
+ }
111
+ >,
112
+ >(config: { name: string; version?: string; tools: Tools }): McpSdkServerConfigWithInstance {
113
+ const defs = Object.entries(config.tools).map(([name, def]) =>
114
+ tool(
115
+ name,
116
+ def.description,
117
+ def.inputSchema.shape as ZodRawShape,
118
+ (args: Record<string, unknown>, extra: unknown) => def.handler(args, extra),
119
+ def.annotations ? { annotations: def.annotations } : undefined
120
+ )
121
+ );
122
+ return createSdkMcpServer({ name: config.name, version: config.version, tools: defs });
123
+ }