teapot-coding-agent 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +178 -0
- package/dist/agent/agent.js +730 -0
- package/dist/agent/llm.js +63 -0
- package/dist/agent/skills.js +87 -0
- package/dist/agent/tools.js +269 -0
- package/dist/bus.js +4 -0
- package/dist/index.js +38 -0
- package/dist/log/events.js +132 -0
- package/dist/master.js +219 -0
- package/dist/scheduler/cron.js +81 -0
- package/dist/server/api.js +267 -0
- package/package.json +53 -0
- package/public/assets/index-CxlAQ_0i.css +1 -0
- package/public/assets/index-DkqgYCzJ.js +9 -0
- package/public/index.html +13 -0
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# teapot 🫖
|
|
2
|
+
|
|
3
|
+
A lightweight harness for running many AI coding agents continuously — simpler
|
|
4
|
+
and lighter than existing harnesses. No TUI. One small web server, a browser
|
|
5
|
+
tab, and any number of long-running agents.
|
|
6
|
+
|
|
7
|
+
## Design principles
|
|
8
|
+
|
|
9
|
+
- **TypeScript + Node.js**, minimal dependencies (`hono`, `openai`, `@hono/node-server`)
|
|
10
|
+
- **Idle cost ≈ 0**: no polling loops; everything is event-driven or driven by
|
|
11
|
+
one 15-second scheduler tick. Verified: master sits at ~0% CPU when idle.
|
|
12
|
+
- **No TUI** — Discord-style chat UI built with **SolidJS + Vite**
|
|
13
|
+
(`frontend/`, built to `public/`): agents as channels, events flowing as chat
|
|
14
|
+
messages, tool calls as compact embeds. Markdown is rendered by a hand-written,
|
|
15
|
+
XSS-safe renderer (`frontend/md.js`)
|
|
16
|
+
- **Human-readable persistence** — append-only JSONL event logs you can read
|
|
17
|
+
with `cat` / `jq`; goal & memory as plain Markdown files in git
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
pnpm install
|
|
23
|
+
mkdir -p ~/.config/teapot-coding-agent
|
|
24
|
+
cp teapot.config.example.json ~/.config/teapot-coding-agent/config.json # edit it
|
|
25
|
+
pnpm dev # or: pnpm build && pnpm start
|
|
26
|
+
# open http://localhost:7788
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Config lookup order: CLI arg → `$TEAPOT_CONFIG` →
|
|
30
|
+
`~/.config/teapot-coding-agent/config.json` → `./teapot.config.json` (legacy).
|
|
31
|
+
Data (event logs) goes to `~/.local/share/teapot-coding-agent` by default.
|
|
32
|
+
Env overrides: `TEAPOT_PORT`, `TEAPOT_API_KEY`, `TEAPOT_BASE_URL`,
|
|
33
|
+
`TEAPOT_MODEL`, `TEAPOT_CONFIG_DIR`, `TEAPOT_DATA_DIR`.
|
|
34
|
+
|
|
35
|
+
## Multiple providers
|
|
36
|
+
|
|
37
|
+
Agents are matched to named OpenAI-compatible providers:
|
|
38
|
+
|
|
39
|
+
```jsonc
|
|
40
|
+
{
|
|
41
|
+
"providers": {
|
|
42
|
+
"openrouter": { "baseUrl": "https://openrouter.ai/api/v1", "apiKey": "sk-or-..." },
|
|
43
|
+
"local": { "baseUrl": "http://localhost:11434/v1", "apiKey": "ollama", "model": "qwen3-coder" }
|
|
44
|
+
},
|
|
45
|
+
"defaultProvider": "openrouter",
|
|
46
|
+
"agents": [
|
|
47
|
+
{ "id": "alpha", "workspace": "workspaces/alpha" }, // default provider
|
|
48
|
+
{ "id": "beta", "workspace": "workspaces/beta", "provider": "local" }, // local model
|
|
49
|
+
{ "id": "gamma", "workspace": "workspaces/gamma", "provider": "openrouter",
|
|
50
|
+
"model": "anthropic/claude-sonnet-4" } // per-agent model
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Per-agent inline `baseUrl`/`apiKey`/`model` override the provider entry.
|
|
56
|
+
|
|
57
|
+
## Architecture
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
master (Hono server, src/master.ts + src/server/api.ts)
|
|
61
|
+
├── Agent alpha (workspace A) ── JSONL event log
|
|
62
|
+
├── Agent beta (workspace B) ── JSONL event log
|
|
63
|
+
└── scheduler tick (15s) → cron tasks → prompts on forked branches
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- **Agent** (`src/agent/agent.ts`): an async loop that alternates LLM turns and
|
|
67
|
+
tool executions until the model stops calling tools, then auto-continues
|
|
68
|
+
toward its goal. Stop/resume at any time via API/UI — stop aborts an
|
|
69
|
+
in-flight LLM call immediately (AbortController). Runaway guards: turn cap
|
|
70
|
+
per round, consecutive-tool-error cap, per-command timeouts.
|
|
71
|
+
- **Context compaction**: message history is token-estimated each turn; past a
|
|
72
|
+
budget (`contextTokenBudget`, default ~96k) older turns are summarized by
|
|
73
|
+
the LLM into dense continuation notes (fallback: safe truncation). Cut
|
|
74
|
+
points never split a tool_call/tool_result pair.
|
|
75
|
+
- **Session persistence**: conversations are rebuilt from the JSONL log on
|
|
76
|
+
restart (`restoreSession`), so agents resume mid-task across master
|
|
77
|
+
restarts instead of starting blank.
|
|
78
|
+
- **LLM** (`src/agent/llm.ts`): official `openai` npm client against any
|
|
79
|
+
OpenAI-compatible endpoint (OpenRouter, vLLM, Ollama...). Model is config,
|
|
80
|
+
never hard-coded.
|
|
81
|
+
- **Tools** (`src/agent/tools.ts`): provider-agnostic JSON-schema function
|
|
82
|
+
specs — `read_file`, `write_file`, `edit_file`, `list_dir`, `bash` (git goes
|
|
83
|
+
through bash), plus meta tools `finish` / `report_progress`. Paths are
|
|
84
|
+
confined to the workspace; bash runs detached in its own process group and
|
|
85
|
+
the whole group is SIGKILLed on timeout.
|
|
86
|
+
- **Goal / knowledge**: `GOAL.md` (goal + status), `AGENTS.md` (project
|
|
87
|
+
knowledge), `MEMORY.md` (agent notes) live in each workspace as normal git-
|
|
88
|
+
editable Markdown. The goal file is the source of truth; the harness re-reads
|
|
89
|
+
it on restart.
|
|
90
|
+
|
|
91
|
+
### Agent Skills
|
|
92
|
+
|
|
93
|
+
Skills are reusable playbooks the agent loads on demand — and writes itself,
|
|
94
|
+
so hard-won procedure knowledge survives sessions:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
<workspace>/skills/<name>/SKILL.md # project skills (git-friendly)
|
|
98
|
+
~/.config/teapot-coding-agent/skills/<name>/SKILL.md # shared across agents
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```markdown
|
|
102
|
+
---
|
|
103
|
+
name: release-checklist
|
|
104
|
+
description: Steps to cut a release safely
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
1. run pnpm test
|
|
108
|
+
2. bump version ...
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
- The system prompt lists every discovered skill (name + description);
|
|
112
|
+
workspace skills override same-named global ones.
|
|
113
|
+
- The agent calls `load_skill(name)` when a task matches and follows it.
|
|
114
|
+
- The agent calls `save_skill(name, description, content)` to distill a
|
|
115
|
+
reusable procedure it developed — available from the next turn, forever.
|
|
116
|
+
|
|
117
|
+
### Session log format (JSONL)
|
|
118
|
+
|
|
119
|
+
One file per agent (`dataDir/<agent>.jsonl`); every conversation *including
|
|
120
|
+
forks* lives in the same interleaved stream:
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{"v":1,"id":"e27","seq":27,"ts":"…","agent":"alpha","session":"sess-alpha-main",
|
|
124
|
+
"branch":"br032sl","parent":"e26","type":"fork",
|
|
125
|
+
"data":{"fromSession":"sess-alpha-main","fromBranch":"br0","fromEvent":"e26","newBranch":"br032sl"}}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- every event carries `session`, `branch`, `parent` (previous event on the same branch), monotonic `seq`
|
|
129
|
+
- a `fork` event records exactly where the new branch split off
|
|
130
|
+
- reconstruct any conversation: filter `branch === X` (or walk `parent` links
|
|
131
|
+
backwards across the fork point)
|
|
132
|
+
- event types: `session_start, fork, prompt, system_note, message, tool_call,
|
|
133
|
+
tool_result, state, progress, error, usage, goal`
|
|
134
|
+
- torn trailing lines are ignored on read → crash-safe append-only log
|
|
135
|
+
|
|
136
|
+
### Progress reports
|
|
137
|
+
|
|
138
|
+
Two complementary mechanisms:
|
|
139
|
+
|
|
140
|
+
1. the agent can call `report_progress` whenever it wants;
|
|
141
|
+
2. the harness injects a progress-report request at the next *turn boundary*
|
|
142
|
+
after `progressIntervalMs` of activity — never mid-turn, so it never
|
|
143
|
+
interrupts a tool call. Reports are first-class `progress` events.
|
|
144
|
+
|
|
145
|
+
### Scheduled tasks
|
|
146
|
+
|
|
147
|
+
Cron-style specs (`*/10 * * * *` or `every 10m`) checked by the single 15 s
|
|
148
|
+
master tick. Tasks run as prompts on **forked branches** by default so periodic
|
|
149
|
+
chatter never disturbs an agent's main line of work.
|
|
150
|
+
|
|
151
|
+
## HTTP API
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
GET /api/agents list snapshots
|
|
155
|
+
GET /api/agents/:id one snapshot
|
|
156
|
+
POST /api/agents/:id/prompt {text, start?}
|
|
157
|
+
POST /api/agents/:id/start | /stop
|
|
158
|
+
POST /api/agents/:id/goal {text} or {status}
|
|
159
|
+
POST /api/agents/:id/fork {} → new branch, same session log
|
|
160
|
+
GET /api/agents/:id/events?limit&branch&session
|
|
161
|
+
GET /api/agents/:id/branches
|
|
162
|
+
GET /api/metrics master rss/heap/load + per-agent stats
|
|
163
|
+
GET /api/events SSE updates (push, no polling)
|
|
164
|
+
GET /brew 418 I'm a teapot (RFC 2324)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Security / execution model
|
|
168
|
+
|
|
169
|
+
Designed for a dedicated agent Linux user; workspaces are path-confined,
|
|
170
|
+
subprocesses run in killable process groups with hard timeouts, and resource
|
|
171
|
+
limits (RLIMIT_* / cgroups) have a natural insertion point in
|
|
172
|
+
`src/agent/tools.ts:runShell`. The master survives agent crashes by
|
|
173
|
+
construction: agent errors never escape their own loop, and global handlers
|
|
174
|
+
keep the process alive.
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
AGPL-3.0-or-later
|