Skip to content

Repository files navigation

General Agent SDK

Build AI agents that actually do things.

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

npm versionLicense: MITTypeScriptNode.jsTests


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 toolsread, 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";importpathfrom"node:path";importosfrom"node:os";// Initialize the SDKconstsdk=awaitcreateGeneralAgentSdk({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: {asyncload(){returnnull;},asyncsave(){},asyncresolveSessionFile(id){returnpath.join(os.tmpdir(),`${id.sessionId}.jsonl`);},},});// Create a sessionconstsession=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 conversationforawait(consteventofsession.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;}}awaitsdk.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:

forawait(consteventofsession.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:

constsdk=awaitcreateGeneralAgentSdk({// ... other options ...hostedTools: [{name: "get_stock_price",description: "Get real-time stock price",inputSchema: {type: "object",properties: {symbol: {type: "string"}},required: ["symbol"],},},],});// Handle tool callsforawait(consteventofsession.streamTurn(userMessage)){if(event.kind==="hosted_tool_call"){// SDK automatically suspends here ⏸️// You execute your logicconstprice=awaitfetchStockPrice(event.input.symbol);// Resume the agent with the result ▶️forawait(constresumedofsession.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 1awaitconsume(session.streamTurn({role: "user",content: [{type: "text",text: "My name is Alice and I like TypeScript."}],}));// Turn 2 — the agent remembers!awaitconsume(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:

constsdk=awaitcreateGeneralAgentSdk({// ...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:

ToolWhat it does
readRead file contents (with line ranges)
writeCreate or overwrite files
editSurgical file edits with diff
apply_patchApply unified diffs
execRun shell commands
web_searchSearch the web (DuckDuckGo / Brave)
web_fetchFetch and parse web pages
subagentsDelegate 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 processsession.setDynamicMcpServers({filesystem: {transport: "stdio",command: "npx",args: ["-y","@modelcontextprotocol/server-filesystem","/data"],},});// Remote HTTP endpointsession.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:

constsession=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

// Createconstsession=sdk.createSession({ ... });// Resume by IDconstresumed=awaitsdk.resumeSession("session-123");// Fork (branch from existing conversation)constforked=awaitsdk.forkSession("session-123",{ ... });// List all sessionsconstsessions=awaitsdk.listSessions();// Read transcript historyconsthistory=awaitsdk.readSessionHistory("session-123");// Reset (clear history, keep config)awaitsession.reset("starting_fresh");// Check token usageconstusage=session.getUsageSnapshot();// → { usedInputTokens: 1234, contextWindow: 200000, usedPct: 0.6 }

Context Compaction

Long conversations don't overflow — the SDK compacts automatically:

awaitsession.maybeCompactByTokens({usedPctThreshold: 85,// trigger at 85% usagecooldownMs: 60_000,// min 60s between compactions});

File Checkpoints

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

constcheckpoints=awaitsession.listCheckpoints();awaitsession.restoreCheckpoint(checkpoints[0].id);

📖 Documentation

ResourceDescription
SDK DOCS/README.mdFull documentation index
SDK DOCS/API-REFERENCE.mdComplete API reference
SDK DOCS/01-hello-world.tsYour first agent
SDK DOCS/02-multi-turn-chat.tsInteractive multi-turn REPL
SDK DOCS/03-hosted-tools.tsCustom tool integration
SDK DOCS/04-session-lifecycle.tsSession management
SDK DOCS/05-hooks.tsLifecycle hooks
SDK DOCS/06-mcp-servers.tsMCP server integration
SDK DOCS/07-compaction.tsContext window management
SDK DOCS/08-subagents.tsSubagent 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

EventPayloadWhen
assistant_delta{ text }Each streaming text chunk
reasoning_delta{ text }Model thinking (extended thinking)
reasoning_endThinking 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)
HookCan modify?Description
before_model_resolveOverride model selection
before_prompt_buildInject context into prompts
before_agent_startFinal pre-run modifications
llm_inputObserve LLM request
llm_outputObserve LLM response + usage
agent_endRun completed
before_tool_callModify args or block execution
after_tool_callObserve tool result
tool_result_persistModify persisted tool result
before_message_writeModify or block transcript writes
session_startSession first used
session_endSession done
before_compactionCompaction starting
after_compactionCompaction finished
before_resetSession about to reset
subagent_spawningBlock subagent creation
subagent_delivery_targetOverride delivery routing
subagent_spawnedChild agent created
subagent_endedChild agent finished
7 Host-bridged hooks (triggered via sdk.emitHook())
HookDescription
inbound_claimIncoming message routing
before_dispatchPre-dispatch filtering
message_receivedMessage received
message_sendingModify/cancel outgoing messages
message_sentMessage delivery confirmation
gateway_startGateway lifecycle
gateway_stopGateway shutdown

License

MIT — built by BabelCloud

About

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

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages