A production-ready, cloud-native agent service deployed on AWS, built with Deep Agents on LangGraph.js, and exposed as a service through the Agent Client Protocol (ACP) standard for IDE integration (like Zed IDE) or REST/WebSocket clients.
The service is a single agent — The Brain — that guides you (as Pinky) through a topic and then either teaches it or writes an article about it.
- Cloud Architecture: Deployed on an AWS Lightsail container service via Terraform, packaged inside Docker containers and fronted by a CloudFront CDN.
- Deep Agent: One
createDeepAgentagent with a persona, custom tools, and a guided conversation flow. - Anthropic Claude: The only supported LLM provider (
ANTHROPIC_API_KEY, defaultclaude-sonnet-5,ANTHROPIC_MAX_TOKENSoutput tokens per reply, default 16000). - Local SQLite Checkpointing: Thread states and checkpoints are saved locally via a custom SQLite checkpointer optimized for performance (using WAL mode).
- Grounded Retrieval (RAG): Explanations and articles are drawn from a pre-compiled store of curated knowledge, retrieved by BM25 fused with vector similarity and returned with the source document and section attached. Measured on a labelled query set at 0.900 recall@10, against 0.597 for the substring matching it replaced — see the Retrieval guide.
- Multiple Entrypoints: Exposes Stdin/Stdout ACP, a REPL CLI, an Express REST API with Server-Sent Events (SSE) streaming support, and a Model Context Protocol (MCP) server.
One Deep Agent, assembled in agent.ts from a model, tools, and a system prompt. The division of labour is deliberate: the model owns the conversation, the tools own the truth — topics, subtopics, and source material are always read from the knowledge store, never invented.
- Persona (
persona.ts): The Brain's voice — eloquent, imperious, addressing the user as Pinky. The persona never overrides technical accuracy. - Journey (
prompts.ts): The conversation flow (greet → topic → subtopic → learn or write an article → repeat) and the teaching loop (decompose → explain → test → re-explain until understood). - Writing standard (
prompts.ts):ARTICLE_CRAFT_PROMPT— the rules every article is written to, distilled from five sources on article craft, plus the layout marks that give a post its shape and the signature that closes it. See the Article Writing Guide. - Layout (
layout.ts): turns an article's own marks — an###deck,:::note|tip|warncallouts,figures — into the blog's MDX. Pure transforms, no I/O. - Tools (
tools.ts):list_topics,list_subtopics,retrieve_content,save_article,update_article,read_article,publish_article,export_article. - Delivery (
artifacts.ts): through the REST server, files a tool writes exist only inside the container, so every write is also recorded as a downloadable artifact, announced on the run's SSE stream and served viaGET /threads/:id/artifacts/:name.patb-clidownloads these to real local disk. See Agent Flow. - Compatibility (
graph.ts):runGraphWorkflow()— the single function every entrypoint calls.
Topics of expertise, read from the knowledge store:
- AWS Cloud Practitioner Certification: Prep materials for the CLF-C02 exam.
- Cellular Automata: Conway's Game of Life, Wolfram's elementary automata, Lenia, particle life.
- English for Certifications: Coaching for IELTS, TOEFL, and Cambridge exams.
- Technical Interview Preparation: Role-based roadmaps for frontend/backend software engineering roles.
Articles are written to ./articles/ as markdown — carrying their own layout and their signature, so what you review is the shape the post will have — and published to the blog through the articles MCP server that ships inside thiagocolen.github.io. Publishing renders that layout, generates a cover image and one illustration per figure, and files the result as a draft. The blog owns its own post format, so this agent is a client of it rather than a second opinion about it. See Agent Flow for the full journey diagram and design rationale.
- SQLite Checkpointer (
src/storage/sqlite.ts): Extends LangGraph'sBaseCheckpointSaverto persist thread history locally insidestate.db. Optimized using SQL PRAGMAs (WAL,synchronous=OFF,temp_store=MEMORY). - AWS S3 Storage (
src/storage/s3.ts): Used to persist state in cloud environments, with an automatic local in-memory fallback for offline/local development. - Knowledge Store (
src/storage/knowledge-store.json): 5,166 pre-compiled, paragraph-level chunks of curated source documents, each carrying a content-addressed id, its area, and the file and heading it came from. Rebuilt withnpm run ingest. - Embeddings (
src/storage/embeddings.bin): One quantised 256-dimension vector per chunk (~1.3MB), built during ingest when a Gemini key is present. Optional by design — without them retrieval runs on BM25 alone.
pinky-and-the-brain/
├── docs/ # Architecture and Specifications records
├── terraform/ # AWS Cloud IaC Configurations
│ ├── main.tf # ECR, Lightsail container service + deployment, CloudFront
│ ├── variables.tf # Deployment settings & regional variables
│ ├── outputs.tf # AWS service endpoints (Lightsail/CloudFront/ECR)
│ └── aws-permissions.json # Single IAM policy covering the create, report and cleanup/teardown scripts
├── src/
│ ├── index.ts # Main readline CLI (ACP Stdin/Stdout Entrypoint)
│ ├── cli.ts # Standalone interactive REPL CLI for local testing
│ ├── server.ts # Express REST API (HTTP, SSE Streaming, Slack/Teams webhooks)
│ ├── mcp.ts # Model Context Protocol (MCP) server
│ ├── graph-sdk.ts # SDK exports for modular reuse of the graph engine
│ ├── config.ts # Configuration parser & Zod schema validator
│ ├── agents/ # The Brain (deep agent)
│ │ ├── types.ts # Run state, progress & BrainAgent interface
│ │ ├── agent.ts # createDeepAgent assembly (model + tools + prompt)
│ │ ├── graph.ts # runGraphWorkflow compatibility layer
│ │ ├── persona.ts # The Brain's voice & catchphrases
│ │ ├── prompts.ts # Journey state machine & teaching loop
│ │ ├── layout.ts # Article layout → the blog's MDX
│ │ ├── tools.ts # Topics, subtopics, retrieval, article files
│ │ └── artifacts.ts # Tool-written files recorded for remote delivery (REST server)
│ ├── protocol/ # ACP JSON-RPC standard parsing
│ │ ├── acp-server.ts # ACP Protocol handler
│ │ └── messages.ts # Validation schemas (Zod)
│ ├── storage/ # State persistence
│ │ ├── sqlite.ts # SQLiteCheckpointer extending LangGraph's BaseCheckpointSaver
│ │ ├── s3.ts # S3 Storage client wrapper (with offline local fallback)
│ │ ├── knowledge-store.json # Pre-compiled chunks: id, content, area, source, heading
│ │ └── embeddings.bin # One quantised 256d vector per chunk (optional)
│ └── utils/ # Shared utilities
│ ├── logger.ts # Centralized console and file logger (agent.log & stderr)
│ ├── messages.ts # Message helper functions
│ ├── model.ts # LLM factory (Anthropic Claude only)
│ ├── retrieval.ts # Tokenising, stemming, BM25, rank fusion (pure)
│ ├── embeddings.ts # Gemini vectors: quantise, cosine, binary store
│ ├── blog-mcp.ts # Client session against the blog's `articles` MCP server
│ ├── image-gen.ts # Cover & figure generation (soft-failing)
│ └── illustration-styles.ts # The style catalogue and the per-article pick
├── scripts/ # Deploy & operations scripts
│ ├── deploy.js # Deploy orchestration script (Docker build, ECR push, Terraform run)
│ ├── eval-retrieval.js # Scores the retrievers against a labelled query set
│ ├── report-infra.ps1 # PowerShell script for AWS infrastructure status audits
│ ├── create-infra.ps1 # PowerShell script provisioning the full AWS stack from nothing
│ ├── cleanup-infra.ps1 # PowerShell script deleting AWS resources - non-project, or all with -IncludeProject (dry run by default)
│ ├── tail-logs.js # Script to stream cloud container logs
│ └── test-tracing.js # Script to verify LangSmith tracing connection
├── package.json # Scripts & dependencies
├── tsconfig.json # TS compilation config
└── vitest.config.ts # Test runner config
- Node.js:
v20.xor higher - npm:
v10.xor higher
-
Clone the repository and navigate into the project directory:
cd pinky-and-the-brain -
Install dependencies:
npm install
-
Create a
.envfile in the root directory:# LLM API Key (Required - Anthropic is the only supported provider) ANTHROPIC_API_KEY=your_anthropic_api_key_here # ANTHROPIC_MODEL=claude-sonnet-5 # API Gateway security key (Required for server and clients) PATBA_API_KEY=your_secret_api_key_here # Local Storage SQLITE_DB_PATH=state.db PORT=8080 # AWS Configuration (Optional, falls back to local sqlite/memory offline) AWS_REGION=sa-east-1 S3_BUCKET_NAME=pinky-and-the-brain-agents-state-store # Optional integrations SLACK_BOT_TOKEN=your_slack_bot_token_here
Compile TypeScript into JavaScript:
npm run buildYou can execute the service locally under different operational interfaces:
Run the local agent directly in your command line:
npm run cliSay hello and The Brain takes it from there:
You: hello
Brain: Behold, Pinky, the four pillars of tonight's potential enlightenment:
1. AWS Cloud Practitioner Certification — ...
2. Cellular Automata — ...
...
You: 2
Brain: [summary of the topic, then its subtopics]
You: An Introduction to Conway's The Game of Life
Brain: What is your desire? 1. Learn about it 2. Write an article about it
You: write an article about it
Brain: Do you have any instructions for this article?
You: three paragraphs
Brain: The deed is done, Pinky! The article resides at:
.../articles/conways-game-of-life.md
Now, where shall it go? 1. Publish it to the blog
2. Save it to a folder 3. Neither
You: 1
Brain: [publishes as a draft, then reports the branch, the commit and the review URL]
Start the Express API gateway to listen for HTTP requests and stream progress via Server-Sent Events (SSE) (defaults to port 8080):
npm run serverRun the stdio-based MCP server to expose the agent to MCP clients (like Claude Desktop):
npm run mcpRun the raw Agent Client Protocol (ACP) JSON-RPC stdin/stdout server:
npm run startTo initialize a handshake, write this payload to stdin:
{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2026-06-24", "capabilities": {}, "clientInfo": {"name": "test"}}}Run the test suite using Vitest:
# Run unit tests
npm run test:unit
# Run integration tests
npm run test:integrationFor production setups or when you want to connect to a remote server without running the full agent orchestrator locally, you should use the Pinky and the Brain CLI (patb-cli).
patb-cli acts as a lightweight wrapper client and gateway that communicates with the cloud-hosted AWS agent service (located at d33ib4uu7f4xpi.cloudfront.net or any custom local/remote URL).
graph TD
A[User / Zed Editor] -->|Interactive Prompt or ACP RPC| B(patb-cli)
B -->|1. Create Thread| C[Remote Service]
B -->|2. Trigger Run| C
C -->|3. Event Stream + artifact announcements| B
B -->|4. Fetch artifact bytes| C
B -->|5. Write file to local disk| D[(Your filesystem)]
B -->|6. Format Output / Progress| A
The agent runs in a container, so a tool that writes with fs writes to that filesystem — a real path, on a disk you cannot reach. Rather than report such a path, the service publishes what it wrote as a retrievable artifact: the run's event stream announces the file, and patb-cli downloads it and writes it to your own disk, printing the path it used.
💾 Saved to D:\_code-projects\articles\game-of-life.md
By default files go to ./articles, relative to wherever you started the CLI. Both the filename and any folder the agent names come from the far side of the network, so writes are confined to that directory unless you opt out:
| Flag | Default | What it does |
|---|---|---|
--out-dir <path> |
./articles |
Directory files are written to (env: PATBA_OUT_DIR) |
--allow-any-path |
off | Permits a write outside --out-dir when you ask the agent for a specific folder |
--host <url> |
the deployed service | Point the client at a different service (env: PATBA_HOST) |
Running the agent locally (npm run cli, npm run mcp) is unaffected: there the tool's write really is on your machine, and it still reports the absolute path directly.
You can run it directly using npx or install it globally:
npm install -g @thiagocolen/patb-cliNavigate to the patb-cli project directory and build it:
cd D:/_code-projects/patb-cli
npm install
npm run build
npm link # optional, links 'patb-cli' command globallyThe CLI requires PATBA_API_KEY to authenticate requests with the remote service. Configure this key using one of the following methods:
- Local
.envFile: Create a.envfile in the folder where you run the CLI:PATBA_API_KEY=your_secret_api_key_here
- Environment Variables:
- Windows (PowerShell):
$env:PATBA_API_KEY="your_secret_api_key_here" - macOS/Linux:
export PATBA_API_KEY="your_secret_api_key_here"
- Windows (PowerShell):
- Interactive REPL Mode (default): Starts a chat session with the remote agent.
patb-cli # or if running from local source folder node dist/index.js - Zed ACP Bridge Mode: Starts the server in bridge mode, speaking JSON-RPC over
stdin/stdout.patb-cli --bridge # or node dist/index.js --bridge
You can configure Zed to use patb-cli as an external agent server.
This is the cleanest approach because you do not need to install the package globally or clone/compile any files locally. Zed will fetch and execute the package on demand.
- Open your Zed configuration file (
Ctrl+Shift+PorCmd+Shift+P->zed: open settings). - Add the custom agent server under the
agent_serversblock:
{
"agent_servers": {
"patb-agent": {
"type": "custom",
"command": "npx",
"args": ["-y", "@thiagocolen/patb-cli", "--bridge"],
"env": {
"PATBA_API_KEY": "your_secret_api_key_here"
}
}
}
}If you prefer to compile the CLI codebase locally:
- Open your Zed configuration file.
- Register the path to your compiled
dist/index.jsfile:
{
"agent_servers": {
"patb-agent": {
"type": "custom",
"command": "node",
"args": [
"D:/_code-projects/patb-cli/dist/index.js",
"--bridge"
],
"env": {
"PATBA_API_KEY": "your_secret_api_key_here"
}
}
}
}(Make sure to use absolute paths with forward slashes /, even on Windows).
- Open the Agent Panel in Zed (using the ✨ icon or shortcut
Cmd+?/Ctrl+?). - Open the thread settings dropdown.
- Select
patb-agentas your active agent. - Prompt the agent (e.g.,
"Design a layout for a RAG search service") and watch the streaming progress updates and responses!