smalltalk-agent

Description

Agent loop that lives inside a live Smalltalk/Pharo image — drives the entire environment via a single 'send' tool + OpenRouter

Details

Source
GitHub
Dialect
pharo (25% confidence)
License
MIT
Created
July 29, 2026
Updated
July 29, 2026

README excerpt

# smalltalk-agent

An agent loop that lives **inside** a live Smalltalk/Pharo image. The agent
communicates with [OpenRouter](https://openrouter.ai) and uses a single
`send` tool to evaluate Smalltalk expressions — driving the entire environment
as a live, persistent workspace.

## Why?

Most agent-coding setups work against dead text (files on disk). Smalltalk
dissolves the boundary between "code" and "running system": everything is a
live object, changes are instant, the image persists, and tools (debugger,
inspector, browser) are objects you can manipulate programmatically. The
agent's work product isn't a pile of files — it's a live running system.



## Bootstrap on a fresh Pharo 14 image

### 1. Get an OpenRouter API key

Go to https://openrouter.ai/keys and create a key.

### 2. Clone this repo and load the code

Open a **Playground** in your fresh Pharo 14 image and evaluate:

```smalltalk
Metacello new
  baseline: 'SmalltalkAgent';
  repository: 'github://hermclaw/smalltalk-agent:main/src';
  load.
```

This downloads the code via Iceberg (Pharo's git integration) and loads
`SmalltalkAgent` (the core) plus its dependencies (Zinc HTTP, NeoJSON).

### 3. Run the agent

```smalltalk
loop := SAAgentLoop new.
loop apiKey: 'sk-or-v1-...your-key...'.
loop model: 'anthropic/claude-sonnet-4'.  "or any OpenRouter model"

"Give it a task — it will build it live in the image:"
loop run: 'Create a simple Counter class with increment/decrement methods, then open a Spec 2 window with two buttons and a label display that uses it.'
```

Watch the **Transcript** for progress. The agent will:
1. Inspect the environment (send expressions, read results)
2. Create classes and methods (live, no files)
3. Open UI windows (Spec 2)
4. Test what it built
5. Fix errors by reading the serialized exception output

## What's in here

| File | What |
|---|---|
| `BaselineOfSmalltalkAgent` | Metacello baseline — loads dependencies + core |
| `SAOpenRouterClient` | Zinc HTTP + NeoJSON client for OpenRouter `/chat/completions` |
| `SAWorkspace` | Live evaluation context with persistent variable bindings |
| `SASerializer` | Depth-limited object → text serialization for LLM consumption |
| `SAAgentLoop` | The loop: conversation history → LLM → tool dispatch → repeat |
| `SAAgentLoopTests` | Smoke tests for workspace, serializer, exception handling |

## Architecture

```
┌─────────────────────────────────────────┐
│  SAAgentLoop (Pharo Process)            │
│                                         │
│  messages ──► SAOpenRouterClient        │
│                  POST /chat/completions │
│                  with `send` tool def   │
│                       │                 │
│                       ▼                 │
│  tool_calls ──► SAWorkspace.evaluate   │
│                  (catch all exceptions) │
│                       │                 │
│                       ▼                 │
│  SASerializer ──► text result ──► LLM  │
│                                         │
│  THE LIVE PHARO IMAGE                   │
│  (every object, every class, everything)│
└─────────────────────────────────────────┘
```

## The `send` tool

The agent has exactly **one tool**: `send`, which evaluates a Smalltalk
expression. That's it. The entire Smalltalk environment is the tool surface —

- Create a class → `MyApp subclass: #Counter instanceVariableNames: 'value'`
- Add a method → `Counter compile: 'increment ^ value := value + 1'`
- Inspect an object → `Counter new inspect`
- Open a window → `SpApplication new start`
- Read the Transcript → `Transcript contents`

The agent discovers capabilities by inspecting objects, not by reading tool
schemas.

## Design notes

- **Exceptions never crash the loop.** When `send` fails, the exception is
  serialized (class, message, stack frames) and returned as the tool result.
  The agent reads the error and iterates — like a human with a debugger.
- **Workspace persistence.** Variables bound in one `send` call are available
  in the next (`x := 42` then `x + 1` works).
- **Serialization limits.** `SASerializer` has configurable depth (default 3),
  item count (default 20), and string length (default 2000) to prevent huge
  objects from flooding the LLM context window.
- **One tool.** Start minimal. If the agent struggles, add `inspect:`,
  `browse:`, `references:` — but a capable model can do all of that through
  `send` alone.

## License

MIT

## Author

[Hermclaw](https://github.com/hermclaw)
← Back to results