🤖 CodeRun Agents SDK v1.0.6

CodeRun Agents SDK Overview

CodeRun Agents SDK (coderun-agent) is a standalone, lightweight, multi-provider AI Agent framework in plain JavaScript (ESM). It enables developers to build tool-augmented autonomous AI agents with real-time streaming, multi-agent delegation, and Human-in-the-Loop (HIL) safety approval workflows.

⚡ Pure Plain JavaScript (ESM): Written using strict traditional function declarations (function name() {}). Zero TypeScript compilation needed. Works natively in Node.js, Web Browsers, Electron, and server environments.

Quickstart

Install coderun-agent via npm:

npm install coderun-agent

Initialize and execute your agent in 5 lines of code:

import { createAgent } from 'coderun-agent';

var agent = createAgent({
  name: 'CodeAssistant',
  instructions: 'You are a helpful software engineering assistant.',
  provider: 'openai-compatible',
  baseurl: 'http://localhost:11434/v1',
  apikey: 'ollama',
  model: 'qwen2.5-coder:7b'
});

agent.run('Write a function to reverse a string.').then(function(result) {
  console.log(result.content);
});

Agents & createAgent

An agent is configured using a single configuration object passed to createAgent(config). An agent instance exposes methods to execute prompts, track token usage, manage lifecycle state, and connect optional MCP servers.

Agent Methods Reference

  • agent.run(prompt, runOptions): Executes a prompt turn loop. Returns a Promise resolving to a structured result object.
  • agent.run(prompt, { history }): Passes caller-owned continuation history for one run.
  • The agent does not retain history between runs, preventing accidental context leakage.
  • result.history: Returns the complete transcript produced during that run.
  • agent.getUsage(): Returns cumulative token usage metrics (prompt_tokens, completion_tokens, total_tokens).
  • agent.resetContext(): Resets usage and the agent's local lifecycle state.
  • agent.connectMcp(config): Connects to an MCP server and discovers its tools.
  • agent.closeMcp(): Closes connected MCP sessions and local MCP server processes.

Plug-and-Play MCP Servers

Install the optional MCP client package when you want to use existing MCP servers:

npm install @modelcontextprotocol/client

Connect open-source filesystem, GitHub, or other MCP servers through stdio:

var agent = createAgent({
  provider: 'openai-compatible',
  baseurl: 'http://localhost:11434/v1',
  apikey: 'ollama',
  model: 'minimax-m3:cloud'
});

await agent.connectMcp({
  name: 'filesystem',
  transport: 'stdio',
  command: 'npx',
  args: [ '-y', '@modelcontextprotocol/server-filesystem', process.cwd() ]
});

var result = await agent.run('List the project files with the MCP filesystem tool.');
await agent.closeMcp();

For a remote server, use transport: 'streamable-http', provide its url, and add authentication headers when required:

await agent.connectMcp({
  name: 'remote-github',
  transport: 'streamable-http',
  url: 'https://example.com/mcp',
  headers: { Authorization: 'Bearer YOUR_TOKEN' }
});

MCP tools are discovered automatically, converted to the agent's tool format, validated, permission-checked, executed through the normal agent loop, and returned to the model. The MCP client is loaded lazily only when connectMcp() is called.

Tools & Schemas (tool)

Define custom tools using the tool helper function. Tools accept standard JSON schema objects or Zod schemas (z.object(...)).

import { tool } from 'coderun-agent';

function executeWeather(args) {
  var city = args ? args.city : 'Tokyo';
  return Promise.resolve('Weather in ' + city + ' is sunny 25°C.');
}

var getWeatherTool = tool({
  name: 'get_weather',
  description: 'Get current weather for a city',
  parameters: {
    type: 'object',
    properties: {
      city: { type: 'string', description: 'City name e.g. Tokyo' }
    },
    required: ['city']
  },
  execute: executeWeather
});

Tool Suite Integration (coderun-tools, coderun-browser, coderun-desktop)

coderun-agent seamlessly connects with the complete CodeRun Tool Suite. You can pass tools from all three packages together in a single agent:

import { createAgent } from 'coderun-agent';
import coderunTools from 'coderun-tools';
import coderunBrowser from 'coderun-browser';
import coderunDesktop from 'coderun-desktop';

// Combine definitions from all three libraries
var allTools = [].concat(
  coderunTools.getDefinitions(),
  coderunBrowser.getDefinitions(),
  coderunDesktop.getDefinitions()
);

