zwarm 3.4.0__py3-none-any.whl → 3.7.0__py3-none-any.whl
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.
- zwarm/cli/interactive.py +420 -52
- zwarm/cli/main.py +127 -14
- zwarm/cli/pilot.py +52 -4
- zwarm/core/costs.py +55 -183
- zwarm/core/environment.py +55 -1
- zwarm/core/registry.py +329 -0
- zwarm/orchestrator.py +64 -12
- zwarm/sessions/__init__.py +48 -9
- zwarm/sessions/base.py +501 -0
- zwarm/sessions/claude.py +481 -0
- zwarm/sessions/manager.py +85 -458
- zwarm/tools/delegation.py +126 -61
- {zwarm-3.4.0.dist-info → zwarm-3.7.0.dist-info}/METADATA +70 -21
- {zwarm-3.4.0.dist-info → zwarm-3.7.0.dist-info}/RECORD +16 -13
- {zwarm-3.4.0.dist-info → zwarm-3.7.0.dist-info}/WHEEL +0 -0
- {zwarm-3.4.0.dist-info → zwarm-3.7.0.dist-info}/entry_points.txt +0 -0
zwarm/tools/delegation.py
CHANGED
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
Delegation tools for the orchestrator.
|
|
3
3
|
|
|
4
4
|
These are the core tools that orchestrators use to delegate work to executors.
|
|
5
|
-
They use the
|
|
5
|
+
They use the same session managers that `zwarm interactive` uses - no special
|
|
6
6
|
MCP integration, no separate code path.
|
|
7
7
|
|
|
8
8
|
The orchestrator LLM has access to the exact same tools a human would use.
|
|
9
9
|
|
|
10
|
+
Supports multiple adapters:
|
|
11
|
+
- codex: OpenAI's Codex CLI (default)
|
|
12
|
+
- claude: Anthropic's Claude Code CLI
|
|
13
|
+
|
|
10
14
|
Tools:
|
|
11
|
-
- delegate: Start a new
|
|
15
|
+
- delegate: Start a new session (with adapter selection)
|
|
12
16
|
- converse: Continue a conversation (inject follow-up message)
|
|
13
17
|
- check_session: Check status of a session
|
|
14
18
|
- end_session: End/kill a session
|
|
@@ -26,24 +30,53 @@ from wbal.helper import weaveTool
|
|
|
26
30
|
if TYPE_CHECKING:
|
|
27
31
|
from zwarm.orchestrator import Orchestrator
|
|
28
32
|
|
|
33
|
+
# Available adapters
|
|
34
|
+
ADAPTERS = ["codex", "claude"]
|
|
35
|
+
|
|
29
36
|
|
|
30
37
|
def _get_session_manager(orchestrator: "Orchestrator"):
|
|
31
38
|
"""
|
|
32
|
-
Get the
|
|
39
|
+
Get the default session manager for list/get operations.
|
|
33
40
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
The session manager is created eagerly in Orchestrator.model_post_init()
|
|
38
|
-
and shared with the environment for observe() visibility.
|
|
41
|
+
Uses CodexSessionManager as the default since all adapters share
|
|
42
|
+
the same .zwarm/sessions/ directory structure.
|
|
39
43
|
"""
|
|
40
|
-
# Should already exist from model_post_init, but create if not
|
|
41
44
|
if not hasattr(orchestrator, "_session_manager") or orchestrator._session_manager is None:
|
|
42
45
|
from zwarm.sessions import CodexSessionManager
|
|
43
46
|
orchestrator._session_manager = CodexSessionManager(orchestrator.working_dir / ".zwarm")
|
|
44
47
|
return orchestrator._session_manager
|
|
45
48
|
|
|
46
49
|
|
|
50
|
+
def _get_adapter_manager(orchestrator: "Orchestrator", adapter: str):
|
|
51
|
+
"""
|
|
52
|
+
Get the session manager for a specific adapter.
|
|
53
|
+
|
|
54
|
+
Each adapter has its own manager for start_session/inject_message,
|
|
55
|
+
but they all share the same .zwarm/sessions/ directory.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
orchestrator: The orchestrator instance
|
|
59
|
+
adapter: Adapter name ("codex" or "claude")
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Session manager for the specified adapter
|
|
63
|
+
"""
|
|
64
|
+
# Initialize adapter managers dict if needed
|
|
65
|
+
if not hasattr(orchestrator, "_adapter_managers"):
|
|
66
|
+
orchestrator._adapter_managers = {}
|
|
67
|
+
|
|
68
|
+
# Return cached manager if exists
|
|
69
|
+
if adapter in orchestrator._adapter_managers:
|
|
70
|
+
return orchestrator._adapter_managers[adapter]
|
|
71
|
+
|
|
72
|
+
# Create new manager for this adapter
|
|
73
|
+
from zwarm.sessions import get_session_manager
|
|
74
|
+
manager = get_session_manager(adapter, str(orchestrator.working_dir / ".zwarm"))
|
|
75
|
+
orchestrator._adapter_managers[adapter] = manager
|
|
76
|
+
|
|
77
|
+
return manager
|
|
78
|
+
|
|
79
|
+
|
|
47
80
|
def _truncate(text: str, max_len: int = 200) -> str:
|
|
48
81
|
"""Truncate text with ellipsis."""
|
|
49
82
|
if len(text) <= max_len:
|
|
@@ -53,7 +86,8 @@ def _truncate(text: str, max_len: int = 200) -> str:
|
|
|
53
86
|
|
|
54
87
|
def _format_session_header(session) -> str:
|
|
55
88
|
"""Format a nice session header."""
|
|
56
|
-
|
|
89
|
+
adapter = getattr(session, "adapter", "codex")
|
|
90
|
+
return f"[{session.short_id}] {adapter} ({session.status.value})"
|
|
57
91
|
|
|
58
92
|
|
|
59
93
|
def _get_total_tokens(session) -> int:
|
|
@@ -129,38 +163,43 @@ def delegate(
|
|
|
129
163
|
task: str,
|
|
130
164
|
model: str | None = None,
|
|
131
165
|
working_dir: str | None = None,
|
|
166
|
+
adapter: str = "codex",
|
|
132
167
|
) -> dict[str, Any]:
|
|
133
168
|
"""
|
|
134
|
-
Delegate work to
|
|
135
|
-
|
|
136
|
-
This spawns a codex session - the exact same way `zwarm interactive` does.
|
|
137
|
-
All sessions run async - you get a session_id immediately and poll for results.
|
|
169
|
+
Delegate work to an executor agent. Returns immediately - sessions run async.
|
|
138
170
|
|
|
139
|
-
|
|
171
|
+
Supports multiple adapters:
|
|
172
|
+
- codex: OpenAI's Codex CLI (default, fast, good for code tasks)
|
|
173
|
+
- claude: Claude Code CLI (powerful, good for complex reasoning)
|
|
140
174
|
|
|
141
|
-
|
|
142
|
-
1. delegate(task="
|
|
143
|
-
2. sleep(30)
|
|
144
|
-
3. peek_session(session_id) ->
|
|
145
|
-
4.
|
|
146
|
-
5. check_session(session_id) ->
|
|
175
|
+
WORKFLOW:
|
|
176
|
+
1. delegate(task="...") -> session_id
|
|
177
|
+
2. sleep(30)
|
|
178
|
+
3. peek_session(session_id) -> {is_running: true/false}
|
|
179
|
+
4. If is_running, goto 2
|
|
180
|
+
5. check_session(session_id) -> FULL response
|
|
147
181
|
|
|
148
182
|
Args:
|
|
149
183
|
task: Clear description of what to do. Be specific about requirements.
|
|
150
|
-
model: Model override (
|
|
151
|
-
working_dir: Directory for
|
|
184
|
+
model: Model override (codex: gpt-5.1-codex-mini, claude: sonnet).
|
|
185
|
+
working_dir: Directory for executor to work in (default: orchestrator's dir).
|
|
186
|
+
adapter: Which executor to use - "codex" (default) or "claude".
|
|
152
187
|
|
|
153
188
|
Returns:
|
|
154
|
-
{session_id, status: "running",
|
|
189
|
+
{session_id, status: "running", adapter}
|
|
155
190
|
|
|
156
191
|
Example:
|
|
157
192
|
delegate(task="Add a logout button to the navbar")
|
|
158
|
-
|
|
159
|
-
peek_session(session_id) # Check progress
|
|
160
|
-
|
|
161
|
-
Example with web search (always available):
|
|
162
|
-
delegate(task="Find the latest FastAPI docs and add OAuth2 auth")
|
|
193
|
+
delegate(task="Refactor auth to OAuth2", adapter="claude")
|
|
163
194
|
"""
|
|
195
|
+
# Validate adapter
|
|
196
|
+
if adapter not in ADAPTERS:
|
|
197
|
+
return {
|
|
198
|
+
"success": False,
|
|
199
|
+
"error": f"Unknown adapter: {adapter}. Available: {ADAPTERS}",
|
|
200
|
+
"hint": f"Use one of: {ADAPTERS}",
|
|
201
|
+
}
|
|
202
|
+
|
|
164
203
|
# Validate working directory
|
|
165
204
|
effective_dir, dir_error = _validate_working_dir(
|
|
166
205
|
working_dir,
|
|
@@ -175,24 +214,28 @@ def delegate(
|
|
|
175
214
|
"hint": "Use the default working directory or ask user to update allowed_dirs config",
|
|
176
215
|
}
|
|
177
216
|
|
|
178
|
-
# Get the session manager
|
|
179
|
-
manager =
|
|
217
|
+
# Get the session manager for this adapter
|
|
218
|
+
manager = _get_adapter_manager(self, adapter)
|
|
180
219
|
|
|
181
|
-
# Determine model
|
|
182
|
-
|
|
220
|
+
# Determine model (defaults vary by adapter)
|
|
221
|
+
if model:
|
|
222
|
+
effective_model = model
|
|
223
|
+
elif self.config.executor.model:
|
|
224
|
+
effective_model = self.config.executor.model
|
|
225
|
+
else:
|
|
226
|
+
# Use adapter-specific defaults
|
|
227
|
+
effective_model = manager.default_model
|
|
183
228
|
|
|
184
229
|
# Determine sandbox mode
|
|
185
230
|
sandbox = self.config.executor.sandbox or "workspace-write"
|
|
186
231
|
|
|
187
|
-
# Start the session
|
|
188
|
-
# Web search is enabled via .codex/config.toml (symlink to .zwarm/config.toml)
|
|
232
|
+
# Start the session
|
|
189
233
|
session = manager.start_session(
|
|
190
234
|
task=task,
|
|
191
235
|
working_dir=effective_dir,
|
|
192
236
|
model=effective_model,
|
|
193
237
|
sandbox=sandbox,
|
|
194
238
|
source=f"orchestrator:{self.instance_id or 'default'}",
|
|
195
|
-
adapter="codex",
|
|
196
239
|
)
|
|
197
240
|
|
|
198
241
|
# Return immediately - session runs in background
|
|
@@ -202,6 +245,8 @@ def delegate(
|
|
|
202
245
|
"session_id": session.id,
|
|
203
246
|
"status": "running",
|
|
204
247
|
"task": _truncate(task, 100),
|
|
248
|
+
"adapter": adapter,
|
|
249
|
+
"model": effective_model,
|
|
205
250
|
"hint": "Use sleep() then check_session(session_id) to monitor progress",
|
|
206
251
|
}
|
|
207
252
|
|
|
@@ -213,15 +258,18 @@ def converse(
|
|
|
213
258
|
message: str,
|
|
214
259
|
) -> dict[str, Any]:
|
|
215
260
|
"""
|
|
216
|
-
Continue a conversation with a
|
|
261
|
+
Continue a conversation with a session.
|
|
217
262
|
|
|
218
263
|
This injects a follow-up message into the session, providing the
|
|
219
264
|
conversation history as context. Like chatting with a developer.
|
|
220
265
|
Returns immediately - use sleep() + check_session() to poll for the response.
|
|
221
266
|
|
|
267
|
+
Works with any adapter (codex or claude) - automatically uses the
|
|
268
|
+
correct adapter based on the session's original adapter.
|
|
269
|
+
|
|
222
270
|
Args:
|
|
223
271
|
session_id: The session to continue (from delegate() result).
|
|
224
|
-
message: Your next message
|
|
272
|
+
message: Your next message.
|
|
225
273
|
|
|
226
274
|
Returns:
|
|
227
275
|
{session_id, turn, status: "running"}
|
|
@@ -231,10 +279,10 @@ def converse(
|
|
|
231
279
|
sleep(30)
|
|
232
280
|
check_session(session_id) # Get response
|
|
233
281
|
"""
|
|
234
|
-
|
|
282
|
+
# First get session to determine adapter
|
|
283
|
+
default_manager = _get_session_manager(self)
|
|
284
|
+
session = default_manager.get_session(session_id)
|
|
235
285
|
|
|
236
|
-
# Get current session
|
|
237
|
-
session = manager.get_session(session_id)
|
|
238
286
|
if not session:
|
|
239
287
|
return {
|
|
240
288
|
"success": False,
|
|
@@ -258,8 +306,12 @@ def converse(
|
|
|
258
306
|
"hint": "Start a new session with delegate()",
|
|
259
307
|
}
|
|
260
308
|
|
|
309
|
+
# Get the correct adapter manager for this session
|
|
310
|
+
adapter = getattr(session, "adapter", "codex")
|
|
311
|
+
manager = _get_adapter_manager(self, adapter)
|
|
312
|
+
|
|
261
313
|
# Inject the follow-up message
|
|
262
|
-
# This uses
|
|
314
|
+
# This uses the adapter's inject_message() which:
|
|
263
315
|
# 1. Builds context from previous messages
|
|
264
316
|
# 2. Starts a new turn with the context + new message (background process)
|
|
265
317
|
updated_session = manager.inject_message(session_id, message)
|
|
@@ -278,6 +330,7 @@ def converse(
|
|
|
278
330
|
"session_id": session_id,
|
|
279
331
|
"turn": updated_session.turn,
|
|
280
332
|
"status": "running",
|
|
333
|
+
"adapter": adapter,
|
|
281
334
|
"you_said": _truncate(message, 100),
|
|
282
335
|
"hint": "Use sleep() then check_session(session_id) to see the response",
|
|
283
336
|
}
|
|
@@ -289,18 +342,19 @@ def check_session(
|
|
|
289
342
|
session_id: str,
|
|
290
343
|
) -> dict[str, Any]:
|
|
291
344
|
"""
|
|
292
|
-
Check the status of a session.
|
|
345
|
+
Check the status of a session and get the FULL response.
|
|
346
|
+
|
|
347
|
+
This is your primary tool for seeing what an executor accomplished.
|
|
348
|
+
Returns the complete, untruncated response from the agent.
|
|
293
349
|
|
|
294
|
-
Use this
|
|
295
|
-
|
|
296
|
-
- Get current status and message count
|
|
297
|
-
- View the latest response
|
|
350
|
+
Use this after peek_session() shows the session is done, or when
|
|
351
|
+
you need to see the full details of what was accomplished.
|
|
298
352
|
|
|
299
353
|
Args:
|
|
300
354
|
session_id: The session to check.
|
|
301
355
|
|
|
302
356
|
Returns:
|
|
303
|
-
{session_id, status,
|
|
357
|
+
{session_id, status, response (FULL), tokens, runtime}
|
|
304
358
|
"""
|
|
305
359
|
manager = _get_session_manager(self)
|
|
306
360
|
|
|
@@ -312,12 +366,12 @@ def check_session(
|
|
|
312
366
|
"hint": "Use list_sessions() to see available sessions",
|
|
313
367
|
}
|
|
314
368
|
|
|
315
|
-
# Get latest response
|
|
369
|
+
# Get latest response - FULL, not truncated
|
|
316
370
|
response_text = ""
|
|
317
371
|
messages = manager.get_messages(session_id)
|
|
318
372
|
for msg in reversed(messages):
|
|
319
373
|
if msg.role == "assistant":
|
|
320
|
-
response_text = msg.content
|
|
374
|
+
response_text = msg.content # Full content, no truncation
|
|
321
375
|
break
|
|
322
376
|
|
|
323
377
|
# Build log path
|
|
@@ -331,8 +385,8 @@ def check_session(
|
|
|
331
385
|
"is_running": session.is_running,
|
|
332
386
|
"turn": session.turn,
|
|
333
387
|
"message_count": len(messages),
|
|
334
|
-
"task": _truncate(session.task, 80),
|
|
335
|
-
"response":
|
|
388
|
+
"task": _truncate(session.task, 80), # Task can stay truncated
|
|
389
|
+
"response": response_text if response_text else "(no response yet)", # FULL response
|
|
336
390
|
"tokens": _get_total_tokens(session),
|
|
337
391
|
"runtime": session.runtime,
|
|
338
392
|
"log_file": log_path,
|
|
@@ -353,15 +407,21 @@ def peek_session(
|
|
|
353
407
|
session_id: str,
|
|
354
408
|
) -> dict[str, Any]:
|
|
355
409
|
"""
|
|
356
|
-
Quick peek at a session - minimal info for
|
|
410
|
+
Quick peek at a session - minimal info for FAST POLLING.
|
|
411
|
+
|
|
412
|
+
Use this in your polling loop to check if a session is done:
|
|
413
|
+
1. delegate() -> start work
|
|
414
|
+
2. sleep(30)
|
|
415
|
+
3. peek_session() -> is_running? If yes, goto 2
|
|
416
|
+
4. check_session() -> get FULL response
|
|
357
417
|
|
|
358
|
-
Returns
|
|
418
|
+
Returns truncated preview only. Once done, use check_session() for full response.
|
|
359
419
|
|
|
360
420
|
Args:
|
|
361
421
|
session_id: The session to peek at.
|
|
362
422
|
|
|
363
423
|
Returns:
|
|
364
|
-
{session_id, status, latest_message}
|
|
424
|
+
{session_id, status, is_running, latest_message (truncated preview)}
|
|
365
425
|
"""
|
|
366
426
|
manager = _get_session_manager(self)
|
|
367
427
|
|
|
@@ -393,18 +453,23 @@ def get_trajectory(
|
|
|
393
453
|
full: bool = False,
|
|
394
454
|
) -> dict[str, Any]:
|
|
395
455
|
"""
|
|
396
|
-
Get the
|
|
456
|
+
Get the step-by-step trajectory of what the agent did.
|
|
397
457
|
|
|
398
|
-
Shows reasoning, commands, tool calls, and responses in order.
|
|
399
|
-
|
|
400
|
-
the
|
|
458
|
+
Shows reasoning, commands, tool calls, and responses in execution order.
|
|
459
|
+
Use this to understand HOW the agent approached a task, debug failures,
|
|
460
|
+
or verify the agent took the right steps.
|
|
401
461
|
|
|
402
462
|
Args:
|
|
403
463
|
session_id: The session to get trajectory for.
|
|
404
|
-
full: If True, include
|
|
464
|
+
full: If True, include FULL untruncated content for all steps.
|
|
465
|
+
If False (default), returns concise summaries.
|
|
405
466
|
|
|
406
467
|
Returns:
|
|
407
|
-
{steps: [...], step_count}
|
|
468
|
+
{steps: ["[thinking] ...", "[command] $ ...", "[response] ..."], step_count}
|
|
469
|
+
|
|
470
|
+
When to use:
|
|
471
|
+
- check_session() -> what did the agent conclude? (FULL response)
|
|
472
|
+
- get_trajectory() -> what steps did the agent take? (step-by-step)
|
|
408
473
|
"""
|
|
409
474
|
manager = _get_session_manager(self)
|
|
410
475
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zwarm
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.7.0
|
|
4
4
|
Summary: Multi-Agent CLI Orchestration Research Platform
|
|
5
5
|
Requires-Python: <3.14,>=3.13
|
|
6
6
|
Requires-Dist: prompt-toolkit>=3.0.52
|
|
@@ -13,26 +13,30 @@ Description-Content-Type: text/markdown
|
|
|
13
13
|
|
|
14
14
|
# zwarm
|
|
15
15
|
|
|
16
|
-
Multi-agent CLI for orchestrating coding agents. Spawn, manage, and converse with multiple
|
|
16
|
+
Multi-agent CLI for orchestrating coding agents. Spawn, manage, and converse with multiple coding agent sessions in parallel.
|
|
17
|
+
|
|
18
|
+
**Supports both [Codex CLI](https://github.com/openai/codex) and [Claude Code CLI](https://claude.com/claude-code).**
|
|
17
19
|
|
|
18
20
|
## Installation
|
|
19
21
|
|
|
20
22
|
```bash
|
|
21
|
-
# From
|
|
22
|
-
|
|
23
|
-
uv sync
|
|
23
|
+
# From PyPI
|
|
24
|
+
pip install zwarm
|
|
24
25
|
|
|
25
|
-
# Or
|
|
26
|
-
uv pip install
|
|
26
|
+
# Or with uv
|
|
27
|
+
uv pip install zwarm
|
|
27
28
|
```
|
|
28
29
|
|
|
29
30
|
**Requirements:**
|
|
30
31
|
- Python 3.13+
|
|
31
|
-
-
|
|
32
|
+
- At least one of:
|
|
33
|
+
- `codex` CLI installed and authenticated (OpenAI)
|
|
34
|
+
- `claude` CLI installed and authenticated (Anthropic)
|
|
32
35
|
|
|
33
36
|
**Environment:**
|
|
34
37
|
```bash
|
|
35
38
|
export OPENAI_API_KEY="sk-..." # Required for Codex
|
|
39
|
+
export ANTHROPIC_API_KEY="sk-..." # Required for Claude
|
|
36
40
|
export WEAVE_PROJECT="entity/zwarm" # Optional: Weave tracing
|
|
37
41
|
```
|
|
38
42
|
|
|
@@ -78,6 +82,19 @@ zwarm interactive
|
|
|
78
82
|
|
|
79
83
|
---
|
|
80
84
|
|
|
85
|
+
## Multi-Adapter Support
|
|
86
|
+
|
|
87
|
+
zwarm supports multiple executor backends:
|
|
88
|
+
|
|
89
|
+
| Adapter | CLI | Models | Config |
|
|
90
|
+
|---------|-----|--------|--------|
|
|
91
|
+
| **Codex** | `codex` | gpt-5.1-codex-mini, etc. | `.zwarm/codex.toml` |
|
|
92
|
+
| **Claude** | `claude` | sonnet, opus, haiku | `.zwarm/claude.toml` |
|
|
93
|
+
|
|
94
|
+
You can mix adapters in the same session - for example, use Claude Opus for complex reasoning tasks and Codex Mini for quick edits.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
81
98
|
## Pilot Mode
|
|
82
99
|
|
|
83
100
|
**You chat with an LLM that delegates to coding agents.** Best of both worlds - LLM intelligence with human oversight.
|
|
@@ -85,12 +102,14 @@ zwarm interactive
|
|
|
85
102
|
```bash
|
|
86
103
|
zwarm pilot
|
|
87
104
|
zwarm pilot --task "Add user authentication"
|
|
105
|
+
zwarm pilot --resume # Resume previous session
|
|
88
106
|
```
|
|
89
107
|
|
|
90
108
|
### Features
|
|
91
109
|
|
|
92
110
|
- **Conversational**: Chat naturally, the LLM handles delegation
|
|
93
111
|
- **Checkpoints**: Every turn is saved, time-travel with `:goto`
|
|
112
|
+
- **Resume**: Continue where you left off with `--resume`
|
|
94
113
|
- **Multiline input**: Use `"""` for pasting large prompts
|
|
95
114
|
- **Status bar**: See token usage, cost estimates, context window
|
|
96
115
|
|
|
@@ -103,7 +122,8 @@ zwarm pilot --task "Add user authentication"
|
|
|
103
122
|
| `:history` | Show turn checkpoints |
|
|
104
123
|
| `:goto T3` | Jump back to turn 3 |
|
|
105
124
|
| `:sessions` | List executor sessions |
|
|
106
|
-
| `:
|
|
125
|
+
| `:save` | Save current state |
|
|
126
|
+
| `:quit` | Exit (auto-saves) |
|
|
107
127
|
|
|
108
128
|
### Example
|
|
109
129
|
|
|
@@ -162,7 +182,7 @@ zwarm interactive
|
|
|
162
182
|
| Command | Description |
|
|
163
183
|
|---------|-------------|
|
|
164
184
|
| `spawn "task" [--search]` | Start a new session (--search enables web) |
|
|
165
|
-
| `ls` | Dashboard of all sessions (with costs) |
|
|
185
|
+
| `ls` | Dashboard of all sessions (with costs, models) |
|
|
166
186
|
| `? ID` / `peek ID` | Quick status check |
|
|
167
187
|
| `show ID` | Full session details |
|
|
168
188
|
| `traj ID` | Show trajectory (steps taken) |
|
|
@@ -170,6 +190,7 @@ zwarm interactive
|
|
|
170
190
|
| `c ID "msg"` | Continue conversation |
|
|
171
191
|
| `kill ID \| all` | Stop session(s) |
|
|
172
192
|
| `rm ID \| all` | Delete session(s) |
|
|
193
|
+
| `!command` | Run shell command (e.g., `!git status`) |
|
|
173
194
|
| `help` | Show all commands |
|
|
174
195
|
| `quit` | Exit |
|
|
175
196
|
|
|
@@ -188,9 +209,9 @@ $ zwarm interactive
|
|
|
188
209
|
> ls
|
|
189
210
|
⟳ 2 running
|
|
190
211
|
|
|
191
|
-
ID │ │ Task │ Tokens │ Cost
|
|
192
|
-
abc123 │ ⟳ │ Add tests for the auth... │ 5,234 │ $0.052
|
|
193
|
-
def456 │ ⟳ │ Fix type errors in utils... │ 2,100 │ $0.021
|
|
212
|
+
ID │ │ Task │ Model │ Tokens │ Cost
|
|
213
|
+
abc123 │ ⟳ │ Add tests for the auth... │ codex-mini │ 5,234 │ $0.052
|
|
214
|
+
def456 │ ⟳ │ Fix type errors in utils... │ codex-mini │ 2,100 │ $0.021
|
|
194
215
|
|
|
195
216
|
> watch abc123
|
|
196
217
|
Watching abc123... (Ctrl+C to stop)
|
|
@@ -201,6 +222,11 @@ Watching abc123... (Ctrl+C to stop)
|
|
|
201
222
|
> c abc123 "Also add edge case tests"
|
|
202
223
|
✓ Injected message, session running
|
|
203
224
|
|
|
225
|
+
> !git status
|
|
226
|
+
On branch main
|
|
227
|
+
Changes not staged for commit:
|
|
228
|
+
...
|
|
229
|
+
|
|
204
230
|
> kill all
|
|
205
231
|
✓ Killed abc123
|
|
206
232
|
✓ Killed def456
|
|
@@ -225,7 +251,7 @@ The orchestrator LLM has access to:
|
|
|
225
251
|
|
|
226
252
|
| Tool | Description |
|
|
227
253
|
|------|-------------|
|
|
228
|
-
| `delegate(task,
|
|
254
|
+
| `delegate(task, adapter="codex")` | Start a new coding session |
|
|
229
255
|
| `converse(id, msg)` | Continue a session |
|
|
230
256
|
| `check_session(id)` | Get full session details |
|
|
231
257
|
| `peek_session(id)` | Quick status check |
|
|
@@ -235,7 +261,9 @@ The orchestrator LLM has access to:
|
|
|
235
261
|
|
|
236
262
|
**Async-first**: All sessions run in the background. The orchestrator uses `sleep()` to wait, then checks on progress.
|
|
237
263
|
|
|
238
|
-
**
|
|
264
|
+
**Multi-adapter**: Pass `adapter="claude"` or `adapter="codex"` to `delegate()` to choose the backend.
|
|
265
|
+
|
|
266
|
+
**Web Search**: Enable `web_search=True` in config for tasks needing current info (API docs, latest releases, etc.).
|
|
239
267
|
|
|
240
268
|
### Watchers
|
|
241
269
|
|
|
@@ -285,9 +313,9 @@ spawn → running → completed/failed/killed
|
|
|
285
313
|
|
|
286
314
|
```
|
|
287
315
|
.zwarm/sessions/<uuid>/
|
|
288
|
-
├── meta.json # Status, task, model, tokens, cost
|
|
316
|
+
├── meta.json # Status, task, model, adapter, tokens, cost
|
|
289
317
|
└── turns/
|
|
290
|
-
├── turn_1.jsonl # Raw
|
|
318
|
+
├── turn_1.jsonl # Raw executor output for turn 1
|
|
291
319
|
├── turn_2.jsonl # Output after continue
|
|
292
320
|
└── ...
|
|
293
321
|
```
|
|
@@ -305,6 +333,7 @@ zwarm init
|
|
|
305
333
|
This creates:
|
|
306
334
|
- `.zwarm/config.toml` - Runtime settings (Weave, watchers)
|
|
307
335
|
- `.zwarm/codex.toml` - Codex CLI settings (model, reasoning)
|
|
336
|
+
- `.zwarm/claude.toml` - Claude CLI settings (model, permissions)
|
|
308
337
|
- `zwarm.yaml` - Project context (optional, with `--with-project`)
|
|
309
338
|
|
|
310
339
|
### Config Files
|
|
@@ -318,9 +347,11 @@ project = "your-entity/zwarm"
|
|
|
318
347
|
max_steps = 50
|
|
319
348
|
|
|
320
349
|
[executor]
|
|
321
|
-
adapter = "codex_mcp"
|
|
322
350
|
web_search = false # Enable web search for all delegated sessions
|
|
323
351
|
|
|
352
|
+
[pilot]
|
|
353
|
+
max_steps_per_turn = 25
|
|
354
|
+
|
|
324
355
|
[watchers]
|
|
325
356
|
enabled = ["progress", "budget", "delegation", "delegation_reminder"]
|
|
326
357
|
```
|
|
@@ -329,6 +360,13 @@ enabled = ["progress", "budget", "delegation", "delegation_reminder"]
|
|
|
329
360
|
```toml
|
|
330
361
|
model = "gpt-5.1-codex-mini"
|
|
331
362
|
model_reasoning_effort = "high" # low | medium | high
|
|
363
|
+
full_auto = true
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**`.zwarm/claude.toml`** - Controls the Claude Code CLI:
|
|
367
|
+
```toml
|
|
368
|
+
model = "sonnet" # sonnet | opus | haiku
|
|
369
|
+
full_danger = true # Skip permission prompts
|
|
332
370
|
```
|
|
333
371
|
|
|
334
372
|
**`zwarm.yaml`** - Project-specific context:
|
|
@@ -355,6 +393,7 @@ zwarm init --yes # Quick setup with defaults
|
|
|
355
393
|
|
|
356
394
|
# Interfaces
|
|
357
395
|
zwarm pilot # Conversational LLM guidance (recommended)
|
|
396
|
+
zwarm pilot --resume # Resume previous session
|
|
358
397
|
zwarm interactive # Direct session control REPL
|
|
359
398
|
zwarm orchestrate # Fully autonomous LLM
|
|
360
399
|
|
|
@@ -377,13 +416,15 @@ zwarm reset # Reset .zwarm/ state
|
|
|
377
416
|
zwarm/
|
|
378
417
|
├── src/zwarm/
|
|
379
418
|
│ ├── sessions/ # Session substrate
|
|
380
|
-
│ │
|
|
419
|
+
│ │ ├── base.py # BaseSessionManager (ABC)
|
|
420
|
+
│ │ ├── manager.py # CodexSessionManager
|
|
421
|
+
│ │ └── claude.py # ClaudeSessionManager
|
|
381
422
|
│ ├── cli/
|
|
382
423
|
│ │ ├── main.py # CLI commands
|
|
383
424
|
│ │ ├── pilot.py # Pilot REPL
|
|
384
425
|
│ │ └── interactive.py # Interactive REPL
|
|
385
426
|
│ ├── tools/
|
|
386
|
-
│ │ └── delegation.py # Orchestrator tools
|
|
427
|
+
│ │ └── delegation.py # Orchestrator tools (multi-adapter)
|
|
387
428
|
│ ├── core/
|
|
388
429
|
│ │ ├── config.py # Configuration
|
|
389
430
|
│ │ ├── checkpoints.py # Time-travel primitives
|
|
@@ -392,5 +433,13 @@ zwarm/
|
|
|
392
433
|
│ ├── watchers/ # Trajectory alignment
|
|
393
434
|
│ ├── prompts/ # System prompts
|
|
394
435
|
│ └── orchestrator.py # Orchestrator agent
|
|
395
|
-
└──
|
|
436
|
+
└── docs/
|
|
437
|
+
├── CONCEPTS.md # Architecture diagrams
|
|
438
|
+
└── INTERNALS.md # Developer documentation
|
|
396
439
|
```
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## License
|
|
444
|
+
|
|
445
|
+
MIT
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
zwarm/__init__.py,sha256=3i3LMjHwIzE-LFIS2aUrwv3EZmpkvVMe-xj1h97rcSM,837
|
|
2
|
-
zwarm/orchestrator.py,sha256=
|
|
2
|
+
zwarm/orchestrator.py,sha256=CqZpe4h8-Z7rpy-boN2W9-sZdFfLONOnNMp2MJP2pbc,25292
|
|
3
3
|
zwarm/test_orchestrator_watchers.py,sha256=QpoaehPU7ekT4XshbTOWnJ2H0wRveV3QOZjxbgyJJLY,807
|
|
4
4
|
zwarm/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
zwarm/cli/interactive.py,sha256=
|
|
6
|
-
zwarm/cli/main.py,sha256=
|
|
7
|
-
zwarm/cli/pilot.py,sha256=
|
|
5
|
+
zwarm/cli/interactive.py,sha256=81w9vw6yoqFS6l973OPgoNEgV9Ngcy-3Gj_LhHhRtto,41641
|
|
6
|
+
zwarm/cli/main.py,sha256=vQGgbAkg_8ei5UTF-IvTtRv3Vxb85NNApVECwT1ghlg,77443
|
|
7
|
+
zwarm/cli/pilot.py,sha256=13eKX5yMaSQh16nHAqBsInVHmBiAy9DxUfCvNBHrgCI,42512
|
|
8
8
|
zwarm/core/__init__.py,sha256=nEdpEHMFo0gEEKgX-eKHabyOdrOI6UXfWqLu3FfZDao,376
|
|
9
9
|
zwarm/core/checkpoints.py,sha256=D6sXCMB7Sa1kchQ9_lQx_rabwc5-_7jbuynWgA1nkNY,6560
|
|
10
10
|
zwarm/core/compact.py,sha256=Y8C7Gs-5-WOU43WRvQ863Qzd5xtuEqR6Aw3r2p8_-i8,10907
|
|
11
11
|
zwarm/core/config.py,sha256=m3Vm6U_BNtEDu_cz2d6E3p_RNQfRHWaq-946mDru9-8,12656
|
|
12
|
-
zwarm/core/costs.py,sha256=
|
|
13
|
-
zwarm/core/environment.py,sha256=
|
|
12
|
+
zwarm/core/costs.py,sha256=Z-5o-ZQWRCfFv0mTHev4Ke1AzyXKhXWO6ss7S8eBX9U,1485
|
|
13
|
+
zwarm/core/environment.py,sha256=v7wwVCTIOt_qfiJEe774oM4vIYnlb28s6LJXucJdjoo,8735
|
|
14
14
|
zwarm/core/models.py,sha256=PrC3okRBVJxISUa1Fax4KkagqLT6Xub-kTxC9drN0sY,10083
|
|
15
|
+
zwarm/core/registry.py,sha256=-3mwW4MZ8LKM9SuNBR-r5KWFcQx5F7gqnKfghikxzAI,9484
|
|
15
16
|
zwarm/core/state.py,sha256=MzrvODKEiJovI7YI1jajW4uukineZ3ezmW5oQinMgjg,11563
|
|
16
17
|
zwarm/core/test_compact.py,sha256=WSdjCB5t4YMcknsrkmJIUsVOPY28s4y9GnDmu3Z4BFw,11878
|
|
17
18
|
zwarm/core/test_config.py,sha256=bXXd3OHhK-ndC7wAxePWIdpu73s4O1eScxi3xDzrZwA,4828
|
|
@@ -19,10 +20,12 @@ zwarm/core/test_models.py,sha256=sWTIhMZvuLP5AooGR6y8OR2EyWydqVfhmGrE7NPBBnk,845
|
|
|
19
20
|
zwarm/prompts/__init__.py,sha256=DI307o712F8qQyDt5vwnFgpVBrxpKwjhr0MaBHLzr9E,334
|
|
20
21
|
zwarm/prompts/orchestrator.py,sha256=AkVbEpT91QbYFjUYOzm0d37wXrpm0esLBD1MG_W-3FI,15367
|
|
21
22
|
zwarm/prompts/pilot.py,sha256=BcaV04-43FZyrtmoqCbA7DqnTlQ330TcDp9wNGhRojo,5586
|
|
22
|
-
zwarm/sessions/__init__.py,sha256=
|
|
23
|
-
zwarm/sessions/
|
|
23
|
+
zwarm/sessions/__init__.py,sha256=5fPkl6JRS_GwPn9hi5iv3dzIpGWu_yghPtvPZdujhnM,1728
|
|
24
|
+
zwarm/sessions/base.py,sha256=UA5E39xDx5q4qX2rUcvhSX7cfrCEqTS3F9Tj-ubmrJA,16538
|
|
25
|
+
zwarm/sessions/claude.py,sha256=hBP_TpNFJjR29IRGJFB3rlG7Z9uWEYSbBGV61tpIr00,16672
|
|
26
|
+
zwarm/sessions/manager.py,sha256=Vq5PePzKfy658EVG24SFsUMXQc1OGgOm8vdOX_WPMF8,18530
|
|
24
27
|
zwarm/tools/__init__.py,sha256=FpqxwXJA6-fQ7C-oLj30jjK_0qqcE7MbI0dQuaB56kU,290
|
|
25
|
-
zwarm/tools/delegation.py,sha256=
|
|
28
|
+
zwarm/tools/delegation.py,sha256=Pg4aZ6JlInN9wLE7rmuGCHct5Nf_HUSnWkUjLEnTzWc,24050
|
|
26
29
|
zwarm/watchers/__init__.py,sha256=a96s7X6ruYkF2ItWWOZ3Q5QUOMOoeCW4Vz8XXcYLXPM,956
|
|
27
30
|
zwarm/watchers/base.py,sha256=r1GoPlj06nOT2xp4fghfSjxbRyFFFQUB6HpZbEyO2OY,3834
|
|
28
31
|
zwarm/watchers/builtin.py,sha256=IL5QwwKOIqWEfJ_uQWb321Px4i5OLtI_vnWQMudqKoA,19064
|
|
@@ -30,7 +33,7 @@ zwarm/watchers/llm_watcher.py,sha256=yJGpE3BGKNZX3qgPsiNtJ5d3UJpiTT1V-A-Rh4AiMYM
|
|
|
30
33
|
zwarm/watchers/manager.py,sha256=XZjBVeHjgCUlkTUeHqdvBvHoBC862U1ik0fG6nlRGog,5587
|
|
31
34
|
zwarm/watchers/registry.py,sha256=A9iBIVIFNtO7KPX0kLpUaP8dAK7ozqWLA44ocJGnOw4,1219
|
|
32
35
|
zwarm/watchers/test_watchers.py,sha256=zOsxumBqKfR5ZVGxrNlxz6KcWjkcdp0QhW9WB0_20zM,7855
|
|
33
|
-
zwarm-3.
|
|
34
|
-
zwarm-3.
|
|
35
|
-
zwarm-3.
|
|
36
|
-
zwarm-3.
|
|
36
|
+
zwarm-3.7.0.dist-info/METADATA,sha256=z5xFvdle_1d65TbtyKlYmG63IQPIfbp1Fx-yKve9Ens,11311
|
|
37
|
+
zwarm-3.7.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
38
|
+
zwarm-3.7.0.dist-info/entry_points.txt,sha256=u0OXq4q8d3yJ3EkUXwZfkS-Y8Lcy0F8cWrcQfoRxM6Q,46
|
|
39
|
+
zwarm-3.7.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|