zwarm 1.3.10__py3-none-any.whl → 2.0.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/adapters/codex_mcp.py +475 -181
- zwarm/cli/main.py +483 -143
- zwarm/core/config.py +2 -0
- zwarm/orchestrator.py +41 -2
- zwarm/prompts/orchestrator.py +29 -13
- zwarm/sessions/__init__.py +2 -0
- zwarm/sessions/manager.py +87 -8
- zwarm/tools/delegation.py +356 -324
- zwarm/watchers/builtin.py +100 -6
- zwarm-2.0.0.dist-info/METADATA +309 -0
- {zwarm-1.3.10.dist-info → zwarm-2.0.0.dist-info}/RECORD +13 -13
- zwarm-1.3.10.dist-info/METADATA +0 -525
- {zwarm-1.3.10.dist-info → zwarm-2.0.0.dist-info}/WHEEL +0 -0
- {zwarm-1.3.10.dist-info → zwarm-2.0.0.dist-info}/entry_points.txt +0 -0
zwarm/watchers/builtin.py
CHANGED
|
@@ -7,10 +7,99 @@ from __future__ import annotations
|
|
|
7
7
|
import re
|
|
8
8
|
from typing import Any
|
|
9
9
|
|
|
10
|
+
from wbal.helper import TOOL_CALL_TYPE, TOOL_RESULT_TYPE
|
|
10
11
|
from zwarm.watchers.base import Watcher, WatcherContext, WatcherResult, WatcherAction
|
|
11
12
|
from zwarm.watchers.registry import register_watcher
|
|
12
13
|
|
|
13
14
|
|
|
15
|
+
def _get_field(item: Any, name: str, default: Any = None) -> Any:
|
|
16
|
+
if isinstance(item, dict):
|
|
17
|
+
return item.get(name, default)
|
|
18
|
+
return getattr(item, name, default)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _content_to_text(content: Any) -> str:
|
|
22
|
+
if content is None:
|
|
23
|
+
return ""
|
|
24
|
+
if isinstance(content, str):
|
|
25
|
+
return content
|
|
26
|
+
if isinstance(content, list):
|
|
27
|
+
parts = []
|
|
28
|
+
for part in content:
|
|
29
|
+
text = _content_to_text(part)
|
|
30
|
+
if text:
|
|
31
|
+
parts.append(text)
|
|
32
|
+
return "\n".join(parts)
|
|
33
|
+
if isinstance(content, dict):
|
|
34
|
+
text = content.get("text") or content.get("content") or content.get("refusal")
|
|
35
|
+
return str(text) if text is not None else ""
|
|
36
|
+
text = getattr(content, "text", None)
|
|
37
|
+
if text is None:
|
|
38
|
+
text = getattr(content, "refusal", None)
|
|
39
|
+
return str(text) if text is not None else ""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _normalize_tool_call(tool_call: Any) -> dict[str, Any]:
|
|
43
|
+
if isinstance(tool_call, dict):
|
|
44
|
+
if isinstance(tool_call.get("function"), dict):
|
|
45
|
+
return tool_call
|
|
46
|
+
name = tool_call.get("name", "")
|
|
47
|
+
arguments = tool_call.get("arguments", "")
|
|
48
|
+
call_id = tool_call.get("call_id")
|
|
49
|
+
else:
|
|
50
|
+
name = getattr(tool_call, "name", "")
|
|
51
|
+
arguments = getattr(tool_call, "arguments", "")
|
|
52
|
+
call_id = getattr(tool_call, "call_id", None)
|
|
53
|
+
|
|
54
|
+
normalized = {
|
|
55
|
+
"type": "function",
|
|
56
|
+
"function": {"name": name, "arguments": arguments},
|
|
57
|
+
}
|
|
58
|
+
if call_id:
|
|
59
|
+
normalized["call_id"] = call_id
|
|
60
|
+
return normalized
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _normalize_messages(messages: list[Any]) -> list[dict[str, Any]]:
|
|
64
|
+
normalized: list[dict[str, Any]] = []
|
|
65
|
+
for item in messages:
|
|
66
|
+
item_type = _get_field(item, "type")
|
|
67
|
+
role = _get_field(item, "role")
|
|
68
|
+
content = ""
|
|
69
|
+
tool_calls: list[dict[str, Any]] = []
|
|
70
|
+
|
|
71
|
+
if item_type in (TOOL_CALL_TYPE, "function_call"):
|
|
72
|
+
tool_calls = [_normalize_tool_call(item)]
|
|
73
|
+
role = role or "assistant"
|
|
74
|
+
else:
|
|
75
|
+
raw_tool_calls = _get_field(item, "tool_calls") or []
|
|
76
|
+
if raw_tool_calls and not isinstance(raw_tool_calls, list):
|
|
77
|
+
raw_tool_calls = [raw_tool_calls]
|
|
78
|
+
if raw_tool_calls:
|
|
79
|
+
tool_calls = [
|
|
80
|
+
_normalize_tool_call(tc) for tc in raw_tool_calls
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
if role or item_type == "message" or item_type is None:
|
|
84
|
+
content = _content_to_text(_get_field(item, "content"))
|
|
85
|
+
|
|
86
|
+
if item_type == TOOL_RESULT_TYPE and not content:
|
|
87
|
+
content = _content_to_text(_get_field(item, "output"))
|
|
88
|
+
role = role or "tool"
|
|
89
|
+
|
|
90
|
+
if not role and not content and not tool_calls:
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
normalized.append(
|
|
94
|
+
{
|
|
95
|
+
"role": role,
|
|
96
|
+
"content": content or "",
|
|
97
|
+
"tool_calls": tool_calls,
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
return normalized
|
|
101
|
+
|
|
102
|
+
|
|
14
103
|
@register_watcher("progress")
|
|
15
104
|
class ProgressWatcher(Watcher):
|
|
16
105
|
"""
|
|
@@ -26,14 +115,15 @@ class ProgressWatcher(Watcher):
|
|
|
26
115
|
description = "Detects when agent is stuck or spinning"
|
|
27
116
|
|
|
28
117
|
async def observe(self, ctx: WatcherContext) -> WatcherResult:
|
|
118
|
+
messages = _normalize_messages(ctx.messages)
|
|
29
119
|
config = self.config
|
|
30
120
|
max_same_calls = config.get("max_same_calls", 3)
|
|
31
121
|
min_progress_steps = config.get("min_progress_steps", 5)
|
|
32
122
|
|
|
33
123
|
# Check for repeated tool calls
|
|
34
|
-
if len(
|
|
124
|
+
if len(messages) >= max_same_calls * 2:
|
|
35
125
|
recent_assistant = [
|
|
36
|
-
m for m in
|
|
126
|
+
m for m in messages[-max_same_calls * 2 :]
|
|
37
127
|
if m.get("role") == "assistant"
|
|
38
128
|
]
|
|
39
129
|
if len(recent_assistant) >= max_same_calls:
|
|
@@ -144,9 +234,10 @@ class ScopeWatcher(Watcher):
|
|
|
144
234
|
|
|
145
235
|
# Check last few messages for avoid keywords
|
|
146
236
|
if avoid_keywords:
|
|
237
|
+
messages = _normalize_messages(ctx.messages)
|
|
147
238
|
recent_content = " ".join(
|
|
148
239
|
m.get("content", "") or ""
|
|
149
|
-
for m in
|
|
240
|
+
for m in messages[-max_tangent_steps * 2:]
|
|
150
241
|
).lower()
|
|
151
242
|
|
|
152
243
|
for keyword in avoid_keywords:
|
|
@@ -174,6 +265,7 @@ class PatternWatcher(Watcher):
|
|
|
174
265
|
description = "Watches for configurable patterns in output"
|
|
175
266
|
|
|
176
267
|
async def observe(self, ctx: WatcherContext) -> WatcherResult:
|
|
268
|
+
messages = _normalize_messages(ctx.messages)
|
|
177
269
|
config = self.config
|
|
178
270
|
patterns = config.get("patterns", [])
|
|
179
271
|
|
|
@@ -189,7 +281,7 @@ class PatternWatcher(Watcher):
|
|
|
189
281
|
continue
|
|
190
282
|
|
|
191
283
|
# Check recent messages
|
|
192
|
-
for msg in
|
|
284
|
+
for msg in messages[-10:]:
|
|
193
285
|
content = msg.get("content", "") or ""
|
|
194
286
|
if compiled.search(content):
|
|
195
287
|
action = pattern_config.get("action", "nudge")
|
|
@@ -236,11 +328,12 @@ class DelegationWatcher(Watcher):
|
|
|
236
328
|
]
|
|
237
329
|
|
|
238
330
|
async def observe(self, ctx: WatcherContext) -> WatcherResult:
|
|
331
|
+
messages = _normalize_messages(ctx.messages)
|
|
239
332
|
config = self.config
|
|
240
333
|
strict = config.get("strict", True) # If True, nudge. If False, just warn.
|
|
241
334
|
|
|
242
335
|
# Check recent messages for bash tool calls
|
|
243
|
-
for msg in
|
|
336
|
+
for msg in messages[-10:]:
|
|
244
337
|
if msg.get("role") != "assistant":
|
|
245
338
|
continue
|
|
246
339
|
|
|
@@ -370,6 +463,7 @@ class DelegationReminderWatcher(Watcher):
|
|
|
370
463
|
}
|
|
371
464
|
|
|
372
465
|
async def observe(self, ctx: WatcherContext) -> WatcherResult:
|
|
466
|
+
messages = _normalize_messages(ctx.messages)
|
|
373
467
|
config = self.config
|
|
374
468
|
threshold = config.get("threshold", 10) # Max consecutive non-delegation calls
|
|
375
469
|
lookback = config.get("lookback", 30) # How many messages to check
|
|
@@ -378,7 +472,7 @@ class DelegationReminderWatcher(Watcher):
|
|
|
378
472
|
consecutive_non_delegation = 0
|
|
379
473
|
|
|
380
474
|
# Look through recent messages in reverse order
|
|
381
|
-
for msg in reversed(
|
|
475
|
+
for msg in reversed(messages[-lookback:]):
|
|
382
476
|
if msg.get("role") != "assistant":
|
|
383
477
|
continue
|
|
384
478
|
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zwarm
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Multi-Agent CLI Orchestration Research Platform
|
|
5
|
+
Requires-Python: <3.14,>=3.13
|
|
6
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
7
|
+
Requires-Dist: pyyaml>=6.0
|
|
8
|
+
Requires-Dist: rich>=13.0.0
|
|
9
|
+
Requires-Dist: typer>=0.9.0
|
|
10
|
+
Requires-Dist: wbal>=0.4.0
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# zwarm
|
|
14
|
+
|
|
15
|
+
Multi-agent CLI for orchestrating coding agents. Spawn, manage, and converse with multiple Codex sessions in parallel.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# From the workspace
|
|
21
|
+
cd /path/to/labs
|
|
22
|
+
uv sync
|
|
23
|
+
|
|
24
|
+
# Or install directly
|
|
25
|
+
uv pip install -e ./zwarm
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Requirements:**
|
|
29
|
+
- Python 3.13+
|
|
30
|
+
- `codex` CLI installed and authenticated
|
|
31
|
+
|
|
32
|
+
**Environment:**
|
|
33
|
+
```bash
|
|
34
|
+
export OPENAI_API_KEY="sk-..." # Required for Codex
|
|
35
|
+
export WEAVE_PROJECT="entity/zwarm" # Optional: Weave tracing
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Two Modes
|
|
39
|
+
|
|
40
|
+
zwarm has two ways to orchestrate coding agents:
|
|
41
|
+
|
|
42
|
+
| Mode | Who's in charge | Use case |
|
|
43
|
+
|------|-----------------|----------|
|
|
44
|
+
| `zwarm interactive` | **You** | Manual control, experimentation |
|
|
45
|
+
| `zwarm orchestrate` | **LLM** | Autonomous task execution |
|
|
46
|
+
|
|
47
|
+
Both use the **same underlying session manager** - the orchestrator LLM has access to the exact same tools you do.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Interactive Mode
|
|
52
|
+
|
|
53
|
+
**You are the orchestrator.** Spawn sessions, check on them, continue conversations.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
zwarm interactive
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Commands
|
|
60
|
+
|
|
61
|
+
| Command | Description |
|
|
62
|
+
|---------|-------------|
|
|
63
|
+
| `spawn "task"` | Start a session (waits for completion) |
|
|
64
|
+
| `spawn --async "task"` | Start async (returns immediately) |
|
|
65
|
+
| `spawn -d /path "task"` | Start in specific directory |
|
|
66
|
+
| `ls` | List all sessions |
|
|
67
|
+
| `? <id>` | Quick peek: status + latest message |
|
|
68
|
+
| `show <id>` | Full details: all messages, tokens, etc. |
|
|
69
|
+
| `c <id> "msg"` | Continue conversation (waits) |
|
|
70
|
+
| `ca <id> "msg"` | Continue async (returns immediately) |
|
|
71
|
+
| `kill <id>` | Stop a running session |
|
|
72
|
+
| `rm <id>` | Delete session entirely |
|
|
73
|
+
| `killall` | Stop all running sessions |
|
|
74
|
+
| `clean` | Remove sessions older than 7 days |
|
|
75
|
+
| `q` | Quit |
|
|
76
|
+
|
|
77
|
+
### Example Session
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
$ zwarm interactive
|
|
81
|
+
|
|
82
|
+
> spawn "Add a login function to auth.py"
|
|
83
|
+
✓ Started session a1b2c3d4, waiting...
|
|
84
|
+
[a1b2c3d4] codex (completed) - 32s
|
|
85
|
+
Response: I've added a login function with JWT support...
|
|
86
|
+
|
|
87
|
+
> spawn --async "Fix the type errors in utils.py"
|
|
88
|
+
✓ Session: b2c3d4e5 (running in background)
|
|
89
|
+
|
|
90
|
+
> spawn --async "Add unit tests for auth.py"
|
|
91
|
+
✓ Session: c3d4e5f6 (running in background)
|
|
92
|
+
|
|
93
|
+
> ls
|
|
94
|
+
1 running | 2 done
|
|
95
|
+
|
|
96
|
+
ID │ │ T │ Task │ Updated │ Last Message
|
|
97
|
+
a1b2c3d4 │ ✓ │ 1 │ Add a login function... │ 2m │ I've added a login function...
|
|
98
|
+
b2c3d4e5 │ ✓ │ 1 │ Fix the type errors... │ 30s ★ │ Fixed 3 type errors in...
|
|
99
|
+
c3d4e5f6 │ ● │ 1 │ Add unit tests... │ 5s │ (working...)
|
|
100
|
+
|
|
101
|
+
> ? b2c3d4e5
|
|
102
|
+
✓ b2c3d4e5 Fixed 3 type errors: Optional[str] -> str | None, added missing...
|
|
103
|
+
|
|
104
|
+
> c a1b2c3d4 "Now add password hashing with bcrypt"
|
|
105
|
+
Continuing session a1b2c3d4...
|
|
106
|
+
[a1b2c3d4] codex (completed) - 28s
|
|
107
|
+
Response: Done! I've updated the login function to use bcrypt...
|
|
108
|
+
|
|
109
|
+
> rm b2c3d4e5
|
|
110
|
+
✓ Deleted session b2c3d4e5
|
|
111
|
+
|
|
112
|
+
> q
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Session Status Icons
|
|
116
|
+
|
|
117
|
+
| Icon | Status |
|
|
118
|
+
|------|--------|
|
|
119
|
+
| `●` | Running |
|
|
120
|
+
| `✓` | Completed |
|
|
121
|
+
| `✗` | Failed |
|
|
122
|
+
| `○` | Killed |
|
|
123
|
+
| `★` | Recently completed (< 60s) |
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Orchestrate Mode
|
|
128
|
+
|
|
129
|
+
**An LLM is the orchestrator.** Give it a task and it delegates to coding agents.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
zwarm orchestrate --task "Build a REST API with authentication"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The orchestrator LLM uses the same tools available in interactive mode:
|
|
136
|
+
|
|
137
|
+
| Tool | Description |
|
|
138
|
+
|------|-------------|
|
|
139
|
+
| `delegate(task, ...)` | Start a new session |
|
|
140
|
+
| `converse(id, msg)` | Continue a conversation |
|
|
141
|
+
| `peek_session(id)` | Quick status check |
|
|
142
|
+
| `check_session(id)` | Full session details |
|
|
143
|
+
| `list_sessions()` | List all sessions with `needs_attention` flags |
|
|
144
|
+
| `end_session(id, delete=False)` | Kill/delete a session |
|
|
145
|
+
|
|
146
|
+
### Task Input
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# Direct
|
|
150
|
+
zwarm orchestrate --task "Build a REST API"
|
|
151
|
+
|
|
152
|
+
# From file
|
|
153
|
+
zwarm orchestrate --task-file task.md
|
|
154
|
+
|
|
155
|
+
# From stdin
|
|
156
|
+
echo "Fix the bug in auth.py" | zwarm orchestrate
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Configuration
|
|
162
|
+
|
|
163
|
+
zwarm looks for config in this order:
|
|
164
|
+
1. `--config` flag
|
|
165
|
+
2. `.zwarm/config.toml`
|
|
166
|
+
3. `config.toml` in working directory
|
|
167
|
+
|
|
168
|
+
### Minimal Config
|
|
169
|
+
|
|
170
|
+
```toml
|
|
171
|
+
[weave]
|
|
172
|
+
enabled = true
|
|
173
|
+
project = "your-entity/zwarm"
|
|
174
|
+
|
|
175
|
+
[executor]
|
|
176
|
+
adapter = "codex_mcp"
|
|
177
|
+
model = "gpt-5.1-codex-mini"
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Full Config Reference
|
|
181
|
+
|
|
182
|
+
```toml
|
|
183
|
+
[orchestrator]
|
|
184
|
+
lm = "gpt-5-mini"
|
|
185
|
+
max_steps = 100
|
|
186
|
+
|
|
187
|
+
[orchestrator.compaction]
|
|
188
|
+
enabled = true
|
|
189
|
+
max_tokens = 100000
|
|
190
|
+
threshold_pct = 0.85
|
|
191
|
+
target_pct = 0.7
|
|
192
|
+
|
|
193
|
+
[executor]
|
|
194
|
+
adapter = "codex_mcp"
|
|
195
|
+
model = "gpt-5.1-codex-mini"
|
|
196
|
+
sandbox = "workspace-write"
|
|
197
|
+
timeout = 300
|
|
198
|
+
|
|
199
|
+
[weave]
|
|
200
|
+
enabled = true
|
|
201
|
+
project = "your-entity/zwarm"
|
|
202
|
+
|
|
203
|
+
[watchers]
|
|
204
|
+
enabled = true
|
|
205
|
+
watchers = [
|
|
206
|
+
{ name = "progress" },
|
|
207
|
+
{ name = "budget", config = { max_steps = 50, max_sessions = 10 } },
|
|
208
|
+
{ name = "delegation_reminder", config = { threshold = 10 } },
|
|
209
|
+
]
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Session Management
|
|
215
|
+
|
|
216
|
+
Sessions are the core abstraction. Each session is a conversation with a Codex agent.
|
|
217
|
+
|
|
218
|
+
### Lifecycle
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
spawn → running → completed/failed
|
|
222
|
+
↓
|
|
223
|
+
continue → running → completed
|
|
224
|
+
↓
|
|
225
|
+
continue → ...
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Storage
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
.zwarm/sessions/<uuid>/
|
|
232
|
+
├── meta.json # Status, task, model, messages, tokens
|
|
233
|
+
└── turns/
|
|
234
|
+
├── turn_1.jsonl # Raw codex output for turn 1
|
|
235
|
+
├── turn_2.jsonl # Output after first continue
|
|
236
|
+
└── ...
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Sync vs Async
|
|
240
|
+
|
|
241
|
+
| Mode | Spawn | Continue | Use case |
|
|
242
|
+
|------|-------|----------|----------|
|
|
243
|
+
| **Sync** | `spawn "task"` | `c id "msg"` | Sequential work, immediate feedback |
|
|
244
|
+
| **Async** | `spawn --async "task"` | `ca id "msg"` | Parallel work, batch processing |
|
|
245
|
+
|
|
246
|
+
Async sessions return immediately. Poll with `ls` or `?` to check status.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Watchers
|
|
251
|
+
|
|
252
|
+
Watchers monitor agent behavior and intervene when needed.
|
|
253
|
+
|
|
254
|
+
| Watcher | Purpose |
|
|
255
|
+
|---------|---------|
|
|
256
|
+
| `progress` | Detects stuck/spinning agents |
|
|
257
|
+
| `budget` | Enforces step/session limits |
|
|
258
|
+
| `scope` | Detects scope creep |
|
|
259
|
+
| `delegation_reminder` | Nudges orchestrator to delegate |
|
|
260
|
+
|
|
261
|
+
Configure in `config.toml`:
|
|
262
|
+
|
|
263
|
+
```toml
|
|
264
|
+
[watchers]
|
|
265
|
+
enabled = true
|
|
266
|
+
watchers = [
|
|
267
|
+
{ name = "progress" },
|
|
268
|
+
{ name = "budget", config = { max_steps = 50 } },
|
|
269
|
+
]
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## CLI Reference
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
zwarm init # Initialize .zwarm/ in current directory
|
|
278
|
+
zwarm interactive # Start interactive REPL
|
|
279
|
+
zwarm orchestrate # Start LLM orchestrator
|
|
280
|
+
zwarm exec # Run single executor directly (testing)
|
|
281
|
+
zwarm status # Show current state
|
|
282
|
+
zwarm history # Show event history
|
|
283
|
+
zwarm clean # Remove old sessions
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Project Structure
|
|
289
|
+
|
|
290
|
+
```
|
|
291
|
+
zwarm/
|
|
292
|
+
├── src/zwarm/
|
|
293
|
+
│ ├── sessions/ # Session management (core)
|
|
294
|
+
│ │ ├── manager.py # CodexSessionManager
|
|
295
|
+
│ │ └── __init__.py
|
|
296
|
+
│ ├── tools/
|
|
297
|
+
│ │ └── delegation.py # Orchestrator tools (delegate, converse, etc.)
|
|
298
|
+
│ ├── cli/
|
|
299
|
+
│ │ └── main.py # CLI commands and interactive REPL
|
|
300
|
+
│ ├── core/
|
|
301
|
+
│ │ ├── config.py # Configuration loading
|
|
302
|
+
│ │ ├── compact.py # Context window management
|
|
303
|
+
│ │ └── state.py # State persistence
|
|
304
|
+
│ ├── watchers/ # Trajectory alignment
|
|
305
|
+
│ └── orchestrator.py # Orchestrator agent
|
|
306
|
+
├── docs/
|
|
307
|
+
│ └── INTERNALS.md # Technical architecture
|
|
308
|
+
└── README.md
|
|
309
|
+
```
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
zwarm/__init__.py,sha256=3i3LMjHwIzE-LFIS2aUrwv3EZmpkvVMe-xj1h97rcSM,837
|
|
2
|
-
zwarm/orchestrator.py,sha256=
|
|
2
|
+
zwarm/orchestrator.py,sha256=FGr-_MpG_UGUFAa19oenh7MVifE6frK6URS8ii0x0IA,22952
|
|
3
3
|
zwarm/test_orchestrator_watchers.py,sha256=QpoaehPU7ekT4XshbTOWnJ2H0wRveV3QOZjxbgyJJLY,807
|
|
4
4
|
zwarm/adapters/__init__.py,sha256=O0b-SfZpb6txeNqFkXZ2aaf34yLFYreznyrAV25jF_Q,656
|
|
5
5
|
zwarm/adapters/base.py,sha256=fZlQviTgVvOcwnxduTla6WuM6FzQJ_yoHMW5SxwVgQg,2527
|
|
6
6
|
zwarm/adapters/claude_code.py,sha256=vAjsjD-_JjARmC4_FBSILQZmQCBrk_oNHo18a9ubuqk,11481
|
|
7
|
-
zwarm/adapters/codex_mcp.py,sha256=
|
|
7
|
+
zwarm/adapters/codex_mcp.py,sha256=EhdkM3gj5hc01AcM1ERhtfZbydK390yN4Pg3dawKIGU,48791
|
|
8
8
|
zwarm/adapters/registry.py,sha256=EdyHECaNA5Kv1od64pYFBJyA_r_6I1r_eJTNP1XYLr4,1781
|
|
9
9
|
zwarm/adapters/test_codex_mcp.py,sha256=0qhVzxn_KF-XUS30gXSJKwMdR3kWGsDY9iPk1Ihqn3w,10698
|
|
10
10
|
zwarm/adapters/test_registry.py,sha256=otxcVDONwFCMisyANToF3iy7Y8dSbCL8bTmZNhxNuF4,2383
|
|
11
11
|
zwarm/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
zwarm/cli/main.py,sha256
|
|
12
|
+
zwarm/cli/main.py,sha256=-kxDul3cPv9vqLLS9ePTjSf1TXo8E3TCuS6p-t4u4hQ,87985
|
|
13
13
|
zwarm/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
14
|
zwarm/core/compact.py,sha256=Y8C7Gs-5-WOU43WRvQ863Qzd5xtuEqR6Aw3r2p8_-i8,10907
|
|
15
|
-
zwarm/core/config.py,sha256=
|
|
15
|
+
zwarm/core/config.py,sha256=331i4io9uEnloFwUMjTPJ5_lQFKJR1nhTpA4SPfSpiI,11748
|
|
16
16
|
zwarm/core/environment.py,sha256=HVDpDZEpDSfyh9-wHZMzMKVUPKvioBkPVWeiME2JmFo,5435
|
|
17
17
|
zwarm/core/models.py,sha256=PrC3okRBVJxISUa1Fax4KkagqLT6Xub-kTxC9drN0sY,10083
|
|
18
18
|
zwarm/core/state.py,sha256=MzrvODKEiJovI7YI1jajW4uukineZ3ezmW5oQinMgjg,11563
|
|
@@ -20,18 +20,18 @@ zwarm/core/test_compact.py,sha256=WSdjCB5t4YMcknsrkmJIUsVOPY28s4y9GnDmu3Z4BFw,11
|
|
|
20
20
|
zwarm/core/test_config.py,sha256=26ozyiFOdjFF2c9Q-HDfFM6GOLfgw_5FZ55nTDMNYA8,4888
|
|
21
21
|
zwarm/core/test_models.py,sha256=sWTIhMZvuLP5AooGR6y8OR2EyWydqVfhmGrE7NPBBnk,8450
|
|
22
22
|
zwarm/prompts/__init__.py,sha256=FiaIOniLrIyfD3_osxT6I7FfyKjtctbf8jNs5QTPs_s,213
|
|
23
|
-
zwarm/prompts/orchestrator.py,sha256
|
|
24
|
-
zwarm/sessions/__init__.py,sha256=
|
|
25
|
-
zwarm/sessions/manager.py,sha256=
|
|
23
|
+
zwarm/prompts/orchestrator.py,sha256=-VZ3B5t-2ALOTpdZyNZGSjjzaHiTufAuLzrTLgwg70M,15442
|
|
24
|
+
zwarm/sessions/__init__.py,sha256=jRibY8IfmNcnkgNmrgK2T81oa1w71wP_KQp9A1hPL7Q,568
|
|
25
|
+
zwarm/sessions/manager.py,sha256=slCDE0n9-pw6iZ08YZMjxZRwH0aoEi6MAAChLlAsuPw,22212
|
|
26
26
|
zwarm/tools/__init__.py,sha256=FpqxwXJA6-fQ7C-oLj30jjK_0qqcE7MbI0dQuaB56kU,290
|
|
27
|
-
zwarm/tools/delegation.py,sha256=
|
|
27
|
+
zwarm/tools/delegation.py,sha256=axGuDROdJM9xYDG8IscfyQpNGrkDQSFPi9_ElfL0Fzw,20661
|
|
28
28
|
zwarm/watchers/__init__.py,sha256=yYGTbhuImQLESUdtfrYbHYBJNvCNX3B-Ei-vY5BizX8,760
|
|
29
29
|
zwarm/watchers/base.py,sha256=r1GoPlj06nOT2xp4fghfSjxbRyFFFQUB6HpZbEyO2OY,3834
|
|
30
|
-
zwarm/watchers/builtin.py,sha256=
|
|
30
|
+
zwarm/watchers/builtin.py,sha256=IL5QwwKOIqWEfJ_uQWb321Px4i5OLtI_vnWQMudqKoA,19064
|
|
31
31
|
zwarm/watchers/manager.py,sha256=XZjBVeHjgCUlkTUeHqdvBvHoBC862U1ik0fG6nlRGog,5587
|
|
32
32
|
zwarm/watchers/registry.py,sha256=A9iBIVIFNtO7KPX0kLpUaP8dAK7ozqWLA44ocJGnOw4,1219
|
|
33
33
|
zwarm/watchers/test_watchers.py,sha256=zOsxumBqKfR5ZVGxrNlxz6KcWjkcdp0QhW9WB0_20zM,7855
|
|
34
|
-
zwarm-
|
|
35
|
-
zwarm-
|
|
36
|
-
zwarm-
|
|
37
|
-
zwarm-
|
|
34
|
+
zwarm-2.0.0.dist-info/METADATA,sha256=v94KN8eICvONW3-UvteIdL0suEu6Pu3ur3FMxGd9lS0,7680
|
|
35
|
+
zwarm-2.0.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
36
|
+
zwarm-2.0.0.dist-info/entry_points.txt,sha256=u0OXq4q8d3yJ3EkUXwZfkS-Y8Lcy0F8cWrcQfoRxM6Q,46
|
|
37
|
+
zwarm-2.0.0.dist-info/RECORD,,
|