Stream responses. Call tools. Manage sessions. Ship to production.
npm install general-agent-sdk
Quick Start Β· Examples Β· API Reference Β· Documentation
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";
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.
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
}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;
}
}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."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`);
},
},
],
});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 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.
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 answerEach subagent gets its own independent message history and scoped tool access. The subagents tool is excluded from children to prevent infinite recursion.
// 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 }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
});Every file write creates an automatic checkpoint. Roll back anytime:
const checkpoints = await session.listCheckpoints();
await session.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