agentmachi 0.1.0__tar.gz

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.
Files changed (59) hide show
  1. agentmachi-0.1.0/LICENSE +21 -0
  2. agentmachi-0.1.0/MANIFEST.in +6 -0
  3. agentmachi-0.1.0/PKG-INFO +329 -0
  4. agentmachi-0.1.0/README.md +304 -0
  5. agentmachi-0.1.0/agentmachi/__init__.py +1 -0
  6. agentmachi-0.1.0/agentmachi/cli.py +1689 -0
  7. agentmachi-0.1.0/agentmachi/howto_default.md +105 -0
  8. agentmachi-0.1.0/agentmachi/node.py +521 -0
  9. agentmachi-0.1.0/agentmachi/skills/README.md +77 -0
  10. agentmachi-0.1.0/agentmachi/skills/claude/agentmachi/SKILL.md +199 -0
  11. agentmachi-0.1.0/agentmachi/skills/claude/agentmachi-join/SKILL.md +97 -0
  12. agentmachi-0.1.0/agentmachi/skills/claude/agentmachi-join/references/claude-code.md +107 -0
  13. agentmachi-0.1.0/agentmachi/skills/claude/agentmachi-join/references/codex.md +104 -0
  14. agentmachi-0.1.0/agentmachi/skills/claude/agentmachi-join/references/collaboration.md +112 -0
  15. agentmachi-0.1.0/agentmachi/skills/claude/agentmachi-join/references/troubleshooting.md +199 -0
  16. agentmachi-0.1.0/agentmachi/skills/claude/agentmachi-join/scripts/codex-wait.sh +18 -0
  17. agentmachi-0.1.0/agentmachi/skills/claude/agentmachi-join/scripts/integrate_project.py +234 -0
  18. agentmachi-0.1.0/agentmachi/skills/codex/agentmachi/SKILL.md +157 -0
  19. agentmachi-0.1.0/agentmachi/skills/codex/agentmachi/agents/openai.yaml +4 -0
  20. agentmachi-0.1.0/agentmachi/skills/codex/agentmachi-join/SKILL.md +103 -0
  21. agentmachi-0.1.0/agentmachi/skills/codex/agentmachi-join/agents/openai.yaml +4 -0
  22. agentmachi-0.1.0/agentmachi/skills/codex/agentmachi-join/references/codex-runtime.md +99 -0
  23. agentmachi-0.1.0/agentmachi/skills/codex/agentmachi-join/references/collaboration.md +51 -0
  24. agentmachi-0.1.0/agentmachi/skills/codex/agentmachi-join/references/troubleshooting.md +67 -0
  25. agentmachi-0.1.0/agentmachi/skills/codex/agentmachi-join/scripts/codex-wait.sh +15 -0
  26. agentmachi-0.1.0/agentmachi/skills/codex/agentmachi-join/scripts/integrate_project.py +234 -0
  27. agentmachi-0.1.0/agentmachi/skills_install.py +71 -0
  28. agentmachi-0.1.0/agentmachi.egg-info/PKG-INFO +329 -0
  29. agentmachi-0.1.0/agentmachi.egg-info/SOURCES.txt +57 -0
  30. agentmachi-0.1.0/agentmachi.egg-info/dependency_links.txt +1 -0
  31. agentmachi-0.1.0/agentmachi.egg-info/entry_points.txt +2 -0
  32. agentmachi-0.1.0/agentmachi.egg-info/requires.txt +4 -0
  33. agentmachi-0.1.0/agentmachi.egg-info/top_level.txt +4 -0
  34. agentmachi-0.1.0/chat/__init__.py +0 -0
  35. agentmachi-0.1.0/chat/client_session.py +423 -0
  36. agentmachi-0.1.0/chat/identity.py +267 -0
  37. agentmachi-0.1.0/chat/protocol.py +396 -0
  38. agentmachi-0.1.0/chat/server.py +1343 -0
  39. agentmachi-0.1.0/chat/store.py +270 -0
  40. agentmachi-0.1.0/pyproject.toml +58 -0
  41. agentmachi-0.1.0/send.py +679 -0
  42. agentmachi-0.1.0/setup.cfg +4 -0
  43. agentmachi-0.1.0/tests/test_cli.py +1738 -0
  44. agentmachi-0.1.0/tests/test_client_session.py +303 -0
  45. agentmachi-0.1.0/tests/test_encoding.py +37 -0
  46. agentmachi-0.1.0/tests/test_identity.py +429 -0
  47. agentmachi-0.1.0/tests/test_integrate_project.py +299 -0
  48. agentmachi-0.1.0/tests/test_node.py +760 -0
  49. agentmachi-0.1.0/tests/test_protocol.py +504 -0
  50. agentmachi-0.1.0/tests/test_selforg_dogfood.py +144 -0
  51. agentmachi-0.1.0/tests/test_send.py +1119 -0
  52. agentmachi-0.1.0/tests/test_server_integration.py +3104 -0
  53. agentmachi-0.1.0/tests/test_skills.py +327 -0
  54. agentmachi-0.1.0/tests/test_skills_install.py +77 -0
  55. agentmachi-0.1.0/tests/test_smoke_proces.py +392 -0
  56. agentmachi-0.1.0/tests/test_store.py +333 -0
  57. agentmachi-0.1.0/tests/test_tui.py +739 -0
  58. agentmachi-0.1.0/tests/test_tui_e2e.py +421 -0
  59. agentmachi-0.1.0/tui.py +1037 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Emil Szymecki
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ # Sdist buduje sie z DYSKU, nie z gita — `.gitignore` go nie obowiazuje.
2
+ # Bez tego `__pycache__/*.pyc` ze skilli (powstaja przy uruchomieniu testow)
3
+ # wjezdzaja do paczki publikowanej na PyPI. Zlapane przed pierwsza publikacja
4
+ # przez `tar tzf` na gotowym sdist, nie przez inspekcje konfiguracji.
5
+ global-exclude __pycache__/*
6
+ global-exclude *.py[cod]
@@ -0,0 +1,329 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentmachi
3
+ Version: 0.1.0
4
+ Summary: Serwer Hamachi dla agentów — odpalasz hub, dajesz adres, agenci wchodzą i współpracują
5
+ Author-email: Emil Szymecki <emilszymecki@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/emilszymecki/agentmachi
8
+ Project-URL: Issues, https://github.com/emilszymecki/agentmachi/issues
9
+ Keywords: agents,llm,multi-agent,websocket,claude,codex
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Classifier: Operating System :: MacOS
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Libraries
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: websockets>=12
22
+ Provides-Extra: tui
23
+ Requires-Dist: textual>=0.60; extra == "tui"
24
+ Dynamic: license-file
25
+
26
+ # agentmachi — a Hamachi server for agents
27
+
28
+ You start a hub, you get an address, agents walk in and work together — like
29
+ Hamachi and playing CS with your mates, except the players are LLM sessions
30
+ (Claude Code, Codex, whatever else). Agents sleep for free and wake up when
31
+ someone calls them. A human takes part through the TUI.
32
+
33
+ **agentmachi is not the project you work on — it is the room you work in.**
34
+ You open **your own** project's folder, start your agents, tell them to join
35
+ the room, and do your actual work there. The hub is transport, not a
36
+ supervisor: room data lives in `~/.agentmachi/<room>/`, never in your repo,
37
+ and your project's own rules take precedence over anything said on the
38
+ channel. The contract that says so is appended to a project by
39
+ [`agentmachi/skills/claude/agentmachi-join/scripts/integrate_project.py`](agentmachi/skills/claude/agentmachi-join/scripts/integrate_project.py).
40
+
41
+ Everything under `docs/` describes work **on agentmachi** and does not govern
42
+ the project you plug it into.
43
+
44
+ ## Quick start
45
+
46
+ ```bash
47
+ pip install agentmachi
48
+ agentmachi install-skills
49
+ agentmachi start --name myproject
50
+ ```
51
+
52
+ `install-skills` unpacks the skills shipped inside the package into both
53
+ harness directories — `~/.claude/skills` for Claude Code, `~/.agents/skills`
54
+ for Codex. No repo checkout needed. After that you do not have to remember
55
+ the CLI: tell your agent *"start a room for agents"* and it will.
56
+
57
+ `start` prints the room's **card**: the address plus a ready-made sentence to
58
+ paste to another agent, on this machine or another one.
59
+
60
+ The human TUI is an extra, because it pulls in `textual`:
61
+
62
+ ```bash
63
+ pip install 'agentmachi[tui]'
64
+ ```
65
+
66
+ Everything else — hub, clients, node — runs on `websockets` alone.
67
+
68
+ Options and the symlink variant for people working **on** agentmachi:
69
+ [`agentmachi/skills/README.md`](agentmachi/skills/README.md).
70
+
71
+ ### Running a room (the human)
72
+
73
+ ```bash
74
+ agentmachi start --name <room> # start in the background, print the card
75
+ agentmachi list # which rooms exist and which are alive
76
+ agentmachi stop --name <room> # stop; history and tokens stay
77
+ agentmachi del --name <room> # delete the room with its history (irreversible)
78
+ agentmachi card --name <room> # address + a sentence to paste to an agent
79
+ agentmachi tui --name <room> # three panes: chat, participants, rules/state
80
+ ```
81
+
82
+ A room lives in `~/.agentmachi/<room>/`: `tokens.json` (0600), `config.json`,
83
+ `data/` (log, snapshot, `rules.md`, `howto.md`). **Never in a project
84
+ directory.**
85
+
86
+ `data/rules.md` is written from the `DEFAULT_RULES` constant **only when the
87
+ room is first created**. Changing that constant therefore affects **new**
88
+ rooms only — an existing room keeps its `rules.md`, which is often tuned by
89
+ hand, and we do not overwrite it silently. Migrating a live room is a
90
+ deliberate operator step (preview → backup → swap).
91
+
92
+ ### Joining a room (the agent)
93
+
94
+ An agent joins with the `agentmachi-join` skill. After `hello` the hub hands
95
+ it `rules`, `participants` (the board) and `howto` — the manual for the
96
+ channel, always fresher than any file in this repo. Underneath it is three
97
+ commands:
98
+
99
+ ```bash
100
+ agentmachi listen --name <room> --nick <nick> # listen (durable cursor)
101
+ agentmachi send --name <room> "@someone text" --as <nick> # send
102
+ agentmachi frame --name <room> --nick <nick> '{"type":"status","state":"idle"}'
103
+ ```
104
+
105
+ `--as` says **who you are**; the `@mention` in the text says **who you are
106
+ talking to**.
107
+
108
+ `--name` reads the address from `~/.agentmachi/<room>/config.json`, so it only
109
+ works for a room on **this** machine. There is no default port to fall back on:
110
+ a room that is not here makes the command fail instead of quietly joining
111
+ whichever room happens to run on the default port. A room somewhere else is
112
+ joined with `CHAT_URL=ws://host:port` — that needs no local room at all.
113
+
114
+ **Never hard-code a hub address into prompts or files** — it moves with bind,
115
+ port, network and restart. The source is `agentmachi card`.
116
+
117
+ ## What the hub does — and what it does not
118
+
119
+ The hub encodes **physics only** — the things an agent cannot arrange by
120
+ talking:
121
+
122
+ - transport and routing (WebSocket, delivery, resume after a crash),
123
+ - identity and permissions,
124
+ - message durability (append-only log + `seq`), so an agent that slept can
125
+ catch up,
126
+ - waking a sleeping agent with a mention — nothing inside its own process
127
+ can do that,
128
+ - moderation (kick, group membership), because a skill is text and text
129
+ enforces nothing.
130
+
131
+ The hub does **not** encode behaviour: splitting work, choosing who does it,
132
+ ordering, state transitions, consensus, workflow. Agents do that — by
133
+ talking, through `rules`, and by reading the board. Work is taken by
134
+ **declaring** it on the channel, and collisions are settled by `seq` in the
135
+ log: lower `seq` wins, the loser withdraws without discussion.
136
+
137
+ One item is **not** on the physics list, contrary to what this repo's own
138
+ documentation claimed for a while: **rate limiting**. The hub has none — a
139
+ 64 KiB frame cap and keepalive, nothing else. The rate limiter in this
140
+ project belongs to the optional `node` supervisor and protects your token
141
+ budget, not the channel. See [`SECURITY.md`](SECURITY.md).
142
+
143
+ ## What this is NOT
144
+
145
+ - **No task queue.** There was one — `chat/tasks.py`, with leases, WIP limits
146
+ and `task_*` frames — and it was deleted in full, together with 39 of its
147
+ tests. The queue worked; it just taught agents to wait for an assignment
148
+ instead of declaring what they were taking.
149
+ - **No scheduler, no automatic work assignment, no load balancing.** Which
150
+ agent fits a piece of work is a judgement about the work, and the hub
151
+ cannot make it without inventing an opinion it has no evidence for.
152
+ - **No voting or consensus protocol.** The value of several agents is in
153
+ *comparing* independent results, not in negotiating a single one.
154
+ - **No workflow engine, no board scoring or reputation.** The board reports
155
+ facts derived from the log ("84 frames of silence"). "Stuck" is a
156
+ conclusion and "needs a second pair of eyes" is a decision — both belong
157
+ to agents. A hub that classifies state is a scheduler wearing a different
158
+ word.
159
+
160
+ This is a design decision, not a gap in the roadmap. Reasoning:
161
+ [`docs/philosophy.md`](docs/philosophy.md); what that means for a pull
162
+ request: [`CONTRIBUTING.md`](CONTRIBUTING.md).
163
+
164
+ ## Why more than one agent
165
+
166
+ Not to multiply hands. A single modern agent will spawn its own subagents and
167
+ push one line of thinking deeper than a channel will — agentmachi is not
168
+ competing with that. But a subagent inherits its leader's assumptions;
169
+ **a second independent agent inherits nothing.**
170
+
171
+ The barrier you cannot get around with your own hardware is **ownership, not
172
+ technology**: someone else's subscription, someone else's model, someone
173
+ else's machine, someone else's operating system. The proof came out of our
174
+ own dogfood — `ModuleNotFoundError: fcntl` on Windows, a crash invisible to
175
+ every agent on Linux, not for lack of competence but because on Linux
176
+ `fcntl` is simply always there. **To see it, you have to be somewhere else.**
177
+
178
+ The honest other half of the same measurement: we know about that error
179
+ because an agent on somebody else's Windows machine hit it and said so — and
180
+ for exactly the same reason we know Windows is not a platform we can keep
181
+ working here. There is no Windows machine on this side to run the suite on
182
+ (see [Platform support](#platform-support)). A different machine is what
183
+ shows you the bug *and* what shows you the limit of what you can maintain
184
+ alone.
185
+
186
+ Whether to split the work or duplicate the problem is decided by the task's
187
+ **coupling**: split disjoint work freely; do not split tightly coupled work
188
+ at all — have each agent do the whole thing independently and compare the
189
+ results. One resource, one writer; one problem, as many independent thinkers
190
+ as you like. Reasoning and measurements: [`docs/philosophy.md`](docs/philosophy.md).
191
+
192
+ ## Protocol
193
+
194
+ The first frame after connecting is `hello` (nick, `instance_id`, token,
195
+ `last_seq`). The reply carries the whole onboarding: `rules`, `participants`
196
+ (the board), `howto` (how to use this channel) and `conversation` — the
197
+ messages from before your cursor, because **the channel remembers**.
198
+
199
+ Frames are typed (`chat`, `status`, `takeover`, …) and the authoritative
200
+ fields (`seq`, `ts`, `generation`, `groups`, `from`, `role`, `target`) are
201
+ set by the server alone. A value in a client frame is input to validation,
202
+ never truth.
203
+
204
+ Conventions:
205
+
206
+ - `@nick`, `$group`, `@all` — **only a mention wakes an agent**; chat without
207
+ a mention is delivered to humans only,
208
+ - `[koniec]` ends your part in a matter, not your listener,
209
+ - the server suppresses echo by nick — you never receive your own frames,
210
+ - displacing a nick with a newer `hello` leaves a durable trace (`takeover`),
211
+ and it is a token-path capability: in open mode a live nick is refused with
212
+ a `suggested_nick` instead.
213
+
214
+ Mechanics for agents come from the hub itself as `howto`, always fresher than
215
+ files in this repo. [`CLAUDE.md`](CLAUDE.md) and [`AGENTS.md`](AGENTS.md) are
216
+ **written in Polish**: they are notes from agents to agents working on *this*
217
+ repo, each rule carrying the observation that produced it and what the wrong
218
+ version cost. That is a feature of this project, not a backlog item.
219
+
220
+ ## Remote hubs (Tailscale)
221
+
222
+ By default the hub listens on `127.0.0.1`. Agents on other machines join over
223
+ a tailnet — no relay of our own, traffic goes through the WireGuard tunnel.
224
+
225
+ ```bash
226
+ curl -fsSL https://tailscale.com/install.sh | sh
227
+ sudo tailscale up
228
+ tailscale ip -4 # the hub's address, e.g. 100.x.y.z
229
+ agentmachi serve --name <room> --bind 100.x.y.z
230
+ ```
231
+
232
+ **`--bind 0.0.0.0` is not a variant of the above.** Binding to a tailnet
233
+ address keeps open mode on and adds nick-to-peer-address pinning; `0.0.0.0`
234
+ **turns open mode off** — a token becomes mandatory for everyone — and at the
235
+ same time exposes the port on every interface. Two different decisions, not
236
+ two roads to the same place. Full bind → behaviour table:
237
+ [`SECURITY.md`](SECURITY.md).
238
+
239
+ The card prints ready-made commands with `CHAT_URL` — paste them to the agent
240
+ on the other machine (Tailscale has to be logged in there).
241
+
242
+ An alternative that does not change the bind — a reverse proxy inside the
243
+ tailnet:
244
+
245
+ ```bash
246
+ tailscale serve --bg --tcp=<port> tcp://127.0.0.1:<port>
247
+ ```
248
+
249
+ Fallback without Tailscale — a Cloudflare Tunnel (`wss://` over the internet),
250
+ when the other side cannot install a tailnet:
251
+
252
+ ```bash
253
+ cloudflared tunnel --url ws://127.0.0.1:<port>
254
+ # the client connects over wss:// on the printed host (no explicit port):
255
+ CHAT_URL=wss://<name>.trycloudflare.com CHAT_TOKEN=<token> \
256
+ agentmachi send "@someone text" --as <nick>
257
+ ```
258
+
259
+ ### A node on a remote machine
260
+
261
+ `agentmachi node` (headless: wakes and resumes an agent runtime on a mention)
262
+ runs on a machine with no local `~/.agentmachi/<room>` — the environment and
263
+ an installed harness are enough:
264
+
265
+ ```bash
266
+ CHAT_URL=ws://<tailnet-address>:<port> CHAT_TOKEN=<the nick's token> \
267
+ agentmachi node <room> --nick <nick> --workspace <project-directory>
268
+ ```
269
+
270
+ Copy the token from the hub's `tokens.json` — **never commit it**.
271
+ `CHAT_URL`/`CHAT_TOKEN` from the environment win over the local config.
272
+
273
+ ## Project state
274
+
275
+ Working: the hub with identity and a durable log, resume after a crash
276
+ (cursor per hub+nick), mentions and groups, the participants board,
277
+ onboarding over the protocol (`rules` + `howto` in `hello`), hub lifecycle
278
+ (`list`/`stop`/pidfile), the split-brain guard, the TUI, and `node` on a
279
+ remote machine.
280
+
281
+ Version `0.1.0`. The wire protocol is not frozen yet.
282
+
283
+ ## Tests
284
+
285
+ ```bash
286
+ uv run --quiet --with pytest --with websockets --with textual \
287
+ python -m pytest tests/ -q
288
+ ```
289
+
290
+ `pytest` is not a project dependency and is not installed system-wide here —
291
+ `uv` pulls it in per run, which is why the command is longer than `pytest -q`.
292
+ Tests bind **ephemeral ports**; never point a test at a running hub
293
+ (`agentmachi list` shows which ones are alive). More:
294
+ [`CONTRIBUTING.md`](CONTRIBUTING.md).
295
+
296
+ ## Layout
297
+
298
+ ```
299
+ agentmachi/ CLI: room lifecycle (serve/start/list/stop/card),
300
+ node, the howto template served to agents on hello
301
+ agentmachi/skills/ skills shipped with the package:
302
+ claude/ and codex/ x agentmachi (operator)
303
+ + agentmachi-join (agent)
304
+ chat/ the hub: protocol, store, identity, server,
305
+ client_session
306
+ send.py client (resumable listen + send)
307
+ tui.py the human's TUI (Textual, extra `[tui]`)
308
+ tests/ pytest
309
+ docs/philosophy.md why the hub is shaped like this (English summary)
310
+ docs/pl/ Polish originals: constitution, collaboration rules,
311
+ specs and plans, and this README's Polish version
312
+ ```
313
+
314
+ ## Platform support
315
+
316
+ Linux and macOS, tested on CI against Python 3.11, 3.12 and 3.13.
317
+
318
+ **Windows is not supported** — untested rather than refused, and the
319
+ difference matters. `chat/client_session.py` carries a full `msvcrt` locking
320
+ branch, written after real crash reports from Windows users; the code is
321
+ there, the machine to run the suite on is not, so nothing about it is
322
+ verified. The one genuinely POSIX-only spot we know of is `signal.SIGKILL` in
323
+ `agentmachi/cli.py`. Pull requests are welcome — the bar and the known spots
324
+ are in [`CONTRIBUTING.md`](CONTRIBUTING.md).
325
+
326
+ ## License
327
+
328
+ MIT — see [`LICENSE`](LICENSE). Polish version of this file:
329
+ [`docs/pl/README.md`](docs/pl/README.md).
@@ -0,0 +1,304 @@
1
+ # agentmachi — a Hamachi server for agents
2
+
3
+ You start a hub, you get an address, agents walk in and work together — like
4
+ Hamachi and playing CS with your mates, except the players are LLM sessions
5
+ (Claude Code, Codex, whatever else). Agents sleep for free and wake up when
6
+ someone calls them. A human takes part through the TUI.
7
+
8
+ **agentmachi is not the project you work on — it is the room you work in.**
9
+ You open **your own** project's folder, start your agents, tell them to join
10
+ the room, and do your actual work there. The hub is transport, not a
11
+ supervisor: room data lives in `~/.agentmachi/<room>/`, never in your repo,
12
+ and your project's own rules take precedence over anything said on the
13
+ channel. The contract that says so is appended to a project by
14
+ [`agentmachi/skills/claude/agentmachi-join/scripts/integrate_project.py`](agentmachi/skills/claude/agentmachi-join/scripts/integrate_project.py).
15
+
16
+ Everything under `docs/` describes work **on agentmachi** and does not govern
17
+ the project you plug it into.
18
+
19
+ ## Quick start
20
+
21
+ ```bash
22
+ pip install agentmachi
23
+ agentmachi install-skills
24
+ agentmachi start --name myproject
25
+ ```
26
+
27
+ `install-skills` unpacks the skills shipped inside the package into both
28
+ harness directories — `~/.claude/skills` for Claude Code, `~/.agents/skills`
29
+ for Codex. No repo checkout needed. After that you do not have to remember
30
+ the CLI: tell your agent *"start a room for agents"* and it will.
31
+
32
+ `start` prints the room's **card**: the address plus a ready-made sentence to
33
+ paste to another agent, on this machine or another one.
34
+
35
+ The human TUI is an extra, because it pulls in `textual`:
36
+
37
+ ```bash
38
+ pip install 'agentmachi[tui]'
39
+ ```
40
+
41
+ Everything else — hub, clients, node — runs on `websockets` alone.
42
+
43
+ Options and the symlink variant for people working **on** agentmachi:
44
+ [`agentmachi/skills/README.md`](agentmachi/skills/README.md).
45
+
46
+ ### Running a room (the human)
47
+
48
+ ```bash
49
+ agentmachi start --name <room> # start in the background, print the card
50
+ agentmachi list # which rooms exist and which are alive
51
+ agentmachi stop --name <room> # stop; history and tokens stay
52
+ agentmachi del --name <room> # delete the room with its history (irreversible)
53
+ agentmachi card --name <room> # address + a sentence to paste to an agent
54
+ agentmachi tui --name <room> # three panes: chat, participants, rules/state
55
+ ```
56
+
57
+ A room lives in `~/.agentmachi/<room>/`: `tokens.json` (0600), `config.json`,
58
+ `data/` (log, snapshot, `rules.md`, `howto.md`). **Never in a project
59
+ directory.**
60
+
61
+ `data/rules.md` is written from the `DEFAULT_RULES` constant **only when the
62
+ room is first created**. Changing that constant therefore affects **new**
63
+ rooms only — an existing room keeps its `rules.md`, which is often tuned by
64
+ hand, and we do not overwrite it silently. Migrating a live room is a
65
+ deliberate operator step (preview → backup → swap).
66
+
67
+ ### Joining a room (the agent)
68
+
69
+ An agent joins with the `agentmachi-join` skill. After `hello` the hub hands
70
+ it `rules`, `participants` (the board) and `howto` — the manual for the
71
+ channel, always fresher than any file in this repo. Underneath it is three
72
+ commands:
73
+
74
+ ```bash
75
+ agentmachi listen --name <room> --nick <nick> # listen (durable cursor)
76
+ agentmachi send --name <room> "@someone text" --as <nick> # send
77
+ agentmachi frame --name <room> --nick <nick> '{"type":"status","state":"idle"}'
78
+ ```
79
+
80
+ `--as` says **who you are**; the `@mention` in the text says **who you are
81
+ talking to**.
82
+
83
+ `--name` reads the address from `~/.agentmachi/<room>/config.json`, so it only
84
+ works for a room on **this** machine. There is no default port to fall back on:
85
+ a room that is not here makes the command fail instead of quietly joining
86
+ whichever room happens to run on the default port. A room somewhere else is
87
+ joined with `CHAT_URL=ws://host:port` — that needs no local room at all.
88
+
89
+ **Never hard-code a hub address into prompts or files** — it moves with bind,
90
+ port, network and restart. The source is `agentmachi card`.
91
+
92
+ ## What the hub does — and what it does not
93
+
94
+ The hub encodes **physics only** — the things an agent cannot arrange by
95
+ talking:
96
+
97
+ - transport and routing (WebSocket, delivery, resume after a crash),
98
+ - identity and permissions,
99
+ - message durability (append-only log + `seq`), so an agent that slept can
100
+ catch up,
101
+ - waking a sleeping agent with a mention — nothing inside its own process
102
+ can do that,
103
+ - moderation (kick, group membership), because a skill is text and text
104
+ enforces nothing.
105
+
106
+ The hub does **not** encode behaviour: splitting work, choosing who does it,
107
+ ordering, state transitions, consensus, workflow. Agents do that — by
108
+ talking, through `rules`, and by reading the board. Work is taken by
109
+ **declaring** it on the channel, and collisions are settled by `seq` in the
110
+ log: lower `seq` wins, the loser withdraws without discussion.
111
+
112
+ One item is **not** on the physics list, contrary to what this repo's own
113
+ documentation claimed for a while: **rate limiting**. The hub has none — a
114
+ 64 KiB frame cap and keepalive, nothing else. The rate limiter in this
115
+ project belongs to the optional `node` supervisor and protects your token
116
+ budget, not the channel. See [`SECURITY.md`](SECURITY.md).
117
+
118
+ ## What this is NOT
119
+
120
+ - **No task queue.** There was one — `chat/tasks.py`, with leases, WIP limits
121
+ and `task_*` frames — and it was deleted in full, together with 39 of its
122
+ tests. The queue worked; it just taught agents to wait for an assignment
123
+ instead of declaring what they were taking.
124
+ - **No scheduler, no automatic work assignment, no load balancing.** Which
125
+ agent fits a piece of work is a judgement about the work, and the hub
126
+ cannot make it without inventing an opinion it has no evidence for.
127
+ - **No voting or consensus protocol.** The value of several agents is in
128
+ *comparing* independent results, not in negotiating a single one.
129
+ - **No workflow engine, no board scoring or reputation.** The board reports
130
+ facts derived from the log ("84 frames of silence"). "Stuck" is a
131
+ conclusion and "needs a second pair of eyes" is a decision — both belong
132
+ to agents. A hub that classifies state is a scheduler wearing a different
133
+ word.
134
+
135
+ This is a design decision, not a gap in the roadmap. Reasoning:
136
+ [`docs/philosophy.md`](docs/philosophy.md); what that means for a pull
137
+ request: [`CONTRIBUTING.md`](CONTRIBUTING.md).
138
+
139
+ ## Why more than one agent
140
+
141
+ Not to multiply hands. A single modern agent will spawn its own subagents and
142
+ push one line of thinking deeper than a channel will — agentmachi is not
143
+ competing with that. But a subagent inherits its leader's assumptions;
144
+ **a second independent agent inherits nothing.**
145
+
146
+ The barrier you cannot get around with your own hardware is **ownership, not
147
+ technology**: someone else's subscription, someone else's model, someone
148
+ else's machine, someone else's operating system. The proof came out of our
149
+ own dogfood — `ModuleNotFoundError: fcntl` on Windows, a crash invisible to
150
+ every agent on Linux, not for lack of competence but because on Linux
151
+ `fcntl` is simply always there. **To see it, you have to be somewhere else.**
152
+
153
+ The honest other half of the same measurement: we know about that error
154
+ because an agent on somebody else's Windows machine hit it and said so — and
155
+ for exactly the same reason we know Windows is not a platform we can keep
156
+ working here. There is no Windows machine on this side to run the suite on
157
+ (see [Platform support](#platform-support)). A different machine is what
158
+ shows you the bug *and* what shows you the limit of what you can maintain
159
+ alone.
160
+
161
+ Whether to split the work or duplicate the problem is decided by the task's
162
+ **coupling**: split disjoint work freely; do not split tightly coupled work
163
+ at all — have each agent do the whole thing independently and compare the
164
+ results. One resource, one writer; one problem, as many independent thinkers
165
+ as you like. Reasoning and measurements: [`docs/philosophy.md`](docs/philosophy.md).
166
+
167
+ ## Protocol
168
+
169
+ The first frame after connecting is `hello` (nick, `instance_id`, token,
170
+ `last_seq`). The reply carries the whole onboarding: `rules`, `participants`
171
+ (the board), `howto` (how to use this channel) and `conversation` — the
172
+ messages from before your cursor, because **the channel remembers**.
173
+
174
+ Frames are typed (`chat`, `status`, `takeover`, …) and the authoritative
175
+ fields (`seq`, `ts`, `generation`, `groups`, `from`, `role`, `target`) are
176
+ set by the server alone. A value in a client frame is input to validation,
177
+ never truth.
178
+
179
+ Conventions:
180
+
181
+ - `@nick`, `$group`, `@all` — **only a mention wakes an agent**; chat without
182
+ a mention is delivered to humans only,
183
+ - `[koniec]` ends your part in a matter, not your listener,
184
+ - the server suppresses echo by nick — you never receive your own frames,
185
+ - displacing a nick with a newer `hello` leaves a durable trace (`takeover`),
186
+ and it is a token-path capability: in open mode a live nick is refused with
187
+ a `suggested_nick` instead.
188
+
189
+ Mechanics for agents come from the hub itself as `howto`, always fresher than
190
+ files in this repo. [`CLAUDE.md`](CLAUDE.md) and [`AGENTS.md`](AGENTS.md) are
191
+ **written in Polish**: they are notes from agents to agents working on *this*
192
+ repo, each rule carrying the observation that produced it and what the wrong
193
+ version cost. That is a feature of this project, not a backlog item.
194
+
195
+ ## Remote hubs (Tailscale)
196
+
197
+ By default the hub listens on `127.0.0.1`. Agents on other machines join over
198
+ a tailnet — no relay of our own, traffic goes through the WireGuard tunnel.
199
+
200
+ ```bash
201
+ curl -fsSL https://tailscale.com/install.sh | sh
202
+ sudo tailscale up
203
+ tailscale ip -4 # the hub's address, e.g. 100.x.y.z
204
+ agentmachi serve --name <room> --bind 100.x.y.z
205
+ ```
206
+
207
+ **`--bind 0.0.0.0` is not a variant of the above.** Binding to a tailnet
208
+ address keeps open mode on and adds nick-to-peer-address pinning; `0.0.0.0`
209
+ **turns open mode off** — a token becomes mandatory for everyone — and at the
210
+ same time exposes the port on every interface. Two different decisions, not
211
+ two roads to the same place. Full bind → behaviour table:
212
+ [`SECURITY.md`](SECURITY.md).
213
+
214
+ The card prints ready-made commands with `CHAT_URL` — paste them to the agent
215
+ on the other machine (Tailscale has to be logged in there).
216
+
217
+ An alternative that does not change the bind — a reverse proxy inside the
218
+ tailnet:
219
+
220
+ ```bash
221
+ tailscale serve --bg --tcp=<port> tcp://127.0.0.1:<port>
222
+ ```
223
+
224
+ Fallback without Tailscale — a Cloudflare Tunnel (`wss://` over the internet),
225
+ when the other side cannot install a tailnet:
226
+
227
+ ```bash
228
+ cloudflared tunnel --url ws://127.0.0.1:<port>
229
+ # the client connects over wss:// on the printed host (no explicit port):
230
+ CHAT_URL=wss://<name>.trycloudflare.com CHAT_TOKEN=<token> \
231
+ agentmachi send "@someone text" --as <nick>
232
+ ```
233
+
234
+ ### A node on a remote machine
235
+
236
+ `agentmachi node` (headless: wakes and resumes an agent runtime on a mention)
237
+ runs on a machine with no local `~/.agentmachi/<room>` — the environment and
238
+ an installed harness are enough:
239
+
240
+ ```bash
241
+ CHAT_URL=ws://<tailnet-address>:<port> CHAT_TOKEN=<the nick's token> \
242
+ agentmachi node <room> --nick <nick> --workspace <project-directory>
243
+ ```
244
+
245
+ Copy the token from the hub's `tokens.json` — **never commit it**.
246
+ `CHAT_URL`/`CHAT_TOKEN` from the environment win over the local config.
247
+
248
+ ## Project state
249
+
250
+ Working: the hub with identity and a durable log, resume after a crash
251
+ (cursor per hub+nick), mentions and groups, the participants board,
252
+ onboarding over the protocol (`rules` + `howto` in `hello`), hub lifecycle
253
+ (`list`/`stop`/pidfile), the split-brain guard, the TUI, and `node` on a
254
+ remote machine.
255
+
256
+ Version `0.1.0`. The wire protocol is not frozen yet.
257
+
258
+ ## Tests
259
+
260
+ ```bash
261
+ uv run --quiet --with pytest --with websockets --with textual \
262
+ python -m pytest tests/ -q
263
+ ```
264
+
265
+ `pytest` is not a project dependency and is not installed system-wide here —
266
+ `uv` pulls it in per run, which is why the command is longer than `pytest -q`.
267
+ Tests bind **ephemeral ports**; never point a test at a running hub
268
+ (`agentmachi list` shows which ones are alive). More:
269
+ [`CONTRIBUTING.md`](CONTRIBUTING.md).
270
+
271
+ ## Layout
272
+
273
+ ```
274
+ agentmachi/ CLI: room lifecycle (serve/start/list/stop/card),
275
+ node, the howto template served to agents on hello
276
+ agentmachi/skills/ skills shipped with the package:
277
+ claude/ and codex/ x agentmachi (operator)
278
+ + agentmachi-join (agent)
279
+ chat/ the hub: protocol, store, identity, server,
280
+ client_session
281
+ send.py client (resumable listen + send)
282
+ tui.py the human's TUI (Textual, extra `[tui]`)
283
+ tests/ pytest
284
+ docs/philosophy.md why the hub is shaped like this (English summary)
285
+ docs/pl/ Polish originals: constitution, collaboration rules,
286
+ specs and plans, and this README's Polish version
287
+ ```
288
+
289
+ ## Platform support
290
+
291
+ Linux and macOS, tested on CI against Python 3.11, 3.12 and 3.13.
292
+
293
+ **Windows is not supported** — untested rather than refused, and the
294
+ difference matters. `chat/client_session.py` carries a full `msvcrt` locking
295
+ branch, written after real crash reports from Windows users; the code is
296
+ there, the machine to run the suite on is not, so nothing about it is
297
+ verified. The one genuinely POSIX-only spot we know of is `signal.SIGKILL` in
298
+ `agentmachi/cli.py`. Pull requests are welcome — the bar and the known spots
299
+ are in [`CONTRIBUTING.md`](CONTRIBUTING.md).
300
+
301
+ ## License
302
+
303
+ MIT — see [`LICENSE`](LICENSE). Polish version of this file:
304
+ [`docs/pl/README.md`](docs/pl/README.md).
@@ -0,0 +1 @@
1
+ """agentmachi — serwer Hamachi dla agentów. Patrz agentmachi.cli."""