zwarm 3.2.1__py3-none-any.whl → 3.4.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/sessions/manager.py CHANGED
@@ -6,6 +6,7 @@ Architecture:
6
6
  - Output is streamed to .zwarm/sessions/<session_id>/output.jsonl
7
7
  - Session metadata stored in meta.json
8
8
  - Can inject follow-up messages by starting new turns with context
9
+ - Config is read from .zwarm/codex.toml and passed via -c overrides
9
10
  """
10
11
 
11
12
  from __future__ import annotations
@@ -15,6 +16,7 @@ import os
15
16
  import signal
16
17
  import subprocess
17
18
  import time
19
+ import tomllib
18
20
  from dataclasses import dataclass, field
19
21
  from datetime import datetime
20
22
  from enum import Enum
@@ -25,16 +27,18 @@ from uuid import uuid4
25
27
 
26
28
  class SessionStatus(str, Enum):
27
29
  """Status of a codex session."""
28
- PENDING = "pending" # Created but not started
29
- RUNNING = "running" # Process is running
30
+
31
+ PENDING = "pending" # Created but not started
32
+ RUNNING = "running" # Process is running
30
33
  COMPLETED = "completed" # Process exited successfully
31
- FAILED = "failed" # Process exited with error
32
- KILLED = "killed" # Manually killed
34
+ FAILED = "failed" # Process exited with error
35
+ KILLED = "killed" # Manually killed
33
36
 
34
37
 
35
38
  @dataclass
36
39
  class SessionMessage:
37
40
  """A message in a session's history."""
41
+
38
42
  role: str # "user", "assistant", "system", "tool"
39
43
  content: str
40
44
  timestamp: str = ""
@@ -61,6 +65,7 @@ class SessionMessage:
61
65
  @dataclass
62
66
  class CodexSession:
63
67
  """A managed Codex session."""
68
+
64
69
  id: str
65
70
  task: str
66
71
  status: SessionStatus
@@ -180,6 +185,48 @@ class CodexSessionManager:
180
185
  self.sessions_dir = self.state_dir / "sessions"
181
186
  self.sessions_dir.mkdir(parents=True, exist_ok=True)
182
187
 
188
+ def _load_codex_config(self) -> dict[str, Any]:
189
+ """
190
+ Load codex.toml from state_dir.
191
+
192
+ Returns parsed TOML as dict, or empty dict if not found.
193
+ """
194
+ codex_toml = self.state_dir / "codex.toml"
195
+ if not codex_toml.exists():
196
+ return {}
197
+ try:
198
+ with open(codex_toml, "rb") as f:
199
+ return tomllib.load(f)
200
+ except Exception:
201
+ return {}
202
+
203
+ def _build_codex_overrides(self, config: dict[str, Any]) -> list[str]:
204
+ """
205
+ Convert codex.toml config to -c override flags.
206
+
207
+ Handles nested sections like [features] and [sandbox_workspace_write].
208
+
209
+ Returns list of ["-c", "key=value", "-c", "key2=value2", ...]
210
+ """
211
+ overrides = []
212
+
213
+ def add_override(key: str, value: Any):
214
+ """Add a -c override for a key=value pair."""
215
+ if isinstance(value, bool):
216
+ value = "true" if value else "false"
217
+ overrides.extend(["-c", f"{key}={value}"])
218
+
219
+ for key, value in config.items():
220
+ if isinstance(value, dict):
221
+ # Nested section like [features] or [sandbox_workspace_write]
222
+ for subkey, subvalue in value.items():
223
+ add_override(f"{key}.{subkey}", subvalue)
224
+ else:
225
+ # Top-level key
226
+ add_override(key, value)
227
+
228
+ return overrides
229
+
183
230
  def _session_dir(self, session_id: str) -> Path:
184
231
  """Get the directory for a session."""
185
232
  return self.sessions_dir / session_id
@@ -228,7 +275,10 @@ class CodexSessionManager:
228
275
  # Update status if process died OR output indicates completion
229
276
  # (output check is more reliable than PID check due to PID reuse)
230
277
  if session.status == SessionStatus.RUNNING:
231
- if self._is_output_complete(session.id, session.turn) or not session.is_running:
278
+ if (
279
+ self._is_output_complete(session.id, session.turn)
280
+ or not session.is_running
281
+ ):
232
282
  self._update_session_status(session)
233
283
 
