Package Exports
- solver-sdk
Readme
Code Solver SDK
AI-powered code analysis SDK with WorkCoins billing system. 100% typed, production ready.
Install
npm install solver-sdk@10.1.0
# или
npm install solver-sdk@latestQuick Start
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
Quick Reference
Projects (19) - управление проектами и индексация
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)Chat (13) - AI диалоги и streaming
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?)Delta Chunking (7) - синхронизация файлов
initSync(projectId, request) | uploadChunkBatch(projectId, chunks)
uploadChunksWithRetry(projectId, chunks, options?)
finalizeSync(projectId) | getSyncStatus(projectId)
cancelSync(projectId) | cleanupFiles(projectId, activeFiles)Search (4) - поиск кода
searchCode(projectId, params) | searchFunctions(projectId, params)
semanticSearch(projectId, params) | getFunctionStats(projectId)Tools (5) - AI инструменты
getSchemas() | findToolByName(name) | getToolsStats()
validateToolSchema(tool) | createToolSchema(name, desc, props, required?)Models (5) - AI модели
getAllModels() | getModels() | getAvailableModels()
getProviderModels(providerId) | getModelInfo(modelId)Updates (5) - обновления SDK
checkForUpdates(options) | getChangelog(version, locale)
sendStats(event) | getLatestVersion(channel?) | checkAvailability()Credits (3) - WorkCoins баланс
getBalance() | getStatus() | estimate(tokens, model?, operationType?)User (3) - профиль и лимиты
getProfile() | getLimitStatus() | checkAvailability()Auth (2) - аутентификация
revokeToken(token) | logout()Auth
// 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
// Конфигурация (рекомендуется для 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
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
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
// 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
// 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
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
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
// Текст ответа 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
Пример использования
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 компонента
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 автоматически:
- Обнаруживает invalid JSON в tool responses
- Оборачивает в валидный объект
{"INVALID_JSON": "..."} - Отправляет retry запрос с увеличенным max_tokens
Метрики доступны в DEBUG режиме логирования.
TODO Tracking
SDK автоматически передает события прогресса выполнения задач от Claude. Когда AI работает над сложной задачей (рефакторинг, добавление фичи), Claude создает TODO список для отслеживания прогресса.
Пример использования
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 события
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; // Описание текущей задачи
};
}Пример:
{
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:
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:
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
import {
ChatMessage,
ChatOptions,
Project,
ProjectState,
SearchResult,
UserProfile,
LimitStatus,
LimitExceededError,
RateLimitError
} from 'solver-sdk';Debug
const sdk = await CodeSolverSDK.create({
debug: 'verbose', // silent | error | warn | info | debug | verbose
webSocket: { debug: true }
});Delta Chunking (Indexing)
// ✅ 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
// Инвалидировать файл при изменении
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
// 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
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
interface SyncProgressEvent {
projectId: string;
progress: number; // 0-100
processedFiles: number; // 450
totalFiles: number; // 722
stage: 'receiving_chunks' | 'processing_chunks' | 'creating_embeddings' | 'finalizing';
}RecoveryInfo
interface RecoveryInfo {
needsRecovery: boolean;
progress: {
processedFiles: number;
totalFiles: number;
percentage: number; // 0-100
};
canResume: boolean;
}CreditsStatus
interface CreditsStatus {
status: 'ok' | 'warning' | 'critical' | 'depleted';
balance: CreditsBalance;
canMakeRequest: boolean;
message: string;
action?: CreditsAction;
}CreditsBalance
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
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
const state = await sdk.projects.getProjectState(projectId);
console.log(state.totalFiles); // Should be > 0
// If 0: update backend to latest versionNo WebSocket updates
// 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
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 на сервере:
# Включить 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.orgContent Block Types для Server-Side Tools
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 editorStop Reason: pause_turn
Когда модель использует server-side tool, вы получите stop_reason: 'pause_turn':
// В 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:
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)
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)
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)
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)
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)
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[]): ToolSchemaUser (sdk.user)
getProfile(): Promise<UserProfile>
getLimitStatus(): Promise<LimitStatus>
checkAvailability(): Promise<boolean>Credits (sdk.credits)
getBalance(): Promise<CreditsBalance>
getStatus(): Promise<CreditsStatus>
estimate(tokens: number, model?: ModelType, operationType?: OperationType): Promise<CreditsEstimate>Auth (sdk.auth)
revokeToken(token: string): Promise<{ ok: boolean }>
logout(): Promise<{ ok: boolean }>Models (sdk.models)
getAllModels(): Promise<any[]>
getModels(): Promise<any[]>
getAvailableModels(): Promise<any[]>
getProviderModels(providerId: string): Promise<ProviderModels>
getModelInfo(modelId: string): Promise<any>Updates (sdk.updates)
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
// 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): voidWebSocket Options (v10.1.0)
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
checkHealth(): Promise<boolean>
version: string
baseURL: stringDocumentation
All methods have full JSDoc comments with examples. Check your IDE autocomplete for detailed reference.
For full API documentation: See API Reference section above.