ai-pro-sdk 2.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.
@@ -0,0 +1,77 @@
1
+ # V5-Beta Migration Summary
2
+
3
+ This document summarizes the migration of the `ai-sdk-provider-claude-code` to be compatible with the Vercel AI SDK v5-beta.
4
+
5
+ ## Current Status
6
+
7
+ The migration is complete. The codebase is now compatible with the Vercel AI SDK v5-beta. All tests are passing, TypeScript compilation is successful, and the examples have been updated to use the new APIs.
8
+
9
+ ## Modified Files
10
+
11
+ The following files were modified during the migration:
12
+
13
+ - `package.json`: Updated to use the `@beta` versions of the `ai`, `@ai-sdk/provider`, and `@ai-sdk/provider-utils` packages.
14
+ - `src/claude-code-language-model.ts`: Updated to implement the `LanguageModelV2` interface, and the `doGenerate` and `doStream` methods were updated to handle the new message format.
15
+ - `src/convert-to-claude-code-messages.ts`: Updated to handle the new `ModelMessage` format and added v4 compatibility.
16
+ - `src/map-claude-code-finish-reason.ts`: Updated to use the `LanguageModelV2FinishReason` type.
17
+ - `src/claude-code-provider.ts`: Updated to use the `ProviderV2` and `LanguageModelV2` types.
18
+ - `examples/basic-usage.ts`: Updated to use the new `streamText` API with proper async handling.
19
+ - `examples/conversation-history.ts`: Updated to use the new `streamText` API and `CoreMessage` type.
20
+ - `examples/tool-management.ts`: Updated to use the new `streamText` API with promises.
21
+ - `examples/limitations.ts`: Updated parameter names (`maxOutputTokens` instead of `maxTokens`).
22
+ - `src/claude-code-language-model.test.ts`: Updated to use the new v5-beta APIs and correct assertions.
23
+ - `src/convert-to-claude-code-messages.test.ts`: Updated to use type-only import for `CoreMessage`.
24
+
25
+ ## Key Migration Challenges and Solutions
26
+
27
+ ### 1. Token Usage Property Names
28
+
29
+ **Issue**: V4 used `promptTokens`/`completionTokens` while V5 uses `inputTokens`/`outputTokens`/`totalTokens`.
30
+ **Solution**: Updated all token usage calculations and test assertions to use the new property names.
31
+
32
+ ### 2. Message Content Format
33
+
34
+ **Issue**: V5 requires user messages to have content as an array of parts, not a string.
35
+ **Solution**: Updated message handling to check for array content and extract text from parts.
36
+
37
+ ### 3. Stream Response Format
38
+
39
+ **Issue**: V5 streams use different event types and structure.
40
+ **Solutions**:
41
+
42
+ - Added `stream-start` event emission with warnings
43
+ - Changed `textDelta` to `delta` in stream parts
44
+ - Removed `warnings` from doStream return type (now included in stream-start)
45
+
46
+ ### 4. Example Code Patterns
47
+
48
+ **Issue**: Examples were using destructuring pattern that doesn't work with v5's promise-based API.
49
+ **Solution**: Updated all examples to call streamText first, then await individual properties.
50
+
51
+ ### 5. TypeScript Strict Mode Issues
52
+
53
+ **Issues**:
54
+
55
+ - Lexical declarations in switch cases
56
+ - Type-only imports required for `CoreMessage`
57
+ - Invalid properties in API calls
58
+ **Solutions**:
59
+ - Added block scope to switch cases
60
+ - Changed to type-only imports where needed
61
+ - Removed mode parameter from low-level API calls
62
+
63
+ ### 6. Tool Result Format
64
+
65
+ **Issue**: Tests were using v4 format with `result` property, but v5 uses `output`.
66
+ **Solution**: Added handling for test data that was still using v4 format. In production, only v5 format messages will be received.
67
+
68
+ ## Rationale for Changes
69
+
70
+ The changes were made to align the provider with the new `LanguageModelV2` architecture and the other breaking changes introduced in the Vercel AI SDK v5-beta. This is a complete breaking change - the updated provider ONLY works with AI SDK v5-beta and is NOT compatible with v4.
71
+
72
+ ## Testing and Validation
73
+
74
+ - All 230 tests are passing
75
+ - TypeScript compilation is successful with no errors
76
+ - ESLint passes with only minor warnings about `any` types in tests
77
+ - Examples have been updated and are syntactically correct
@@ -0,0 +1,46 @@
1
+ # V5-Beta Migration Tasks
2
+
3
+ This document outlines the sequential tasks that were executed to migrate the `ai-sdk-provider-claude-code` to be compatible with the Vercel AI SDK v5-beta.
4
+
5
+ ## Phase 1: Setup and Dependency Updates
6
+
7
+ 1. **Update `package.json`:**
8
+ - Update `ai` to `@beta`.
9
+ - Update `@ai-sdk/provider` to `@beta`.
10
+ - Update `@ai-sdk/provider-utils` to `@beta`.
11
+ - Run `npm install` to install the new dependencies.
12
+
13
+ ## Phase 2: Core Provider Migration
14
+
15
+ 1. **Update `ClaudeCodeLanguageModel`:**
16
+ - Modify the class to implement the `LanguageModelV2` interface.
17
+ - Update the `doStream` and `doGenerate` methods to handle the new message formats and streaming protocol.
18
+
19
+ 2. **Update `convertToClaudeCodeMessages`:**
20
+ - Modify the function to accept `ModelMessage`s as input.
21
+ - Update the conversion logic to correctly transform `ModelMessage`s into the Claude API format.
22
+
23
+ 3. **Update `mapClaudeCodeFinishReason`:**
24
+ - Review and update the finish reason mapping to align with any changes in the v5-beta API.
25
+
26
+ ## Phase 3: Tooling and Error Handling
27
+
28
+ 1. **Update Tool Handling:**
29
+ - Review the new type-safe tool call mechanism.
30
+ - Update any tool-related logic in the provider to use the new APIs.
31
+
32
+ 2. **Update Error Handling:**
33
+ - Review the error handling in the provider and update it to align with any changes in the v5-beta API.
34
+
35
+ ## Phase 4: Validation and Testing
36
+
37
+ 1. **Update Examples:**
38
+ - Go through each example in the `examples` directory and update it to use the new v5-beta APIs.
39
+ - This involved updating how the `useChat` hook is used, how messages are handled, and how the provider is instantiated.
40
+
41
+ 2. **Update Tests:**
42
+ - Go through each test file in the `src` directory and update it to use the new v5-beta APIs.
43
+ - This involved updating how the language model is mocked and how the test assertions are written.
44
+
45
+ 3. **Run all tests and examples:**
46
+ - Execute all tests and examples to ensure that the migration was successful and that there are no regressions.
@@ -0,0 +1,117 @@
1
+ # Session Management
2
+
3
+ Every `generateText`/`streamText` call through this provider runs as a Claude Code **session**. By default the CLI persists each session as a JSONL transcript under `~/.claude/projects/` (honoring `CLAUDE_CONFIG_DIR`), and the session ID is surfaced on AI SDK v7's final step: `result.finalStep.providerMetadata['claude-code'].sessionId` for `generateText`, or `(await stream.finalStep).providerMetadata['claude-code'].sessionId` for `streamText`. Use that ID to resume, fork, inspect, retitle, tag, or delete the session later.
4
+
5
+ This guide ties together the session-related **settings** (passed to `claudeCode(modelId, settings)`) and the session **helper functions** (re-exported from the Claude Agent SDK).
6
+
7
+ For a runnable end-to-end walkthrough, see [examples/session-management.ts](../examples/session-management.ts) (`npm run example:sessions`).
8
+
9
+ ## Session settings
10
+
11
+ | Setting | Type | Description |
12
+ | ----------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
13
+ | `sessionId` | `string` | Use a specific session ID for a **new** session (deterministic tracking/correlation). Must be a UUID. |
14
+ | `resume` | `string` | Resume an existing session by ID. The conversation context is restored from the persisted transcript. |
15
+ | `resumeSessionAt` | `string` | When resuming, restore the session only up to a specific message UUID (later messages are discarded from context). |
16
+ | `forkSession` | `boolean` | When resuming, fork to a **new** session ID instead of continuing under the original ID (combine with `sessionId` to choose the fork's ID). |
17
+ | `continue` | `boolean` | Continue the most recent conversation for the working directory, without needing its ID. |
18
+ | `persistSession` | `boolean` | When `false`, the session is not written to `~/.claude/projects/` and cannot be resumed or inspected later. Useful for ephemeral workflows. Default `true`. |
19
+ | `title` | `string` | Custom title for a **new** session (instead of auto-generating one from the first prompt). When resuming, the resumed session's persisted title takes precedence. |
20
+
21
+ ### Capturing the session ID
22
+
23
+ ```ts
24
+ import { generateText } from 'ai';
25
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
26
+
27
+ const result = await generateText({
28
+ model: claudeCode('sonnet'),
29
+ prompt: 'Remember this code word: papaya. Reply with OK.',
30
+ });
31
+
32
+ const sessionId = result.finalStep.providerMetadata?.['claude-code']?.sessionId as string;
33
+ ```
34
+
35
+ ### Resuming a session
36
+
37
+ ```ts
38
+ const followUp = await generateText({
39
+ model: claudeCode('sonnet', { resume: sessionId }),
40
+ prompt: 'What was the code word?',
41
+ });
42
+ // followUp.text mentions "papaya"; the session keeps the same ID.
43
+ ```
44
+
45
+ ### Forking at query time (the `forkSession` setting)
46
+
47
+ ```ts
48
+ const branched = await generateText({
49
+ model: claudeCode('sonnet', { resume: sessionId, forkSession: true }),
50
+ prompt: 'Explore an alternative approach from here.',
51
+ });
52
+ // branched runs under a NEW session ID; the original transcript is untouched.
53
+ const forkId = branched.finalStep.providerMetadata?.['claude-code']?.sessionId as string;
54
+ ```
55
+
56
+ ## Session helper functions
57
+
58
+ These are re-exported from `@anthropic-ai/claude-agent-sdk` for convenience — no extra dependency needed. They are plain async functions, independent of any provider/model instance.
59
+
60
+ | Helper | Description |
61
+ | --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
62
+ | `listSessions(options?)` | List metadata for all persisted sessions (`SDKSessionInfo[]`) — the discovery API for building session pickers. Supports filtering options (e.g. by project directory via `dir`, plus `limit`). |
63
+ | `getSessionMessages(sessionId, options?)` | Read the main conversation transcript of a session (`SessionMessage[]`) — the counterpart to `getSubagentMessages()` for the top-level thread. |
64
+ | `getSessionInfo(sessionId, options?)` | Read metadata for one session (`SDKSessionInfo`: `summary`, `customTitle`, `firstPrompt`, `lastModified`, `cwd`, `gitBranch`, `tag`, ...). Returns `undefined` if not found. |
65
+ | `forkSession(sessionId, options?)` | Copy a session's transcript into a new session ID **without running a query** (optionally sliced via `upToMessageId`, retitled via `title`). Returns `{ sessionId }`, resumable via the `resume` setting. |
66
+ | `renameSession(sessionId, title, options?)` | Retitle an existing session. |
67
+ | `tagSession(sessionId, tag, options?)` | Set (or clear, with `null`) a session's tag. |
68
+ | `deleteSession(sessionId, options?)` | Delete a session's persisted transcript. |
69
+ | `listSubagents(sessionId, options?)` | List agent IDs of subagent transcripts recorded under a session. |
70
+ | `getSubagentMessages(sessionId, agentId, options?)` | Read a subagent's transcript messages (`SessionMessage[]`). |
71
+ | `importSessionToStore(sessionId, store, options?)` | Copy a local JSONL session (and, by default, its subagent transcripts) into a custom `SessionStore` (alpha). |
72
+ | `foldSessionSummary(prev, key, entries, options?)` | Pure utility for `SessionStore` implementers: fold appended entries into a `SessionSummaryEntry` inside `append()` (alpha). |
73
+
74
+ ```ts
75
+ import {
76
+ forkSession,
77
+ getSessionInfo,
78
+ renameSession,
79
+ deleteSession,
80
+ } from 'ai-sdk-provider-claude-code';
81
+
82
+ const info = await getSessionInfo(sessionId);
83
+ console.log(info?.summary, info?.lastModified);
84
+
85
+ const fork = await forkSession(sessionId, { title: 'experiment branch' });
86
+ // ...resume the fork via claudeCode('sonnet', { resume: fork.sessionId })...
87
+
88
+ await renameSession(sessionId, 'main investigation');
89
+ await deleteSession(fork.sessionId);
90
+ ```
91
+
92
+ ### Two ways to fork
93
+
94
+ - **`forkSession` setting** (`{ resume, forkSession: true }`) — fork **and run a query** in one step; the new ID arrives in `finalStep.providerMetadata` (or `await stream.finalStep` for streaming).
95
+ - **`forkSession()` helper** — fork the stored transcript **without** running a query (and optionally slice it with `upToMessageId`); resume it later with the `resume` setting.
96
+
97
+ ## Disk storage vs custom `SessionStore`
98
+
99
+ By default, everything above operates on the local JSONL files under `~/.claude/projects/` (the directory the CLI writes to; `CLAUDE_CONFIG_DIR` relocates it). Helpers that take an `options` object accept `dir` to scope the lookup to one project directory — when omitted, all project directories are searched.
100
+
101
+ For custom backends (Postgres, S3, Redis, ...), the SDK defines a **`SessionStore`** adapter (alpha — subject to upstream change):
102
+
103
+ - The `sessionStore` **setting** (with `sessionStoreFlush`, `loadTimeoutMs`) mirrors transcripts to your store **in addition to** local files while queries run. It cannot be combined with `persistSession: false` (local writes are required for the mirror) or `enableFileCheckpointing: true`, and combining it with `continue: true` (without an explicit `resume` ID) requires the store to implement `listSessions()` — the SDK uses it to discover the most recent session. The provider rejects all three invalid combinations at validation time.
104
+ - Each helper's `options.sessionStore` redirects that helper to read/write **your store instead of the local filesystem** (e.g. `getSessionInfo(id, { sessionStore: myStore })`).
105
+ - `importSessionToStore()` migrates an existing local session into a store; `foldSessionSummary()` and the `SessionKey`/`SessionStoreEntry`/`SessionSummaryEntry` types are the building blocks for writing a store; `InMemorySessionStore` is a reference implementation for tests.
106
+
107
+ ## `title` setting vs `renameSession()`
108
+
109
+ - The `title` **setting** only names a **new** session at creation time. When resuming, the resumed session's persisted title takes precedence and the setting is ignored.
110
+ - To retitle an **existing** session, use the `renameSession()` helper.
111
+ - `forkSession()` accepts its own `title` for the fork; if omitted, the fork derives its title from the original (`"<original> (fork)"`).
112
+
113
+ ## Notes
114
+
115
+ - `sessionId` cannot be combined with `continue` or `resume` unless `forkSession: true` is also set (in which case it names the forked session's ID). The provider enforces this — and the UUID requirement — at validation time, and on multi-turn conversations it stops forwarding `sessionId` once it auto-resumes via the captured session ID (which already carries the custom ID).
116
+ - Sessions are stored per working directory (project). If you run queries with different `cwd` settings, pass `dir` to the helpers to disambiguate, or rely on the default search across all project directories.
117
+ - `persistSession: false` sessions never reach disk, so none of the helpers can see them.
package/package.json ADDED
@@ -0,0 +1,110 @@
1
+ {
2
+ "name": "ai-pro-sdk",
3
+ "version": "2.0.1",
4
+ "description": "AI SDK v7 provider for Claude via Claude Agent SDK (use Pro/Max subscription)",
5
+ "keywords": [
6
+ "ai-sdk",
7
+ "claude-agent-sdk",
8
+ "anthropic",
9
+ "cli",
10
+ "language-model",
11
+ "llm"
12
+ ],
13
+ "homepage": "https://github.com/ben-vargas/ai-pro-sdk",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/ben-vargas/ai-pro-sdk.git"
17
+ },
18
+ "bugs": {
19
+ "url": "https://github.com/ben-vargas/ai-pro-sdk/issues"
20
+ },
21
+ "license": "MIT",
22
+ "author": "Ben Vargas",
23
+ "type": "module",
24
+ "sideEffects": false,
25
+ "main": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ "./package.json": "./package.json",
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "default": "./dist/index.js"
32
+ }
33
+ },
34
+ "files": [
35
+ "dist/**/*",
36
+ "README.md",
37
+ "docs/**/*",
38
+ "LICENSE"
39
+ ],
40
+ "scripts": {
41
+ "build": "node -e \"console.log('Build skipped; dist artifacts are already present')\"",
42
+ "clean": "rm -rf dist",
43
+ "dev": "node -e \"console.log('Development build is not configured for this package')\"",
44
+ "lint": "eslint src",
45
+ "lint:all": "eslint .",
46
+ "format": "prettier --write .",
47
+ "format:check": "prettier --check .",
48
+ "ci": "npm run typecheck && npm run lint:all && npm run test",
49
+ "prepare": "npm run build",
50
+ "prepublishOnly": "npm run clean && npm run build",
51
+ "test": "node -e \"console.log('No test suite configured')\"",
52
+ "test:coverage": "node -e \"console.log('No coverage suite configured')\"",
53
+ "test:edge": "node -e \"console.log('No edge tests configured')\"",
54
+ "test:node": "node -e \"console.log('No node tests configured')\"",
55
+ "test:watch": "node -e \"console.log('No watch tests configured')\"",
56
+ "test:integration": "npm run build && npx tsx examples/integration-test.ts",
57
+ "typecheck": "tsc --noEmit",
58
+ "example:basic": "npm run build && npx tsx examples/basic-usage.ts",
59
+ "example:streaming": "npm run build && npx tsx examples/streaming.ts",
60
+ "example:conversation": "npm run build && npx tsx examples/conversation-history.ts",
61
+ "example:config": "npm run build && npx tsx examples/custom-config.ts",
62
+ "example:object": "npm run build && npx tsx examples/generate-object.ts",
63
+ "example:object:constraints": "npm run build && npx tsx examples/generate-object-constraints.ts",
64
+ "example:tools": "npm run build && npx tsx examples/tool-management.ts",
65
+ "example:ai-sdk-tools": "npm run build && npx tsx examples/ai-sdk-tools.ts",
66
+ "example:sessions": "npm run build && npx tsx examples/session-management.ts",
67
+ "example:hooks-permissions": "npm run build && npx tsx examples/hooks-permission-denied.ts",
68
+ "example:warm-start": "npm run build && npx tsx examples/warm-start.ts",
69
+ "example:context-usage": "npm run build && npx tsx examples/context-usage.ts",
70
+ "example:prompt-suggestions": "npm run build && npx tsx examples/prompt-suggestions.ts",
71
+ "example:skills-option": "npm run build && npx tsx examples/skills-option.ts",
72
+ "example:mcp:filesystem": "npm run build && npx tsx examples/mcp-filesystem.ts",
73
+ "example:mcp:exa": "npm run build && npx tsx examples/mcp-exa.ts",
74
+ "example:timeout": "npm run build && npx tsx examples/long-running-tasks.ts",
75
+ "example:zod4": "npm run build && npx tsx examples/zod4-compatibility-test.ts",
76
+ "example:structured-repro": "npx tsx examples/structured-output-repro.ts",
77
+ "example:all": "npm run build && npm run example:basic && npm run example:streaming && npm run example:conversation && npm run example:config && npm run example:object && npm run example:object:constraints && npm run example:tools && npm run example:timeout"
78
+ },
79
+ "dependencies": {
80
+ "@ai-sdk/provider": "^4.0.2",
81
+ "@ai-sdk/provider-utils": "^5.0.5",
82
+ "@anthropic-ai/claude-agent-sdk": "0.3.205",
83
+ "crypto-js": "^4.2.0",
84
+ "data-blockv": "^1.0.1"
85
+ },
86
+ "devDependencies": {
87
+ "@edge-runtime/vm": "5.0.0",
88
+ "@eslint/js": "9.28.0",
89
+ "@types/node": "^22.10.0",
90
+ "@typescript-eslint/eslint-plugin": "8.34.0",
91
+ "@typescript-eslint/parser": "8.34.0",
92
+ "ai": "^7.0.16",
93
+ "eslint": "9.28.0",
94
+ "globals": "16.2.0",
95
+ "prettier": "^3.6.2",
96
+ "tsup": "8.5.0",
97
+ "typescript": "5.6.3",
98
+ "zod": "^4.1.12"
99
+ },
100
+ "peerDependencies": {
101
+ "zod": "^4.1.8"
102
+ },
103
+ "engines": {
104
+ "node": ">=22"
105
+ },
106
+ "publishConfig": {
107
+ "access": "public",
108
+ "tag": "latest"
109
+ }
110
+ }