agentainer 0.1.7 → 2.0.1
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 +248 -677
- package/agentainer +16 -18
- package/agentainer.example.yaml +86 -0
- package/bin/agentainer.js +9 -8
- package/examples/academic-coauthor.yaml +123 -0
- package/examples/accessibility-audit.yaml +152 -0
- package/examples/affiliate-product-reviews.yaml +106 -0
- package/examples/api-design.yaml +157 -0
- package/examples/app-store-optimization.yaml +108 -0
- package/examples/brainstorm.yaml +27 -128
- package/examples/brand-voice-style-guide.yaml +109 -0
- package/examples/bug-hunt.yaml +51 -96
- package/examples/candidate-screen.yaml +122 -0
- package/examples/case-study-writer.yaml +100 -0
- package/examples/changelog-release-notes.yaml +114 -0
- package/examples/chatbot-builder.yaml +138 -0
- package/examples/code-review.yaml +73 -0
- package/examples/comparison-guide-writer.yaml +106 -0
- package/examples/competitive-intel.yaml +126 -0
- package/examples/content-studio.yaml +91 -0
- package/examples/course-creator.yaml +133 -0
- package/examples/customer-support-triage.yaml +118 -0
- package/examples/daily-briefing.yaml +119 -0
- package/examples/data-pipeline-builder.yaml +135 -0
- package/examples/debate.yaml +16 -90
- package/examples/design-system.yaml +138 -0
- package/examples/ebook-generator.yaml +90 -0
- package/examples/ecommerce-listing-optimizer.yaml +126 -0
- package/examples/email-newsletter.yaml +103 -0
- package/examples/faq-knowledge-sync.yaml +107 -0
- package/examples/game-design.yaml +122 -0
- package/examples/glossary-term-writer.yaml +103 -0
- package/examples/incident-response.yaml +52 -109
- package/examples/knowledge-base.yaml +115 -0
- package/examples/landing-page-converter.yaml +103 -0
- package/examples/legal-contract-review.yaml +118 -0
- package/examples/linkedin-ghostwriter.yaml +93 -0
- package/examples/localization.yaml +56 -123
- package/examples/meeting-notes.yaml +111 -0
- package/examples/migration-planner.yaml +127 -0
- package/examples/onboarding-buddy.yaml +111 -0
- package/examples/performance-audit.yaml +123 -0
- package/examples/podcast-production.yaml +117 -0
- package/examples/postmortem.yaml +119 -0
- package/examples/pr-review-gate.yaml +123 -0
- package/examples/press-release-wire.yaml +96 -0
- package/examples/product-spec.yaml +107 -0
- package/examples/prompt-engineering-lab.yaml +109 -0
- package/examples/quickstart.yaml +48 -0
- package/examples/rag-builder.yaml +145 -0
- package/examples/refactor-planner.yaml +127 -0
- package/examples/research.yaml +25 -0
- package/examples/resume-tailor.yaml +116 -0
- package/examples/rfp-response.yaml +124 -0
- package/examples/sales-coach.yaml +123 -0
- package/examples/security-audit.yaml +120 -0
- package/examples/seo-audit-and-fix.yaml +138 -0
- package/examples/seo-content-factory.yaml +103 -0
- package/examples/social-media.yaml +103 -0
- package/examples/software-company.yaml +71 -128
- package/examples/startup-validator.yaml +115 -0
- package/examples/tdd-pingpong.yaml +36 -68
- package/examples/technical-documentation.yaml +112 -0
- package/examples/test-factory.yaml +114 -0
- package/examples/tutorial-howto-creator.yaml +111 -0
- package/examples/twitter-x-thread-factory.yaml +91 -0
- package/examples/white-paper-research.yaml +96 -0
- package/examples/writers-room.yaml +49 -111
- package/examples/youtube-script-studio.yaml +107 -0
- package/hooks/claude_stop.sh +5 -3
- package/hooks/codex_notify.sh +4 -3
- package/lib/cli.py +933 -0
- package/lib/config.py +267 -308
- package/lib/hooks.py +246 -0
- package/lib/lock.py +75 -0
- package/lib/log.py +64 -0
- package/lib/mail.py +699 -0
- package/lib/minyaml.py +1 -39
- package/lib/reconcile.py +544 -0
- package/lib/sessions.py +223 -0
- package/lib/supervisor.py +216 -0
- package/lib/telegram.py +372 -0
- package/lib/tmux.py +355 -0
- package/lib/turn.py +167 -0
- package/lib/ui.py +1219 -0
- package/llms.txt +145 -429
- package/package.json +9 -7
- package/scripts/check-deps.js +18 -61
- package/ui/app.js +1136 -0
- package/ui/index.html +404 -0
- package/agents.example.yaml +0 -257
- package/examples/code-review-broadcast.yaml +0 -109
- package/examples/existing-repo.yaml +0 -74
- package/examples/multi-language-broadcast.yaml +0 -127
- package/examples/ping-pong.yaml +0 -89
- package/examples/red-team.yaml +0 -117
- package/examples/research-swarm.yaml +0 -129
- package/lib/swarm.py +0 -2461
package/llms.txt
CHANGED
|
@@ -1,431 +1,147 @@
|
|
|
1
|
-
# Agentainer
|
|
2
|
-
|
|
3
|
-
>
|
|
4
|
-
>
|
|
5
|
-
>
|
|
6
|
-
>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
Only `agents` is required. Relative paths resolve against the config file's directory.
|
|
29
|
-
|
|
30
|
-
```yaml
|
|
31
|
-
swarm:
|
|
32
|
-
name: dev-swarm # label used in prompts/logs; default = config filename
|
|
33
|
-
root: ./workspace # parent of the per-agent folders; default ./workspace
|
|
34
|
-
create_workdirs: true # auto-create missing agent folders; default true
|
|
35
|
-
session_prefix: "" # prepended to tmux session names; default ""
|
|
36
|
-
send_delay_ms: 150 # pause before pasting into a pane
|
|
37
|
-
enter_delay_ms: 250 # pause between pasting and pressing Enter
|
|
38
|
-
max_forward_hops: 3 # auto-forward loop guard
|
|
39
|
-
ready_timeout_ms: 60000 # how long to wait for an agent's input box to respond
|
|
40
|
-
busy_timeout_ms: 900000 # after this, an agent stuck "busy" is treated as idle
|
|
41
|
-
message_format: tagged # tagged | plain; tagged is the default
|
|
42
|
-
max_reply_reminders: 1 # how often to nudge an agent whose reply reached nobody
|
|
43
|
-
resume: false # make `up` reattach to recorded conversations by default
|
|
44
|
-
pane_idle_ms: 2500 # quiet time before a `pane` capture counts as a finished turn
|
|
45
|
-
pane_poll_ms: 700 # pane sampling interval
|
|
46
|
-
pane_scrollback: 400 # lines of scrollback the pane watcher diffs
|
|
47
|
-
tmux_history_limit: 50000 # scrollback per agent pane so you can scroll up when attached; 0 = tmux default
|
|
48
|
-
tmux_mouse: true # enable mouse-wheel scrolling in the panes
|
|
49
|
-
|
|
50
|
-
defaults: # any agent key; applied to agents that omit it
|
|
51
|
-
type: claude
|
|
52
|
-
append_agents_that_you_can_talk_to_prompt: true
|
|
53
|
-
workdir: "{root}/{name}" # optional; see "Working directories" below
|
|
54
|
-
|
|
55
|
-
agent_types: # override built-ins, or define new types
|
|
56
|
-
claude:
|
|
57
|
-
command: "claude --dangerously-skip-permissions"
|
|
58
|
-
capture: hook
|
|
59
|
-
boot_delay_ms: 3000
|
|
60
|
-
|
|
61
|
-
agents:
|
|
62
|
-
- name: developer # REQUIRED. Used as the folder name AND the tmux session name.
|
|
63
|
-
# Must match ^[A-Za-z0-9_][A-Za-z0-9_-]*$
|
|
64
|
-
type: codex # claude | codex | gemini | hermes | any key in agent_types
|
|
65
|
-
command: "codex --yolo" # exact CLI invocation; defaults to the type's command
|
|
66
|
-
can_talk_to: [reviewer] # whitelist. "*" means every other agent. Default: []
|
|
67
|
-
first_prompt: | # typed in after the CLI boots
|
|
68
|
-
You are the DEVELOPER...
|
|
69
|
-
first_prompt_file: ./p.md # alternative to first_prompt (mutually exclusive)
|
|
70
|
-
forward_responses_to: [reviewer] # auto-relay finished turns; must ⊆ can_talk_to
|
|
71
|
-
capture: hook # hook | pane | none | auto (default: from type)
|
|
72
|
-
boot_delay_ms: 3000 # grace period before probing the input box
|
|
73
|
-
ready_probe: true # wait for the input box to echo a token first
|
|
74
|
-
busy_check: true # refuse incoming mail while this agent is mid-turn
|
|
75
|
-
parse_outbound_tags: true # route <swarm-send> blocks written in its replies
|
|
76
|
-
reply_reminder: true # nudge it when it owes a reply but sent nothing
|
|
77
|
-
resume_args: "--resume {session_id}" # appended to command by `up --resume`
|
|
78
|
-
resume_command: "bash -ic 'chy3 --resume {session_id}'" # or replace it entirely
|
|
79
|
-
workdir: ./somewhere # default <root>/<name>; see below
|
|
80
|
-
create_workdir: true # false => a missing workdir is an error
|
|
81
|
-
env: {KEY: value} # extra env vars for its tmux session
|
|
82
|
-
append_agents_that_you_can_talk_to_prompt: true
|
|
83
|
-
in_first_prompt_append_your_task_will_be_sent_in_the_next_prompt: false
|
|
84
|
-
|
|
85
|
-
templates: # override the text Agentainer generates
|
|
86
|
-
comms: | # placeholders: {agent} {swarm} {peers} {prefix} {inbox} {workdir}
|
|
87
|
-
You are {agent}. You may message: {peers}.
|
|
88
|
-
task_notice: |
|
|
89
|
-
Stand by. Your real task arrives in the next message.
|
|
90
|
-
reply_reminder: | # placeholders: {agent} {sender} {id} {peers} {problems}
|
|
91
|
-
You owe {sender} an answer to {id}. Send it inside <swarm-send to="{sender}">.
|
|
92
|
-
send_failed: | # placeholders: {agent} {peers} {problems}
|
|
93
|
-
Your message could not be delivered. {problems}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## Working directories
|
|
97
|
-
|
|
98
|
-
- Default: each agent gets `<root>/<name>`, created on `up`.
|
|
99
|
-
- `workdir` overrides it. Absolute, relative to the config file, or `~`-prefixed.
|
|
100
|
-
Placeholders: `{name}` `{root}` `{swarm}` `{type}`.
|
|
101
|
-
- `defaults.workdir` applies to every agent that omits it. Include `{name}` to give
|
|
102
|
-
each agent its own folder; omit it to put the whole swarm in one directory.
|
|
103
|
-
- `create_workdirs` (swarm-level) and `create_workdir` (per-agent) control creation.
|
|
104
|
-
Set them to `false` when pointing at real repositories, so a mistyped path is an
|
|
105
|
-
error rather than a new empty folder.
|
|
106
|
-
- Several agents may share one workdir. That is legitimate (pair programming in one
|
|
107
|
-
checkout) but they will overwrite each other's files and interleave git commits,
|
|
108
|
-
so `validate` and `up` print a warning.
|
|
109
|
-
- `root` always holds `.swarm/` (logs, inboxes, the `swarm` shim), even when every
|
|
110
|
-
agent works elsewhere.
|
|
111
|
-
|
|
112
|
-
## Tagged messages (the default wire format)
|
|
113
|
-
|
|
114
|
-
Inbound, an agent sees an envelope it can parse unambiguously:
|
|
115
|
-
|
|
116
|
-
<swarm-message from="lead" to="reviewer" id="m-eb4105" reply-to="m-3f9a1c">
|
|
117
|
-
any number of lines, code blocks, backslashes -- all verbatim
|
|
118
|
-
</swarm-message>
|
|
119
|
-
|
|
120
|
-
Outbound, the agent writes a block **in its reply**; the capture hook routes it when
|
|
121
|
-
the turn ends. There is no shell involved, hence no quoting or escaping to get wrong:
|
|
122
|
-
|
|
123
|
-
<swarm-send to="reviewer" reply-to="m-eb4105">
|
|
124
|
-
multi-line body
|
|
125
|
-
</swarm-send>
|
|
126
|
-
|
|
127
|
-
<swarm-broadcast>
|
|
128
|
-
goes to everyone in the sender's can_talk_to
|
|
129
|
-
</swarm-broadcast>
|
|
130
|
-
|
|
131
|
-
- `id` is generated per message; `reply-to` is optional and used for threading.
|
|
132
|
-
- Tagged sends are permission-checked exactly like `swarm send`, and are queued
|
|
133
|
-
automatically if the recipient is busy.
|
|
134
|
-
- A `<swarm-send to="...">` naming something that is not an agent is discarded with a
|
|
135
|
-
warning (this catches an agent echoing the `AGENT_NAME` placeholder from its prompt).
|
|
136
|
-
- Blocks are removed from the text before `forward_responses_to` runs, so a message
|
|
137
|
-
addressed to one peer is not also auto-forwarded to everybody.
|
|
138
|
-
- `message_format: plain` restores the `[swarm] message from <sender>:` header.
|
|
139
|
-
`parse_outbound_tags: false` stops reading tags out of replies. Both are forced off
|
|
140
|
-
when `capture: none`, since there is nothing to read.
|
|
141
|
-
- With `capture: pane` the tags are recovered from scraped terminal text, so they are
|
|
142
|
-
only as reliable as the scrape. Prefer hook-captured agents for tag routing.
|
|
143
|
-
|
|
144
|
-
## Resuming after a restart
|
|
145
|
-
|
|
146
|
-
- Every captured turn records the agent's conversation id in `<root>/.swarm/sessions.yaml`
|
|
147
|
-
(claude hands it to the Stop hook; codex's is read from the newest rollout file under
|
|
148
|
-
that agent's private `CODEX_HOME/sessions`).
|
|
149
|
-
- `swarm up --resume` rebuilds each agent's command from that id, skips re-sending the
|
|
150
|
-
first prompt, and preserves the agent's queued mail and any reply it still owes.
|
|
151
|
-
- Resume recipes: claude `--resume {session_id}`, codex `resume {session_id}`. Neither
|
|
152
|
-
gemini nor hermes exposes a session id, so they always start fresh (with a warning).
|
|
153
|
-
- `resume_args` is appended to `command`. If the command invokes the CLI through an
|
|
154
|
-
alias or wrapper (`bash -ic chy3`), appending is wrong -- set `resume_command` to the
|
|
155
|
-
full replacement, using `{session_id}` (and optionally `{command}`).
|
|
156
|
-
- Starting an agent fresh removes its stale entry, so a later `--resume` cannot reattach
|
|
157
|
-
to a conversation that agent never ran.
|
|
158
|
-
- `swarm sessions` prints what is recorded and the exact command that would resume it.
|
|
159
|
-
|
|
160
|
-
## Reply reminders
|
|
161
|
-
|
|
162
|
-
A model asked a question will often write the answer as prose and end its turn. That
|
|
163
|
-
prose reaches nobody -- only a `<swarm-send>` block is delivered. So:
|
|
164
|
-
|
|
165
|
-
- When agent B receives a message from agent A (and B may message A back), B is
|
|
166
|
-
recorded as owing a reply.
|
|
167
|
-
- If B's turn ends having sent nothing, it is messaged with the `reply_reminder`
|
|
168
|
-
template: who is waiting, the message id to quote as `reply-to`, and the exact block
|
|
169
|
-
to write.
|
|
170
|
-
- If B *tried* to send but the block was malformed -- unclosed, missing `to`, unknown
|
|
171
|
-
recipient, permission denied -- it gets the `send_failed` template naming each fault.
|
|
172
|
-
- At most `max_reply_reminders` nudges (default 1), then Agentainer gives up silently.
|
|
173
|
-
- Anything B delivered counts, including an auto-forward via `forward_responses_to`.
|
|
174
|
-
Reminders are sent from `swarm`, so they never create a reply obligation themselves.
|
|
175
|
-
- `reply_reminder: false` disables it per agent; it is forced off when the agent's
|
|
176
|
-
tags are not parsed (`capture: none` or `message_format: plain`).
|
|
177
|
-
|
|
178
|
-
## The two prompt switches
|
|
179
|
-
|
|
180
|
-
Both are per-agent booleans, both default as shown, and both append to `first_prompt`:
|
|
181
|
-
|
|
182
|
-
- `append_agents_that_you_can_talk_to_prompt` (default `true`) — appends a block naming
|
|
183
|
-
the agents this one may message and showing the `swarm send` / `swarm broadcast` / raw
|
|
184
|
-
`tmux send-keys` syntax. Set it to `false` for an agent with no peers, or when you want
|
|
185
|
-
to write the communication instructions yourself.
|
|
186
|
-
- `in_first_prompt_append_your_task_will_be_sent_in_the_next_prompt` (default `false`) —
|
|
187
|
-
appends a "do not start yet, your task arrives in the next prompt" block. Use it for
|
|
188
|
-
the agent you will drive manually with `swarm send`, so it acknowledges and waits
|
|
189
|
-
instead of inventing work.
|
|
190
|
-
|
|
191
|
-
Final prompt order: `first_prompt`, then the comms block, then the task notice.
|
|
192
|
-
|
|
193
|
-
## capture: how a finished turn is detected
|
|
194
|
-
|
|
195
|
-
| value | works with | how |
|
|
196
|
-
|---|---|---|
|
|
197
|
-
| `hook` | `claude`, `codex` **only** | the CLI runs a program on turn completion; exact |
|
|
198
|
-
| `pane` | anything | poll the tmux pane and diff it after `pane_idle_ms`; heuristic |
|
|
199
|
-
| `none` | anything | no capture |
|
|
200
|
-
| `auto` | anything | use the agent type's default (this is the default) |
|
|
201
|
-
|
|
202
|
-
- `claude` → a `Stop` hook in `<agent-dir>/.claude/settings.json`; reads the transcript.
|
|
203
|
-
- `codex` → a private `CODEX_HOME` at `<agent-dir>/.codex/` with a `notify` program.
|
|
204
|
-
- `gemini`, `hermes` → no such facility, so they fall back to `pane`. Setting
|
|
205
|
-
`capture: hook` on them (or on a custom type) prints a warning and uses `pane`.
|
|
206
|
-
- `pane` capture is terminal scraping. It filters the echo of incoming messages, but
|
|
207
|
-
spinners and redraws can leak through. When output quality matters, prefer
|
|
208
|
-
`capture: none` and tell the agent in its prompt to call `swarm send` itself.
|
|
209
|
-
|
|
210
|
-
`forward_responses_to` requires `capture` to be `hook` or `pane`; combining it with
|
|
211
|
-
`capture: none` is a config error.
|
|
212
|
-
|
|
213
|
-
## Rules the validator enforces
|
|
214
|
-
|
|
215
|
-
- agent names are unique and match `^[A-Za-z0-9_][A-Za-z0-9_-]*$`
|
|
216
|
-
- every name in `can_talk_to` and `forward_responses_to` refers to a real agent
|
|
217
|
-
- an agent may not list itself in `can_talk_to`
|
|
218
|
-
- `forward_responses_to` ⊆ `can_talk_to`
|
|
219
|
-
- `forward_responses_to` is empty unless capture is enabled
|
|
220
|
-
- `type` is a built-in or defined under `agent_types`
|
|
221
|
-
- `first_prompt` and `first_prompt_file` are not both set
|
|
222
|
-
|
|
223
|
-
## Commands
|
|
224
|
-
|
|
225
|
-
```
|
|
226
|
-
agentainer up [--only a,b] [--restart] [--no-prompt] [--attach]
|
|
227
|
-
agentainer down [--only a,b]
|
|
228
|
-
agentainer restart
|
|
229
|
-
agentainer status
|
|
230
|
-
agentainer attach <agent>
|
|
231
|
-
agentainer send --to <agent> [--from <name>] [--file F] [--queue] [--wait]
|
|
232
|
-
[--wait-timeout S] [--ignore-busy] [--force] "message"
|
|
233
|
-
agentainer broadcast "message"
|
|
234
|
-
agentainer queue <agent> [--clear]
|
|
235
|
-
agentainer idle <agent> [--no-drain]
|
|
236
|
-
agentainer inbox <agent> [-n N]
|
|
237
|
-
agentainer logs [agent] [-n N] [-f]
|
|
238
|
-
agentainer validate [--show-prompts]
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
Config resolution: `-c PATH`, else `$SWARM_CONFIG`, else `./agents.yaml`, else the
|
|
242
|
-
`agents.yaml` beside `agentainer`. `agentainer x.yaml` is shorthand for `up -c x.yaml`.
|
|
243
|
-
|
|
244
|
-
## What an agent sees at runtime
|
|
245
|
-
|
|
246
|
-
Environment inside every agent's tmux session:
|
|
247
|
-
|
|
248
|
-
```
|
|
249
|
-
SWARM_AGENT this agent's name SWARM_CONFIG absolute path to the YAML
|
|
250
|
-
SWARM_PEERS comma-separated can_talk_to SWARM_ROOT the swarm root directory
|
|
251
|
-
SWARM_SESSION its tmux session name SWARM_NAME the swarm's name
|
|
252
|
-
PATH has <root>/.swarm/bin first, providing `swarm`
|
|
1
|
+
# 🐝 Agentainer
|
|
2
|
+
|
|
3
|
+
> Zero-dependency multi-agent orchestrator with a file-based mail model. Launch
|
|
4
|
+
> Claude Code, Codex, Gemini, or Hermes each in its own tmux session; let them
|
|
5
|
+
> message each other only where `can_talk_to` allows. No runtime dependencies,
|
|
6
|
+
> no API keys required to exercise the mechanics.
|
|
7
|
+
|
|
8
|
+
## 🧭 What it is
|
|
9
|
+
Agentainer v2 replaces v1's fragile "tagged XML envelope inside prose" messaging
|
|
10
|
+
with a **file-based mail model**: agents *read a file* to receive and *write a
|
|
11
|
+
file* to send. The orchestrator owns all hard logic — routing, ACL, message IDs,
|
|
12
|
+
threading, read-state, queueing, retries, availability, the durable log. The
|
|
13
|
+
model only reads and writes natural-language files.
|
|
14
|
+
|
|
15
|
+
## 📬 Core model (two verbs, four folders)
|
|
16
|
+
- Agents communicate by reading/writing files under their mail dir.
|
|
17
|
+
- `inbox/` — the **one** current unread message (orchestrator releases one at a time).
|
|
18
|
+
- `outbox/<name>/` — write here to send to `<name>`; read `<name>/about.md` for a contact card.
|
|
19
|
+
- `read/` — move a handled message here (best-effort read receipt).
|
|
20
|
+
- `sent/` — the agent's own record of delivered mail (orchestrator moves it here).
|
|
21
|
+
- `failed/` — mail bounced by the orchestrator (ACL violation, etc.), with a `system` explanation.
|
|
22
|
+
|
|
23
|
+
## 🔁 Why a rewrite
|
|
24
|
+
v1 scraped messages out of a fullscreen-TUI pane. That was unreliable across LLMs.
|
|
25
|
+
The v2 mail model works on nearly every tool-calling model, including weak ones.
|
|
26
|
+
|
|
27
|
+
## 🚀 Quick start
|
|
253
28
|
```
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
tmux send-keys -t reviewer -l "raw fallback, skips permissions and logging" \
|
|
261
|
-
&& tmux send-keys -t reviewer Enter
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
Loop guard: a forwarded message carries a hop count that increments on each relay and
|
|
265
|
-
stops at `max_forward_hops`. A message you send with `swarm send` resets it to zero.
|
|
266
|
-
|
|
267
|
-
## Worked examples in this repo
|
|
268
|
-
|
|
269
|
-
- `examples/research-swarm.yaml` — hub and spoke: a lead delegates to a scout
|
|
270
|
-
(gemini), an analyst (codex) and a writer; the writer uses a custom `workdir`.
|
|
271
|
-
- `examples/software-company.yaml` — six agents across all four CLIs with a
|
|
272
|
-
restricted comms graph (developers talk to the architect and QA, never to
|
|
273
|
-
each other).
|
|
274
|
-
- `examples/bug-hunt.yaml` — a hands-free pipeline built on `forward_responses_to`:
|
|
275
|
-
reproduce → diagnose → fix → verify.
|
|
276
|
-
- `examples/existing-repo.yaml` — two agents pairing inside one existing checkout,
|
|
277
|
-
using `create_workdirs: false` so a wrong path fails instead of being created.
|
|
278
|
-
- `examples/red-team.yaml` — adversarial triad: an attacker and defender argue
|
|
279
|
-
through a referee over one existing checkout.
|
|
280
|
-
- `examples/debate.yaml` — two advocates argue opposite sides; a judge
|
|
281
|
-
cross-examines and rules.
|
|
282
|
-
- `examples/writers-room.yaml` — an editor drives a writer, fact-checker and
|
|
283
|
-
critic to a finished article.
|
|
284
|
-
- `examples/incident-response.yaml` — a commander coordinates an investigator,
|
|
285
|
-
responder and scribe under time pressure (hub and spoke).
|
|
286
|
-
- `examples/tdd-pingpong.yaml` — a tester and coder grow a feature test-first,
|
|
287
|
-
one test at a time, via `forward_responses_to`.
|
|
288
|
-
- `examples/brainstorm.yaml` — three idea-generators fan out from a facilitator,
|
|
289
|
-
then a synthesiser ranks them.
|
|
290
|
-
- `examples/localization.yaml` — translate → review → back-check, auto-forwarded
|
|
291
|
-
via `forward_responses_to`; a `NEEDS ANOTHER PASS` verdict loops back to
|
|
292
|
-
the translator with a `<swarm-send>` block.
|
|
293
|
-
- `examples/multi-language-broadcast.yaml` — one translator broadcasts a source to
|
|
294
|
-
N language reviewers; a consolidator compiles and broadcasts back. Built on
|
|
295
|
-
`broadcast`.
|
|
296
|
-
- `examples/code-review-broadcast.yaml` — a coordinator broadcasts a PR to
|
|
297
|
-
specialist reviewers (correctness/security/perf); each replies to the
|
|
298
|
-
coordinator, who synthesizes. Built on `broadcast`.
|
|
299
|
-
- `examples/ping-pong.yaml` — two agents trade a unit of work, each broadcast
|
|
300
|
-
naming who is up next (round-robin via `broadcast`, distinct from
|
|
301
|
-
tdd-pingpong's fixed `forward_responses_to`).
|
|
302
|
-
- `agents.example.yaml` — the fully annotated reference config; every key is
|
|
303
|
-
documented inline.
|
|
304
|
-
|
|
305
|
-
## Recipes
|
|
306
|
-
|
|
307
|
-
**Hub and spoke** — one orchestrator drives specialists, which reply only to it:
|
|
308
|
-
|
|
309
|
-
```yaml
|
|
310
|
-
agents:
|
|
311
|
-
- {name: orchestrator, type: claude, can_talk_to: "*",
|
|
312
|
-
in_first_prompt_append_your_task_will_be_sent_in_the_next_prompt: true}
|
|
313
|
-
- {name: researcher, type: gemini, can_talk_to: [orchestrator]}
|
|
314
|
-
- {name: developer, type: codex, can_talk_to: [orchestrator]}
|
|
29
|
+
cp examples/quickstart.yaml my-swarm.yaml
|
|
30
|
+
agentainer up -c my-swarm.yaml
|
|
31
|
+
agentainer status -c my-swarm.yaml
|
|
32
|
+
agentainer send -c my-swarm.yaml --to orchestrator "Build a CSV->Parquet CLI."
|
|
33
|
+
agentainer logs -c my-swarm.yaml -f
|
|
34
|
+
agentainer down -c my-swarm.yaml
|
|
315
35
|
```
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
one
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
the
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
-
|
|
390
|
-
`
|
|
391
|
-
|
|
392
|
-
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
-
|
|
410
|
-
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
-
|
|
426
|
-
|
|
427
|
-
-
|
|
428
|
-
- [README.md](README.md): human-facing guide, architecture and troubleshooting.
|
|
429
|
-
- [lib/config.py](lib/config.py): schema, defaults and validation — the source of truth.
|
|
430
|
-
- [lib/swarm.py](lib/swarm.py): tmux orchestration, message routing, capture.
|
|
431
|
-
- [hooks/](hooks/): the Claude `Stop` hook and the Codex `notify` program.
|
|
36
|
+
(Install via `npm install` + `npm link` so the `agentainer` bin is on PATH.)
|
|
37
|
+
The quickstart uses `bash` loop "mock agents" (no API keys) so you can watch the
|
|
38
|
+
mailroom route mail safely. Swap each `command` for a real agent CLI to run for real.
|
|
39
|
+
|
|
40
|
+
## 🔒 Key invariants
|
|
41
|
+
- Zero runtime dependencies (Python 3 + bash + tmux; PyYAML optional, bundled `minyaml` fallback).
|
|
42
|
+
- `can_talk_to` ACL is cooperative, documented honestly, not an OS boundary.
|
|
43
|
+
- `user`/`system` are reserved virtual mailboxes.
|
|
44
|
+
- Liveness supervisor heartbeat retained (no event-only redesign).
|
|
45
|
+
- Durable JSONL event log is the source of truth for history.
|
|
46
|
+
|
|
47
|
+
## 🛠️ Commands
|
|
48
|
+
- `validate` — resolve and print the config; launch nothing.
|
|
49
|
+
- `up` — create dirs + mailbox folders, install per-type turn-detection, open one tmux session per agent, send the role.
|
|
50
|
+
- `down` / `restart` — tear down / down+up.
|
|
51
|
+
- `status` / `attach` / `logs` — health, attach, durable JSONL log (`-f` follows).
|
|
52
|
+
- `send` — send a `user` message into the swarm (`--to <agent>`).
|
|
53
|
+
- `user` — toggle `user` availability (`available`/`away`/`inbox`/`send`).
|
|
54
|
+
- `queue` / `inbox` / `idle` — inspect pending mail, current inbox, idle agents.
|
|
55
|
+
- `hook` / `watch` / `supervise` — turn-completion hook, live pane watch, liveness supervisor.
|
|
56
|
+
- `serve` — mail-app control-plane UI on `127.0.0.1` (per-agent threaded mail rendered as markdown with delivery status, reply as `user`, activity timeline + topology graph, live tmux pane + direct type-in, availability toggle, swarm-settings & agent add/edit/delete written back to YAML, and an optional stdlib Telegram bridge that mirrors mail out and routes phone replies back as `user` mail); token required for non-loopback.
|
|
57
|
+
- `add <name> --type T --command C --can-talk-to X` — add an agent to the config and bring it up.
|
|
58
|
+
- `edit <name> -s key=value` — edit an agent's fields and reconcile.
|
|
59
|
+
- `remove <name>` — remove an agent and stop its session.
|
|
60
|
+
- `reconcile` — start agents missing from the running set / stop orphaned sessions.
|
|
61
|
+
|
|
62
|
+
## 🖥️ Control-plane UI (P2–P3)
|
|
63
|
+
`agentainer serve -c swarm.yaml --port 8080 --token <secret>` serves a zero-dependency
|
|
64
|
+
UI (stdlib `http.server` + one vanilla-JS page; no framework, no build). It shows
|
|
65
|
+
swarm/agent status, logs, inbox, queue, a live terminal snapshot of each agent's tmux
|
|
66
|
+
pane, and lets you inject mail as the `user` mailbox. Binds `127.0.0.1` by default;
|
|
67
|
+
a token is required for any non-loopback bind (the UI is a control plane).
|
|
68
|
+
|
|
69
|
+
## 🔄 Dynamic reconcile (P4)
|
|
70
|
+
Change the swarm without a full teardown: `add`/`remove`/`edit` rewrite `agentainer.yaml`
|
|
71
|
+
with a stdlib-only YAML emitter (works with or without PyYAML) and then `reconcile` the
|
|
72
|
+
change into effect.
|
|
73
|
+
|
|
74
|
+
## ✅ Verified CLI behaviours (do not "simplify" these away)
|
|
75
|
+
These are observed, load-bearing behaviours of the shipping CLI. An LLM patching or
|
|
76
|
+
"tidying" a config/command around them will produce a subtly broken swarm — keep them.
|
|
77
|
+
|
|
78
|
+
- **`validate` launches nothing.** It resolves and prints the config only. Safe to run
|
|
79
|
+
in any environment.
|
|
80
|
+
- **`up` installs per-`type` turn detection.** `claude` → Stop hook; `codex` → `notify`
|
|
81
|
+
program; `gemini`/`hermes` → pane polling. This wiring is what makes completion
|
|
82
|
+
detectable; do not assume a bare tmux session is enough.
|
|
83
|
+
- **One message in `inbox/` at a time.** The orchestrator releases the next message
|
|
84
|
+
only after the current one is handled (moved to `read/`, or auto-archived after N
|
|
85
|
+
presentations). An agent never sees a queue of inbox files.
|
|
86
|
+
- **Outgoing mail is swept on stop, not in real time.** Writes to `outbox/<name>/` are
|
|
87
|
+
routed when the agent's turn finishes (Stop hook / `notify` / supervisor tick), not
|
|
88
|
+
the instant the file appears. A long-running turn does not deliver mid-turn.
|
|
89
|
+
- **Every delivered message is stamped** with `From:` / `To:` / `Id:` / `Time:` /
|
|
90
|
+
`Re:` headers by the orchestrator; the sender only wrote natural-language body text.
|
|
91
|
+
- **ACL is enforced at routing.** A send to a name not in `can_talk_to` is bounced: a
|
|
92
|
+
`system` explanation is dropped in the sender's `inbox/` and the file moved to
|
|
93
|
+
`failed/`. `"*"` means everyone-else.
|
|
94
|
+
- **`user` send is held, not bounced, when unavailable.** If `user` is in `can_talk_to`
|
|
95
|
+
but `user` availability is `away`, the mail is queued and a `system` ack is returned;
|
|
96
|
+
it is released when `user` becomes `available`.
|
|
97
|
+
- **`read/` is a best-effort receipt.** Moving mail there marks the sender's `sent/`
|
|
98
|
+
copy read; if the model forgets, the orchestrator auto-archives after N presentations
|
|
99
|
+
so a forgetful agent can't wedge or loop the system.
|
|
100
|
+
- **ACL membership = presence of `outbox/<name>/` + `about.md`.** The orchestrator
|
|
101
|
+
creates these when `can_talk_to` includes the name; the model reads `about.md` as a
|
|
102
|
+
contact card. Don't hand-create these folders expecting routing to follow.
|
|
103
|
+
- **`type` ↔ `command` mismatch is a hard error at `up`.** If `command` launches a
|
|
104
|
+
different CLI than `type` implies, `up` fails with `ConfigError` instead of silently
|
|
105
|
+
pinning the agent `busy` forever.
|
|
106
|
+
- **`capture: none` auto-upgrades to `hook` on hook-backed types** (`claude`/`codex`)
|
|
107
|
+
at config load, with a warning — a `none` capture on those types would blind turn
|
|
108
|
+
detection.
|
|
109
|
+
- **`add`/`remove`/`edit` rewrite the YAML with the stdlib emitter** and then
|
|
110
|
+
`reconcile`; they work with *or without* PyYAML. Don't assume PyYAML is present.
|
|
111
|
+
- **The liveness supervisor is always on at `up`.** It reconciles stale-busy, dead, and
|
|
112
|
+
silent-but-alive agents every tick; `status` reports `supervisor: alive|down`.
|
|
113
|
+
|
|
114
|
+
## ⚠️ Gotchas (the quirks that look like bugs but aren't)
|
|
115
|
+
- **Mail sits in `outbox/<name>/` until the turn ends.** If you `send` as an agent and
|
|
116
|
+
immediately check the recipient's `inbox/`, it may not be there yet — wait for the
|
|
117
|
+
sender's turn to finish (or the next supervisor tick).
|
|
118
|
+
- **`inbox/` holds exactly one file.** Don't script around "the newest of many inbox
|
|
119
|
+
files"; there is never more than one.
|
|
120
|
+
- **A `system` mail in `inbox/` is not spam** — it's the orchestrator talking back
|
|
121
|
+
(bounce explanation, nudge ack, ping). Treat it as first-class input.
|
|
122
|
+
- **`failed/` means "the orchestrator refused this", not "a crash".** Almost always an
|
|
123
|
+
ACL violation. Read the `system` note inside the file.
|
|
124
|
+
- **`user` is a virtual mailbox, not an agent.** `--to user` routes to the operator;
|
|
125
|
+
`user` availability gates delivery (hold-not-bounce). There is no `user` tmux session.
|
|
126
|
+
- **Reserve the names `user` and `system`.** A real agent may not claim them; the
|
|
127
|
+
validator rejects it.
|
|
128
|
+
- **Mismatched `type`/`command` deadlocks silently** if you bypass the validator (e.g.
|
|
129
|
+
hand-editing the YAML and skipping `validate`). Always `validate` before `up`.
|
|
130
|
+
- **`capture: none` is only valid for pane-polling types** (`gemini`/`hermes`). On
|
|
131
|
+
`claude`/`codex` it is auto-corrected to `hook`; if you really need no detection,
|
|
132
|
+
expect that agent to never report completion.
|
|
133
|
+
- **The no-PyYAML path is supported but stricter.** The bundled `minyaml` parser does
|
|
134
|
+
not support multi-document YAML, anchors, or tags. Keep real configs single-document
|
|
135
|
+
and you'll never notice.
|
|
136
|
+
|
|
137
|
+
## 📊 Project status
|
|
138
|
+
- **P1 — Mail runtime (CLI-driven)**: ✅ done.
|
|
139
|
+
- **P2 — UI observability**: ✅ done.
|
|
140
|
+
- **P3 — terminal snapshot + send-from-UI**: ✅ done.
|
|
141
|
+
- **P4 — dynamic reconcile (`add`/`remove`/`edit`/`reconcile`)**: ✅ done.
|
|
142
|
+
- 100% line coverage across all core + UI + reconcile modules, via mock agents (no API keys).
|
|
143
|
+
|
|
144
|
+
## 📚 Docs
|
|
145
|
+
- `ProjectPlan.md` — the design (source of truth).
|
|
146
|
+
- `README.md` — user-facing overview, install, commands, FAQ.
|
|
147
|
+
- v1 reference (read-only): `/root/AgentSwarm/docs/PROJECT-DOCUMENTATION.md`.
|