234
284
  if status is None or session.status == status:
@@ -244,7 +294,10 @@ class CodexSessionManager:
244
294
  session = self._load_session(session_id)
245
295
  if session:
246
296
  if session.status == SessionStatus.RUNNING:
247
- if self._is_output_complete(session.id, session.turn) or not session.is_running:
297
+ if (
298
+ self._is_output_complete(session.id, session.turn)
299
+ or not session.is_running
300
+ ):
248
301
  self._update_session_status(session)
249
302
  return session
250
303
 
@@ -254,7 +307,10 @@ class CodexSessionManager:
254
307
  session = self._load_session(session_dir.name)
255
308
  if session:
256
309
  if session.status == SessionStatus.RUNNING:
257
- if self._is_output_complete(session.id, session.turn) or not session.is_running:
310
+ if (
311
+ self._is_output_complete(session.id, session.turn)
312
+ or not session.is_running
313
+ ):
258
314
  self._update_session_status(session)
259
315
  return session
260
316
 
@@ -280,7 +336,12 @@ class CodexSessionManager:
280
336
  event = json.loads(line)
281
337
  event_type = event.get("type", "")
282
338
  # Check for any completion marker
283
- if event_type in ("turn.completed", "task.completed", "completed", "done"):
339
+ if event_type in (
340
+ "turn.completed",
341
+ "task.completed",
342
+ "completed",
343
+ "done",
344
+ ):
284
345
  return True
285
346
  # Also check for error as a form of completion
286
347
  if event_type == "error":
@@ -325,7 +386,7 @@ class CodexSessionManager:
325
386
  self,
326
387
  task: str,
327
388
  working_dir: Path | None = None,
328
- model: str = "gpt-5.1-codex-mini",
389
+ model: str | None = None,
329
390
  sandbox: str = "workspace-write",
330
391
  source: str = "user",
331
392
  adapter: str = "codex",
@@ -336,18 +397,31 @@ class CodexSessionManager:
336
397
  Args:
337
398
  task: The task description
338
399
  working_dir: Working directory for codex (default: cwd)
339
- model: Model to use
340
- sandbox: Sandbox mode
400
+ model: Model override (default: from codex.toml or gpt-5.1-codex-mini)
401
+ sandbox: Sandbox mode (ignored if full_danger=true in codex.toml)
341
402
  source: Who spawned this session ("user" or "orchestrator:<id>")
342
403
  adapter: Which adapter to use ("codex", "claude_code")
343
404
 
344
405
  Returns:
345
406
  The created session
407
+
408
+ Note:
409
+ Settings are read from .zwarm/codex.toml and passed via -c overrides.
410
+ Run `zwarm init` to set up the config.
346
411
  """
347
412
  session_id = str(uuid4())
348
413
  working_dir = working_dir or Path.cwd()
349
414
  now = datetime.now().isoformat()
350
415
 
416
+ # Load codex config from .zwarm/codex.toml
417
+ codex_config = self._load_codex_config()
418
+
419
+ # Get model from config or use default
420
+ effective_model = model or codex_config.get("model", "gpt-5.1-codex-mini")
421
+
422
+ # Check if full_danger mode is enabled
423
+ full_danger = codex_config.get("full_danger", False)
424
+
351
425
  session = CodexSession(
352
426
  id=session_id,
353
427
  task=task,
@@ -355,7 +429,7 @@ class CodexSessionManager:
355
429
  working_dir=working_dir,
356
430
  created_at=now,
357
431
  updated_at=now,
358
- model=model,
432
+ model=effective_model,
359
433
  turn=1,
360
434
  messages=[SessionMessage(role="user", content=task, timestamp=now)],
361
435
  source=source,
@@ -366,22 +440,28 @@ class CodexSessionManager:
366
440
  session_dir = self._session_dir(session_id)
367
441
  session_dir.mkdir(parents=True, exist_ok=True)
368
442
 
369
- # Build command
370
- cmd = [
371
- "codex", "exec",
443
+ # Build command with -c overrides from codex.toml
444
+ # This ensures each .zwarm dir has its own codex config
445
+ cmd = ["codex"]
446
+
447
+ # Add -c overrides from codex.toml (excluding special keys we handle separately)
448
+ config_for_overrides = {k: v for k, v in codex_config.items() if k not in ("model", "full_danger")}
449
+ cmd.extend(self._build_codex_overrides(config_for_overrides))
450
+
451
+ # Add exec command and flags
452
+ cmd.extend([
453
+ "exec",
372
454
  "--json",
373
- "--full-auto",
374
455
  "--skip-git-repo-check",
375
- "--model", model,
376
- "-C", str(working_dir.absolute()),
377
- ]
378
-
379
- # Add sandbox mode
380
- if sandbox == "danger-full-access":
456
+ "--model",
457
+ effective_model,
458
+ "-C",
459
+ str(working_dir.absolute()),
460
+ ])
461
+
462
+ # Full danger mode bypasses all safety controls
463
+ if full_danger:
381
464
  cmd.append("--dangerously-bypass-approvals-and-sandbox")
382
- elif sandbox == "workspace-write":
383
- # Default codex behavior
384
- pass
385
465
 
386
466
  cmd.extend(["--", task])
387
467
 
@@ -449,18 +529,29 @@ Continue from where you left off, addressing the user's new message."""
449
529
  # Start new turn
