npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@northflare/agent

v0.1.3

Published

A TypeScript SDK for creating coding agents that can interact with various sandboxed execution environments - built on top of the AI SDK

Readme

AI Code Agents

A TypeScript SDK for creating AI agents that interact with sandboxed code execution environments. Built on the Vercel AI SDK, it provides a flexible, type-safe framework for building AI coding agents with comprehensive tool support and environment abstraction.

Key Features

  • 🔓 No Vendor Lock-in: Environment abstraction layer works across any execution environment (local, Docker, cloud sandboxes). Model-agnostic architecture supports any AI provider through the Vercel AI SDK.
  • 🛡️ Type-Safe: Full TypeScript support with strict typing and comprehensive Zod schemas for all tool inputs/outputs.
  • 🔧 Flexible Tool System: Several built-in tools with configurable safety levels (readonly, basic, all). Easy to extend with custom tools.
  • 🌍 Environment Abstraction: Write tools once, run anywhere. All tools work seamlessly across different environment implementations.
  • 📦 Multiple Environments: Support for single or multiple environments per agent, enabling complex multi-context workflows.
  • 🎯 Step-by-Step Execution: Built-in step tracking with optional verbose debug logging (debug: true or DEBUG=1) for transparent agent behavior.
  • 🔌 MCP Server Support: Full integration with Model Context Protocol (MCP) servers for extending tool capabilities.
  • 💾 Session Management: Built-in session persistence and resumption with external storage support.

Installation

You will need:

  • Node.js 20+
  • npm or another package manager
  • AI SDK v5+ and zod v4+ (see below)
npm install @northflare/agent ai zod

# Install the provider you plan to use (the SDK no longer bundles any providers):
npm install @ai-sdk/anthropic  # For Claude models
# or
npm install @ai-sdk/openai     # For OpenAI models

The SDK expects a LanguageModel instance at runtime; bring your own provider package or construct the model in whatever way your application prefers.

Quick Start

import { query } from "@northflare/agent";
import { openai } from "@ai-sdk/openai"; // Alternative: OpenAI models

// Create an agent using the SDK interface
const response = query({
  prompt: "Create a simple Node.js HTTP server in server.js",
  options: {
    model: openai("gpt-5.1"),
    maxTurns: 10,
    cwd: "/workspace",
    allowedTools: ["read_file", "write_file", "run_command", "todo_write"],
  },
});

// Process streaming messages
for await (const message of response) {
  if (message.type === "system" && message.subtype === "init") {
    console.log(`Session ID: ${message.session_id}`);
  }

  if (message.type === "assistant") {
    console.log(`Assistant: ${message.message.content}`);
  }

  if (message.type === "result") {
    console.log(`Result: ${message.result}`);
    break;
  }
}

Debug logging

Set debug: true in options or export DEBUG=1 to emit detailed, prefixed console logs ([agent-sdk:agent], [agent-sdk:mcp]) that cover session creation/resume, tool filtering, MCP initialization/status, agent creation/invocation, and cleanup.

Tools

Tools enable agents to interact with their environments. Each tool has a well-defined purpose with comprehensive input/output validation.

File Operations:

  • read_file - Read file contents
  • write_file - Write or create files
  • delete_file - Delete files
  • edit_file - Edit files with search/replace operations
  • multi_edit_file - Multiple atomic edits to a single file with array of search/replace operations
  • move_file - Move or rename files
  • copy_file - Copy files
  • read_many_files - Batch read multiple files

Directory & Search:

  • get_project_file_structure - Get complete project tree structure
  • glob - Pattern-based file search
  • list_directory - List directory contents

Task Management:

  • todo_read - Read and filter todo items with statistics and current task tracking
  • todo_write - Write/update todo items with status and priority tracking

Execution & External:

  • run_command - Execute shell commands
  • url_fetcher - Fetch content from URLs and analyze with prompts (includes caching)

Control:

  • submit - Submits the current task with the required result string that is sent to the user as the final reply
  • task - Launch specialized sub-agents for complex, multi-step tasks

Usage Examples

Basic SDK Interface Usage

import { query } from "@northflare/agent";
import { anthropic } from "@ai-sdk/anthropic";

const response = query({
  prompt: "Create a Python script that calculates fibonacci numbers",
  options: {
    model: anthropic("MODEL_ID_PLACEHOLDER"),
    maxTurns: 5,
    allowedTools: ["read_file", "write_file", "run_command"],
  },
});

for await (const message of response) {
  if (message.type === "assistant") {
    console.log(message.message.content);
  }
  if (message.type === "result") {
    console.log("Final result:", message.result);
  }
}

With Session Management

Resume previous conversations with session IDs:

