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

general-agent-runtime

v0.0.4

Published

A small, composable general-purpose AI agent runtime for TypeScript.

Downloads

645

Readme

General Agent Runtime

A small, composable TypeScript runtime for building tool-using AI agents with streaming events, persistent sessions, hooks, and long-context compaction.

Status

The core v0 Runtime architecture is complete through Phase 11.

Current verification baseline:

npm run typecheck      ✅
npm test               ✅ 16 files / 140 tests
npm run test:integration ✅ 2 files / 2 tests
npm run build          ✅
npm run test:package   ✅
git diff --check       ✅

v0 is complete: the core Runtime, public-API examples, live integration tests, and minimal CLI have all been implemented and verified against a real OpenAI-compatible llama.cpp endpoint. MCP, Skills, Memory, Approval, and SubAgent are intentionally v1+.

Installation

npm install general-agent-runtime

The package is ESM-only and requires Node.js 22 or newer.

Quick start

The Runtime does not discover configuration from files, environment variables, or user directories. The host application resolves those sources and passes explicit Runtime configuration to createAgent(config):

import { createAgent } from "general-agent-runtime";

const agent = await createAgent({
  model: {
    provider: "openai-compatible",
    baseUrl: "https://api.openai.com/v1",
    apiKey: "your-key",
    model: "gpt-5.6",
    contextWindow: 128_000,
  },
});

for await (const event of agent.run("Explain what this project does in one paragraph.")) {
  if (event.type === "message_delta") {
    process.stdout.write(event.data.delta);
  }
}

createAgent(config) requires an explicit configuration object. The supplied partial configuration is merged over built-in Runtime defaults, validated as a complete RuntimeConfig, frozen, and passed into the default composition root. Configuration discovery belongs to the host layer, such as a CLI, TUI, desktop client, service, or test harness.

For the default OpenAI-compatible provider, model.contextWindow declares the model context window in tokens. Runtime projection budgeting and compaction use this value through ModelCapabilities.contextWindow. This is an explicit host-supplied capability declaration rather than automatic model metadata discovery. When a custom ModelProvider is injected through the Builder, that provider remains responsible for its own getCapabilities() result.

Agent API

The main SDK surface is intentionally small:

interface Agent {
  run(input, options?): AsyncIterable<AgentEvent>;
  resume(sessionId, options?): Promise<SessionProjection>;
  abort(runId): void;
  getState(runId): Readonly<AgentRunState> | undefined;
}

Streaming events

agent.run() returns an AsyncIterable<AgentEvent>. Common events include:

run_start
turn_start
message_start
message_delta
tool_start
tool_end
compact_start
compact_end
message_end
turn_end
error
run_end

The same logical event sequence is visible to registered Event sinks and the Agent stream.

Session resume

A new run creates a JSONL Session automatically. Capture its sessionId from the event stream and pass it back later:

let sessionId;

for await (const event of agent.run(
  "Remember that the project codename is Atlas.",
  { sessionMetadata: { cwd: process.cwd() } },
)) {
  sessionId ??= event.sessionId;
}

const projection = await agent.resume(sessionId);

for await (const event of agent.run(
  "What is the project codename?",
  { sessionId },
)) {
  if (event.type === "message_delta") {
    process.stdout.write(event.data.delta);
  }
}

resume() reopens and projects the persisted Session and returns that SessionProjection to the host. Continuing the conversation is still done through run(..., { sessionId }).

Session catalog

Session persistence details stay inside Runtime. Hosts that need session browsing or management should create the public Runtime facade and use its SessionCatalog:

import {
  DEFAULT_RUNTIME_CONFIG,
  createRuntime,
} from "general-agent-runtime";

const runtime = createRuntime({
  config: {
    ...structuredClone(DEFAULT_RUNTIME_CONFIG),
    session: {
      ...structuredClone(DEFAULT_RUNTIME_CONFIG.session),
      directory: "./sessions",
    },
  },
  cwd: process.cwd(),
});

const sessions = runtime.sessions;
const currentProject = await sessions.list({ cwd: runtime.cwd });
const descriptor = await sessions.get(sessionId);

await sessions.updateMetadata(sessionId, { title: "Atlas work" });
await sessions.delete(sessionId);

SessionDescriptor exposes id, optional title / cwd, plus createdAt and updatedAt. Multiple session_meta entries are merged by Runtime, with later fields overriding earlier fields. JSONL entries, Store, Projector, serializer, path normalization, and Catalog implementations are not part of the package-root API.

