Skip to content

babelcloud/general-agent-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

63 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

General Agent SDK

Build AI agents that actually do things.

Stream responses. Call tools. Manage sessions. Ship to production.

npm version License: MIT TypeScript Node.js Tests


npm install general-agent-sdk

Quick Start Β· Examples Β· API Reference Β· Documentation


Why General Agent SDK?

Most "agent frameworks" give you wrappers around chat completions. General Agent SDK gives you a full execution kernel β€” the agent runs tools autonomously, suspends for human input, resumes across restarts, and streams every event back to your app in real time.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Your App (Host)                                        β”‚
β”‚                                                         β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚   β”‚  General Agent SDK                               β”‚  β”‚
β”‚   β”‚                                                  β”‚  β”‚
β”‚   β”‚   User ──→ LLM ──→ Tool ──→ LLM ──→ Tool ──→ βœ… β”‚  β”‚
β”‚   β”‚              β”‚        ↑        β”‚        ↑        β”‚  β”‚
β”‚   β”‚              β”‚   built-in      β”‚   hosted tool   β”‚  β”‚
β”‚   β”‚              β”‚   (read, exec,  β”‚   (your code)   β”‚  β”‚
β”‚   β”‚              β”‚    web_search)  β”‚                  β”‚  β”‚
β”‚   β”‚              β”‚                 β”‚                  β”‚  β”‚
β”‚   β”‚              └── stream events back to host ──→  β”‚  β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                         β”‚
β”‚   You control: credentials, persistence, tools, hooks   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

What it does

  • Autonomous tool loops β€” agent calls tools and continues thinking until done
  • 8 built-in tools β€” read, write, edit, exec, web_search, web_fetch, apply_patch, subagents
  • Hosted tools β€” define your own tools, SDK suspends & resumes
  • Multi-turn memory β€” sessions remember across turns automatically
  • Real-time streaming β€” every token, tool call, and result as events
  • MCP integration β€” plug in any MCP server (stdio or HTTP)
  • 26 lifecycle hooks β€” intercept anything from model selection to tool execution
  • Subagent delegation β€” spawn child agents with scoped instructions

What makes it different

  • Not a wrapper β€” it's a complete agent execution engine
  • Host-owned β€” you control persistence, credentials, and policy
  • Restart-safe β€” hosted tool pauses survive process restarts
  • Production-ready β€” context compaction, file checkpoints, error boundaries
  • Type-safe β€” full TypeScript with zero any in public API
  • Tested β€” 133 tests + real API E2E verification
  • Lightweight β€” ~180KB packaged, 6 dependencies
  • Escape hatch friendly β€” use as much or as little as you need

⚑ Quick Start

1. Install

npm install general-agent-sdk

2. Set your API key

export ANTHROPIC_API_KEY="sk-ant-..."

# Optional: use a proxy
# export ANTHROPIC_BASE_URL="https://your-proxy.example.com"

3. Build your first agent

import { createGeneralAgentSdk } from "general-agent-sdk";
import { randomUUID } from "node:crypto";
import path from "node:path";
import os from "node:os";

// Initialize the SDK
const sdk = await createGeneralAgentSdk({
  workspaceDir: process.cwd(),
  stateDir: path.join(process.cwd(), ".agent-state"),
  agentDir: path.join(process.cwd(), ".agent"),
  profileId: "default",
  pluginMode: "disabled",
  logger: {
    onDebug() {}, onInfo() {}, onWarn() {}, onError() {},
  },
  sessionStore: {
    async load() { return null; },
    async save() {},
    async resolveSessionFile(id) {
      return path.join(os.tmpdir(), `${id.sessionId}.jsonl`);
    },
  },
});

// Create a session
const session = sdk.createSession({
  identity: { mode: "general", sessionId: randomUUID(), sessionKey: "my-app:default" },
  systemPrompt: "You are a helpful assistant.",
  modelRef: "claude-sonnet-4-20250514",
  sessionFile: path.join(os.tmpdir(), "session.jsonl"),
});