import { query } from "@northflare/agent";
import { anthropic } from "@ai-sdk/anthropic";

// First conversation
const response1 = query({
  prompt: "Create a React component for a todo list",
  options: { model: anthropic("MODEL_ID_PLACEHOLDER") },
});

let sessionId: string;
for await (const message of response1) {
  if (message.type === "system" && message.subtype === "init") {
    sessionId = message.session_id;
  }
}

// Resume the conversation later
const response2 = query({
  prompt: "Now add localStorage persistence to the todo list",
  options: {
    model: anthropic("MODEL_ID_PLACEHOLDER"),
    resume: sessionId,
  },
});

With External Session Storage

Persist sessions to a database or filesystem:

import { query } from "@northflare/agent";
import { anthropic } from "@ai-sdk/anthropic";
import fs from "fs/promises";

const response = query({
  prompt: "Build a web application",
  options: {
    model: anthropic("MODEL_ID_PLACEHOLDER"),
    // Load session from storage
    loadSession: async (sessionId) => {
      try {
        const data = await fs.readFile(`sessions/${sessionId}.json`, "utf-8");
        return JSON.parse(data);
      } catch {
        return null;
      }
    },
    // Save session to storage
    saveSession: async (sessionId, session) => {
      await fs.writeFile(
        `sessions/${sessionId}.json`,
        JSON.stringify(session, null, 2)
      );
    },
  },
});

With MCP Servers

Extend agent capabilities with MCP servers:

import { query } from "@northflare/agent";
import { anthropic } from "@ai-sdk/anthropic";

const response = query({
  prompt: "Search for information about TypeScript decorators",
  options: {
    model: anthropic("MODEL_ID_PLACEHOLDER"),
    mcpServers: {
      "web-search": {
        command: "npx",
        args: ["-y", "@modelcontextprotocol/server-google-search"],
        env: {
          GOOGLE_API_KEY: process.env.GOOGLE_API_KEY,
          GOOGLE_CSE_ID: process.env.GOOGLE_CSE_ID,
        },
      },
    },
  },
});

With Custom Tool Restrictions

Control which tools the agent can use:

import { query } from "@northflare/agent";
import { anthropic } from "@ai-sdk/anthropic";

const response = query({
  prompt: "Analyze the project structure and create a report",
  options: {
    model: anthropic("MODEL_ID_PLACEHOLDER"),
    // Only allow read operations
    allowedTools: [
      "read_file",
      "read_many_files",
      "get_project_file_structure",
      "glob",
      "list_directory",
    ],
    // Or explicitly disallow dangerous operations
    disallowedTools: ["run_command", "delete_file"],
  },
});

With Task Management Tools

Use the todo tools for complex multi-step tasks:

import { query } from "@northflare/agent";
import { anthropic } from "@ai-sdk/anthropic";

const response = query({
  prompt: "Refactor the authentication module with proper error handling",
  options: {
    model: anthropic("MODEL_ID_PLACEHOLDER"),
    allowedTools: [
      "read_file",
      "write_file",
      "edit_file",
      "multi_edit_file", // For batch edits
      "todo_read", // Track task progress
      "todo_write", // Update task status
      "run_command",
    ],
  },
});

With External Data Fetching

Fetch and analyze external content:

import { query } from "@northflare/agent";
import { anthropic } from "@ai-sdk/anthropic";

const response = query({
  prompt: "Fetch the latest TypeScript documentation and create a summary",
  options: {
    model: anthropic("MODEL_ID_PLACEHOLDER"),
    allowedTools: [
      "url_fetcher", // Fetch and analyze web content
      "write_file", // Save the summary
    ],
  },
});

With Vision Support (Image Input)

The SDK supports image inputs in streaming mode with three different vision modes:

Direct Vision Mode (visionModel: true)

Images are sent directly to a vision-capable model:

import { query } from "@northflare/agent";
import { anthropic } from "@ai-sdk/anthropic";

const response = query({
  prompt: [
    { type: "text", text: "What do you see in this image?" },
    { type: "image", image: new URL("https://example.com/image.jpg") },
  ],
  options: {
    model: anthropic("MODEL_ID_PLACEHOLDER"), // Must be vision-capable
    visionModel: true, // Enable direct vision mode
  },
});

Tool-Based Vision Mode (visionModel: string)

Use a separate vision model as a tool for analysis:

import { query } from "@northflare/agent";
import { anthropic } from "@ai-sdk/anthropic";
import {
  saveImage,
  loadImage,
} from "@northflare/agent/image-storage/filesystem";

