Stream responses. Call tools. Manage sessions. Ship to production.
npm install 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 │
└─────────────────────────────────────────────────────────┘
|
|
npm install general-agent-sdkexport ANTHROPIC_API_KEY="sk-ant-..."# Optional: use a proxy# export ANTHROPIC_BASE_URL="https://your-proxy.example.com"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.
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}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;}}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."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`);},},],});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.
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.
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 answerEach subagent gets its own independent message history and scoped tool access. The subagents tool is excluded from children to prevent infinite recursion.
// 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 }Long conversations don't overflow — the SDK compacts automatically:
awaitsession.maybeCompactByTokens({usedPctThreshold: 85,// trigger at 85% usagecooldownMs: 60_000,// min 60s between compactions});Every file write creates an automatic checkpoint. Roll back anytime:
constcheckpoints=awaitsession.listCheckpoints();awaitsession.restoreCheckpoint(checkpoints[0].id);| 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"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
# 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 | 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 |
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 |
MIT — built by BabelCloud