TypeScript implementation of the AI Agent framework from Manning Publications' Build an AI Agent from Scratch.
This is a complete TypeScript port of the Python scratch_agents package, providing the same functionality with TypeScript idioms and type safety.
scratch_agents_ts/
├── package.json # npm package configuration
├── tsconfig.json # TypeScript configuration
├── src/
│ ├── index.ts # Main exports
│ ├── types.ts # Message, ToolCall, ToolResult, Event, ContentItem
│ ├── context.ts # ExecutionContext, AgentResult, PendingToolCall, ToolConfirmation
│ ├── llm.ts # LlmRequest, LlmResponse, LlmClient
│ ├── agent.ts # Agent (ReAct loop)
│ ├── rag.ts # Embeddings, chunking, vector search
│ ├── callbacks.ts # approvalCallback, createSearchCompressor
│ ├── planning.ts # Task, createTasksTool, reflectionTool
│ ├── skills.ts # SkillInfo, discoverSkills, generateSkillsPrompt
│ ├── transfer.ts # createTransferTool
│ ├── remote.ts # RemoteAgent (A2A)
│ ├── a2a_server.ts # MathAgentExecutor
│ ├── utils.ts # displayTrace, flattenEvents
│ ├── tools/
│ │ ├── index.ts
│ │ ├── base.ts # BaseTool, FunctionTool, createTool
│ │ ├── helpers.ts # formatToolDefinition, zodToJsonSchema
│ │ ├── calculator.ts # Calculator tool
│ │ ├── search.ts # Web search (Tavily)
│ │ ├── file_tools.ts # File operations
│ │ ├── code_execution.ts # E2B sandbox tools
│ │ ├── memory_tool.ts # Memory injection
│ │ ├── agent_tool.ts # AgentTool wrapper
│ │ └── mcp.ts # MCP integration
│ ├── memory/
│ │ ├── index.ts
│ │ ├── session.ts # Session, BaseSessionManager, InMemorySessionManager
│ │ ├── long_term.ts # TaskMemory, TaskMemoryManager
│ │ └── context_optimizer.ts # ContextOptimizer, sliding window, compaction
│ ├── workflows/
│ │ ├── index.ts
│ │ ├── sequential.ts # SequentialWorkflow
│ │ ├── parallel.ts # ParallelWorkflow
│ │ └── loop.ts # LoopWorkflow
│ └── eval/
│ ├── index.ts
│ ├── gaia.ts # GAIA benchmark evaluation
│ └── prompts.ts # Evaluation prompts
└── dist/ # Compiled output
# Install dependencies
npm install
# Build the project
npm run build
# Type check
npm run typecheckSet the following environment variables:
OPENAI_API_KEY=sk-... # Required for LLM calls
TAVILY_API_KEY=tvly-... # Required for web searchimport { Agent, LlmClient, calculator, searchWeb } from "scratch-agents-ts";
// Create LLM client
const client = new LlmClient("gpt-4o-mini");
// Create agent with tools
const agent = new Agent({
model: client,
tools: [calculator, searchWeb],
instructions: "You are a helpful assistant.",
maxSteps: 10,
});
// Run the agent
const result = await agent.run({ userInput: "What is 2 + 2?" });
console.log(result.output);| Module | Description |
|---|---|
types.ts |
Core types with Zod schemas for validation |
context.ts |
Execution state management |
llm.ts |
LLM API abstraction using OpenAI SDK |
agent.ts |
Main Agent class with ReAct loop |
tools/base.ts |
BaseTool abstract class and FunctionTool |
memory/ |
Session and long-term memory systems |
workflows/ |
Sequential, parallel, and loop workflows |
rag.ts |
Embeddings, chunking, and vector search |
| Python | TypeScript |
|---|---|
pydantic.BaseModel |
zod.Schema + TypeScript type |
@dataclass |
TypeScript class |
ABC (abstract class) |
abstract class |
@tool decorator |
createTool() factory |
TYPE_CHECKING |
import type |
asyncio.gather() |
Promise.all() |
litellm |
OpenAI SDK |
import { FunctionTool, formatToolDefinition } from "scratch-agents-ts";
const myTool = new FunctionTool(
async (args: { input: string }) => {
return `Processed: ${args.input}`;
},
{
name: "my_tool",
description: "Process input string",
toolDefinition: formatToolDefinition(
"my_tool",
"Process input string",
{
type: "object",
properties: {
input: { type: "string", description: "Input to process" },
},
required: ["input"],
}
),
}
);import { z } from "zod";
import { Agent, LlmClient } from "scratch-agents-ts";
const OutputSchema = z.object({
answer: z.string(),
confidence: z.number(),
});
const agent = new Agent({
model: new LlmClient("gpt-4o-mini"),
outputType: OutputSchema,
});
const result = await agent.run({ userInput: "What is the capital of France?" });
// result.output is validated against OutputSchemaimport { SequentialWorkflow, ParallelWorkflow, LoopWorkflow } from "scratch-agents-ts";
// Sequential: run agents one after another
const sequential = new SequentialWorkflow({ agents: [agent1, agent2] });
// Parallel: run agents concurrently
const parallel = new ParallelWorkflow({ agents: [agent1, agent2] });
// Loop: run until stop condition
const loop = new LoopWorkflow({
agents: [agent],
stopCondition: (result, iteration) => result.output !== undefined,
maxIterations: 10,
});import { InMemorySessionManager, TaskMemoryManager } from "scratch-agents-ts";
// Session management for multi-turn conversations
const sessionManager = new InMemorySessionManager();
const agent = new Agent({
model: client,
sessionManager,
});
// Run with session ID
await agent.run({ userInput: "Hello", sessionId: "user-123" });
await agent.run({ userInput: "What did I say?", sessionId: "user-123" });| Chapter | Topic | Key Modules |
|---|---|---|
| CH02 | LLM API Basics | eval/gaia.ts |
| CH03 | Tools and Function Calling | tools/helpers.ts, tools/calculator.ts, tools/search.ts |
| CH04 | ReAct Agent | types.ts, context.ts, llm.ts, agent.ts, tools/base.ts |
| CH05 | RAG and File Tools | rag.ts, callbacks.ts, tools/file_tools.ts |
| CH06 | Memory Systems | memory/session.ts, memory/long_term.ts, memory/context_optimizer.ts |
| CH07 | Planning and Reflection | planning.ts |
| CH08 | Code Execution | tools/code_execution.ts, skills.ts |
| CH09 | Multi-Agent Systems | workflows/, transfer.ts, tools/agent_tool.ts |
| CH10 | Evaluation | eval/prompts.ts |
zod- Schema validationopenai- OpenAI SDK for LLM calls@anthropic-ai/sdk- Anthropic SDK (optional)@modelcontextprotocol/sdk- MCP protocol supportuuid- UUID generation
npm run build # Compile TypeScript
npm run dev # Watch mode
npm run typecheck # Type checking only
npm run test # Run tests (vitest)