450
530
  session.turn += 1
451
531
  now = datetime.now().isoformat()
452
- session.messages.append(SessionMessage(role="user", content=message, timestamp=now))
532
+ session.messages.append(
533
+ SessionMessage(role="user", content=message, timestamp=now)
534
+ )
453
535
 
454
536
  # Build command
455
- cmd = [
456
- "codex", "exec",
457
- "--json",
458
- "--full-auto",
459
- "--skip-git-repo-check",
460
- "--model", session.model,
461
- "-C", str(session.working_dir.absolute()),
462
- "--", augmented_task,
463
- ]
537
+ # Note: --search is a global flag that must come before 'exec'
538
+ cmd = ["codex"]
539
+ if session.web_search:
540
+ cmd.append("--search")
541
+ cmd.extend(
542
+ [
543
+ "exec",
544
+ "--json",
545
+ "--full-auto",
546
+ "--skip-git-repo-check",
547
+ "--model",
548
+ session.model,
549
+ "-C",
550
+ str(session.working_dir.absolute()),
551
+ "--",
552
+ augmented_task,
553
+ ]
554
+ )
464
555
 
465
556
  # Start process
466
557
  output_path = self._output_path(session.id, session.turn)
@@ -578,7 +669,9 @@ Continue from where you left off, addressing the user's new message."""
578
669
 
579
670
  return all_messages
580
671
 
581
- def _parse_output(self, output_path: Path) -> tuple[list[SessionMessage], dict[str, int], str | None]:
672
+ def _parse_output(
673
+ self, output_path: Path
674
+ ) -> tuple[list[SessionMessage], dict[str, int], str | None]:
582
675
  """
583
676
  Parse JSONL output from codex exec.
584
677
 
@@ -612,11 +705,13 @@ Continue from where you left off, addressing the user's new message."""
612
705
  if item_type == "agent_message":
613
706
  text = item.get("text", "")
614
707
  if text:
615
- messages.append(SessionMessage(
616
- role="assistant",
617
- content=text,
618
- timestamp=datetime.now().isoformat(),
619
- ))
708
+ messages.append(
709
+ SessionMessage(
710
+ role="assistant",
711
+ content=text,
712
+ timestamp=datetime.now().isoformat(),
713
+ )
714
+ )
620
715
 
621
716
  elif item_type == "reasoning":
622
717
  # Could optionally capture reasoning
@@ -625,19 +720,23 @@ Continue from where you left off, addressing the user's new message."""
625
720
  elif item_type == "function_call":
626
721
  # Track tool calls
627
722
  func_name = item.get("name", "unknown")
628
- messages.append(SessionMessage(
629
- role="tool",
630
- content=f"[Calling: {func_name}]",
631
- metadata={"function": func_name},
632
- ))
723
+ messages.append(
724
+ SessionMessage(
725
+ role="tool",
726
+ content=f"[Calling: {func_name}]",
727
+ metadata={"function": func_name},
728
+ )
729
+ )
633
730
 
634
731
  elif item_type == "function_call_output":
635
732
  output = item.get("output", "")
636
733
  if output and len(output) < 500:
637
- messages.append(SessionMessage(
638
- role="tool",
639
- content=f"[Output]: {output[:500]}",
640
- ))
734
+ messages.append(
735
+ SessionMessage(
736
+ role="tool",
737
+ content=f"[Output]: {output[:500]}",
738
+ )
739
+ )
641
740
 
