@levelcode/sdk
v0.2.7
Published
Official SDK for LevelCode — AI coding agent & framework
Maintainers
Readme
@levelcode/sdk
Official SDK for LevelCode - AI coding agent and framework
Installation
npm install @levelcode/sdkPrerequisites
- Create a LevelCode account and get your LevelCode API key here.
Usage
Basic Example
import { LevelCodeClient } from '@levelcode/sdk'
async function main() {
const client = new LevelCodeClient({
// You need to pass in your own API key here.
// Get one here: https://www.levelcode.vercel.app/api-keys
apiKey: process.env.LEVELCODE_API_KEY,
cwd: process.cwd(),
})
// First run
const runState1 = await client.run({
// The agent id. Any agent on the store (https://levelcode.vercel.app/store)
agent: 'levelcode/[email protected]',
prompt: 'Create a simple calculator class',
handleEvent: (event) => {
// All events that happen during the run: agent start/finish, tool calls/results, text responses, errors.
console.log('LevelCode Event', JSON.stringify(event))
},
})
// Continue the same session with a follow-up
const runOrError2 = await client.run({
agent: 'levelcode/[email protected]',
prompt: 'Add unit tests for the calculator',
previousRun: runState1, // <-- this is where your next run differs from the previous run
handleEvent: (event) => {
console.log('LevelCode Event', JSON.stringify(event))
},
})
}
main()Example 2: Custom Agents and Tools
Here, we create a full agent and custom tools that can be reused between runs.
import { z } from 'zod/v4'
import { LevelCodeClient, getCustomToolDefinition } from '@levelcode/sdk'
import type { AgentDefinition } from '@levelcode/sdk'
async function main() {
const client = new LevelCodeClient({
// Note: You need to pass in your own API key.
// Get it here: https://www.levelcode.vercel.app/profile?tab=api-keys
apiKey: process.env.LEVELCODE_API_KEY,
// Optional directory agent runs from (if applicable).
cwd: process.cwd(),
})
// Define your own custom agents!
const myCustomAgent: AgentDefinition = {
id: 'my-custom-agent',
model: 'x-ai/grok-4-fast',
displayName: 'Sentiment analyzer',
toolNames: ['fetch_api_data'], // Defined below!
instructionsPrompt: `
1. Describe the different sentiments in the given prompt.
2. Score the prompt along the following 5 dimensions:
happiness, sadness, anger, fear, and surprise.`,
// ... other AgentDefinition properties
}
// And define your own custom tools!
const myCustomTool = getCustomToolDefinition({
toolName: 'fetch_api_data',
description: 'Fetch data from an API endpoint',
inputSchema: z.object({
url: z.url(),
method: z.enum(['GET', 'POST']).default('GET'),
headers: z.record(z.string(), z.string()).optional(),
}),
exampleInputs: [{ url: 'https://api.example.com/data', method: 'GET' }],
execute: async ({ url, method, headers }) => {
const response = await fetch(url, { method, headers })
const data = await response.text()
return [
{
type: 'json' as const,
value: {
message: `API Response: ${data.slice(0, 5000)}...`,
},
},
]
},
})
const { output } = await client.run({
// Run a custom agent by id. Must match an id in the agentDefinitions field below.
agent: 'my-custom-agent',
prompt: "Today I'm feeling very happy!",
// Provide custom agent and tool definitions:
agentDefinitions: [myCustomAgent],
customToolDefinitions: [myCustomTool],
handleEvent: (event) => {
// All events that happen during the run: agent start/finish, tool calls/results, text responses, errors.
console.log('LevelCode Event', JSON.stringify(event))
},
})
if (output.type === 'error') {
console.error(`The run failed:\n${output.message}`)
} else {
console.log('The run succeeded with output:', output)
}
}
main()API Reference
Knowledge Files
Knowledge files provide project context to the agent. The SDK auto-discovers:
- Project files:
knowledge.md,AGENTS.md, orCLAUDE.mdin each directory (priority order) - User files:
~/.knowledge.md,~/.AGENTS.md, or~/.CLAUDE.md(case-insensitive)
Override with knowledgeFiles (replaces project files) or userKnowledgeFiles (merges with home directory files):
await client.run({
agent: 'levelcode/[email protected]',
prompt: 'Help me refactor',
knowledgeFiles: { 'knowledge.md': '# Guidelines\n- Use TypeScript' },
userKnowledgeFiles: { '~/.knowledge.md': '# Preferences\n- Be concise' },
})File Filtering
The fileFilter option controls which files the agent can read:
const client = new LevelCodeClient({
apiKey: process.env.LEVELCODE_API_KEY,
fileFilter: (filePath) => {
if (filePath === '.env') return { status: 'blocked' }
if (filePath.endsWith('.env.example')) return { status: 'allow-example' }
return { status: 'allow' }
},
})Statuses: 'blocked' (returns [BLOCKED]), 'allow-example' (prefixes content with [TEMPLATE]), 'allow' (normal read).
Default behavior: When no fileFilter is provided, gitignore checking is applied automatically. When a fileFilter IS provided, the caller owns all filtering.
loadLocalAgents(options)
Loads agent definitions from .agents directories on disk.
import { loadLocalAgents, LevelCodeClient } from '@levelcode/sdk'
// Load from default locations (.agents in cwd, parent, or home)
const agents = await loadLocalAgents({ verbose: true })
// Or load from a specific directory
// const agents = await loadLocalAgents({ agentsPath: './my-agents' })
// Or load and validate agents (invalid agents are filtered out)
// const agents = await loadLocalAgents({ validate: true, verbose: true })
// Access source file path for debugging
for (const agent of Object.values(agents)) {
console.log(`${agent.id} loaded from ${agent._sourceFilePath}`)
}
// Use the loaded agents with client.run()
const client = new LevelCodeClient({ apiKey: process.env.LEVELCODE_API_KEY })
const result = await client.run({
agent: 'my-custom-agent',
agentDefinitions: Object.values(agents),
prompt: 'Hello',
})Parameters
agentsPath(string, optional): Path to a specific agents directory. If omitted, searches in{cwd}/.agents,{cwd}/../.agents, and{homedir}/.agents.verbose(boolean, optional): Whether to log errors during loading. Defaults tofalse.validate(boolean, optional): Whether to validate agents after loading. Invalid agents are filtered out. Defaults tofalse.
Returns
Returns a Promise<LoadedAgents> - a Record<string, LoadedAgentDefinition> of agent definitions keyed by their ID.
Each LoadedAgentDefinition extends AgentDefinition with:
_sourceFilePath(string): The file path the agent was loaded from
Supported File Types
.ts,.tsx- TypeScript files (automatically transpiled).js,.mjs,.cjs- JavaScript files
Files ending in .d.ts or .test.ts are excluded.
client.run(options)
Runs a LevelCode agent with the specified options.
Parameters
agent(string, required): The agent to run. Use'base'for the default agent, or specify a custom agent ID if you made your own agent definition (passed with theagentDefinitionsparam).prompt(string, required): The user prompt describing what you want the agent to do.params(object, optional): Additional parameters for the agent. Most agents don't use this, but some custom agents can take a JSON object as input in addition to the user prompt string.handleEvent(function, optional): Callback function that receives every event during execution (assistant messages, tool calls, etc.). This allows you to stream the agent's progress in real-time. We will likely add a token-by-token streaming callback in the future.previousRun(object, optional): JSON state returned from a previousrun()call. Use this to continue a conversation or session with the agent, maintaining context from previous interactions.projectFiles(object, optional): All the files in your project as a plain JavaScript object. Keys should be the full path from your current directory to each file, and values should be the string contents of the file. Example:{ "src/index.ts": "console.log('hi')" }. This helps LevelCode pick good source files for context. Note: This parameter was previously namedallFilesbut has been renamed for clarity.knowledgeFiles(object, optional): Knowledge files to inject into everyrun()call. Uses the same schema asprojectFiles- keys are file paths and values are file contents. These files are added directly to the agent's context.agentDefinitions(array, optional): Array of custom agent definitions. Each object should satisfy the AgentDefinition type.customToolDefinitions(array, optional): Array of custom tool definitions that extend the agent's capabilities. Each tool definition includes a name, Zod schema for input validation, and a handler function. These tools can be called by the agent during execution.maxAgentSteps(number, optional): Maximum number of steps the agent can take before stopping. Use this as a safety measure in case your agent starts going off the rails. A reasonable number is around 20.
Returns
Returns a Promise that resolves to either a "success" or a "failure" object.
- The "success" object contains a
RunStateobject which can be passed into subsequent runs via thepreviousRunparameter to resume the conversation. - The "failure" object contains an
Errorobject with aname,message, andstackproperties.
The RunState object contains:
sessionState: Internal state to be passed to the next runoutput: The agent's output (text, error, or other types)
Team Management
The SDK provides programmatic team management for coordinating multiple agents.
client.createTeam(config)
Creates a new team from a TeamConfig object. If a team with the same name already exists it will be overwritten.
import { LevelCodeClient } from '@levelcode/sdk'
import type { TeamConfig } from '@levelcode/sdk'
const client = new LevelCodeClient({ apiKey: process.env.LEVELCODE_API_KEY })
const config: TeamConfig = {
name: 'my-team',
description: 'Backend refactor squad',
createdAt: Date.now(),
leadAgentId: 'lead-001',
phase: 'planning',
members: [
{
agentId: 'lead-001',
name: 'team-lead',
role: 'coordinator',
agentType: 'coordinator',
model: 'claude-opus-4-6',
joinedAt: Date.now(),
status: 'active',
cwd: process.cwd(),
},
],
settings: { maxMembers: 10, autoAssign: true },
}
client.createTeam(config)client.deleteTeam(name)
Deletes a team by name, removing all associated data (config, tasks, inboxes). Safe to call on a team that does not exist.
client.deleteTeam('my-team')client.getTeamStatus(name)
Returns summary information for a team, or null if the team does not exist or has a corrupted config.
const status = client.getTeamStatus('my-team')
if (status) {
console.log(status.name) // 'my-team'
console.log(status.phase) // 'planning'
console.log(status.memberCount) // 1
}Returns a TeamSummary:
| Field | Type | Description |
| ------------- | ---------- | ---------------------------------- |
| name | string | Team name |
| phase | DevPhase | Current development phase |
| memberCount | number | Number of members in the team |
client.listTeams()
Returns an array of TeamSummary objects for every team. Teams with corrupted config files are silently skipped.
const teams = client.listTeams()
for (const team of teams) {
console.log(`${team.name} — ${team.phase} (${team.memberCount} members)`)
}Team Types
The following types are exported from @levelcode/sdk for use with the team API:
import type {
TeamConfig,
TeamMember,
TeamTask,
TeamRole,
DevPhase,
TeamSummary,
TeamProtocolMessage,
} from '@levelcode/sdk'| Type | Description |
| ----------------------- | ------------------------------------------------------------------ |
| TeamConfig | Full team configuration (name, members, settings, phase) |
| TeamMember | A single member entry (agentId, name, role, model, status) |
| TeamTask | A task within a team (id, subject, status, priority, owner) |
| TeamRole | Union of all role strings ('coordinator', 'senior-engineer', etc.) |
| DevPhase | Development phase ('planning' | 'pre-alpha' | 'alpha' | 'beta' | 'production' | 'mature') |
| TeamSummary | Lightweight summary returned by getTeamStatus and listTeams |
| TeamProtocolMessage | Message type used for inter-agent communication |
License
MIT