var agent = createAgent({
  name: 'FullAutomationAgent',
  instructions: 'You are an autonomous agent with workspace file access, web browser automation, and OS desktop controls.',
  provider: 'openai-compatible',
  baseurl: 'http://localhost:11434/v1',
  model: 'qwen2.5-coder:7b',
  tools: allTools
});

agent.run('Check workspace files, open example.com in browser, and capture desktop screenshot.');

🛡️ Guardrails Pipeline (Input, Tool & Output Safety)

Guardrails allow you to define programmable safety checkpoints at each stage of the agent execution lifecycle. Pass arrays of inspection functions in inputGuardrails, toolGuardrails, and outputGuardrails:

import { createAgent } from 'coderun-agent';

// 1. Input Guardrail: Block prompt injection or destructive requests
function checkPromptSafety(prompt, context) {
  var lower = prompt.toLowerCase();
  if (lower.indexOf('ignore all instructions') >= 0 || lower.indexOf('drop database') >= 0) {
    return { pass: false, error: 'Security tripwire: Unsafe prompt detected.' };
  }
  return { pass: true };
}

// 2. Tool Guardrail: Prevent directory escape outside workspace
function checkWorkspaceBoundary(toolName, args, context) {
  if (args && args.path && typeof args.path === 'string') {
    if (args.path.indexOf('..') >= 0 || args.path.startsWith('/etc') || args.path.startsWith('C:\\Windows')) {
      return { pass: false, error: 'Path traversal forbidden outside workspace.' };
    }
  }
  return { pass: true };
}

// 3. Output Guardrail: Enforce response format rules
function checkOutputFormat(content, context) {
  if (content.indexOf('SUMMARY:') === -1) {
    return { pass: false, error: 'Response must include a "SUMMARY:" section.' };
  }
  return { pass: true };
}

var agent = createAgent({
  name: 'GuardedAgent',
  provider: 'openai-compatible',
  baseurl: 'https://opencode.ai/zen/v1',
  apikey: 'sk-your-key',
  model: 'deepseek-v4-flash-free',
  inputGuardrails: [checkPromptSafety],
  toolGuardrails: [checkWorkspaceBoundary],
  outputGuardrails: [checkOutputFormat]
});

📐 Structured Output Enforcement (outputSchema)

Pass an outputSchema (a Zod schema or standard JSON schema) to guarantee valid, typed JSON output. If the model emits invalid JSON or schema violations, the engine automatically prompts the model to self-correct within the loop:

import { createAgent } from 'coderun-agent';
import { z } from 'zod';

var LeadExtractionSchema = z.object({
  fullName: z.string().describe('Full name of contact'),
  email: z.string().describe('Email address'),
  score: z.number().describe('Lead score from 1-100')
});

var agent = createAgent({
  provider: 'openai-compatible',
  baseurl: 'https://generativelanguage.googleapis.com/v1beta/openai/',
  apikey: 'YOUR_GEMINI_API_KEY',
  model: 'gemini-flash-latest',
  outputSchema: LeadExtractionSchema
});

var result = await agent.run('Extract contact info: John Doe, reachable at john@example.com, high purchase intent (95).');

// Access parsed structured object directly:
console.log(result.structuredOutput);
// Output: { fullName: "John Doe", email: "john@example.com", score: 95 }

Human-in-the-Loop (HIL) Security & Permission Control Guide

Protect your file system, terminal environment, and desktop OS from unauthorized actions using HIL Permission Controls (needsApproval + permissionHandler).

🛡️ Strict Validation Requirement: When needsApproval is configured (as a boolean, an array of tool names, or inside a tool definition), passing a permissionHandler function is strictly mandatory. If missing, createAgent throws an explicit validation error: Error: permissionHandler function is required when needsApproval is configured.

1. Three Approval Configuration Modes

  • Mode 1: Array of Specific Tool Names
    needsApproval: ['delete_file', 'execute_command', 'desktop_click']
    Gates only the specific sensitive tools specified in the array. All other tools run automatically.
  • Mode 2: Global Boolean Flag
    needsApproval: true
    Gates ALL tools passed to the agent. Every single tool call requires user confirmation before execution.
  • Mode 3: Per-Tool Definition Flag
    tool({ name: 'delete_file', needsApproval: true, execute: ... })
    Flags specific custom tools as requiring approval directly inside their declaration.

2. Execution Lifecycle & resolve(true) / resolve(false)