642
741
  elif event_type == "turn.completed":
643
742
  turn_usage = event.get("usage", {})
@@ -645,14 +744,18 @@ Continue from where you left off, addressing the user's new message."""
645
744
  usage[key] = usage.get(key, 0) + value
646
745
  # Compute total_tokens if not present
647
746
  if "total_tokens" not in usage:
648
- usage["total_tokens"] = usage.get("input_tokens", 0) + usage.get("output_tokens", 0)
747
+ usage["total_tokens"] = usage.get("input_tokens", 0) + usage.get(
748
+ "output_tokens", 0
749
+ )
649
750
 
650
751
  elif event_type == "error":
651
752
  error = event.get("message", str(event))
652
753
 
653
754
  return messages, usage, error
654
755
 
655
- def get_trajectory(self, session_id: str, full: bool = False, max_output_len: int = 200) -> list[dict]:
756
+ def get_trajectory(
757
+ self, session_id: str, full: bool = False, max_output_len: int = 200
758
+ ) -> list[dict]:
656
759
  """
657
760
  Get the full trajectory of a session - all steps in order.
658
761
 
@@ -699,13 +802,16 @@ Continue from where you left off, addressing the user's new message."""
699
802
  if item_type == "reasoning":
700
803
  text = item.get("text", "")
701
804
  summary_len = max_output_len if full else 100
702
- trajectory.append({
703
- "turn": turn,
704
- "step": step_num,
705
- "type": "reasoning",
706
- "summary": text[:summary_len] + ("..." if len(text) > summary_len else ""),
707
- "full_text": text if full else None,
708
- })
805
+ trajectory.append(
806
+ {
807
+ "turn": turn,
808
+ "step": step_num,
809
+ "type": "reasoning",
810
+ "summary": text[:summary_len]
811
+ + ("..." if len(text) > summary_len else ""),
812
+ "full_text": text if full else None,
813
+ }
814
+ )
709
815
 
710
816
  elif item_type == "command_execution":
711
817
  cmd = item.get("command", "")
@@ -715,52 +821,62 @@ Continue from where you left off, addressing the user's new message."""
715
821
  output_preview = output[:max_output_len]
716
822
  if len(output) > max_output_len:
717
823
  output_preview += "..."
718
- trajectory.append({
719
- "turn": turn,
720
- "step": step_num,
721
- "type": "command",
722
- "command": cmd,
723
- "output": output_preview.strip(),
724
- "exit_code": exit_code,
725
- })
824
+ trajectory.append(
825
+ {
826
+ "turn": turn,
827
+ "step": step_num,
828
+ "type": "command",
829
+ "command": cmd,
830
+ "output": output_preview.strip(),
831
+ "exit_code": exit_code,
832
+ }
833
+ )
726
834
 
727
835
  elif item_type == "function_call":
728
836
  func_name = item.get("name", "unknown")
729
837
  args = item.get("arguments", {})
730
838
  args_str = str(args)
731
839
  args_len = max_output_len if full else 100
732
- trajectory.append({
733
- "turn": turn,
734
- "step": step_num,
735
- "type": "tool_call",
736
- "tool": func_name,
737
- "args_preview": args_str[:args_len] + ("..." if len(args_str) > args_len else ""),
738
- "full_args": args if full else None,
739
- })
840
+ trajectory.append(
841
+ {
842
+ "turn": turn,
843
+ "step": step_num,
844
+ "type": "tool_call",
845
+ "tool": func_name,
846
+ "args_preview": args_str[:args_len]
847
+ + ("..." if len(args_str) > args_len else ""),
848
+ "full_args": args if full else None,
849
+ }
850
+ )
740
851
 
741
852
  elif item_type == "function_call_output":
742
853
  output = item.get("output", "")
743
854
  output_preview = output[:max_output_len]
744
855
  if len(output) > max_output_len:
745
856
  output_preview += "..."
746
- trajectory.append({
747
- "turn": turn,
748
- "step": step_num,
749
- "type": "tool_output",
750
- "output": output_preview,
751
- })
857
+ trajectory.append(
858
+ {
859
+ "turn": turn,
860
+ "step": step_num,
861
+ "type": "tool_output",
862
+ "output": output_preview,
863
+ }
864
+ )
752
865
 
753
866
  elif item_type == "agent_message":
754
867
  text = item.get("text", "")
755
868
  summary_len = max_output_len if full else 200