const response = query({
  prompt: [
    { type: "text", text: "Analyze this image and write a haiku about it" },
    { type: "image", image: "data:image/jpeg;base64,..." }, // Base64 image
  ],
  options: {
    model: anthropic("MODEL_ID_PLACEHOLDER"), // Main model
    visionModel: "openai/gpt-4-vision-preview", // Vision model as tool
    saveImage, // Required: image storage function
    loadImage, // Required: image retrieval function
  },
});

No Vision Support (visionModel: false or undefined)

Images are not supported and will throw an error if provided:

const response = query({
  prompt: "Process text only",
  options: {
    model: anthropic("MODEL_ID_PLACEHOLDER"),
    visionModel: false, // Explicitly disable vision support
  },
});

Image Storage Adapters

The SDK provides built-in image storage adapters for tool-based vision:

// Filesystem storage (default)
import {
  saveImage,
  loadImage,
} from "@northflare/agent/image-storage/filesystem";

// Memory storage (for testing)
import { MemoryImageStorage } from "@northflare/agent";
const storage = new MemoryImageStorage();

// Custom storage implementation
const customSaveImage = async (id: string, data: string | URL) => {
  // Your custom storage logic (e.g., S3, database)
};

const customLoadImage = async (id: string) => {
  // Your custom retrieval logic
  return storedImageData;
};

With Streaming Input (Interactive Sessions)

Use streaming input for multi-turn conversations with dynamic user input and interruptions:

import { query } from "@northflare/agent";
import { anthropic } from "@ai-sdk/anthropic";
import { v4 as uuidv4 } from "uuid";

// Create an async generator for streaming messages
async function* generateMessages() {
  const sessionId = uuidv4();

  // Initial message - analyze the codebase
  yield {
    type: "user" as const,
    uuid: uuidv4(),
    session_id: sessionId,
    message: {
      role: "user" as const,
      content: "Analyze this codebase for potential performance issues",
    },
    parent_tool_use_id: null,
  };

  // Wait for some condition or user input
  await new Promise((resolve) => setTimeout(resolve, 2000));

  // Follow-up message after some processing
  yield {
    type: "user" as const,
    uuid: uuidv4(),
    session_id: sessionId,
    message: {
      role: "user" as const,
      content: "Now focus on optimizing the database queries",
    },
    parent_tool_use_id: null,
  };

  // Add more messages based on dynamic conditions
  const userDecision = await getUserInput(); // Your custom input function
  if (userDecision === "refactor") {
    yield {
      type: "user" as const,
      uuid: uuidv4(),
      session_id: sessionId,
      message: {
        role: "user" as const,
        content: "Refactor the identified bottlenecks and create unit tests",
      },
      parent_tool_use_id: null,
    };
  }
}

// Process the streaming session
const response = query({
  prompt: generateMessages(), // Pass the async generator
  options: {
    model: anthropic("MODEL_ID_PLACEHOLDER"),
    maxTurns: 10,
    allowedTools: [
      "read_file",
      "write_file",
      "edit_file",
      "multi_edit_file",
      "get_project_file_structure",
      "run_command",
      "todo_write",
      "todo_read",
    ],
  },
});

// Handle streaming responses with interruption capability
const timeoutId = setTimeout(() => {
  console.log("Interrupting after timeout...");
  response.interrupt();
}, 30000); // Interrupt after 30 seconds if needed

try {
  for await (const message of response) {
    if (message.type === "system" && message.subtype === "init") {
      console.log(`Session started: ${message.session_id}`);
    }

    if (message.type === "assistant") {
      // Stream assistant responses as they come
      console.log("Assistant:", message.message.content);
    }

    if (message.type === "stream_event") {
      // Stream events for tool usage
      console.log(`Tool event: ${message.event.type}`);
    }

    if (message.type === "result") {
      if (message.subtype === "success") {
        console.log("Final result:", message.result);
      }
      clearTimeout(timeoutId);
      break;
    }
  }
} catch (error) {
  console.error("Error during streaming:", error);
  clearTimeout(timeoutId);
}

// Helper function for getting user input (example)
async function getUserInput(): Promise<string> {
  // In a real application, this would get actual user input
  // For example, from readline, a web UI, or other input source
  return "refactor";
}

Key features of streaming input:

  • Dynamic Message Generation: Use async generators to yield messages based on runtime conditions
  • Multi-turn Conversations: Maintain context across multiple user inputs
  • Session Persistence: Messages automatically maintain session context
  • Interruption Support: Cancel operations at any time using response.interrupt()
  • Real-time Feedback: Process responses as they stream, not just final results
  • Flexible Control Flow: Add messages based on conditions, user input, or external events

With Output Format

Define a structured output format for the agent:

import { query } from "@northflare/agent";
import { anthropic } from "@ai-sdk/anthropic";