When an agent encounters a tool call that requires approval:

  1. The agent pauses the LLM execution turn loop.
  2. It transitions state to 'waiting' and emits onEvent({ type: 'state_changed', state: 'waiting', tool: toolName, args: args, id: toolId }).
  3. It invokes your custom permissionHandler(toolName, args, toolId) callback.
  4. User Approves (resolve(true)): The agent unfreezes, executes the action on disk/terminal/desktop, and returns the tool output to the LLM.
  5. User Denies (resolve(false)): The agent unfreezes, blocks execution, and feeds "Permission denied by user. Do NOT retry calling this tool." back to the LLM so it can pick a safe alternative.

3. Node.js Terminal CLI Approval Example

import readline from 'readline';
import { createAgent } from 'coderun-agent';
import coderunTools from 'coderun-tools';

// Terminal CLI Permission Handler
function cliPermissionHandler(toolName, args, toolId) {
  return new Promise(function(resolve) {
    console.log('\n⚠️ [SECURITY APPROVAL REQUIRED]');
    console.log('  Tool requested:', toolName);
    console.log('  Arguments:', JSON.stringify(args, null, 2));

    var rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout
    });

    rl.question('Allow tool execution? (y/N): ', function(answer) {
      rl.close();
      if (answer.trim().toLowerCase() === 'y') {
        console.log('🟢 Permission GRANTED by user.');
        resolve(true); // 👈 ALLOW execution!
      } else {
        console.log('🔴 Permission DENIED by user.');
        resolve(false); // 👈 BLOCK execution!
      }
    });
  });
}

var agent = createAgent({
  name: 'SecureCLIAgent',
  provider: 'openai-compatible',
  baseurl: 'http://localhost:11434/v1',
  model: 'qwen2.5-coder:7b',
  tools: coderunTools.getDefinitions(),
  
  // Gate dangerous tools:
  needsApproval: ['delete_file', 'delete_folder', 'execute_command'],
  permissionHandler: cliPermissionHandler
});

4. Web UI / HTML Modal Approval Example

// Web UI / React Modal Permission Handler
function webUiPermissionHandler(toolName, args, toolId) {
  return new Promise(function(resolve) {
    // 1. Show HTML Modal / Dialog in React or DOM:
    showApprovalModal({
      title: 'Tool Permission Request',
      message: 'Agent wants to run ' + toolName,
      details: args,
      onApprove: function() {
        hideModal();
        resolve(true);  // 🟢 User clicked ALLOW!
      },
      onDeny: function() {
        hideModal();
        resolve(false); // 🔴 User clicked DENY!
      }
    });
  });
}

var agent = createAgent({
  name: 'WebUIAgent',
  provider: 'openai-compatible',
  baseurl: 'http://localhost:11434/v1',
  model: 'qwen2.5-coder:7b',
  tools: coderunTools.getDefinitions(),
  needsApproval: true, // Gate all tools
  permissionHandler: webUiPermissionHandler
});

Streaming & Reasoning Tokens

Enable stream: true to receive real-time reasoning/thinking tokens, response text, and tool execution events:

function handleAgentEvent(evt) {
  if (evt.type === 'thinking' && evt.chunk) {
    process.stdout.write(evt.chunk); // 💭 Live reasoning tokens (DeepSeek / Qwen / Claude)
  } else if (evt.type === 'stream' && evt.chunk) {
    process.stdout.write(evt.chunk); // 💬 Live content tokens
  } else if (evt.type === 'tool_call') {
    console.log('🛠️ Tool Called:', evt.tool, evt.args);
  } else if (evt.type === 'tool_result') {
    console.log('✅ Tool Result:', evt.tool);
  }
}

var agent = createAgent({
  name: 'StreamAgent',
  provider: 'openai-compatible',
  baseurl: 'https://opencode.ai/zen/v1',
  apikey: 'YOUR_API_KEY',
  model: 'deepseek-v4-flash-free',
  tools: [ getWeatherTool ],
  stream: true
});

agent.run('What is the weather in Tokyo?', { onEvent: handleAgentEvent });

Agents as Tools (Subagents)

Pass any agent instance directly inside tools: [ subAgent ] to create hierarchical multi-agent delegation chains:

var researcher = createAgent({
  name: 'Researcher',
  instructions: 'You research topics and summarize findings.',
  provider: 'openai-compatible',
  baseurl: 'http://localhost:11434/v1',
  apikey: 'ollama',
  model: 'qwen2.5-coder:7b'
});