756
- trajectory.append({
757
- "turn": turn,
758
- "step": step_num,
759
- "type": "message",
760
- "summary": text[:summary_len] + ("..." if len(text) > summary_len else ""),
761
- "full_text": text if full else None,
762
- "full_length": len(text),
763
- })
869
+ trajectory.append(
870
+ {
871
+ "turn": turn,
872
+ "step": step_num,
873
+ "type": "message",
874
+ "summary": text[:summary_len]
875
+ + ("..." if len(text) > summary_len else ""),
876
+ "full_text": text if full else None,
877
+ "full_length": len(text),
878
+ }
879
+ )
764
880
 
765
881
  return trajectory
766
882
 
@@ -781,7 +897,11 @@ Continue from where you left off, addressing the user's new message."""
781
897
  cleaned = 0
782
898
 
783
899
  for session in self.list_sessions():
784
- if session.status in (SessionStatus.COMPLETED, SessionStatus.FAILED, SessionStatus.KILLED):
900
+ if session.status in (
901
+ SessionStatus.COMPLETED,
902
+ SessionStatus.FAILED,
903
+ SessionStatus.KILLED,
904
+ ):
785
905
  created = datetime.fromisoformat(session.created_at)
786
906
  if created < cutoff:
787
907
  session_dir = self._session_dir(session.id)