const response = query({
  prompt: "Create a REST API for a blog",
  options: {
    model: anthropic("MODEL_ID_PLACEHOLDER"),
    outputFormat: {
      type: "structured",
      schema: {
        type: "object",
        properties: {
          endpoints: {
            type: "array",
            items: {
              type: "object",
              properties: {
                method: { type: "string" },
                path: { type: "string" },
                description: { type: "string" },
              },
            },
          },
          filesCreated: {
            type: "array",
            items: { type: "string" },
          },
        },
      },
    },
  },
});

With Sub-agents

Use specialized sub-agents for complex, multi-step tasks:

import { query } from "@northflare/agent";
import { anthropic } from "@ai-sdk/anthropic";
import type { AgentDefinition } from "@northflare/agent";

// Define specialized sub-agents
const agents: Record<string, AgentDefinition> = {
  "code-reviewer": {
    description: "Reviews code for bugs, security issues, and best practices",
    prompt:
      "You are a thorough code reviewer. Focus on finding bugs, security vulnerabilities, and code quality issues.",
    tools: ["read_file", "grep", "glob"],
    model: "MODEL_ID_PLACEHOLDER", // Use a powerful model for analysis
  },
  "test-writer": {
    description: "Writes comprehensive unit and integration tests",
    prompt:
      "You are a test writing specialist. Write thorough tests with good coverage and edge cases.",
    tools: ["read_file", "write_file", "edit_file"],
    model: "claude-3-haiku-20240307", // Use a faster model for code generation
  },
  documenter: {
    description: "Creates clear and comprehensive documentation",
    prompt:
      "You are a technical documentation expert. Write clear, concise documentation with examples.",
    tools: ["read_file", "write_file"],
    model: "inherit", // Inherit the parent agent's model
  },
};

// Main agent orchestrates sub-agents
const response = query({
  prompt:
    "Review the authentication module, write tests for any issues found, and document the changes",
  options: {
    model: anthropic("MODEL_ID_PLACEHOLDER"),
    agents, // Pass sub-agent definitions
    allowedTools: ["task", "read_file", "write_file"], // Main agent needs 'task' tool
    maxTurns: 15,
  },
});

for await (const message of response) {
  if (message.type === "tool_result" && message.tool_name === "task") {
    console.log("Sub-agent completed:", message.result);
  }
  if (message.type === "result") {
    console.log("All tasks completed:", message.result);
  }
}

Sub-agent Features:

  • Specialized Prompts: Each sub-agent can have its own system prompt for focused behavior
  • Tool Restrictions: Limit sub-agents to specific tools for safety and focus
  • Model Selection: Use different models based on task complexity (inherit to use parent's model)
  • Isolated Context: Sub-agents run with fresh context, preventing confusion
  • Parallel Execution: Multiple sub-agents can run concurrently when invoked in the same message
  • Recursion Protection: Sub-agents cannot invoke other sub-agents (Task tool filtered out)

Interrupting Execution

Stop agent execution at any time:

import { query } from "@northflare/agent";
import { anthropic } from "@ai-sdk/anthropic";

const response = query({
  prompt: "Create multiple components for a dashboard",
  options: { model: anthropic("claude-4-5-sonnet") },
});

// Stop after 5 seconds
setTimeout(() => {
  response.interrupt();
}, 5000);

for await (const message of response) {
  // Process messages until interrupted
}

API Reference

import type { LanguageModel } from "ai";

// Sub-agent definition
interface AgentDefinition {
  description: string; // When to use this agent
  prompt: string; // System prompt for the agent
  tools?: string[]; // Allowed tool names (omit for all)
  model?: string | "inherit"; // Model override (defaults to 'inherit')
}

interface QueryOptions {
  // Core options
  model?: LanguageModel;
  maxTurns?: number;
  abortController?: AbortController;

  // Environment options
  cwd?: string;
  env?: Record<string, string>;
  additionalDirectories?: string[];

  // Tool options
  allowedTools?: string[];
  disallowedTools?: string[];

  // Sub-agents configuration
  agents?: Record<string, AgentDefinition>;

  // Session management
  resume?: string;
  loadSession?: (sessionId: string) => Promise<SessionData | null>;
  saveSession?: (sessionId: string, session: SessionData) => Promise<void>;

  // MCP servers
  mcpServers?: Record<string, MCPServerConfig>;
  onElicitationRequest?: (request: ElicitationRequest) => Promise<any>;

  // Output options
  outputFormat?: OutputFormat;
  systemPrompt?: string | SystemPromptPreset;
  stderr?: (data: Buffer) => void;

  // Diagnostics
  debug?: boolean; // Verbose lifecycle logging (also enabled via DEBUG env)
}

Copyright

This is a fork of AI Code Agents.