agent-lanes 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 (35) hide show
  1. agent_lanes-0.1.0/LICENSE +21 -0
  2. agent_lanes-0.1.0/PKG-INFO +359 -0
  3. agent_lanes-0.1.0/README.md +334 -0
  4. agent_lanes-0.1.0/agent_lanes/__init__.py +5 -0
  5. agent_lanes-0.1.0/agent_lanes/__main__.py +5 -0
  6. agent_lanes-0.1.0/agent_lanes/cli.py +899 -0
  7. agent_lanes-0.1.0/agent_lanes/config.py +175 -0
  8. agent_lanes-0.1.0/agent_lanes/defaults.py +4 -0
  9. agent_lanes-0.1.0/agent_lanes/errors.py +14 -0
  10. agent_lanes-0.1.0/agent_lanes/selftest.py +66 -0
  11. agent_lanes-0.1.0/agent_lanes/server.py +188 -0
  12. agent_lanes-0.1.0/agent_lanes/store.py +549 -0
  13. agent_lanes-0.1.0/agent_lanes/templates/pool/README.md +33 -0
  14. agent_lanes-0.1.0/agent_lanes/templates/pool/dispatchers/POLLING-CHAT-PROMPT.md +207 -0
  15. agent_lanes-0.1.0/agent_lanes/templates/pool/dispatchers/README.md +33 -0
  16. agent_lanes-0.1.0/agent_lanes/templates/pool/dispatchers/claude.sh +6 -0
  17. agent_lanes-0.1.0/agent_lanes/templates/pool/dispatchers/codex.sh +6 -0
  18. agent_lanes-0.1.0/agent_lanes/templates/pool/handoff.yaml +6 -0
  19. agent_lanes-0.1.0/agent_lanes/templates/workspace/README.md +91 -0
  20. agent_lanes-0.1.0/agent_lanes/templates/workspace/REVIEWER-AGENT-PROMPT.md +117 -0
  21. agent_lanes-0.1.0/agent_lanes/templates/workspace/bin/handoff +35 -0
  22. agent_lanes-0.1.0/agent_lanes/templates/workspace/dispatcher.sh +278 -0
  23. agent_lanes-0.1.0/agent_lanes/templates/workspace/handoff.yaml +32 -0
  24. agent_lanes-0.1.0/agent_lanes/timeutil.py +25 -0
  25. agent_lanes-0.1.0/agent_lanes.egg-info/PKG-INFO +359 -0
  26. agent_lanes-0.1.0/agent_lanes.egg-info/SOURCES.txt +33 -0
  27. agent_lanes-0.1.0/agent_lanes.egg-info/dependency_links.txt +1 -0
  28. agent_lanes-0.1.0/agent_lanes.egg-info/entry_points.txt +2 -0
  29. agent_lanes-0.1.0/agent_lanes.egg-info/requires.txt +4 -0
  30. agent_lanes-0.1.0/agent_lanes.egg-info/top_level.txt +1 -0
  31. agent_lanes-0.1.0/pyproject.toml +41 -0
  32. agent_lanes-0.1.0/setup.cfg +4 -0
  33. agent_lanes-0.1.0/tests/test_cli.py +1027 -0
  34. agent_lanes-0.1.0/tests/test_server.py +131 -0
  35. agent_lanes-0.1.0/tests/test_store.py +360 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Leonardo Diehl
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,359 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-lanes
3
+ Version: 0.1.0
4
+ Summary: Local file-backed coordination queue for AI coding agents
5
+ Author: Leonardo Diehl
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/leo-diehl/agent-lanes
8
+ Project-URL: Repository, https://github.com/leo-diehl/agent-lanes
9
+ Project-URL: Issues, https://github.com/leo-diehl/agent-lanes/issues
10
+ Keywords: agents,queue,rpc,ai,claude,codex,orchestration,agent-coordination
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Operating System :: POSIX
17
+ Classifier: Topic :: Software Development
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: PyYAML>=6
22
+ Provides-Extra: test
23
+ Requires-Dist: pytest>=8; extra == "test"
24
+ Dynamic: license-file
25
+
26
+ # agent-lanes
27
+
28
+ [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue)](https://www.python.org/)
29
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
30
+ [![CI](https://github.com/leo-diehl/agent-lanes/actions/workflows/test.yml/badge.svg)](https://github.com/leo-diehl/agent-lanes/actions/workflows/test.yml)
31
+
32
+ **agent-lanes** lets one AI coding agent hand work to another over a shared folder.
33
+ Submit a task, the other agent picks it up, replies, and you read the response —
34
+ like a tiny RPC queue, but the queue is just JSON files on disk. Useful when an
35
+ orchestrator agent wants a code review, a Q&A from a specialist, or a parallel
36
+ sub-task from a different model.
37
+
38
+ ## Glossary
39
+
40
+ - **lane** — a named subscription stream (e.g. `default`, `code-review`).
41
+ - **task** — one unit of work submitted to a lane.
42
+ - **dispatcher** — a long-running process that watches a lane, claims matching
43
+ tasks, and runs them. Can be a shell script (Mode B) or a chat (Mode A).
44
+ - **orchestrator** — the agent that submits tasks and waits for responses.
45
+ - **workspace** — a project directory or pool of project directories sharing one
46
+ queue.
47
+ - **sub-agent** — a fresh agent the dispatcher spawns to do the actual work for
48
+ one task.
49
+ - **vendor** — the LLM provider behind a dispatcher (e.g. `claude`, `codex`).
50
+
51
+ ## Install
52
+
53
+ ```bash
54
+ # From PyPI (once published)
55
+ pip install agent-lanes
56
+
57
+ # From GitHub
58
+ pip install git+https://github.com/leo-diehl/agent-lanes.git
59
+
60
+ # From source (for development)
61
+ git clone https://github.com/leo-diehl/agent-lanes.git
62
+ cd agent-lanes
63
+ pip install -e .
64
+ ```
65
+
66
+ Requires Python 3.11+ on macOS or Linux. POSIX-only — Windows users should run via
67
+ WSL2.
68
+
69
+ agent-lanes is single-host. The lock model assumes all participating processes
70
+ run on one machine and a local POSIX filesystem (`fcntl.flock`). Networked
71
+ filesystems (NFS, SMB) and multi-host setups are not supported.
72
+
73
+ ## Quickstart
74
+
75
+ Set up a workspace pool, attach a long-running dispatcher chat, submit a task. Five
76
+ steps.
77
+
78
+ 1. **Scaffold a workspace pool** — creates the shared queue and per-vendor
79
+ dispatcher artifacts.
80
+
81
+ ```bash
82
+ agent-lanes init-pool ~/myworkspace
83
+ ```
84
+
85
+ Creates `~/myworkspace/.agent-lanes-queue/` (the shared queue) and
86
+ `~/myworkspace/dispatchers/` (per-vendor dispatcher wrappers and a polling
87
+ chat prompt).
88
+
89
+ 2. **Scaffold a project pointed at the pool.**
90
+
91
+ ```bash
92
+ mkdir ~/myworkspace/example-project && cd ~/myworkspace/example-project
93
+ agent-lanes init --queue-root ~/myworkspace/.agent-lanes-queue/state
94
+ mkdir tasks
95
+ ```
96
+
97
+ 3. **Attach a dispatcher.** Open a Claude Code or Codex chat, paste
98
+ `~/myworkspace/dispatchers/POLLING-CHAT-PROMPT.md` into it, fill in the vendor
99
+ identity (one line at the top), and send. The chat is now a long-running
100
+ dispatcher — it polls the shared queue, claims tasks whose `required_vendor`
101
+ matches its vendor, spawns a sub-agent at the requested model and effort,
102
+ captures the result, and responds. Idle costs zero tokens (it's a blocking
103
+ syscall).
104
+
105
+ 4. **Define a task** at `tasks/code-review.yaml`:
106
+
107
+ ```yaml
108
+ lane: default
109
+ metadata:
110
+ required_vendor: claude
111
+ model_class: sonnet
112
+ effort: high
113
+ prompt: |
114
+ Review the request file. Return blocking issues, non-blocking suggestions,
115
+ and a one-line verdict.
116
+ ```
117
+
118
+ 5. **Submit and wait.**
119
+
120
+ ```bash
121
+ TASK_ID=$(./handoff/bin/handoff submit \
122
+ --task tasks/code-review.yaml \
123
+ --request-from outputs/01-step.md \
124
+ --response-to outputs/01-review.md \
125
+ --json | jq -r .task_id)
126
+ ./handoff/bin/handoff wait "$TASK_ID"
127
+ ```
128
+
129
+ The polling chat picks up the task, the sub-agent does the review, the response
130
+ lands in `outputs/01-review.md`, and the wait unblocks.
131
+
132
+ ### Two dispatcher modes
133
+
134
+ **Mode A (chat-as-dispatcher).** The dispatcher runs inside a long-running chat
135
+ (Claude Code or Codex), using your chat subscription rather than the headless
136
+ API. Best when you have a chat subscription and want to see what the dispatcher
137
+ does. Context grows over many tasks; restart the chat periodically. The polling
138
+ chat is vendor-bound only; model and effort come from each task's metadata, so
139
+ the same chat handles tasks at any model/effort the queue receives.
140
+
141
+ **Mode B (bash dispatcher).** The dispatcher is a long-running shell loop calling
142
+ a headless agent CLI (`claude -p`, `codex exec`) for each task. Best for
143
+ unattended use or environments without a chat client. Each task incurs API token
144
+ cost. The bundled dispatcher claims each task with a 15-minute lease and renews
145
+ that lease every minute while the headless child is running; if the dispatcher
146
+ dies, the task becomes reclaimable after the shorter lease instead of waiting for
147
+ the general two-hour claim default.
148
+
149
+ ```bash
150
+ bash ~/myworkspace/dispatchers/claude.sh # one terminal
151
+ bash ~/myworkspace/dispatchers/codex.sh # another terminal
152
+ ```
153
+
154
+ Each wrapper long-polls the queue, claims tasks for its vendor, and spawns a fresh
155
+ headless agent (`claude -p` / `codex exec`) per task. Both modes consume the same
156
+ queue and can run simultaneously.
157
+
158
+ ## Don't want to scaffold by hand?
159
+
160
+ If the Quickstart feels like a lot of moving pieces, point your agent at the
161
+ opinionated workflow guide and let it scaffold a multi-prompt "pack" for you:
162
+
163
+ ```
164
+ Create a prompt pack following docs/prompt-pack-guide.md for: <your task>.
165
+ ```
166
+
167
+ Paste that into a Claude Code or Codex chat. The agent reads
168
+ [`docs/prompt-pack-guide.md`](docs/prompt-pack-guide.md), runs
169
+ `agent-lanes init` (or `init-pool`) for you, writes the executor / reviewer
170
+ prompts, drops `tasks/<id>.yaml` files with the right metadata routing, and
171
+ hands you back a single orchestrator prompt to paste into your driver chat.
172
+
173
+ The guide is convention, not protocol — adapt or ignore as you like.
174
+
175
+ ## Try it
176
+
177
+ The `examples/two-terminal/` directory has a 30-second demo: one shell submits a
178
+ task, another claims and responds. No LLM is involved — pure shell — but it
179
+ exercises the full submit / wait / claim / respond cycle.
180
+
181
+ ```bash
182
+ cd examples/two-terminal
183
+ bash agent-b.sh &
184
+ bash agent-a.sh
185
+ wait
186
+ ```
187
+
188
+ ## Architecture
189
+
190
+ The engine (the `handoff/` folder) is project-level infrastructure: workspace
191
+ metadata, lane definitions, the dispatcher script, the CLI wrapper, runtime state.
192
+ It does **not** contain task definitions. Tasks are separate YAML files that you
193
+ keep wherever your project organizes them (commonly a `tasks/` folder). The engine
194
+ routes tasks by lane; tasks reference lanes by name.
195
+
196
+ Two scaffolding shapes:
197
+
198
+ - **`agent-lanes init`** scaffolds a single project with its own per-project queue
199
+ at `handoff/state/`. Use this for one-off projects.
200
+ - **`agent-lanes init-pool <workspace>`** scaffolds a workspace-level shared queue
201
+ plus per-vendor dispatcher artifacts. Use this when one workspace has many
202
+ projects that should share a single dispatcher pool. Each project then runs
203
+ `agent-lanes init --queue-root <abs-path-to-shared-state>` to point at the
204
+ shared queue instead of creating its own.
205
+
206
+ Routing is metadata-driven. The bundled dispatchers are bound to `claude` or
207
+ `codex`. Custom dispatchers may use other vendor strings — the protocol treats
208
+ `required_vendor` as opaque. Each task carries metadata declaring
209
+ `required_vendor`, `model_class`, and `effort`. Dispatchers inspect metadata, skip
210
+ tasks not for them, claim matching ones, and spawn (or delegate to a sub-agent)
211
+ the actual work at the requested model and effort. The dispatcher is a router;
212
+ the work happens in the spawned agent.
213
+
214
+ ## Define your first task
215
+
216
+ A task is a small standalone YAML file. It declares which lane to route to, any
217
+ default metadata, and the prompt body (inline or via `prompt_file`).
218
+
219
+ ```yaml
220
+ # tasks/code-review.yaml
221
+ lane: default
222
+ metadata:
223
+ required_vendor: claude
224
+ model_class: sonnet
225
+ effort: high
226
+ prompt_file: ../docs/prompts/code-review.md
227
+ ```
228
+
229
+ ```markdown
230
+ <!-- docs/prompts/code-review.md -->
231
+ You are a code reviewer. Read the request file as the artifact under review.
232
+ Respond with blocking issues, non-blocking suggestions, and a one-line verdict.
233
+ ```
234
+
235
+ Per-execution paths (the actual request and response files) come from CLI flags at
236
+ submit time, not from the task file. The same task definition can be reused across
237
+ many submissions with different request/response paths.
238
+
239
+ You can also submit fully inline without a task file:
240
+
241
+ ```bash
242
+ ./handoff/bin/handoff submit \
243
+ --lane default \
244
+ --request-from outputs/01-step.md \
245
+ --response-to outputs/01-review.md \
246
+ --prompt "Review for missing evidence." \
247
+ --metadata required_vendor=claude \
248
+ --metadata model_class=sonnet \
249
+ --metadata effort=high \
250
+ --json
251
+ ```
252
+
253
+ ## Common patterns
254
+
255
+ agent-lanes is structured RPC; review is one application. Five common shapes:
256
+
257
+ **Review / checkpoint.** An orchestrator submits an artifact and waits for a
258
+ verdict (`accept`, `accept-with-follow-ups`, `needs-revision`). The reviewer
259
+ claims, reads the request, and responds with `--verdict`. The original use case.
260
+
261
+ **Q&A.** A primary agent has a question for a specialist. Submit with no verdict
262
+ expectation, wait for the response, integrate the answer.
263
+
264
+ **Delegation.** A parent task fans out to N children on different lanes (or the
265
+ same lane), each handling one subtask. The parent submits all children, then waits
266
+ on each. Useful for parallel reviewers or any embarrassingly-parallel work.
267
+
268
+ **Pipeline.** Each agent's response feeds the next one's request. Threading
269
+ metadata (`thread_id`, `parent_task_id`) lets dispatchers reconstruct
270
+ conversational continuity across iterations.
271
+
272
+ **Vendor-routed pool.** One workspace, many projects, one queue. Mode A and Mode B
273
+ dispatchers are interchangeable consumers; both subscribe to the queue's `default`
274
+ lane and route per task metadata.
275
+
276
+ ## Orchestrator-side usage
277
+
278
+ Paste this into the CLI agent that drives the queue:
279
+
280
+ ```text
281
+ You have access to an agent-lanes engine at ./handoff/. Use it to coordinate with
282
+ other agents.
283
+
284
+ To submit a task:
285
+
286
+ ./handoff/bin/handoff submit \
287
+ --task tasks/<id>.yaml \
288
+ --request-from <path-to-artifact> \
289
+ --response-to <path-where-response-should-be-written> \
290
+ [--metadata key=value ...] \
291
+ --json
292
+
293
+ Capture the returned task_id. To wait for the response:
294
+
295
+ ./handoff/bin/handoff wait <task-id> --json
296
+
297
+ For multi-turn iterations, pass thread metadata so a stateless dispatcher can walk
298
+ the parent chain:
299
+
300
+ --metadata thread_id=<thread> --metadata parent_task_id=<previous-task-id>
301
+
302
+ To inspect without claiming:
303
+
304
+ ./handoff/bin/handoff list --lane <lane> --json
305
+ ./handoff/bin/handoff status <task-id> --json
306
+ ./handoff/bin/handoff status --all --json
307
+ ```
308
+
309
+ ## Reference
310
+
311
+ - [`CONTRACT.md`](CONTRACT.md) — protocol contract: state machine, JSON shapes,
312
+ CLI surface, HTTP routes, metadata convention (§ 14), dispatcher pattern (§ 16),
313
+ shared-queue topology (§ 17).
314
+ - [`CHANGELOG.md`](CHANGELOG.md) — release notes.
315
+
316
+ It's the format the project that drove agent-lanes was originally built for. The protocol does not require it; adopt or adapt as needed.
317
+
318
+ ## Language neutrality
319
+
320
+ The reference implementation is Python (stdlib + PyYAML). The protocol itself is
321
+ language-neutral: state lives on disk as JSON, commands are exposed via a CLI and
322
+ a small HTTP server. TypeScript, Go, and Rust ports are welcome as separate
323
+ packages once the protocol stabilizes.
324
+
325
+ ## Positioning
326
+
327
+ agent-lanes is local-first and protocol-light. Compared to alternatives:
328
+
329
+ - **MCP** is for tool exposure (one agent calling tools). agent-lanes is for agent
330
+ coordination (one agent calling another agent).
331
+ - **A2A** targets remote agent-to-agent calls over networks with auth and
332
+ discovery. agent-lanes is the local equivalent: same shape, no network.
333
+ - **agentpost** and similar frameworks bundle orchestration. agent-lanes ships
334
+ only the queue primitives — your orchestrator stays a few shell commands away.
335
+
336
+ ## Troubleshooting
337
+
338
+ - **`command not found: agent-lanes`** — pip install put it somewhere not on
339
+ PATH; try `python -m agent_lanes` or check `pip show -f agent-lanes` to find
340
+ the binary.
341
+ - **`jq: command not found`** — the bundled bash dispatcher and examples use
342
+ `jq`; install via your package manager (e.g. `brew install jq`,
343
+ `apt install jq`).
344
+ - **`wait` returns nothing for hours** — the long-poll runs for 6 hours by
345
+ default; pass `--timeout <seconds>` for shorter polls. While waiting on a
346
+ claimed task, non-quiet output includes claim owner, claimed age, lease expiry,
347
+ response path, latest event, and suggested next action.
348
+ - **`claim failed: stale request_sha256`** — the request file changed after
349
+ submit; re-submit or re-stage the artifact.
350
+ - **`pip install` fails with "externally-managed-environment"** — your Python
351
+ is PEP 668 managed; use a venv or pass `--break-system-packages` knowingly.
352
+
353
+ ## Status
354
+
355
+ v0.1 — early. APIs may change before v1.0.
356
+
357
+ ## License
358
+
359
+ MIT. See [LICENSE](LICENSE).