@omarestrella/ai-sdk-agent-sdk 1.0.0-beta.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 (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +91 -0
  3. package/dist/src/index.d.ts +4 -0
  4. package/dist/src/index.d.ts.map +1 -0
  5. package/dist/src/index.js +6 -0
  6. package/dist/src/index.js.map +1 -0
  7. package/dist/src/json.d.ts +6 -0
  8. package/dist/src/json.d.ts.map +1 -0
  9. package/dist/src/json.js +29 -0
  10. package/dist/src/json.js.map +1 -0
  11. package/dist/src/language-model.d.ts +23 -0
  12. package/dist/src/language-model.d.ts.map +1 -0
  13. package/dist/src/language-model.js +440 -0
  14. package/dist/src/language-model.js.map +1 -0
  15. package/dist/src/logger.d.ts +15 -0
  16. package/dist/src/logger.d.ts.map +1 -0
  17. package/dist/src/logger.js +142 -0
  18. package/dist/src/logger.js.map +1 -0
  19. package/dist/src/messages.d.ts +14 -0
  20. package/dist/src/messages.d.ts.map +1 -0
  21. package/dist/src/messages.js +92 -0
  22. package/dist/src/messages.js.map +1 -0
  23. package/dist/src/provider.d.ts +15 -0
  24. package/dist/src/provider.d.ts.map +1 -0
  25. package/dist/src/provider.js +19 -0
  26. package/dist/src/provider.js.map +1 -0
  27. package/dist/src/tools.d.ts +21 -0
  28. package/dist/src/tools.d.ts.map +1 -0
  29. package/dist/src/tools.js +82 -0
  30. package/dist/src/tools.js.map +1 -0
  31. package/dist/test/messages.test.d.ts +2 -0
  32. package/dist/test/messages.test.d.ts.map +1 -0
  33. package/dist/test/messages.test.js +173 -0
  34. package/dist/test/messages.test.js.map +1 -0
  35. package/dist/test/tools.test.d.ts +2 -0
  36. package/dist/test/tools.test.d.ts.map +1 -0
  37. package/dist/test/tools.test.js +175 -0
  38. package/dist/test/tools.test.js.map +1 -0
  39. package/package.json +70 -0
  40. package/src/index.ts +11 -0
  41. package/src/json.ts +38 -0
  42. package/src/language-model.ts +526 -0
  43. package/src/logger.ts +171 -0
  44. package/src/messages.ts +102 -0
  45. package/src/provider.ts +45 -0
  46. package/src/tools.ts +112 -0
@@ -0,0 +1,102 @@
1
+ import type { LanguageModelV2Prompt } from "@ai-sdk/provider";
2
+ import { safeJsonStringify } from "./json";
3
+ import { logger } from "./logger";
4
+
5
+ export interface ConvertedPrompt {
6
+ systemPrompt: string;
7
+ prompt: string;
8
+ }
9
+
10
+ /**
11
+ * Converts an AI SDK LanguageModelV2 prompt (array of system/user/assistant/tool messages)
12
+ * into a system prompt string and a user prompt string for the Claude Agent SDK's query().
13
+ *
14
+ * Since we use maxTurns: 1, the Agent SDK makes a single LLM call. We serialize the full
15
+ * conversation history into the prompt so the LLM has context from prior turns.
16
+ */
17
+ export function convertMessages(messages: LanguageModelV2Prompt): ConvertedPrompt {
18
+ logger.debug("Converting messages:", { count: messages.length });
19
+
20
+ const systemParts: string[] = [];
21
+ const conversationParts: string[] = [];
22
+
23
+ for (const message of messages) {
24
+ logger.debug("Processing message:", { role: message.role });
25
+ switch (message.role) {
26
+ case "system": {
27
+ systemParts.push(message.content);
28
+ break;
29
+ }
30
+
31
+ case "user": {
32
+ const parts: string[] = [];
33
+ logger.debug("Processing user message parts:", { count: message.content.length });
34
+ for (const part of message.content) {
35
+ switch (part.type) {
36
+ case "text":
37
+ parts.push(part.text);
38
+ break;
39
+ case "file":
40
+ parts.push(`[File: ${part.filename ?? part.mediaType}]`);
41
+ break;
42
+ }
43
+ }
44
+ if (parts.length > 0) {
45
+ conversationParts.push(`[user]\n${parts.join("\n")}`);
46
+ }
47
+ break;
48
+ }
49
+
50
+ case "assistant": {
51
+ const parts: string[] = [];
52
+ logger.debug("Processing assistant message parts:", { count: message.content.length });
53
+ for (const part of message.content) {
54
+ switch (part.type) {
55
+ case "text":
56
+ parts.push(part.text);
57
+ break;
58
+ case "tool-call":
59
+ parts.push(`[tool_call: ${part.toolName}(${safeJsonStringify(part.input)})]`);
60
+ break;
61
+ case "reasoning":
62
+ parts.push(`[thinking]\n${part.text}\n[/thinking]`);
63
+ break;
64
+ }
65
+ }
66
+ if (parts.length > 0) {
67
+ conversationParts.push(`[assistant]\n${parts.join("\n")}`);
68
+ }
69
+ break;
70
+ }
71
+
72
+ case "tool": {
73
+ const parts: string[] = [];
74
+ for (const part of message.content) {
75
+ const output = part.output;
76
+ let outputText: string;
77
+ if (Array.isArray(output)) {
78
+ // Output is LanguageModelV2ToolResultOutput (array of parts)
79
+ outputText = output
80
+ .map((o) => {
81
+ if (o.type === "text") return o.text;
82
+ return `[${o.type}]`;
83
+ })
84
+ .join("\n");
85
+ } else {
86
+ outputText = typeof output === "string" ? output : safeJsonStringify(output);
87
+ }
88
+ parts.push(`[tool_result: ${part.toolName} (id: ${part.toolCallId})]\n${outputText}`);
89
+ }
90
+ if (parts.length > 0) {
91
+ conversationParts.push(parts.join("\n"));
92
+ }
93
+ break;
94
+ }
95
+ }
96
+ }
97
+
98
+ return {
99
+ systemPrompt: systemParts.join("\n\n"),
100
+ prompt: conversationParts.join("\n\n"),
101
+ };
102
+ }
@@ -0,0 +1,45 @@
1
+ import type { LanguageModelV2 } from "@ai-sdk/provider";
2
+ import {
3
+ ClaudeAgentLanguageModel,
4
+ type ClaudeAgentLanguageModelConfig,
5
+ } from "./language-model";
6
+ import { safeJsonStringify } from "./json";
7
+ import { logger } from "./logger";
8
+
9
+ export interface ClaudeAgentProviderSettings {
10
+ name?: string;
11
+
12
+ /**
13
+ * Working directory for the Agent SDK.
14
+ * @default process.cwd()
15
+ */
16
+ cwd?: string;
17
+ }
18
+
19
+ export interface ClaudeAgentProvider {
20
+ (modelId: string): LanguageModelV2;
21
+ languageModel(modelId: string): LanguageModelV2;
22
+ }
23
+
24
+ export function createClaudeAgent(
25
+ options: ClaudeAgentProviderSettings = {},
26
+ ): ClaudeAgentProvider {
27
+ const config: ClaudeAgentLanguageModelConfig = {
28
+ provider: options.name ?? "claude-agent",
29
+ cwd: options.cwd,
30
+ };
31
+
32
+ logger.debug("Creating agent with:", safeJsonStringify(options));
33
+
34
+ const createLanguageModel = (modelId: string): LanguageModelV2 => {
35
+ return new ClaudeAgentLanguageModel(modelId, config);
36
+ };
37
+
38
+ const provider = function (modelId: string) {
39
+ return createLanguageModel(modelId);
40
+ };
41
+
42
+ provider.languageModel = createLanguageModel;
43
+
44
+ return provider as ClaudeAgentProvider;
45
+ }
package/src/tools.ts ADDED
@@ -0,0 +1,112 @@
1
+ import type { LanguageModelV2FunctionTool } from "@ai-sdk/provider";
2
+ import {
3
+ createSdkMcpServer,
4
+ tool,
5
+ type McpServerConfig,
6
+ } from "@anthropic-ai/claude-agent-sdk";
7
+ import * as z from "zod";
8
+ import { safeJsonStringify } from "./json";
9
+ import { logger } from "./logger";
10
+
11
+ /**
12
+ * The name of the MCP server that hosts AI SDK tools.
13
+ * This is used to identify tools when the Agent SDK returns them
14
+ * in the format: mcp__{SERVER_NAME}__{tool_name}
15
+ */
16
+ export const AI_SDK_MCP_SERVER_NAME = "ai-sdk-tools";
17
+
18
+ /**
19
+ * Extracts Zod schema from AI SDK tool inputSchema using Zod 4's native
20
+ * JSON Schema conversion.
21
+ */
22
+ function extractZodSchema(
23
+ tool: LanguageModelV2FunctionTool,
24
+ ): Record<string, z.ZodTypeAny> {
25
+ const inputSchema = tool.inputSchema as Record<string, unknown> | undefined;
26
+
27
+ if (!inputSchema || typeof inputSchema !== "object") {
28
+ return {};
29
+ }
30
+
31
+ try {
32
+ const zodSchema = z.fromJSONSchema(inputSchema);
33
+ if (zodSchema instanceof z.ZodObject) {
34
+ return zodSchema.shape;
35
+ }
36
+ return { value: zodSchema };
37
+ } catch (error) {
38
+ logger.error("Failed to convert JSON Schema to Zod:", {
39
+ tool: tool.name,
40
+ error,
41
+ });
42
+ return {};
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Converts AI SDK function tool definitions into an in-process Agent SDK MCP server.
48
+ *
49
+ * Each AI SDK tool becomes an MCP tool with proper parameter validation.
50
+ * Since we use maxTurns: 1, the Agent SDK will report tool_use blocks in the
51
+ * assistant message but won't execute them. The AI SDK caller handles actual
52
+ * tool execution.
53
+ */
54
+ export function convertTools(tools: LanguageModelV2FunctionTool[] | undefined):
55
+ | {
56
+ mcpServer: McpServerConfig;
57
+ allowedTools: string[];
58
+ }
59
+ | undefined {
60
+ if (!tools || tools.length === 0) return undefined;
61
+
62
+ logger.debug("Converting tools:", {
63
+ count: tools.length,
64
+ tools: tools.map((t) => t.name),
65
+ });
66
+
67
+ const mcpTools = tools.map((aiTool) => {
68
+ const zodSchema = extractZodSchema(aiTool);
69
+
70
+ logger.debug("Creating tool:", {
71
+ name: aiTool.name,
72
+ schemaKeys: Object.keys(zodSchema),
73
+ });
74
+
75
+ return tool(
76
+ aiTool.name,
77
+ aiTool.description ?? "",
78
+ zodSchema,
79
+ async () => {
80
+ // Stub handler — tool execution is deferred to the AI SDK caller.
81
+ // This should rarely (if ever) be called with maxTurns: 1.
82
+ return {
83
+ content: [
84
+ {
85
+ type: "text" as const,
86
+ text: safeJsonStringify({
87
+ _deferred: true,
88
+ message: "Tool execution deferred to AI SDK caller",
89
+ }),
90
+ },
91
+ ],
92
+ };
93
+ },
94
+ );
95
+ });
96
+
97
+ logger.info("Created MCP server with", mcpTools.length, "tools");
98
+
99
+ const mcpServer = createSdkMcpServer({
100
+ name: AI_SDK_MCP_SERVER_NAME,
101
+ tools: mcpTools,
102
+ });
103
+
104
+ // Generate the allowed tool names with MCP prefix format
105
+ const allowedTools = tools.map(
106
+ (t) => `mcp__${AI_SDK_MCP_SERVER_NAME}__${t.name}`,
107
+ );
108
+
109
+ logger.debug("Allowed tools:", allowedTools);
110
+
111
+ return { mcpServer, allowedTools };
112
+ }