// Stream a conversation
for await (const event of session.streamTurn({
  role: "user",
  content: [{ type: "text", text: "What files are in the current directory?" }],
})) {
  switch (event.kind) {
    case "assistant_delta":
      process.stdout.write(event.text);
      break;
    case "tool_call":
      console.log(`\nπŸ”§ ${event.toolName}(${JSON.stringify(event.input)})`);
      break;
    case "tool_result":
      console.log(`βœ… Done`);
      break;
    case "turn_complete":
      console.log(`\n\nβœ… Finished (${event.stopReason})`);
      break;
  }
}

await sdk.shutdown();

That's it. The agent will autonomously read the directory, think about the results, and give you a formatted answer β€” all streamed in real time.


🎯 Core Concepts

The Event Stream

Every interaction returns an AsyncIterable<GeneralAgentStreamEvent>. No callbacks, no observers β€” just a for await loop:

for await (const event of session.streamTurn(input)) {
  // event.kind tells you what happened:
  //
  //   "assistant_delta"    β†’ streaming text chunk
  //   "reasoning_delta"    β†’ model thinking (extended thinking)
  //   "tool_call"          β†’ agent is calling a built-in tool
  //   "tool_result"        β†’ tool returned a result
  //   "hosted_tool_call"   β†’ YOUR tool was requested (SDK suspends)
  //   "usage_snapshot"     β†’ token usage update
  //   "turn_complete"      β†’ this turn is done
}

Hosted Tools β€” Your Code, Their Brain

Define tools that the AI can call. You implement the logic, the SDK handles the orchestration:

const sdk = await createGeneralAgentSdk({
  // ... other options ...
  hostedTools: [
    {
      name: "get_stock_price",
      description: "Get real-time stock price",
      inputSchema: {
        type: "object",
        properties: { symbol: { type: "string" } },
        required: ["symbol"],
      },
    },
  ],
});

// Handle tool calls
for await (const event of session.streamTurn(userMessage)) {
  if (event.kind === "hosted_tool_call") {
    // SDK automatically suspends here ⏸️

    // You execute your logic
    const price = await fetchStockPrice(event.input.symbol);

    // Resume the agent with the result ▢️
    for await (const resumed of session.submitHostedToolResult({
      callId: event.callId,
      output: { price, currency: "USD" },
    })) {
      if (resumed.kind === "assistant_delta") process.stdout.write(resumed.text);
    }
    break;
  }
}

Multi-Turn Sessions

Sessions automatically maintain conversation history. The agent remembers everything:

// Turn 1
await consume(session.streamTurn({
  role: "user",
  content: [{ type: "text", text: "My name is Alice and I like TypeScript." }],
}));

// Turn 2 β€” the agent remembers!
await consume(session.streamTurn({
  role: "user",
  content: [{ type: "text", text: "What's my name and what do I like?" }],
}));
// β†’ "Your name is Alice and you like TypeScript."

Hooks β€” Intercept Everything

26 hooks let you observe, modify, or block any part of the agent lifecycle:

const sdk = await createGeneralAgentSdk({
  // ...
  hooks: [
    // Dynamically switch models
    {
      pluginId: "my-app",
      hookName: "before_model_resolve",
      handler: (event) => ({
        modelOverride: isComplexTask(event.prompt)
          ? "claude-opus-4-20250514"
          : "claude-sonnet-4-20250514",
      }),
    },
    // Block dangerous tool calls
    {
      pluginId: "my-app",
      hookName: "before_tool_call",
      handler: (event) => {
        if (event.toolName === "exec" && event.params.command?.includes("rm -rf")) {
          return { block: true, blockReason: "Dangerous command blocked" };
        }
      },
    },
    // Audit all LLM calls
    {
      pluginId: "my-app",
      hookName: "llm_output",
      handler: (event) => {
        console.log(`[audit] ${event.model}: ${event.usage?.input}in/${event.usage?.output}out tokens`);
      },
    },
  ],
});

