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/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 SAME CodexSessionManager that `zwarm interactive` uses - no special
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 codex session
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 CodexSessionManager - the SINGLE source of truth for sessions.
39
+ Get the default session manager for list/get operations.
33
40
 
34
- Both `zwarm interactive` and `zwarm orchestrate` use the same session manager.
35
- The orchestrator is just another user that happens to be an LLM.
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
- return f"[{session.short_id}] codex ({session.status.value})"
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 a Codex agent.
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
- Web search is always enabled via .codex/config.toml (set up by `zwarm init`).
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
- Workflow pattern:
142
- 1. delegate(task="Add logout button") -> session_id
143
- 2. sleep(30) -> give it time
144
- 3. peek_session(session_id) -> check if done
145
- 4. Repeat 2-3 if still running
146
- 5. check_session(session_id) -> get full results
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 (default: gpt-5.1-codex-mini).
151
- working_dir: Directory for codex to work in (default: orchestrator's dir).
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", task, hint}
189
+ {session_id, status: "running", adapter}
155
190
 
156
191
  Example:
157
192
  delegate(task="Add a logout button to the navbar")
158
- sleep(30)
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 (same one zwarm interactive uses)
179
- manager = _get_session_manager(self)
217
+ # Get the session manager for this adapter
218
+ manager = _get_adapter_manager(self, adapter)
180
219
 
181
- # Determine model
182
- effective_model = model or self.config.executor.model or "gpt-5.1-codex-mini"
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 using CodexSessionManager
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 codex session.
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 to codex.
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
- manager = _get_session_manager(self)
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 CodexSessionManager.inject_message() which:
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 to:
295
- - Check if an async session has finished
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, messages, response}
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": _truncate(response_text, 500) if response_text else "(no response yet)",
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 fast polling.
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 just status and latest message. Use check_session() for full details.
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 full trajectory of a session - all steps the agent took.
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
- Useful for understanding HOW the agent completed a task, not just
400
- the final result.
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 full untruncated content (default: False for summary view).
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.4.0
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 Codex sessions in parallel.
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 the workspace
22
- cd /path/to/labs
23
- uv sync
23
+ # From PyPI
24
+ pip install zwarm
24
25
 
25
- # Or install directly
26
- uv pip install -e ./zwarm
26
+ # Or with uv
27
+ uv pip install zwarm
27
28
  ```
28
29
 
29
30
  **Requirements:**
30
31
  - Python 3.13+
31
- - `codex` CLI installed and authenticated
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
- | `:quit` | Exit |
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, web_search=False)` | Start a new coding session |
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
- **Web Search**: Pass `web_search=True` to `delegate()` for tasks needing current info (API docs, latest releases, etc.).
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 codex output for turn 1
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
- │ │ └── manager.py # CodexSessionManager (ground truth)
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
- └── STATE.md # Current project state
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=1IitiuhlCRGd512p3ObwiwI8c2zDzq1yzLTs_y6Mv_o,22803
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=S9oi0gvfb7v4kRZvAb686zDTAqElv6dD_oqjcFNPXzM,25911
6
- zwarm/cli/main.py,sha256=ejJWQ6izr49dLgT35Kx0llWBW-QTBNs4_o19ntBeye0,71490
7
- zwarm/cli/pilot.py,sha256=QP3y_9utmvRVs-Hp4orc28ZnvN9hvdOhgOD-YV4Cs-s,40265
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=pLyixKn9hbDvtzQofOqajeK3XxOpoHPpA8nw8QM7j3g,5403
13
- zwarm/core/environment.py,sha256=zrgh0N3Ng4HI2F1gCYkcQVGzjQPKiIFWuRe1OPRuRn0,6558
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=jRibY8IfmNcnkgNmrgK2T81oa1w71wP_KQp9A1hPL7Q,568
23
- zwarm/sessions/manager.py,sha256=bLxwFA9D4EBW3wCC6OqolKcBiZtv8igz0Z7ZGA7EcVc,31559
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=wXKIb7eCL23c5NUwWi1G6JNg1PyXnwW-TNn7NrqchaE,21697
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.4.0.dist-info/METADATA,sha256=sw3IPlN6M89rjJ3GQIH_Pyyyn_AAdw64DaqjgqtDT0E,9670
34
- zwarm-3.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
35
- zwarm-3.4.0.dist-info/entry_points.txt,sha256=u0OXq4q8d3yJ3EkUXwZfkS-Y8Lcy0F8cWrcQfoRxM6Q,46
36
- zwarm-3.4.0.dist-info/RECORD,,
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