
Security News
RubyGems Adds Cooldown Feature to Bundler for Newly Published Gems
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.
@sowonai/crewx-sdk
Advanced tools
Core SDK for building custom AI agent integrations and tools on top of SowonAI CrewX.
The CrewX SDK provides the foundational interfaces, types, and utilities for building AI agent systems. It's designed to be framework-agnostic and extensible, allowing you to:
npm install @sowonai/crewx-sdk
For peer dependencies (if using NestJS):
npm install @nestjs/common @nestjs/core reflect-metadata rxjs
The SDK provides a high-level createCrewxAgent factory function for simplified agent creation:
import { createCrewxAgent } from '@sowonai/crewx-sdk';
// Create an agent with configuration
const { agent, onEvent } = await createCrewxAgent({
provider: {
namespace: 'cli',
id: 'codex',
apiKey: process.env.CODEX_TOKEN,
},
enableCallStack: true,
defaultAgentId: 'my-agent',
});
// Subscribe to agent events
onEvent('callStackUpdated', (stack) => {
console.log('Call stack:', stack.map(f => `${f.depth}: ${f.agentId}`));
});
onEvent('agentStarted', ({ agentId, mode }) => {
console.log(`Agent ${agentId} started in ${mode} mode`);
});
// Execute a query (read-only)
const queryResult = await agent.query({
prompt: 'What is the current status?',
context: 'Project: CrewX',
messages: [
{
id: 'msg-1',
text: 'Previous message',
timestamp: new Date().toISOString(),
isAssistant: false,
},
],
});
console.log(queryResult.content);
// Execute an action (write mode)
const executeResult = await agent.execute({
prompt: 'Create a summary document',
context: 'Project root: /path/to/project',
});
console.log(executeResult.content);
import {
MentionParser,
loadAvailableAgents,
type AgentConfig,
type AIProvider
} from '@sowonai/crewx-sdk';
// Parse agent mentions from user input
const parser = new MentionParser();
const parsed = parser.parse('@claude analyze this code');
console.log(parsed.mentions); // ['claude']
console.log(parsed.cleanedPrompt); // 'analyze this code'
import {
type ConversationMessage,
type ConversationThread,
type IConversationHistoryProvider,
getConversationConfig
} from '@sowonai/crewx-sdk';
// Implement custom conversation storage
class MyConversationProvider implements IConversationHistoryProvider {
async fetchHistory(options) {
// Fetch from your database
return {
messages: [],
metadata: { platform: 'my-platform' }
};
}
async saveMessage(message, threadId) {
// Save to your database
}
// ... other methods
}
import { DocumentManager } from '@sowonai/crewx-sdk';
const docManager = new DocumentManager();
// Load markdown documents
await docManager.loadDocument('./docs/api.md', 'markdown');
// Get document content
const content = docManager.getDocument('./docs/api.md');
console.log(content);
The fundamental interface for all AI providers:
interface AIProvider {
respond(
prompt: string,
options?: AIQueryOptions
): Promise<AIResponse>;
}
interface AIQueryOptions {
messages?: ConversationMessage[];
pipedContext?: string;
model?: string;
// ... other options
}
interface AIResponse {
content: string;
metadata?: Record<string, unknown>;
}
Interface for conversation storage implementations:
interface IConversationHistoryProvider {
fetchHistory(options: FetchHistoryOptions): Promise<ConversationThread>;
saveMessage(message: ConversationMessage, threadId: string): Promise<void>;
createThread(threadId: string, metadata?: Record<string, unknown>): Promise<void>;
listThreads(): Promise<Array<{ id: string; updatedAt: Date }>>;
}
Manage timeout settings for AI operations:
import { getTimeoutConfig, getDefaultTimeoutConfig } from '@sowonai/crewx-sdk';
// Get timeout configuration
const timeout = getTimeoutConfig();
// Use default timeouts
const defaults = getDefaultTimeoutConfig();
console.log(defaults.default); // 120000ms
console.log(defaults.query); // 60000ms
console.log(defaults.execute); // 300000ms
Configure conversation behavior:
import { getConversationConfig, DEFAULT_CONVERSATION_CONFIG } from '@sowonai/crewx-sdk';
const config = getConversationConfig();
// Or use defaults
console.log(DEFAULT_CONVERSATION_CONFIG);
Parse agent mentions from text:
import { MentionParser, type ParsedMentions } from '@sowonai/crewx-sdk';
const parser = new MentionParser();
// Parse single mention
const result = parser.parse('@claude help me');
// result.mentions: ['claude']
// result.cleanedPrompt: 'help me'
// Parse multiple mentions
const multi = parser.parse('@claude @gemini compare approaches');
// multi.mentions: ['claude', 'gemini']
// Override model
const withModel = parser.parse('@claude:opus analyze');
// Model override detected
Handle errors consistently:
import { getErrorMessage, getErrorStack, isError } from '@sowonai/crewx-sdk';
try {
// ... operation
} catch (err) {
if (isError(err)) {
console.error(getErrorMessage(err));
console.error(getErrorStack(err));
}
}
Create and configure an agent with event support:
import {
createCrewxAgent,
type CrewxAgentConfig,
type CrewxAgent,
type AgentQueryRequest,
type AgentExecuteRequest,
type CallStackFrame
} from '@sowonai/crewx-sdk';
// Configuration interface
interface CrewxAgentConfig {
provider?: {
namespace: string;
id: string;
apiKey?: string;
model?: string;
};
knowledgeBase?: {
path?: string;
sources?: string[];
};
enableCallStack?: boolean;
defaultAgentId?: string;
}
// Create agent
const { agent, onEvent, eventBus } = await createCrewxAgent({
provider: { namespace: 'cli', id: 'codex' },
enableCallStack: true,
});
// Agent interface
agent.query(request: AgentQueryRequest): Promise<AgentResult>
agent.execute(request: AgentExecuteRequest): Promise<AgentResult>
agent.getCallStack(): CallStackFrame[]
The event system supports lifecycle and call stack tracking:
// Subscribe to events
const unsubscribe = onEvent('eventName', (payload) => {
console.log('Event:', payload);
});
// Supported events:
// - 'callStackUpdated': CallStackFrame[]
// - 'agentStarted': { agentId: string, mode: 'query' | 'execute' }
// - 'agentCompleted': { agentId: string, success: boolean }
// - 'toolCallStarted': { toolName: string, args: any }
// - 'toolCallCompleted': { toolName: string, result: any }
// Unsubscribe when done
unsubscribe();
// Direct event bus access for advanced usage
eventBus.emit('customEvent', { data: 'value' });
eventBus.listenerCount('eventName');
eventBus.clear(); // Remove all listeners
import type {
AgentConfig,
AgentsConfig,
AgentInfo,
AgentQueryOptions,
AgentResponse,
RemoteAgentConfigInput,
RemoteAgentInfo
} from '@sowonai/crewx-sdk';
import { ExecutionMode, SecurityLevel } from '@sowonai/crewx-sdk';
import { AIProvider, AIQueryOptions, AIResponse } from '@sowonai/crewx-sdk';
export class MyCustomProvider implements AIProvider {
async respond(
prompt: string,
options?: AIQueryOptions
): Promise<AIResponse> {
// Implement your AI provider logic
const result = await this.callMyAI(prompt, options);
return {
content: result.text,
metadata: {
model: result.model,
tokens: result.usage
}
};
}
private async callMyAI(prompt: string, options?: AIQueryOptions) {
// Your implementation
return {
text: 'Response from my AI',
model: 'my-model-v1',
usage: { total: 100 }
};
}
}
import {
IConversationHistoryProvider,
ConversationMessage,
ConversationThread,
FetchHistoryOptions
} from '@sowonai/crewx-sdk';
export class DatabaseConversationProvider implements IConversationHistoryProvider {
constructor(private db: MyDatabase) {}
async fetchHistory(options: FetchHistoryOptions): Promise<ConversationThread> {
const messages = await this.db.query(
'SELECT * FROM messages WHERE thread_id = ?',
[options.threadId]
);
return {
messages: messages.map(this.toMessage),
metadata: { platform: 'database' }
};
}
async saveMessage(message: ConversationMessage, threadId: string): Promise<void> {
await this.db.insert('messages', {
thread_id: threadId,
...message
});
}
async createThread(threadId: string, metadata?: Record<string, unknown>): Promise<void> {
await this.db.insert('threads', { id: threadId, metadata });
}
async listThreads() {
return await this.db.query('SELECT id, updated_at FROM threads');
}
private toMessage(row: any): ConversationMessage {
return {
id: row.id,
userId: row.user_id,
text: row.text,
timestamp: row.created_at,
isAssistant: row.is_assistant,
metadata: row.metadata
};
}
}
The SDK provides reusable components that were previously CLI-only. These abstractions enable custom platform integrations while maintaining consistency.
The SDK provides a flexible message formatting system that supports multiple platforms (Slack, Terminal, API, etc.) and allows custom formatters.
Terminal Formatter (Built-in)
import { BaseMessageFormatter, type StructuredMessage } from '@sowonai/crewx-sdk';
const formatter = new BaseMessageFormatter();
// Format messages for terminal display
const history = formatter.formatHistory(messages, {
includeUserId: true,
includeTimestamp: true,
timestampFormat: 'iso', // 'iso' | 'relative' | 'unix'
});
console.log(history);
// Output:
// [2025-10-17T10:00:00Z] user123: Hello!
// [2025-10-17T10:00:05Z] assistant: How can I help?
Slack Formatter (Built-in)
For Slack bot integrations, use the Slack-specific formatter that handles threading, mentions, and rich formatting:
import { SlackMessageFormatter } from '@sowonai/crewx-sdk';
const slackFormatter = new SlackMessageFormatter();
// Format for Slack with rich text support
const formatted = slackFormatter.formatForSlack(messages, {
includeTimestamp: true,
useThreading: true,
preserveMentions: true,
});
// Format agent response with Slack-specific blocks
const response = slackFormatter.formatAgentResponse({
content: 'Task completed successfully!',
agentId: 'backend',
metadata: { status: 'success' }
});
// Send to Slack
await slackClient.chat.postMessage({
channel: channelId,
blocks: response.blocks,
thread_ts: threadId,
});
API/JSON Formatter
For API responses or structured data:
import {
BaseMessageFormatter,
type StructuredMessage,
type ConversationMetadata
} from '@sowonai/crewx-sdk';
class APIFormatter extends BaseMessageFormatter {
formatForAPI(messages: StructuredMessage[]): {
messages: Array<{
id: string;
author: { id: string; isBot: boolean };
content: string;
timestamp: string;
metadata?: Record<string, unknown>;
}>;
meta: ConversationMetadata;
} {
return {
messages: messages.map(msg => ({
id: msg.id,
author: {
id: msg.userId || 'unknown',
isBot: msg.isAssistant || false,
},
content: msg.text,
timestamp: msg.timestamp,
metadata: msg.metadata,
})),
meta: {
platform: 'api',
totalMessages: messages.length,
generatedAt: new Date().toISOString(),
},
};
}
}
const apiFormatter = new APIFormatter();
const response = apiFormatter.formatForAPI(messages);
// Return as JSON API response
res.json(response);
Create your own formatter for custom platforms:
import {
BaseMessageFormatter,
StructuredMessage,
FormatterOptions
} from '@sowonai/crewx-sdk';
class DiscordFormatter extends BaseMessageFormatter {
formatMessage(msg: StructuredMessage, options: FormatterOptions): string {
const timestamp = options.includeTimestamp
? `<t:${Math.floor(new Date(msg.timestamp).getTime() / 1000)}:R> `
: '';
const author = msg.isAssistant ? '🤖 **Bot**' : `👤 **${msg.userId}**`;
return `${timestamp}${author}: ${msg.text}`;
}
formatForDiscordEmbed(
message: string,
options: { color?: number; title?: string }
) {
return {
embeds: [{
title: options.title || 'Agent Response',
description: message,
color: options.color || 0x5865F2,
timestamp: new Date().toISOString(),
}],
};
}
}
const discordFormatter = new DiscordFormatter();
const embed = discordFormatter.formatForDiscordEmbed(
'Analysis complete!',
{ title: 'Backend Agent', color: 0x00FF00 }
);
await discordChannel.send(embed);
The formatter system supports rich metadata for enhanced context:
import {
BaseMessageFormatter,
type StructuredMessage,
type ConversationMetadata
} from '@sowonai/crewx-sdk';
const messages: StructuredMessage[] = [
{
id: 'msg-1',
userId: 'user123',
text: 'What is the status?',
timestamp: new Date().toISOString(),
isAssistant: false,
metadata: {
platform: 'slack',
channelId: 'C123456',
threadTs: '1234567890.123456',
userAgent: 'SlackBot/1.0',
},
},
{
id: 'msg-2',
userId: 'backend-agent',
text: 'All systems operational.',
timestamp: new Date().toISOString(),
isAssistant: true,
metadata: {
agentId: 'backend',
model: 'claude-3-5-sonnet',
processingTime: 1234,
tokenUsage: { input: 50, output: 100 },
},
},
];
const formatter = new BaseMessageFormatter();
// Format with metadata extraction
const formatted = formatter.formatHistory(messages, {
includeUserId: true,
includeTimestamp: true,
extractMetadata: true,
});
// Access metadata
messages.forEach(msg => {
if (msg.metadata?.tokenUsage) {
console.log(`Tokens used: ${msg.metadata.tokenUsage.input + msg.metadata.tokenUsage.output}`);
}
});
If you're migrating from the CLI's internal formatter to the SDK formatter:
Before (CLI internal)
// This was CLI-only code
import { MessageFormatter } from '../cli/src/utils/message-formatter';
const formatter = new MessageFormatter();
const result = formatter.format(messages);
After (SDK)
// Now use SDK's BaseMessageFormatter
import { BaseMessageFormatter } from '@sowonai/crewx-sdk';
const formatter = new BaseMessageFormatter();
const result = formatter.formatHistory(messages, {
includeUserId: true,
includeTimestamp: true,
});
Key Changes:
@sowonai/crewx-sdk instead of CLI internalsformatHistory() method instead of format()extractMetadata optionSlack Migration
// Before (CLI)
import { SlackFormatter } from '../cli/src/slack/formatter';
// After (SDK)
import { SlackMessageFormatter } from '@sowonai/crewx-sdk';
const formatter = new SlackMessageFormatter();
// Same API, now available in SDK
If you're building a CLI tool and want to add Slack formatting support:
Step 1: Install SDK
npm install @sowonai/crewx-sdk
Step 2: Import Slack Formatter
import { SlackMessageFormatter } from '@sowonai/crewx-sdk';
import { WebClient } from '@slack/web-api';
const slackClient = new WebClient(process.env.SLACK_BOT_TOKEN);
const formatter = new SlackMessageFormatter();
Step 3: Format Messages for Slack
async function sendToSlack(
channelId: string,
content: string,
threadTs?: string
) {
// Format using SDK formatter
const formatted = formatter.formatAgentResponse({
content,
agentId: 'my-cli-agent',
metadata: {
source: 'cli',
timestamp: new Date().toISOString(),
},
});
// Send to Slack
await slackClient.chat.postMessage({
channel: channelId,
text: content, // Fallback text
blocks: formatted.blocks,
thread_ts: threadTs,
});
}
Step 4: Handle Conversation History
import {
SlackMessageFormatter,
type StructuredMessage
} from '@sowonai/crewx-sdk';
async function formatSlackThread(threadTs: string) {
// Fetch Slack thread
const thread = await slackClient.conversations.replies({
channel: channelId,
ts: threadTs,
});
// Convert to StructuredMessage format
const messages: StructuredMessage[] = thread.messages.map(msg => ({
id: msg.ts,
userId: msg.user || 'bot',
text: msg.text || '',
timestamp: new Date(parseFloat(msg.ts) * 1000).toISOString(),
isAssistant: !!msg.bot_id,
metadata: {
platform: 'slack',
threadTs: msg.thread_ts,
},
}));
// Format for display or processing
const formatter = new SlackMessageFormatter();
const formatted = formatter.formatHistory(messages, {
includeTimestamp: true,
useThreading: true,
});
return formatted;
}
Step 5: Error Handling
try {
await sendToSlack(channelId, 'Task completed!', threadTs);
} catch (error) {
// Format error for Slack
const errorMessage = formatter.formatAgentResponse({
content: `❌ Error: ${error.message}`,
agentId: 'cli-agent',
metadata: { status: 'error' },
});
await slackClient.chat.postMessage({
channel: channelId,
blocks: errorMessage.blocks,
});
}
Use built-in providers or create custom ones:
import {
BaseAIProvider,
ClaudeProvider,
GeminiProvider,
CopilotProvider,
CodexProvider,
type LoggerLike,
type BaseAIProviderOptions
} from '@sowonai/crewx-sdk';
// Use built-in provider
const claude = new ClaudeProvider({
apiKey: process.env.ANTHROPIC_API_KEY,
logger: console,
enableToolUse: true,
model: 'claude-3-5-sonnet-20241022',
});
// Custom provider
class MyProvider extends BaseAIProvider {
constructor(options: BaseAIProviderOptions) {
super(options);
}
async query(prompt: string, options: AIQueryOptions): Promise<AIResponse> {
// Custom implementation
return { content: 'Response', metadata: {} };
}
}
Manage remote agent communications:
import {
RemoteAgentManager,
FetchRemoteTransport,
MockRemoteTransport,
type RemoteAgentConfig
} from '@sowonai/crewx-sdk';
// Production transport
const transport = new FetchRemoteTransport({
timeout: 30000,
headers: { 'Authorization': `Bearer ${token}` },
});
// Testing transport
const mockTransport = new MockRemoteTransport({
'agent-1': { content: 'Mocked response', success: true },
});
const manager = new RemoteAgentManager({
transport,
enableLogging: true,
logger: console,
});
// Load remote agent
await manager.loadAgent({
id: 'backend',
url: 'https://api.example.com/agent',
apiKey: process.env.REMOTE_API_KEY,
tools: ['search', 'analyze'],
});
// Query remote agent
const result = await manager.queryAgent('backend', 'Analyze codebase');
console.log(result.content);
Some internal APIs are available for advanced use cases:
import { /* internal exports */ } from '@sowonai/crewx-sdk/internal';
// Note: Internal APIs may change between minor versions
// Use at your own risk
The SDK works seamlessly with NestJS:
import { Injectable } from '@nestjs/common';
import { DocumentManager } from '@sowonai/crewx-sdk';
@Injectable()
export class MyService {
constructor(private readonly docManager: DocumentManager) {}
async loadDocs() {
await this.docManager.loadDocument('./docs/api.md', 'markdown');
}
}
import {
SERVER_NAME,
PREFIX_TOOL_NAME,
DEFAULT_MAX_FILE_SIZE,
DEFAULT_MAX_FILES
} from '@sowonai/crewx-sdk';
console.log(SERVER_NAME); // 'crewx'
console.log(PREFIX_TOOL_NAME); // 'crewx_'
The SDK provides the following export paths:
@sowonai/crewx-sdk - Main public API@sowonai/crewx-sdk/internal - Internal utilities (use with caution)@sowonai/crewx-sdk/package.json - Package metadataThe SDK is written in TypeScript and includes full type definitions. No additional @types packages are needed.
import type { AIProvider, AgentConfig } from '@sowonai/crewx-sdk';
// Full type safety
const config: AgentConfig = {
id: 'my-agent',
provider: 'cli/claude',
// ... TypeScript will guide you
};
# Run unit tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with UI
npm run test:ui
# Run tests with coverage
npm run test:coverage
# Build the package
npm run build
# Output: dist/
Contributions to the SDK require signing our Contributor License Agreement (CLA).
Please follow these steps:
npm test and npm run buildApache-2.0 License - See LICENSE for details.
The SDK provides TemplateContext and AgentMetadata exports for dynamic template processing:
import { TemplateContext, AgentMetadata } from '@sowonai/crewx-sdk';
// Use TemplateContext for dynamic prompts
const context: TemplateContext = {
env: process.env,
agent: {
id: 'claude',
name: 'Claude Assistant',
provider: 'cli/claude',
model: 'claude-3-5-sonnet'
},
agentMetadata: {
specialties: ['code-analysis', 'architecture'],
capabilities: ['file-operations', 'web-search'],
description: 'Advanced reasoning and analysis specialist'
},
mode: 'query',
platform: 'cli'
};
For detailed usage, see Template Variables Guide.
crewx - Full-featured CLI tool built on this SDKBuilt by SowonLabs
FAQs
SowonAI CrewX SDK
We found that @sowonai/crewx-sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.

Security News
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.

Security News
Federal audit finds NIST lacked a plan to clear the NVD backlog, wasted funds on duplicate work, and delayed use of CISA data.