🧰 Built-in Tools

The agent comes pre-loaded with powerful tools:

Tool What it does
read Read file contents (with line ranges)
write Create or overwrite files
edit Surgical file edits with diff
apply_patch Apply unified diffs
exec Run shell commands
web_search Search the web (DuckDuckGo / Brave)
web_fetch Fetch and parse web pages
subagents Delegate tasks to child agents

The agent decides which tools to use. You can restrict available tools per session, and every tool call flows through the before_tool_call / after_tool_call hooks.


πŸ”Œ MCP Integration

Plug in any Model Context Protocol server:

// Local process
session.setDynamicMcpServers({
  filesystem: {
    transport: "stdio",
    command: "npx",
    args: ["-y", "@modelcontextprotocol/server-filesystem", "/data"],
  },
});

// Remote HTTP endpoint
session.setDynamicMcpServers({
  my_api: {
    transport: "http",
    url: "https://mcp.example.com/api",
    headers: { Authorization: "Bearer token" },
  },
});

MCP tools show up alongside built-in tools. The agent uses them seamlessly.


πŸ€– Subagents

The agent can spawn child agents to divide and conquer:

const session = sdk.createSession({
  // ...
  systemPrompt: `You are a project manager.
    Use the subagents tool to delegate tasks to specialists.`,
});

// The agent will autonomously:
// 1. Break the task into subtasks
// 2. Spawn child agents with scoped instructions
// 3. Collect results
// 4. Synthesize a final answer

Each subagent gets its own independent message history and scoped tool access. The subagents tool is excluded from children to prevent infinite recursion.


πŸ“Š Session Management

// Create
const session = sdk.createSession({ ... });

// Resume by ID
const resumed = await sdk.resumeSession("session-123");

// Fork (branch from existing conversation)
const forked = await sdk.forkSession("session-123", { ... });

// List all sessions
const sessions = await sdk.listSessions();

// Read transcript history
const history = await sdk.readSessionHistory("session-123");

// Reset (clear history, keep config)
await session.reset("starting_fresh");

// Check token usage
const usage = session.getUsageSnapshot();
// β†’ { usedInputTokens: 1234, contextWindow: 200000, usedPct: 0.6 }

Context Compaction

Long conversations don't overflow β€” the SDK compacts automatically:

await session.maybeCompactByTokens({
  usedPctThreshold: 85,  // trigger at 85% usage
  cooldownMs: 60_000,    // min 60s between compactions
});

File Checkpoints

Every file write creates an automatic checkpoint. Roll back anytime:

const checkpoints = await session.listCheckpoints();
await session.restoreCheckpoint(checkpoints[0].id);

πŸ“– Documentation

Resource Description
SDK DOCS/README.md Full documentation index
SDK DOCS/API-REFERENCE.md Complete API reference
SDK DOCS/01-hello-world.ts Your first agent
SDK DOCS/02-multi-turn-chat.ts Interactive multi-turn REPL
SDK DOCS/03-hosted-tools.ts Custom tool integration
SDK DOCS/04-session-lifecycle.ts Session management
SDK DOCS/05-hooks.ts Lifecycle hooks
SDK DOCS/06-mcp-servers.ts MCP server integration
SDK DOCS/07-compaction.ts Context window management
SDK DOCS/08-subagents.ts Subagent delegation

All examples are runnable β€” just set your API key and go:

export ANTHROPIC_API_KEY="sk-ant-..."
npx tsx "SDK DOCS/01-hello-world.ts"

πŸ—οΈ Architecture

