UNPKG

solver-sdk

Version:

SDK for WorkAI API - AI-powered code analysis with WorkCoins billing system

1,262 lines (1,012 loc) 41.8 kB
# Code Solver SDK AI-powered code analysis SDK with WorkCoins billing system. 100% typed, production ready. ## Install ```bash npm install solver-sdk@10.1.0 # или npm install solver-sdk@latest ``` ## Quick Start ```typescript import { CodeSolverSDK } from 'solver-sdk'; const sdk = await CodeSolverSDK.create({ baseURL: 'https://api.example.com', getAuthToken: async () => getToken() }); // Projects const projects = await sdk.projects.getAllProjects(); await sdk.projects.createProject('MyProject'); // Chat with Thinking const response = await sdk.chat.chat([ { role: 'user', content: 'explain this code' } ], { thinking: { type: 'enabled', budget_tokens: 10000 } }); // Search const results = await sdk.search.searchCode({ projectId, query: 'authentication function' }); // User & Updates const profile = await sdk.user.getProfile(); const update = await sdk.updates.checkForUpdates({...}); ``` ## API Overview | Feature | Methods | Status | |---------|---------|--------| | **Projects** | 19 | Full CRUD + state | | **Delta Chunking** | 7 | Encrypted sync | | **Chat** | 13 | Streaming + Extended Thinking | | **Search** | 4 | Vector-based | | **Tools** | 5 | AI tool schemas | | **Models** | 5 | Provider info | | **Updates** | 5 | Auto-check + changelog | | **User** | 3 | Profile + Limits | | **Credits** | 3 | Balance + Status + Estimates | | **Auth** | 2 | Token revocation + logout | **Total: 66 methods** | [Full API Reference](#api-reference) ## Quick Reference <details> <summary><b>Projects (19)</b> - управление проектами и индексация</summary> ```typescript getAllProjects() | getProject(id) | createProject(name) | deleteProject(id) findOrCreateProject(name) | getReadyProjects() | getProjectState(id) getProjectStatus(id) | getIndexingStatus(id) | cancelIndexing(id) clearIndexingError(id) | getRecoveryStatus(id) | resumeSync(id) cancelRecovery(id) | initializeDeltaSync(id) | resetIndexing(id) restartIndexing(id) | getFilePathMapping(id) ``` </details> <details> <summary><b>Chat (13)</b> - AI диалоги и streaming</summary> ```typescript chat(messages, options?) | chatCompletion(messages, options?) chatWithRegionFailover(messages, options?) | streamChat(messages, options?) streamPrompt(prompt, options?) | sendContinuation(requestId, messages) resumeChat(requestId, messages, partialText, options?) sendPromptWithRegionFailover(prompt, options?) | checkAvailability() cancelRequest(requestId) | getStreamsStats() | cleanupStaleStreams() cancelUserStreams(reason?) ``` </details> <details> <summary><b>Delta Chunking (7)</b> - синхронизация файлов</summary> ```typescript initSync(projectId, request) | uploadChunkBatch(projectId, chunks) uploadChunksWithRetry(projectId, chunks, options?) finalizeSync(projectId) | getSyncStatus(projectId) cancelSync(projectId) | cleanupFiles(projectId, activeFiles) ``` </details> <details> <summary><b>Search (4)</b> - поиск кода</summary> ```typescript searchCode(projectId, params) | searchFunctions(projectId, params) semanticSearch(projectId, params) | getFunctionStats(projectId) ``` </details> <details> <summary><b>Tools (5)</b> - AI инструменты</summary> ```typescript getSchemas() | findToolByName(name) | getToolsStats() validateToolSchema(tool) | createToolSchema(name, desc, props, required?) ``` </details> <details> <summary><b>Models (5)</b> - AI модели</summary> ```typescript getAllModels() | getModels() | getAvailableModels() getProviderModels(providerId) | getModelInfo(modelId) ``` </details> <details> <summary><b>Updates (5)</b> - обновления SDK</summary> ```typescript checkForUpdates(options) | getChangelog(version, locale) sendStats(event) | getLatestVersion(channel?) | checkAvailability() ``` </details> <details> <summary><b>Credits (3)</b> - WorkCoins баланс</summary> ```typescript getBalance() | getStatus() | estimate(tokens, model?, operationType?) ``` </details> <details> <summary><b>User (3)</b> - профиль и лимиты</summary> ```typescript getProfile() | getLimitStatus() | checkAvailability() ``` </details> <details> <summary><b>Auth (2)</b> - аутентификация</summary> ```typescript revokeToken(token) | logout() ``` </details> ## Auth ```typescript // Local dev const sdk = await CodeSolverSDK.create({ baseURL: 'http://localhost:3000', getAuthToken: async () => 'your-token' }); // Production with token refresh + error handling import { AuthenticationError } from 'solver-sdk'; const sdk = await CodeSolverSDK.create({ baseURL: 'https://api.example.com', getAuthToken: async () => { if (isTokenExpired()) { await refreshToken(); } return getToken(); } }); // All methods throw AuthenticationError on 401 try { await sdk.user.getProfile(); } catch (error) { if (error instanceof AuthenticationError) { await refreshToken(); // retry } } ``` ## WebSocket (Real-time) v10.1.0 ```typescript // Конфигурация (рекомендуется для production) const sdk = await CodeSolverSDK.create({ baseURL: 'https://api.workai.su', webSocket: { enabled: true, disableAutoReconnect: true, // Используйте свой reconnect координатор logLevel: 'warn', // 'silent' | 'error' | 'warn' | 'info' | 'debug' maxRetries: 0, connectionTimeout: 15000, } }); // Connect await sdk.connectWebSocket(); // Subscribe (ВАЖНО: вызвать в течение 30 секунд после connect!) sdk.projectSync?.subscribeToProject(projectId); // События sdk.projectSync?.on('connected', () => console.log('✅ Connected')); sdk.projectSync?.on('disconnected', ({ reason }) => console.log('🔌 Disconnected:', reason)); sdk.projectSync?.on('project-sync-joined', (e) => console.log('📋 Subscribed:', e.projectId)); sdk.projectSync?.on('disconnect-idle', (e) => console.log('⏰ Idle timeout:', e.idleTime)); sdk.projectSync?.on('reconnect_exhausted', (e) => console.log('💀 Retries exhausted:', e.attempts)); // Прогресс синхронизации sdk.projectSync?.on('sync-progress', (data) => { console.log(`Progress: ${data.processedFiles}/${data.totalFiles} (${data.progress}%)`); }); sdk.projectSync?.on('sync-completed', (data) => { console.log(`Done: ${data.statistics?.totalFiles} files`); }); // Ошибки кодами: IP_CONNECTIONS_EXCEEDED, MAX_CONNECTIONS_EXCEEDED, etc.) sdk.projectSync?.on('error', (e) => { if (e.code === 'IP_CONNECTIONS_EXCEEDED') { // Cooldown 60 секунд перед повторной попыткой } }); ``` ### WebSocket Events (v10.1.0) | Event | Описание | |-------|----------| | `connected` | Подключение установлено | | `disconnected` | Отключение (+ reason) | | `project-sync-joined` | Подписка на проект подтверждена | | `project-sync-left` | Отписка подтверждена | | `sync-status-update` | Изменение статуса | | `sync-progress` | Прогресс (processedFiles/totalFiles) | | `sync-completed` | Завершение синхронизации | | `error` | Ошибки (IP_CONNECTIONS_EXCEEDED, etc.) | | `disconnect-idle` | Отключение по idle timeout (30s без subscribe) | | `reconnect_exhausted` | Исчерпаны попытки reconnect | ## Error Handling All SDK methods throw typed exceptions. All errors have: `statusCode`, `errorType`, `message`, `requestId`, `timestamp`. ### Available Error Types ```typescript import { AuthenticationError, // 401 - token expired/invalid ForbiddenError, // 403 - access denied ValidationError, // 422 - invalid data NotFoundError, // 404 - resource not found ConflictError, // 409 - data conflict BadRequestError, // 400 - bad request InternalServerError, // 500 - server error ServiceUnavailableError,// 503 - service down GatewayTimeoutError, // 504 - timeout LimitExceededError, // 403 - WorkCoins limit exceeded RateLimitError, // 429 - too many requests NetworkError, // 0 - network failure TimeoutError, // 408 - request timeout DatabaseError // 400 - database error } from 'solver-sdk'; ``` ### Common Patterns ```typescript import { AuthenticationError, LimitExceededError, RateLimitError } from 'solver-sdk'; try { await sdk.chat.streamChat([{ role: 'user', content: 'Hello' }]); } catch (error) { // Auth errors - refresh token and retry if (error instanceof AuthenticationError) { await refreshToken(); return retry(); } // WorkCoins limit - show upgrade prompt if (error instanceof LimitExceededError) { console.log(`WorkCoins exhausted: ${error.message}`); if (error.action) { console.log(`Recommendation: ${error.action.suggestion}`); console.log(`Action URL: ${error.action.url}`); } } // Rate limit - wait and retry if (error instanceof RateLimitError) { await new Promise(r => setTimeout(r, error.getRetryDelayMs())); return retry(); } } ``` ### Proactive Limit Checking ```typescript // Check WorkCoins before expensive operations const status = await sdk.credits.getStatus(); if (!status.canMakeRequest) { console.error('No WorkCoins available'); return; } if (status.status === 'warning') { console.warn(`Low on WorkCoins: ${status.message}`); } // Make request await sdk.chat.streamChat([...]); ``` ### WorkCoins (Credits) API ```typescript // Get balance const balance = await sdk.credits.getBalance(); console.log(`${balance.creditsRemaining} / ${balance.creditsLimit} WorkCoins`); // Get status with recommendations const status = await sdk.credits.getStatus(); console.log(`Status: ${status.status}`); // 'ok' | 'warning' | 'critical' | 'depleted' // Estimate cost const estimate = await sdk.credits.estimate(50000, 'haiku', 'code_generation'); console.log(`Estimated: ${estimate.estimatedCredits} WorkCoins`); ``` ## Streaming Tool Use (Создание/Редактирование файлов) SDK передаёт **только официальные события Anthropic API** через callback `onEvent`. Когда AI создаёт или редактирует файлы, параметры инструментов стримятся **посимвольно** через официальное событие `input_json_delta`. > **Рекомендация Anthropic:** Используйте стандартное событие `content_block_delta` с типом `input_json_delta` для real-time визуализации прогресса. Это обеспечивает совместимость с обновлениями API и гарантирует стабильность работы. ### Real-time отображение прогресса через официальный API ```typescript import { PartialJsonAccumulator, safeParsePartialJson } from '@code-solver/sdk'; // 🛡️ СПОСОБ 1: Используем PartialJsonAccumulator (рекомендуется) const toolAccumulators = new Map<number, PartialJsonAccumulator>(); for await (const chunk of sdk.chat.streamChat(messages, { projectId: 'project-id', tools: ['read_file', 'search_replace'], onEvent: (type, data) => { // 🎯 Стриминг параметров инструмента if (type === 'content_block_delta' && data.delta?.type === 'input_json_delta') { const index = data.index; // Инициализируем accumulator для блока if (!toolAccumulators.has(index)) { toolAccumulators.set(index, new PartialJsonAccumulator(console)); } const accumulator = toolAccumulators.get(index)!; accumulator.add(data.delta.partial_json); // Пытаемся распарсить автоматическим восстановлением) const parsedInput = accumulator.tryParse(); if (parsedInput) { // 💡 Показываем прогресс с безопасно распарсенными данными console.log(`📝 ${parsedInput.operation || 'Обработка'}: ${parsedInput.file_path || '...'}`); updateProgress({ file: parsedInput.file_path, operation: parsedInput.operation, bytesReceived: accumulator.getAccumulated().length }); } } // Инструмент готов к выполнению (полный JSON получен) if (type === 'content_block_stop') { const index = data.index; const accumulator = toolAccumulators.get(index); if (accumulator) { const toolInput = accumulator.tryParse(); if (toolInput) { console.log('🔧 Инструмент готов:', toolInput); } else { console.error('❌ Не удалось распарсить JSON инструмента'); } toolAccumulators.delete(index); // Очищаем } } } })) { // Обрабатываем остальные события... } ``` **🛡️ СПОСОБ 2: Ручная работа с safeParsePartialJson** ```typescript const toolInputs = new Map<number, string>(); for await (const chunk of sdk.chat.streamChat(messages, { onEvent: (type, data) => { if (type === 'content_block_delta' && data.delta?.type === 'input_json_delta') { const index = data.index; const accumulated = (toolInputs.get(index) || '') + data.delta.partial_json; toolInputs.set(index, accumulated); // Безопасный парсинг с автоматическим восстановлением const parsedInput = safeParsePartialJson(accumulated, console); if (parsedInput?.file_path) { console.log(`📝 ${parsedInput.operation}: ${parsedInput.file_path}`); } } if (type === 'content_block_stop') { const fullJson = toolInputs.get(data.index); if (fullJson) { const toolInput = safeParsePartialJson(fullJson, console); console.log('🔧 Инструмент готов:', toolInput); toolInputs.delete(data.index); } } } })); ``` ### Официальные события Anthropic API | Событие | Данные | Описание | |---------|--------|----------| | `message_start` | `{ message: {...} }` | Начало ответа AI | | `content_block_start` | `{ index, content_block }` | Начало блока (text/thinking/tool_use) | | **`content_block_delta`** | `{ index, delta }` | **Стриминг содержимого** | | `content_block_stop` | `{ index }` | Завершение блока | | `message_delta` | `{ delta }` | Метаданные ответа | | `message_stop` | `{}` | Конец ответа | ### Типы delta для `content_block_delta` ```typescript // Текст ответа AI delta: { type: 'text_delta', text: 'Создаю функцию...' } // Мышление AI (Extended Thinking) delta: { type: 'thinking_delta', thinking: 'Анализирую требования...' } // 🎯 Параметры инструмента (посимвольно!) delta: { type: 'input_json_delta', partial_json: '{"file_path": "src/app' } ``` ## Защита от сетевых задержек при стриминге ### Проблема При нестабильной сети чанки JSON могут приходить с задержками и рывками, что приводит к ошибкам парсинга на клиенте. Например: ``` Ошибка: Unterminated string in JSON at position 96 {"pattern": "Dashboard", "explanation": "Ищу компоненты dashboard} ↑ обрыв ``` ### Решение SDK предоставляет утилиты для безопасной работы с partial JSON: **1. PartialJsonAccumulator** - накапливает чанки и безопасно парсит: - Автоматически восстанавливает незакрытые кавычки - Балансирует скобки {} и [] - Возвращает null если JSON еще неполный **2. safeParsePartialJson** - парсит с автоматическим восстановлением: - Пытается исправить типичные проблемы чанкинга - Логирует процесс восстановления (опционально) - Безопасно возвращает null при неудаче ### Серверная защита Сервер использует несколько слоев защиты: - **Умный чанкинг** - режет JSON только на безопасных границах (после `,`, `}`, `]`) - **Event batching** - сглаживает отправку событий (16мс интервал, ~60fps) - **Валидация tool_use** - проверяет JSON перед отправкой клиенту - **Метрики качества** - отслеживает принудительные разрывы (должно быть 0) ### Рекомендации для клиентов ✅ **DO:** - Всегда используйте `PartialJsonAccumulator` для накопления чанков - Обрабатывайте `content_block_stop` для финального парсинга - Логируйте ошибки парсинга для диагностики ❌ **DON'T:** - Не парсите partial JSON напрямую через `JSON.parse()` - Не игнорируйте ошибки парсинга - они указывают на проблемы - Не ожидайте что каждый чанк содержит валидный JSON ### Пример использования ```typescript import { PartialJsonAccumulator } from '@code-solver/sdk'; const accumulator = new PartialJsonAccumulator(console); // При получении чанка if (event.type === 'content_block_delta' && event.delta?.type === 'input_json_delta') { accumulator.add(event.delta.partial_json); // Пробуем распарсить для UI превью (может быть null) const preview = accumulator.tryParse(); if (preview?.file_path) { updateProgressUI(preview.file_path); } } // При завершении блока if (event.type === 'content_block_stop') { const finalInput = accumulator.tryParse(); if (finalInput) { console.log('✅ Tool готов:', finalInput); } else { console.error('❌ Не удалось распарсить tool input'); } accumulator.reset(); } ``` ### Пример UI компонента ```typescript function ToolProgressIndicator() { const [currentTool, setCurrentTool] = useState<{ file?: string; operation?: string; progress: number; } | null>(null); // В onEvent callback if (type === 'content_block_delta' && data.delta?.type === 'input_json_delta') { // Извлекаем инфо из partial JSON const fileMatch = accumulated.match(/"file_path"\s*:\s*"([^"]*)"/); const opMatch = accumulated.match(/"operation"\s*:\s*"([^"]*)"/); setCurrentTool({ file: fileMatch?.[1], operation: opMatch?.[1] || 'processing', progress: accumulated.length }); } return currentTool && ( <div className="tool-progress"> <span className="operation">{currentTool.operation}</span> <span className="file">{currentTool.file}</span> <div className="progress-bar" style={{ width: `${currentTool.progress / 10}%` }} /> </div> ); } ``` ## Fine-grained Tool Streaming SDK автоматически использует fine-grained tool streaming для всех запросов с tools. Это значительно снижает latency при генерации больших tool inputs: - **Без fine-grained**: задержки 15+ секунд, мелкие чанки - **С fine-grained**: задержки ~3 секунды, крупные чанки ### Обработка Invalid JSON При использовании fine-grained streaming возможны incomplete JSON responses (особенно при достижении max_tokens). SDK автоматически: 1. Обнаруживает invalid JSON в tool responses 2. Оборачивает в валидный объект `{"INVALID_JSON": "..."}` 3. Отправляет retry запрос с увеличенным max_tokens Метрики доступны в DEBUG режиме логирования. ## TODO Tracking SDK автоматически передает события прогресса выполнения задач от Claude. Когда AI работает над сложной задачей (рефакторинг, добавление фичи), Claude создает TODO список для отслеживания прогресса. ### Пример использования ```typescript import { ChatApi } from '@vscursor/solver-sdk'; const api = new ChatApi(baseURL); for await (const chunk of api.streamChat(messages, { thinking: true })) { if (chunk.type === 'todo_update') { const event = chunk as any; // Отображаем прогресс-бар console.log(`📋 Прогресс: ${event.progress?.completed}/${event.progress?.total}`); if (event.progress?.current) { console.log(`🔧 Текущая задача: ${event.progress.current}`); } // Отображаем все задачи event.todos.forEach((todo: any) => { const icon = todo.status === 'completed' ? '✅' : todo.status === 'in_progress' ? '🔧' : '⏳'; const text = todo.status === 'in_progress' && todo.activeForm ? `${todo.content} (${todo.activeForm})` : todo.content; console.log(`${icon} ${text}`); }); } } ``` ### Формат TODO события ```typescript interface TodoUpdateEvent { type: 'todo_update'; operation: 'write' | 'read'; todos: Array<{ id: string; // Уникальный ID (например: "todo-1234567890-abc") content: string; // Описание задачи status: 'pending' | 'in_progress' | 'completed' | 'cancelled'; activeForm?: string; // Дополнительная информация для in_progress }>; tool_use_id: string; // ID tool_use от Claude progress: { completed: number; // Количество завершенных задач total: number; // Общее количество задач current: string; // Описание текущей задачи }; } ``` **Пример:** ```typescript { type: 'todo_update', operation: 'write', todos: [ { id: 'todo-1763533466409-uxptasdq3', content: 'Analyze codebase', status: 'completed', }, { id: 'todo-1763533466409-r67wnbk83', content: 'Implement feature', status: 'in_progress', activeForm: 'Creating authentication logic...' }, { id: 'todo-1763533466409-2se2juf10', content: 'Add tests', status: 'pending' } ], tool_use_id: 'toolu_01ABC123', progress: { completed: 1, total: 3, current: 'Implement feature' } } ``` ### ⚠️ Важно: Backend автоматически обрабатывает manage_todo_list Backend не требует явной отправки `tool_result` для `manage_todo_list` от клиента. Событие `todo_update` отправляется для UI widget, но `tool_result` создается автоматически на стороне backend. **Клиент должен только обрабатывать `todo_update` события для обновления UI:** ```typescript if (chunk.type === 'todo_update') { // Обновить TODO widget в UI updateTodoWidget(chunk.todos, chunk.progress); // Показать прогресс-бар updateProgressBar(chunk.progress.completed, chunk.progress.total); // Показать текущую задачу if (chunk.progress.current) { showCurrentTask(chunk.progress.current); } // НЕ нужно отправлять tool_result обратно! // Backend создает tool_result автоматически } ``` **Почему так работает:** `manage_todo_list` - это информационный CLIENT tool, который: - Не блокирует выполнение задач - Используется только для UI tracking - Автоматически помечается как успешный на backend - Не требует реального выполнения на клиенте ### Статусы TODO Backend поддерживает оба формата статусов для совместимости: | Backend Status | Cursor Format | Описание | |---------------|---------------|----------| | `pending` | `not-started` | Задача еще не начата | | `in_progress` | `in-progress` | Задача выполняется прямо сейчас | | `completed` | `completed` | Задача завершена | | `cancelled` | `cancelled` | Задача отменена | **Примечание:** Backend автоматически нормализует `not-started` `pending` и `in-progress` `in_progress` для внутренней обработки. ### Интеграция в UI **React компонент с использованием progress:** ```typescript interface TodoProgressViewProps { todos: Array<{ id: string; content: string; status: string; activeForm?: string; }>; progress: { completed: number; total: number; current: string; }; } function TodoProgressView({ todos, progress }: TodoProgressViewProps) { const progressPercent = Math.round((progress.completed / progress.total) * 100); return ( <div className="todo-progress"> {/* Прогресс-бар */} <div className="progress-bar"> <div className="progress-fill" style={{ width: `${progressPercent}%` }} aria-valuenow={progressPercent} aria-valuemin={0} aria-valuemax={100} /> </div> {/* Текстовый прогресс */} <div className="progress-text"> {progress.completed}/{progress.total} tasks completed </div> {/* Текущая задача (если есть) */} {progress.current && ( <div className="current-task"> 🔧 <strong>Working on:</strong> {progress.current} </div> )} {/* Список задач */} <ul className="todo-list"> {todos.map(todo => { const icon = getIcon(todo.status); const text = todo.status === 'in_progress' && todo.activeForm ? `${todo.content} (${todo.activeForm})` : todo.content; return ( <li key={todo.id} className={`todo-item ${todo.status}`}> <span className="todo-icon">{icon}</span> <span className="todo-text">{text}</span> </li> ); })} </ul> </div> ); } function getIcon(status: string): string { switch (status) { case 'completed': return '✅'; case 'in_progress': return '🔧'; case 'cancelled': return '❌'; case 'pending': default: return '⏳'; } } ``` ## Types ```typescript import { ChatMessage, ChatOptions, Project, ProjectState, SearchResult, UserProfile, LimitStatus, LimitExceededError, RateLimitError } from 'solver-sdk'; ``` ## Debug ```typescript const sdk = await CodeSolverSDK.create({ debug: 'verbose', // silent | error | warn | info | debug | verbose webSocket: { debug: true } }); ``` ## Delta Chunking (Indexing) ```typescript // Main indexing method await sdk.deltaManager.syncEncryptedChunks(projectId, chunks, rootHash, { batchSize: 50, onProgress: (current, total) => console.log(`${current}/${total}`) }); // Check sync status const state = await sdk.projects.getProjectState(projectId, clientRootHash); if (state.syncRequired) { // Needs sync } // Cancel ongoing sync await sdk.deltaManager.cancelSync(projectId); ``` ### File Invalidation ```typescript // Инвалидировать файл при изменении const result = await sdk.deltaManager.invalidateFile(projectId, { filePath: 'src/app.tsx', reason: 'file_changed' }); console.log(`Invalidated ${result.chunksInvalidated} chunks`); console.log(`Cache invalidated: ${result.cacheInvalidated}`); // Отправить новые chunks после инвалидации await sdk.deltaManager.syncEncryptedChunks(projectId, newChunks, rootHash); ``` ## Session Recovery ```typescript // Always check on project open const recovery = await sdk.projects.getRecoveryStatus(projectId); if (recovery.needsRecovery) { const { processedFiles, totalFiles, percentage } = recovery.progress; // Show dialog: "Continue (67%) — 480/642 files" or "Start Fresh" await sdk.projects.resumeSync(projectId); // Continue // OR await sdk.projects.cancelRecovery(projectId); // Start over } ``` ## Key Types ### ProjectState ```typescript interface ProjectState { projectId: string; merkleRootHash: string | null; totalFiles: number; // For UI display indexingStatus: 'pending' | 'in-progress' | 'complete' | 'failed'; syncRequired?: boolean; // Compare with client hash } ``` ### SyncProgressEvent ```typescript interface SyncProgressEvent { projectId: string; progress: number; // 0-100 processedFiles: number; // 450 totalFiles: number; // 722 stage: 'receiving_chunks' | 'processing_chunks' | 'creating_embeddings' | 'finalizing'; } ``` ### RecoveryInfo ```typescript interface RecoveryInfo { needsRecovery: boolean; progress: { processedFiles: number; totalFiles: number; percentage: number; // 0-100 }; canResume: boolean; } ``` ### CreditsStatus ```typescript interface CreditsStatus { status: 'ok' | 'warning' | 'critical' | 'depleted'; balance: CreditsBalance; canMakeRequest: boolean; message: string; action?: CreditsAction; } ``` ### CreditsBalance ```typescript interface CreditsBalance { creditsLimit: number; // Monthly credits limit creditsUsed: number; // Total credits used creditsRemaining: number; // Credits remaining percentUsed: number; // Usage percentage (0-100) bonusCredits?: number; // Bonus credits available bonusCreditsUsed?: number; // Bonus credits used bonusCreditsRemaining?: number; // Bonus credits remaining autoPurchaseEnabled: boolean; // Is auto-purchase enabled purchasedCredits?: number; // Credits purchased this month via auto-purchase resetDate?: Date; // Monthly reset date } ``` ### CreditsAction ```typescript interface CreditsAction { type: 'info' | 'upgrade' | 'enable_auto_purchase' | 'buy_credits'; suggestion: string; // User-facing recommendation url: string; // Action URL (e.g. '/dashboard#billing') options?: string[]; // Available options ['enable_auto_purchase', 'buy_credits', 'upgrade'] } ``` ## Troubleshooting ### "0 files" after indexing ```typescript const state = await sdk.projects.getProjectState(projectId); console.log(state.totalFiles); // Should be > 0 // If 0: update backend to latest version ``` ### No WebSocket updates ```typescript // 1. Check connection console.log(sdk.isWebSocketConnected); // should be true // 2. Verify subscription sdk.projectSync.subscribeToProject(projectId); // 3. Ensure OAuth token (production) const sdk = await CodeSolverSDK.create({ getAuthToken: () => authManager.getAccessToken() }); // 4. Fallback to HTTP polling const status = await sdk.projects.getIndexingStatus(projectId); ``` ### Interrupted sync ```typescript const recovery = await sdk.projects.getRecoveryStatus(projectId); if (recovery.needsRecovery) { // Option 1: Resume await sdk.projects.resumeSync(projectId); // Option 2: Cancel and restart await sdk.projects.cancelRecovery(projectId); } ``` ## 🔍 Web Search Tool SDK поддерживает Anthropic Web Search Tool - server-side инструмент для поиска в интернете. ### Конфигурация Web search настраивается через environment variables на сервере: ```bash # Включить web search WEB_SEARCH_ENABLED=true # Максимальное количество searches за один запрос WEB_SEARCH_MAX_USES=5 # Whitelist доменов (optional) WEB_SEARCH_ALLOWED_DOMAINS=wikipedia.org,stackoverflow.com,github.com # Blacklist доменов (optional) WEB_SEARCH_BLOCKED_DOMAINS=spam.com,malicious-site.org ``` ### Content Block Types для Server-Side Tools ```typescript type ServerSideContentBlock = | 'server_tool_use' // Server-side tool использование | 'web_search_tool_result' // Результаты поиска | 'web_fetch_tool_result' // Результаты web fetch | 'code_execution_tool_result' // Результаты code execution | 'text_editor_code_execution_tool_result'; // Результаты text editor ``` ### Stop Reason: pause_turn Когда модель использует server-side tool, вы получите `stop_reason: 'pause_turn'`: ```typescript // В streaming событии message_delta { type: 'message_delta', delta: { stop_reason: 'pause_turn' // Server-side tool execution } } // После выполнения придут результаты { type: 'content_block_start', content_block: { type: 'web_search_tool_result', content: [ { type: 'web_search_result', url: 'https://example.com', title: 'Example Page', encrypted_content: '...', page_age: '2024-01-15' } ] } } ``` ### Usage Tracking Web search запросы отслеживаются в `usage.server_tool_use`: ```typescript interface Usage { input_tokens: number; output_tokens: number; cache_read_input_tokens?: number; cache_creation_input_tokens?: number; server_tool_use?: { web_search_requests?: number; // Количество searches code_execution_requests?: number; // Количество code executions }; } ``` ### Pricing **Official Anthropic Pricing:** - **$10 за 1000 web searches** - **$0.01 за один search** Web search конвертируется в WorkCoins credits автоматически на основе текущего exchange rate. --- ## API Reference ### Projects (`sdk.projects`) ```typescript getAllProjects(): Promise<Project[]> getProjects(): Promise<Project[]> getProject(projectId: string): Promise<Project> createProject(name: string, data?: any, options?: ProjectOptions): Promise<Project> findOrCreateProject(name: string): Promise<Project> deleteProject(projectId: string): Promise<void> getReadyProjects(): Promise<Project[]> getProjectState(projectId: string, clientRootHash?: string): Promise<ProjectState> getProjectStatus(projectId: string): Promise<any> getIndexingStatus(projectId: string): Promise<any> cancelIndexing(projectId: string): Promise<boolean> clearIndexingError(projectId: string): Promise<boolean> getRecoveryStatus(projectId: string): Promise<RecoveryInfo> resumeSync(projectId: string, options?: any): Promise<any> cancelRecovery(projectId: string): Promise<void> initializeDeltaSync(projectId: string, params: any): Promise<any> resetIndexing(projectId: string): Promise<any> restartIndexing(projectId: string): Promise<any> getFilePathMapping(projectId: string): Promise<any> ``` ### Delta Manager (`sdk.deltaManager`) ```typescript initSync(projectId: string, request: SyncInitRequest): Promise<any> uploadChunkBatch(projectId: string, chunks: any[]): Promise<any> uploadChunksWithRetry(projectId: string, chunks: any[], options?: any): Promise<any> finalizeSync(projectId: string): Promise<SyncResult> getSyncStatus(projectId: string): Promise<SyncStatus> cancelSync(projectId: string): Promise<{ success: boolean; message?: string }> cleanupFiles(projectId: string, activeFiles: string[]): Promise<any> ``` ### Chat (`sdk.chat`) ```typescript chat(messages: ChatMessage[], options?: ChatOptions): Promise<ChatResponse> chatCompletion(messages: ChatMessage[], options?: ChatOptions): Promise<ChatResponse> chatWithRegionFailover(messages: ChatMessage[], options?: ChatOptions): Promise<ChatResponse> streamChat(messages: ChatMessage[], options?: ChatStreamOptions): AsyncGenerator<ChatStreamChunk> streamPrompt(prompt: string, options?: ChatStreamOptions): AsyncGenerator<ChatStreamChunk> sendContinuation(requestId: string, messages: ChatMessage[]): AsyncGenerator<ChatStreamChunk> resumeChat(requestId: string, messages: ChatMessage[], partialText: string, options?: ChatStreamOptions): AsyncGenerator<ChatStreamChunk> sendPromptWithRegionFailover(prompt: string, options?: ChatOptions): Promise<string> checkAvailability(): Promise<boolean> cancelRequest(requestId: string): Promise<void> getStreamsStats(): Promise<any> cleanupStaleStreams(timeoutMs?: number): Promise<any> cancelUserStreams(reason?: string): Promise<number> ``` ### Search (`sdk.search`) ```typescript searchCode(projectIdOrParams: string | SearchCodeParams, params?: SearchCodeParams): Promise<SearchResult[]> searchFunctions(projectIdOrParams: string | SearchFunctionsParams, params?: SearchFunctionsParams): Promise<FunctionSearchResult> semanticSearch(projectId: string, params: SearchCodeParams): Promise<SearchResult[]> getFunctionStats(projectId: string): Promise<{ stats: { totalFunctions: number } }> ``` ### Tools (`sdk.tools`) ```typescript getSchemas(): Promise<ToolsResponse> findToolByName(name: string): Promise<ToolSchema | null> getToolsStats(): Promise<{ total: number; categories: Record<string, number>; mostUsed?: string[] }> validateToolSchema(tool: ToolSchema): boolean createToolSchema(name: string, description: string, properties: any, required?: string[]): ToolSchema ``` ### User (`sdk.user`) ```typescript getProfile(): Promise<UserProfile> getLimitStatus(): Promise<LimitStatus> checkAvailability(): Promise<boolean> ``` ### Credits (`sdk.credits`) ```typescript getBalance(): Promise<CreditsBalance> getStatus(): Promise<CreditsStatus> estimate(tokens: number, model?: ModelType, operationType?: OperationType): Promise<CreditsEstimate> ``` ### Auth (`sdk.auth`) ```typescript revokeToken(token: string): Promise<{ ok: boolean }> logout(): Promise<{ ok: boolean }> ``` ### Models (`sdk.models`) ```typescript getAllModels(): Promise<any[]> getModels(): Promise<any[]> getAvailableModels(): Promise<any[]> getProviderModels(providerId: string): Promise<ProviderModels> getModelInfo(modelId: string): Promise<any> ``` ### Updates (`sdk.updates`) ```typescript checkForUpdates(options: UpdateCheckOptions): Promise<UpdateResponse> getChangelog(version: string, locale: string): Promise<string> sendStats(event: UpdateStatsEvent): Promise<void> getLatestVersion(channel?: string): Promise<LatestVersionInfo> checkAvailability(): Promise<boolean> ``` ### WebSocket (`sdk.projectSync`) v10.1.0 ```typescript // Connection connectWebSocket(): Promise<void> disconnectWebSocket(): void isWebSocketConnected: boolean // Subscription projectSync.subscribeToProject(projectId: string): void projectSync.unsubscribeFromProject(projectId: string): void // Events (v10.1.0 - полный список) projectSync.on('connected', callback): void projectSync.on('disconnected', callback): void projectSync.on('project-sync-joined', callback): void // NEW projectSync.on('project-sync-left', callback): void // NEW projectSync.on('sync-status-update', callback): void projectSync.on('sync-progress', callback): void projectSync.on('sync-completed', callback): void projectSync.on('error', callback): void projectSync.on('disconnect-idle', callback): void // NEW projectSync.on('reconnect_exhausted', callback): void // NEW projectSync.off(event: string, callback): void ``` ### WebSocket Options (v10.1.0) ```typescript webSocket: { enabled?: boolean; // default: true connectionTimeout?: number; // default: 10000 (ms) maxRetries?: number; // default: 3 retryDelay?: number; // default: 2000 (ms) debug?: boolean; // default: false disableAutoReconnect?: boolean; // NEW: полностью отключить SDK reconnect logLevel?: 'silent' | 'error' | 'warn' | 'info' | 'debug'; // NEW: контроль логов } ``` ### Utility ```typescript checkHealth(): Promise<boolean> version: string baseURL: string ``` ## Documentation All methods have full JSDoc comments with examples. Check your IDE autocomplete for detailed reference. **For full API documentation:** See [API Reference](#api-reference) section above. ---