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/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