zwarm/tools/delegation.py CHANGED
@@ -136,6 +136,8 @@ def delegate(
136
136
  This spawns a codex session - the exact same way `zwarm interactive` does.
137
137
  All sessions run async - you get a session_id immediately and poll for results.
138
138
 
139
+ Web search is always enabled via .codex/config.toml (set up by `zwarm init`).
140
+
139
141
  Workflow pattern:
140
142
  1. delegate(task="Add logout button") -> session_id
141
143
  2. sleep(30) -> give it time
@@ -155,6 +157,9 @@ def delegate(
155
157
  delegate(task="Add a logout button to the navbar")
156
158
  sleep(30)
157
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")
158
163
  """
159
164
  # Validate working directory
160
165
  effective_dir, dir_error = _validate_working_dir(
@@ -180,7 +185,7 @@ def delegate(
180
185
  sandbox = self.config.executor.sandbox or "workspace-write"
181
186
 
182
187
  # Start the session using CodexSessionManager
183
- # This is the SAME method that `zwarm interactive` uses
188
+ # Web search is enabled via .codex/config.toml (symlink to .zwarm/config.toml)
184
189
  session = manager.start_session(
185
190
  task=task,
186
191
  working_dir=effective_dir,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zwarm
3
- Version: 3.2.1
3
+ Version: 3.4.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
@@ -161,7 +161,7 @@ zwarm interactive
161
161
 
162
162
  | Command | Description |
163
163
  |---------|-------------|
164
- | `spawn "task"` | Start a new session |
164
+ | `spawn "task" [--search]` | Start a new session (--search enables web) |
165
165
  | `ls` | Dashboard of all sessions (with costs) |
166
166
  | `? ID` / `peek ID` | Quick status check |
167
167
  | `show ID` | Full session details |
@@ -225,7 +225,7 @@ The orchestrator LLM has access to:
225
225
 
226
226
  | Tool | Description |
227
227
  |------|-------------|
228
- | `delegate(task)` | Start a new coding session |
228
+ | `delegate(task, web_search=False)` | Start a new coding session |
229
229
  | `converse(id, msg)` | Continue a session |
230
230
  | `check_session(id)` | Get full session details |
231
231
  | `peek_session(id)` | Quick status check |
@@ -235,6 +235,8 @@ The orchestrator LLM has access to:
235
235
 
236
236
  **Async-first**: All sessions run in the background. The orchestrator uses `sleep()` to wait, then checks on progress.
237
237
 
238
+ **Web Search**: Pass `web_search=True` to `delegate()` for tasks needing current info (API docs, latest releases, etc.).
239
+
238
240
  ### Watchers
239
241
 
240
242
  Watchers monitor the orchestrator and intervene when needed:
@@ -317,6 +319,7 @@ max_steps = 50
317
319
 
318
320
  [executor]
319
321
  adapter = "codex_mcp"
322
+ web_search = false # Enable web search for all delegated sessions
320
323
 
321
324
  [watchers]
322
325
  enabled = ["progress", "budget", "delegation", "delegation_reminder"]
@@ -1,35 +1,28 @@
1
1
  zwarm/__init__.py,sha256=3i3LMjHwIzE-LFIS2aUrwv3EZmpkvVMe-xj1h97rcSM,837
2
- zwarm/orchestrator.py,sha256=JGRGuJP05Nf5QibuWytjQAC_NuGGaGUR3G-tLq4SVxY,23624
2
+ zwarm/orchestrator.py,sha256=1IitiuhlCRGd512p3ObwiwI8c2zDzq1yzLTs_y6Mv_o,22803
3
3
  zwarm/test_orchestrator_watchers.py,sha256=QpoaehPU7ekT4XshbTOWnJ2H0wRveV3QOZjxbgyJJLY,807
4
- zwarm/adapters/__init__.py,sha256=O0b-SfZpb6txeNqFkXZ2aaf34yLFYreznyrAV25jF_Q,656
5
- zwarm/adapters/base.py,sha256=fZlQviTgVvOcwnxduTla6WuM6FzQJ_yoHMW5SxwVgQg,2527
6
- zwarm/adapters/claude_code.py,sha256=vAjsjD-_JjARmC4_FBSILQZmQCBrk_oNHo18a9ubuqk,11481
7
- zwarm/adapters/codex_mcp.py,sha256=EhdkM3gj5hc01AcM1ERhtfZbydK390yN4Pg3dawKIGU,48791
8
- zwarm/adapters/registry.py,sha256=EdyHECaNA5Kv1od64pYFBJyA_r_6I1r_eJTNP1XYLr4,1781
9
- zwarm/adapters/test_codex_mcp.py,sha256=0qhVzxn_KF-XUS30gXSJKwMdR3kWGsDY9iPk1Ihqn3w,10698
10
- zwarm/adapters/test_registry.py,sha256=otxcVDONwFCMisyANToF3iy7Y8dSbCL8bTmZNhxNuF4,2383
11
4
  zwarm/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- zwarm/cli/interactive.py,sha256=0-KFo8tCHdlycgzprW2QCJaN5IKAo2CS3t3n6Mhfd4Q,25893
13
- zwarm/cli/main.py,sha256=bekPGLv6YbWOs8npbcurneC-i6H0GhMsKso2R5Uf3vU,70220
14
- zwarm/cli/pilot.py,sha256=ev6UfniLJKDR_GMSEA8JjWdTbtfRK0MLGk3VBCXpwTg,38011
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
15
8
  zwarm/core/__init__.py,sha256=nEdpEHMFo0gEEKgX-eKHabyOdrOI6UXfWqLu3FfZDao,376
16
9
  zwarm/core/checkpoints.py,sha256=D6sXCMB7Sa1kchQ9_lQx_rabwc5-_7jbuynWgA1nkNY,6560
17
10
  zwarm/core/compact.py,sha256=Y8C7Gs-5-WOU43WRvQ863Qzd5xtuEqR6Aw3r2p8_-i8,10907
18
- zwarm/core/config.py,sha256=331i4io9uEnloFwUMjTPJ5_lQFKJR1nhTpA4SPfSpiI,11748
11
+ zwarm/core/config.py,sha256=m3Vm6U_BNtEDu_cz2d6E3p_RNQfRHWaq-946mDru9-8,12656
19
12
  zwarm/core/costs.py,sha256=pLyixKn9hbDvtzQofOqajeK3XxOpoHPpA8nw8QM7j3g,5403
20
13
  zwarm/core/environment.py,sha256=zrgh0N3Ng4HI2F1gCYkcQVGzjQPKiIFWuRe1OPRuRn0,6558
21
14
  zwarm/core/models.py,sha256=PrC3okRBVJxISUa1Fax4KkagqLT6Xub-kTxC9drN0sY,10083
22
15
  zwarm/core/state.py,sha256=MzrvODKEiJovI7YI1jajW4uukineZ3ezmW5oQinMgjg,11563
23
16
  zwarm/core/test_compact.py,sha256=WSdjCB5t4YMcknsrkmJIUsVOPY28s4y9GnDmu3Z4BFw,11878
24
- zwarm/core/test_config.py,sha256=26ozyiFOdjFF2c9Q-HDfFM6GOLfgw_5FZ55nTDMNYA8,4888
17
+ zwarm/core/test_config.py,sha256=bXXd3OHhK-ndC7wAxePWIdpu73s4O1eScxi3xDzrZwA,4828
25
18
  zwarm/core/test_models.py,sha256=sWTIhMZvuLP5AooGR6y8OR2EyWydqVfhmGrE7NPBBnk,8450
26
19
  zwarm/prompts/__init__.py,sha256=DI307o712F8qQyDt5vwnFgpVBrxpKwjhr0MaBHLzr9E,334
27
20
  zwarm/prompts/orchestrator.py,sha256=AkVbEpT91QbYFjUYOzm0d37wXrpm0esLBD1MG_W-3FI,15367
28
21
  zwarm/prompts/pilot.py,sha256=BcaV04-43FZyrtmoqCbA7DqnTlQ330TcDp9wNGhRojo,5586
29
22
  zwarm/sessions/__init__.py,sha256=jRibY8IfmNcnkgNmrgK2T81oa1w71wP_KQp9A1hPL7Q,568
30
- zwarm/sessions/manager.py,sha256=Aq7Wh-WW7ZMP8LgGa3g70wfGg6E2GYjJOBucy6HUfGc,27700
23
+ zwarm/sessions/manager.py,sha256=bLxwFA9D4EBW3wCC6OqolKcBiZtv8igz0Z7ZGA7EcVc,31559
31
24
  zwarm/tools/__init__.py,sha256=FpqxwXJA6-fQ7C-oLj30jjK_0qqcE7MbI0dQuaB56kU,290
32
- zwarm/tools/delegation.py,sha256=lw4jJk1zVpgPhuvkUVfjUWFFMJotpJXXVY1YfqiP_Iw,21468
25
+ zwarm/tools/delegation.py,sha256=wXKIb7eCL23c5NUwWi1G6JNg1PyXnwW-TNn7NrqchaE,21697
33
26
  zwarm/watchers/__init__.py,sha256=a96s7X6ruYkF2ItWWOZ3Q5QUOMOoeCW4Vz8XXcYLXPM,956
34
27
  zwarm/watchers/base.py,sha256=r1GoPlj06nOT2xp4fghfSjxbRyFFFQUB6HpZbEyO2OY,3834
35
28
  zwarm/watchers/builtin.py,sha256=IL5QwwKOIqWEfJ_uQWb321Px4i5OLtI_vnWQMudqKoA,19064
@@ -37,7 +30,7 @@ zwarm/watchers/llm_watcher.py,sha256=yJGpE3BGKNZX3qgPsiNtJ5d3UJpiTT1V-A-Rh4AiMYM
37
30
  zwarm/watchers/manager.py,sha256=XZjBVeHjgCUlkTUeHqdvBvHoBC862U1ik0fG6nlRGog,5587
38
31
  zwarm/watchers/registry.py,sha256=A9iBIVIFNtO7KPX0kLpUaP8dAK7ozqWLA44ocJGnOw4,1219
39
32
  zwarm/watchers/test_watchers.py,sha256=zOsxumBqKfR5ZVGxrNlxz6KcWjkcdp0QhW9WB0_20zM,7855
40
- zwarm-3.2.1.dist-info/METADATA,sha256=JkWHnaILTCMmy_n6Xam4uLXuRU5XFDPSiOOzleaYQvg,9429
41
- zwarm-3.2.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
42
- zwarm-3.2.1.dist-info/entry_points.txt,sha256=u0OXq4q8d3yJ3EkUXwZfkS-Y8Lcy0F8cWrcQfoRxM6Q,46
43
- zwarm-3.2.1.dist-info/RECORD,,
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,,
@@ -1,21 +0,0 @@
1
- """
2
- Adapters: Executor wrappers for CLI coding agents.
3
-
4
- Adapters provide a unified interface to different coding CLIs (Codex, Claude Code).
5
- Use the registry to discover and instantiate adapters by name.
6
- """
7
-
8
- from zwarm.adapters.base import ExecutorAdapter
9
- from zwarm.adapters.registry import register_adapter, get_adapter, list_adapters, adapter_exists
10
-
11
- # Import built-in adapters to register them
12
- from zwarm.adapters import codex_mcp as _codex_mcp # noqa: F401
13
- from zwarm.adapters import claude_code as _claude_code # noqa: F401
14
-
15
- __all__ = [
16
- "ExecutorAdapter",
17
- "register_adapter",
18
- "get_adapter",
19
- "list_adapters",
20
- "adapter_exists",
21
- ]