Runtime does not generate titles. Title generation, rename UX, delete confirmation, fuzzy search, relative time formatting, and session pickers are client concerns. Hosts receive Runtime-normalized cwd from createRuntime() instead of importing path-normalization helpers.

Abort

Use an AbortSignal when the caller owns cancellation:

const controller = new AbortController();

const stream = agent.run("Do a long task", {
  signal: controller.signal,
});

controller.abort("user_cancelled");

When a runId is already known, agent.abort(runId) can cancel that active run directly.

Tools

The default builder registers four core tools:

  • read_file
  • write_file
  • list_directory
  • run_command

All model tool calls pass through ToolExecutor for input validation, Hook control, timeout/abort handling, bounded parallelism, error normalization, and output truncation.

Runtime implementation classes are intentionally not exported from the package root. Clients should use createAgent(config) for the Agent-only facade or createRuntime({ config, cwd }) when they also need Session management. Both entrypoints return frozen plain-object facades rather than concrete implementation instances, so clients do not receive internal constructors or dependency fields.

Store, Projector, RunManager, Builder, provider adapters, loop internals, serializer helpers, and cwd comparison helpers remain Runtime implementation details. New extension points should be added as explicit stable public contracts instead of exposing concrete internals.

Hooks

Hooks form the Runtime control plane. They can observe or control lifecycle points without placing policy inside the Loop.

Typical uses:

  • modify or block a tool call with before_tool;
  • inspect tool output with after_tool;
  • modify the final model request with before_model;
  • stop a run at lifecycle boundaries;
  • observe compaction and errors.

before_model may modify messages, tools, temperature, max output tokens, and provider options, but it cannot change the resolved model.

Long-context handling

The Runtime separates projection from compaction:

Context sources
   ↓
Context snapshot
   ↓
Projection
   ↓
Budget evaluation
   ↓
Compaction when required
   ↓
ModelRequest

Compaction supports deterministic compression, semantic compaction, durable checkpoints, and bounded reactive recovery from provider context-limit errors. Original Session history remains append-only.

Examples

examples/basic-chat.ts
examples/tool-call.ts
examples/resume-session.ts

Run one with:

npm run dev -- examples/basic-chat.ts "Hello"
npm run dev -- examples/tool-call.ts
npm run dev -- examples/resume-session.ts

CLI

From this repository, run the minimal interactive CLI with:

npm run cli

After installing the npm package, run:

npx general-agent-runtime

Continue an existing Session:

npx general-agent-runtime --session <session-id>

The CLI is a host application around the public Agent API. It maps AGENT_PROVIDER, AGENT_MODEL, AGENT_BASE_URL, AGENT_API_KEY, AGENT_CONTEXT_WINDOW, and AGENT_SESSION_DIR into explicit Runtime configuration before calling createAgent(config); the Runtime itself does not read environment variables.

Tests

Default tests are deterministic unit tests:

npm test

Live OpenAI-compatible integration tests are separate:

npm run test:integration

The live-test harness owns its configuration discovery. It optionally reads agent.config.json and AGENT_* environment variables, merges them in the test layer, and passes the resulting explicit configuration to the Runtime. If neither source is present, live tests skip.

Package verification

Before publishing, build a real tarball and verify it from a clean TypeScript consumer:

npm run test:package

test:package creates the real tarball in a temporary directory, installs it into a clean consumer, checks TypeScript declaration resolution, verifies the root ESM import, and runs the installed CLI. prepack automatically runs a clean build, typecheck, and unit tests before npm creates the package. The published tarball is restricted to dist/, README.md, and npm metadata; tests, examples, local configuration, sessions, coverage, design documents, and release scripts are not published.

Publish with:

npm publish

The current package name is general-agent-runtime. Confirm the name is still available immediately before the first publish.

Architecture

The main ownership model is:

createAgent / createRuntime
        ↓
      Agent
        ↓
   RunManager
        ↓
    AgentLoop
   ↙   ↓   ↘
Context Model Tools
        ↓
   Compaction

The Loop owns orchestration and state transitions, not component algorithms. Session and Context integration are bridged through top-level adapters, preserving one-way core dependencies.

Design documents

  • ARCHITECTURE.md
  • INTERFACE_DESIGN.md
  • IMPLEMENTATION_PLAN.md
  • REFACTOR_PLAN.md