zwarm 3.3.0__tar.gz → 3.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {zwarm-3.3.0 → zwarm-3.4.0}/PKG-INFO +1 -1
- {zwarm-3.3.0 → zwarm-3.4.0}/pyproject.toml +1 -1
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/cli/interactive.py +1 -1
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/cli/main.py +20 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/cli/pilot.py +54 -5
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/core/config.py +2 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/orchestrator.py +11 -1
- {zwarm-3.3.0 → zwarm-3.4.0}/.gitignore +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/README.md +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/__init__.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/cli/__init__.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/core/__init__.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/core/checkpoints.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/core/compact.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/core/costs.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/core/environment.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/core/models.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/core/state.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/core/test_compact.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/core/test_config.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/core/test_models.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/prompts/__init__.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/prompts/orchestrator.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/prompts/pilot.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/sessions/__init__.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/sessions/manager.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/test_orchestrator_watchers.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/tools/__init__.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/tools/delegation.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/watchers/__init__.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/watchers/base.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/watchers/builtin.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/watchers/llm_watcher.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/watchers/manager.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/watchers/registry.py +0 -0
- {zwarm-3.3.0 → zwarm-3.4.0}/src/zwarm/watchers/test_watchers.py +0 -0
|
@@ -307,7 +307,7 @@ def cmd_show(manager, session_id: str):
|
|
|
307
307
|
icon = STATUS_ICONS.get(session.status.value, "?")
|
|
308
308
|
console.print(f"\n{icon} [bold cyan]{session.short_id}[/] - {session.status.value}")
|
|
309
309
|
console.print(f" [dim]Task:[/] {session.task}")
|
|
310
|
-
console.print(f" [dim]Turn:[/] {session.turn} | [dim]Runtime:[/] {session.runtime
|
|
310
|
+
console.print(f" [dim]Turn:[/] {session.turn} | [dim]Runtime:[/] {session.runtime}")
|
|
311
311
|
|
|
312
312
|
# Token usage with cost estimate
|
|
313
313
|
usage = session.token_usage
|
|
@@ -218,6 +218,26 @@ def orchestrate(
|
|
|
218
218
|
if orchestrator.instance_id and not instance:
|
|
219
219
|
console.print(f" [dim]Instance: {orchestrator.instance_id[:8]}[/]")
|
|
220
220
|
|
|
221
|
+
# Set up step callback for live progress display
|
|
222
|
+
def step_callback(step_num: int, tool_results: list) -> None:
|
|
223
|
+
"""Print tool calls and results as they happen."""
|
|
224
|
+
if not tool_results:
|
|
225
|
+
return
|
|
226
|
+
for tool_info, result in tool_results:
|
|
227
|
+
name = tool_info.get("name", "?")
|
|
228
|
+
# Truncate args for display
|
|
229
|
+
args_str = str(tool_info.get("args", {}))
|
|
230
|
+
if len(args_str) > 80:
|
|
231
|
+
args_str = args_str[:77] + "..."
|
|
232
|
+
# Truncate result for display
|
|
233
|
+
result_str = str(result)
|
|
234
|
+
if len(result_str) > 100:
|
|
235
|
+
result_str = result_str[:97] + "..."
|
|
236
|
+
console.print(f"[dim]step {step_num}[/] → [cyan]{name}[/]({args_str})")
|
|
237
|
+
console.print(f" └ {result_str}")
|
|
238
|
+
|
|
239
|
+
orchestrator._step_callback = step_callback
|
|
240
|
+
|
|
221
241
|
# Run the orchestrator loop
|
|
222
242
|
console.print("[bold]--- Orchestrator running ---[/]\n")
|
|
223
243
|
result = orchestrator.run(task=task)
|
|
@@ -594,6 +594,18 @@ def execute_step_with_events(
|
|
|
594
594
|
"""
|
|
595
595
|
had_message = False
|
|
596
596
|
|
|
597
|
+
# Update environment with current progress before perceive
|
|
598
|
+
# This ensures the observation has fresh step/token counts
|
|
599
|
+
if hasattr(orchestrator, "env") and hasattr(orchestrator.env, "update_progress"):
|
|
600
|
+
total_tokens = getattr(orchestrator, "_total_tokens", 0)
|
|
601
|
+
executor_usage = orchestrator.get_executor_usage() if hasattr(orchestrator, "get_executor_usage") else {}
|
|
602
|
+
orchestrator.env.update_progress(
|
|
603
|
+
step_count=getattr(orchestrator, "_step_count", 0),
|
|
604
|
+
max_steps=getattr(orchestrator, "maxSteps", 50),
|
|
605
|
+
total_tokens=total_tokens,
|
|
606
|
+
executor_tokens=executor_usage.get("total_tokens", 0),
|
|
607
|
+
)
|
|
608
|
+
|
|
597
609
|
# Execute perceive (updates environment observation)
|
|
598
610
|
orchestrator.perceive()
|
|
599
611
|
|
|
@@ -649,7 +661,7 @@ def execute_step_with_events(
|
|
|
649
661
|
def run_until_response(
|
|
650
662
|
orchestrator: Any,
|
|
651
663
|
renderer: EventRenderer,
|
|
652
|
-
max_steps: int =
|
|
664
|
+
max_steps: int = 60,
|
|
653
665
|
) -> List[tuple]:
|
|
654
666
|
"""
|
|
655
667
|
Run the orchestrator until it produces a message response.
|
|
@@ -657,7 +669,7 @@ def run_until_response(
|
|
|
657
669
|
Keeps stepping while the agent only produces tool calls.
|
|
658
670
|
Stops when:
|
|
659
671
|
- Agent produces a text message (returns to user)
|
|
660
|
-
- Max steps reached
|
|
672
|
+
- Max steps reached (configurable via orchestrator.max_steps_per_turn)
|
|
661
673
|
- Stop condition triggered
|
|
662
674
|
|
|
663
675
|
This is wrapped as a weave.op to group all child calls per turn.
|
|
@@ -665,7 +677,7 @@ def run_until_response(
|
|
|
665
677
|
Args:
|
|
666
678
|
orchestrator: The orchestrator instance
|
|
667
679
|
renderer: Event renderer for output
|
|
668
|
-
max_steps: Safety limit on steps per turn
|
|
680
|
+
max_steps: Safety limit on steps per turn (default: 60)
|
|
669
681
|
|
|
670
682
|
Returns:
|
|
671
683
|
All tool results from the turn
|
|
@@ -703,6 +715,9 @@ def run_until_response(
|
|
|
703
715
|
if not results:
|
|
704
716
|
break
|
|
705
717
|
|
|
718
|
+
# Show session status at end of turn (if there are any sessions)
|
|
719
|
+
render_session_status(orchestrator, renderer)
|
|
720
|
+
|
|
706
721
|
return all_results
|
|
707
722
|
|
|
708
723
|
return _run_turn()
|
|
@@ -758,6 +773,38 @@ def get_sessions_snapshot(orchestrator: Any) -> Dict[str, Any]:
|
|
|
758
773
|
return {"sessions": []}
|
|
759
774
|
|
|
760
775
|
|
|
776
|
+
def render_session_status(orchestrator: Any, renderer: EventRenderer) -> None:
|
|
777
|
+
"""
|
|
778
|
+
Render a compact session status line if there are active sessions.
|
|
779
|
+
|
|
780
|
+
Shows: "Sessions: 2 running, 1 done, 0 failed"
|
|
781
|
+
Only displays if there are any sessions.
|
|
782
|
+
"""
|
|
783
|
+
if not hasattr(orchestrator, "_session_manager"):
|
|
784
|
+
return
|
|
785
|
+
|
|
786
|
+
sessions = orchestrator._session_manager.list_sessions()
|
|
787
|
+
if not sessions:
|
|
788
|
+
return
|
|
789
|
+
|
|
790
|
+
running = sum(1 for s in sessions if s.status.value == "running")
|
|
791
|
+
completed = sum(1 for s in sessions if s.status.value == "completed")
|
|
792
|
+
failed = sum(1 for s in sessions if s.status.value == "failed")
|
|
793
|
+
|
|
794
|
+
# Build status line with colors
|
|
795
|
+
parts = []
|
|
796
|
+
if running > 0:
|
|
797
|
+
parts.append(f"[cyan]{running} running[/]")
|
|
798
|
+
if completed > 0:
|
|
799
|
+
parts.append(f"[green]{completed} done[/]")
|
|
800
|
+
if failed > 0:
|
|
801
|
+
parts.append(f"[red]{failed} failed[/]")
|
|
802
|
+
|
|
803
|
+
if parts:
|
|
804
|
+
status_line = ", ".join(parts)
|
|
805
|
+
console.print(f"[dim]Sessions:[/] {status_line}")
|
|
806
|
+
|
|
807
|
+
|
|
761
808
|
def run_pilot(
|
|
762
809
|
orchestrator: Any,
|
|
763
810
|
*,
|
|
@@ -814,7 +861,8 @@ def _run_pilot_repl(
|
|
|
814
861
|
})
|
|
815
862
|
|
|
816
863
|
renderer.reset_turn()
|
|
817
|
-
|
|
864
|
+
max_steps = getattr(orchestrator.config.orchestrator, "max_steps_per_turn", 60)
|
|
865
|
+
results = run_until_response(orchestrator, renderer, max_steps=max_steps)
|
|
818
866
|
|
|
819
867
|
# Record checkpoint
|
|
820
868
|
state.record(
|
|
@@ -1103,8 +1151,9 @@ def _run_pilot_repl(
|
|
|
1103
1151
|
|
|
1104
1152
|
# Execute steps until agent responds with a message
|
|
1105
1153
|
renderer.reset_turn()
|
|
1154
|
+
max_steps = getattr(orchestrator.config.orchestrator, "max_steps_per_turn", 60)
|
|
1106
1155
|
try:
|
|
1107
|
-
results = run_until_response(orchestrator, renderer)
|
|
1156
|
+
results = run_until_response(orchestrator, renderer, max_steps=max_steps)
|
|
1108
1157
|
except Exception as e:
|
|
1109
1158
|
renderer.error(f"Step failed: {e}")
|
|
1110
1159
|
# Remove the user message on failure
|
|
@@ -60,6 +60,7 @@ class OrchestratorConfig:
|
|
|
60
60
|
prompt: str | None = None # path to prompt yaml
|
|
61
61
|
tools: list[str] = field(default_factory=lambda: ["delegate", "converse", "check_session", "end_session", "bash"])
|
|
62
62
|
max_steps: int = 50
|
|
63
|
+
max_steps_per_turn: int = 60 # Max tool-call steps before returning to user (pilot mode)
|
|
63
64
|
parallel_delegations: int = 4
|
|
64
65
|
compaction: CompactionConfig = field(default_factory=CompactionConfig)
|
|
65
66
|
|
|
@@ -172,6 +173,7 @@ class ZwarmConfig:
|
|
|
172
173
|
"prompt": self.orchestrator.prompt,
|
|
173
174
|
"tools": self.orchestrator.tools,
|
|
174
175
|
"max_steps": self.orchestrator.max_steps,
|
|
176
|
+
"max_steps_per_turn": self.orchestrator.max_steps_per_turn,
|
|
175
177
|
"parallel_delegations": self.orchestrator.parallel_delegations,
|
|
176
178
|
"compaction": {
|
|
177
179
|
"enabled": self.orchestrator.compaction.enabled,
|
|
@@ -81,6 +81,8 @@ class Orchestrator(YamlAgent):
|
|
|
81
81
|
"total_tokens": 0,
|
|
82
82
|
}
|
|
83
83
|
)
|
|
84
|
+
# Callback for step progress (used by CLI to print tool calls)
|
|
85
|
+
_step_callback: Callable[[int, list[tuple[dict[str, Any], Any]]], None] | None = PrivateAttr(default=None)
|
|
84
86
|
|
|
85
87
|
def model_post_init(self, __context: Any) -> None:
|
|
86
88
|
"""Initialize state after model creation."""
|
|
@@ -149,6 +151,10 @@ class Orchestrator(YamlAgent):
|
|
|
149
151
|
"""Access state manager."""
|
|
150
152
|
return self._state
|
|
151
153
|
|
|
154
|
+
def get_executor_usage(self) -> dict[str, int]:
|
|
155
|
+
"""Get aggregated token usage from executor sessions."""
|
|
156
|
+
return self._executor_usage
|
|
157
|
+
|
|
152
158
|
def save_state(self) -> None:
|
|
153
159
|
"""Save orchestrator state for resume."""
|
|
154
160
|
self._state.save_orchestrator_messages(self.messages)
|
|
@@ -550,7 +556,11 @@ Review what was accomplished in the previous session and delegate new tasks as n
|
|
|
550
556
|
}
|
|
551
557
|
# NUDGE and CONTINUE just continue
|
|
552
558
|
|
|
553
|
-
self.step()
|
|
559
|
+
tool_results = self.step()
|
|
560
|
+
|
|
561
|
+
# Call step callback if registered (for CLI progress display)
|
|
562
|
+
if self._step_callback:
|
|
563
|
+
self._step_callback(self._step_count, tool_results)
|
|
554
564
|
|
|
555
565
|
if self.stopCondition:
|
|
556
566
|
break
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|