TypeScript SDK
The TypeScript SDK ships as dual ESM + CJS, works in Node.js 18+ and modern browsers, and is fully typed. It uses fetch natively with exponential retry on 5xx (1s / 2s / 4s, 3 retries).
Installation
terminal
npm install crewlayer
# or
pnpm add crewlayer
# or
yarn add crewlayerClient Initialization
client.ts
import { CrewLayerClient } from 'crewlayer'
const client = new CrewLayerClient({
baseUrl: 'http://localhost:8000', // default: http://localhost:8000
apiKey: process.env.CREWLAYER_API_KEY!,
timeout: 30_000, // ms, default: 30000
})
// Or from env vars: CREWLAYER_API_KEY, CREWLAYER_BASE_URL
const client = new CrewLayerClient()client.agents
agents.ts
// Create
const agent = await client.agents.create({
name: 'my-agent',
description: 'Sales assistant',
tags: ['production'],
config: { model: 'claude-opus-4-8' },
})
const agentId = agent.id
// Get
const agent = await client.agents.get(agentId)
// List
const { items, nextCursor } = await client.agents.list({ status: 'idle', limit: 20 })
// Update
await client.agents.update(agentId, { name: 'my-agent-v2' })
// Delete
await client.agents.delete(agentId)
// Status
const status = await client.agents.getStatus(agentId)
await client.agents.setStatus(agentId, { status: 'working', sessionId: 'session-uuid' })
// Status history
const history = await client.agents.statusHistory(agentId, { limit: 10 })client.memory
memory.ts
// Save
const memory = await client.memory.save(agentId, {
content: 'User prefers dark mode and TypeScript.',
importance: 0.85,
})
// Recall (semantic similarity)
const memories = await client.memory.recall(agentId, {
query: 'user interface preferences',
topK: 5,
minSimilarity: 0.7,
})
memories.forEach(m => console.log(`[${m.similarity.toFixed(2)}] ${m.content}`))
// List (paginated)
const page = await client.memory.list(agentId, { limit: 20 })
// Delete
await client.memory.delete(agentId, memoryId)
// Stats
const stats = await client.memory.stats(agentId)
// Short-term messages
await client.memory.appendMessage(agentId, {
sessionId,
role: 'user',
content: 'Hello!',
})
const messages = await client.memory.getMessages(agentId, { sessionId })
await client.memory.clearMessages(agentId, { sessionId })client.actions
actions.ts
const t0 = Date.now()
const result = await myTool(input)
await client.actions.log(agentId, {
toolName: 'web_search',
inputParams: { query: 'latest AI news' },
outputResult: { count: result.length },
status: 'success',
durationMs: Date.now() - t0,
sessionId,
})
// List
const { items } = await client.actions.list(agentId, {
tool: 'web_search',
status: 'error',
limit: 50,
})
// Stats
const stats = await client.actions.stats(agentId)client.sessions
sessions.ts
// Create session
const session = await client.sessions.create(agentId, { episodeId })
const sessionId = session.id
// Close and extract memories
const result = await client.sessions.close(sessionId)
console.log(`Extracted ${result.memoriesExtracted} memories`)client.context
context.ts
// Write
const entry = await client.context.write('pipeline', 'step1', { done: true, count: 42 })
console.log(`Written at version ${entry.version}`)
// Read
const { value } = await client.context.read('pipeline', 'step1')
// Delete
await client.context.delete('pipeline', 'step1')
// History & rollback
const history = await client.context.history('pipeline', 'step1')
await client.context.rollback('pipeline', 'step1', { version: 2 })SSE Streaming
Subscribe to blackboard namespace changes in real time using Server-Sent Events:
streaming.ts
import { openContextStream } from 'crewlayer'
const stream = openContextStream(client, 'pipeline')
// Async iterator — each event is a key change in the namespace
for await (const event of stream) {
console.log(`Key "${event.key}" changed to`, event.value)
console.log(`Version: ${event.version}`)
if (shouldStop) {
stream.close()
break
}
}
// React hook usage (Next.js / React)
import { useContextStream } from 'crewlayer/react'
function PipelineStatus() {
const { events } = useContextStream(client, 'pipeline')
return {JSON.stringify(events, null, 2)}
}Vercel AI SDK Adapter
The SDK ships a thin adapter that wires CrewLayer memory into streamText / generateText route handlers:
app/api/chat/route.ts
import { streamText } from 'ai'
import { anthropic } from '@ai-sdk/anthropic'
import { CrewLayerClient } from 'crewlayer'
import { withCrewLayerMemory } from 'crewlayer/vercel-ai'
const cl = new CrewLayerClient({ apiKey: process.env.CREWLAYER_API_KEY! })
const AGENT_ID = '550e8400-e29b-41d4-a716-446655440000'
export async function POST(req: Request) {
const { messages, sessionId } = await req.json()
// Pull relevant memories for context injection
const enriched = await withCrewLayerMemory(cl, AGENT_ID, {
messages,
sessionId,
topK: 5,
injectAs: 'system', // adds memories as a system message prefix
})
const result = streamText({
model: anthropic('claude-opus-4-8'),
messages: enriched.messages,
onFinish: async ({ text }) => {
// Persist the assistant response and optionally extract memories
await cl.memory.appendMessage(AGENT_ID, {
sessionId,
role: 'assistant',
content: text,
})
},
})
return result.toDataStreamResponse()
}Error Handling
errors.ts
import { CrewLayerError, AuthError, NotFoundError, RateLimitError } from 'crewlayer'
try {
const agent = await client.agents.get('non-existent-id')
} catch (err) {
if (err instanceof NotFoundError) {
console.log('Agent not found')
} else if (err instanceof AuthError) {
console.log('Invalid API key or missing scope')
} else if (err instanceof RateLimitError) {
console.log(`Rate limited. Retry after ${err.retryAfterMs}ms`)
} else if (err instanceof CrewLayerError) {
console.log(`API error ${err.status}: ${err.message}`)
}
}Build output
terminal
cd sdk-typescript
npm install
npm test # vitest — 72 tests
npm run build # tsup → dist/index.js (CJS) + dist/index.mjs (ESM)