var manager = createAgent({
  name: 'Manager',
  instructions: 'Delegate research tasks to the Researcher agent.',
  provider: 'openai-compatible',
  baseurl: 'http://localhost:11434/v1',
  apikey: 'ollama',
  model: 'qwen2.5-coder:7b',
  tools: [ researcher ] // 👈 Automatically available as delegate_to_researcher tool!
});

Model Providers

coderun-agent connects to any LLM endpoint via standard provider drivers:

Provider Name BaseURL Format Example Model
openai-compatible http://localhost:11434/v1 qwen2.5-coder:7b (Ollama)
openai-compatible https://generativelanguage.googleapis.com/v1beta/openai/ gemini-flash-latest (Google Gemini)
openai-compatible https://opencode.ai/zen/v1 deepseek-v4-flash-free (OpenCode)
openai-compatible https://api.openai.com/v1 gpt-4o (OpenAI)
anthropic Direct SDK Connection claude-3-5-sonnet-20241022

Options Reference Table

Field Required Description
name No Defaults to function/agent name (e.g. "Agent").
instructions No System prompt/instructions shown to the LLM.
provider Yes "openai-compatible" or "anthropic".
baseurl OpenAI-compatible only HTTP endpoint URL. Anthropic uses its native SDK endpoint by default.
apikey No API key or bearer token. Optional when the provider SDK environment variable (OPENAI_API_KEY, ANTHROPIC_API_KEY) is set.
model No Model string identifier (default: "qwen2.5-coder:7b").
tools No Array of tools (custom tools, coderun-tools, or subagents).
inputGuardrails No Array of check functions: [function(prompt, context) { ... }]. Blocks unsafe prompts before LLM call.
toolGuardrails No Array of check functions: [function(toolName, args, context) { ... }]. Blocks invalid or unsafe tool parameters.
outputGuardrails No Array of check functions: [function(content, context) { ... }]. Validates final output against compliance rules.
outputSchema No Zod schema or JSON schema. Enforces valid structured JSON output with automatic LLM self-correction.
needsApproval No Approval policy: true or array of tool names ['delete_file'].
permissionHandler Conditional Mandatory function when needsApproval is set: function(toolName, args, id) returning true or false.
stream No Boolean indicating if streaming is enabled (default: true).
workspace No Workspace directory path (defaults to process.cwd()).
timeoutMs No Abort the provider request and cooperative tool work after the specified number of milliseconds. Timeout results use status: 'timeout'.
maxIterations No Maximum agent-loop turns (default: 50). Termination returns status: 'max_iterations_reached'.
maxRetries No Provider request retry count on transient failures (default: 3).
temperature No Sampling temperature passed to the provider.
maxTokens / max_tokens No Max generated tokens per request. Anthropic default: 4096; OpenAI-compatible defaults to the provider's server default when omitted.
parallelTools No Execute multiple tool calls concurrently in one iteration (default: false).
toolChoice / tool_choice No Control tool selection: 'auto', 'none', 'required', or a specific tool object.
responseFormat / response_format No Provider response format object (e.g. { type: 'json_object' }).
streamOptions / stream_options No Streaming extras; set false to omit stream_options for endpoints that reject them.
maxHistoryMessages / maxHistory No Keep only the most recent N history messages (turn-aware pruning). Disabled when omitted.
maxContextTokens No Approximate token budget for the whole request; oldest complete turns are trimmed to fit. Disabled when omitted.
maxToolOutputChars No Truncate tool result text stored in history (default: 6000, set 0 to disable).
onEvent No Global streaming/state event callback: function(evt).
askPermission No Alias for permissionHandler.
subagents No Array of agent instances made available as delegation tools.
signal No Caller-provided AbortSignal for cancelling provider and cooperative tool work. Aborted results use status: 'aborted'.

Result Object Reference Table

Field Type Description
success Boolean Indicates clean completion of the agent turn loop. Iteration-limit termination returns false.
content String Final text answer generated by the assistant.
structuredOutput Object Parsed JSON object returned when outputSchema is provided.
thinking String Accumulated reasoning/thinking tokens.
toolCalls Array Array of executed tool objects: [{ id, name, args, output }].
usage Object Token consumption: { prompt_tokens, completion_tokens, total_tokens }.
history Array Complete transcript produced during the current run. It is not automatically reused by a later run.
status String Result status: 'completed' on success; otherwise 'max_iterations_reached', 'timeout', 'aborted', 'schema_validation_failed', 'guardrail_blocked', or 'error'.
iterations Number Number of agent-loop turns consumed by this run.
rawResponse Object Raw provider response (non-streaming). For streaming, contains { streamed, model, chunksCount, finishReason }.