general-agent-sdk/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ index.ts              β†’ Package entry point
β”‚   β”œβ”€β”€ public/               β†’ Stable public API types
β”‚   β”‚   β”œβ”€β”€ sdk.ts            β†’ createGeneralAgentSdk()
β”‚   β”‚   β”œβ”€β”€ session.ts        β†’ GeneralAgentSession interface
β”‚   β”‚   β”œβ”€β”€ events.ts         β†’ Stream event types
β”‚   β”‚   β”œβ”€β”€ hooks.ts          β†’ 26 hook definitions
β”‚   β”‚   β”œβ”€β”€ types.ts          β†’ Shared types
β”‚   β”‚   β”œβ”€β”€ host-tools.ts     β†’ Hosted tool types
β”‚   β”‚   └── persistence.ts    β†’ Storage adapter
β”‚   β”œβ”€β”€ core/                 β†’ Runtime implementation
β”‚   β”‚   β”œβ”€β”€ embedded-runner/  β†’ Session + factory
β”‚   β”‚   β”œβ”€β”€ compaction/       β†’ Context compaction
β”‚   β”‚   β”œβ”€β”€ mcp/              β†’ MCP client (stdio + http)
β”‚   β”‚   β”œβ”€β”€ model/            β†’ Model context windows
β”‚   β”‚   β”œβ”€β”€ plugins/          β†’ Hook runner
β”‚   β”‚   β”œβ”€β”€ sessions/         β†’ Metadata + transcript repair
β”‚   β”‚   └── checkpoints/      β†’ File checkpoint manager
β”‚   β”œβ”€β”€ tools/                β†’ Built-in tool implementations
β”‚   β”œβ”€β”€ loop/                 β†’ Agent execution loop
β”‚   └── providers/            β†’ LLM provider adapters
β”œβ”€β”€ SDK DOCS/                 β†’ Examples + API reference
β”œβ”€β”€ tests/                    β†’ 133 tests (unit/integration/contract/e2e)
└── manifests/                β†’ Upstream provenance tracking

πŸ”§ Development

# Install
pnpm install

# Type check
pnpm run check

# Build
pnpm run build

# Run tests
pnpm run test          # 133 unit + integration tests
pnpm run test:e2e      # package smoke test

# Verify upstream provenance
node scripts/verify-upstream-snapshot.mjs

πŸ“‹ Event Reference

Event Payload When
assistant_delta { text } Each streaming text chunk
reasoning_delta { text } Model thinking (extended thinking)
reasoning_end β€” Thinking complete
tool_call { callId, toolName, input } Built-in tool invoked
tool_result { callId, toolName, output } Tool returned result
tool_error { callId, toolName, error } Tool failed
hosted_tool_call { callId, toolName, input } Your tool requested (SDK suspends)
usage_snapshot { snapshot } Token usage update
compaction_started { reason } Context compaction begins
compaction_finished { reason, tokensAfter? } Compaction complete
turn_complete { stopReason } Turn finished

πŸͺ Hook Reference

19 SDK-native hooks (auto-fired by runtime)
Hook Can modify? Description
before_model_resolve βœ… Override model selection
before_prompt_build βœ… Inject context into prompts
before_agent_start βœ… Final pre-run modifications
llm_input β€” Observe LLM request
llm_output β€” Observe LLM response + usage
agent_end β€” Run completed
before_tool_call βœ… Modify args or block execution
after_tool_call β€” Observe tool result
tool_result_persist βœ… Modify persisted tool result
before_message_write βœ… Modify or block transcript writes
session_start β€” Session first used
session_end β€” Session done
before_compaction β€” Compaction starting
after_compaction β€” Compaction finished
before_reset β€” Session about to reset
subagent_spawning βœ… Block subagent creation
subagent_delivery_target βœ… Override delivery routing
subagent_spawned β€” Child agent created
subagent_ended β€” Child agent finished
7 Host-bridged hooks (triggered via sdk.emitHook())
Hook Description
inbound_claim Incoming message routing
before_dispatch Pre-dispatch filtering
message_received Message received
message_sending Modify/cancel outgoing messages
message_sent Message delivery confirmation
gateway_start Gateway lifecycle
gateway_stop Gateway shutdown

License

MIT β€” built by BabelCloud

About

TypeScript SDK for building AI agents that call tools, manage sessions, and ship to production.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors