zwarm 3.2.1__py3-none-any.whl → 3.6.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 +346 -30
- zwarm/cli/main.py +221 -90
- zwarm/cli/pilot.py +107 -9
- zwarm/core/config.py +26 -9
- zwarm/core/costs.py +55 -183
- zwarm/core/registry.py +329 -0
- zwarm/core/test_config.py +2 -3
- zwarm/orchestrator.py +17 -43
- zwarm/sessions/__init__.py +48 -9
- zwarm/sessions/base.py +501 -0
- zwarm/sessions/claude.py +481 -0
- zwarm/sessions/manager.py +233 -486
- zwarm/tools/delegation.py +93 -31
- {zwarm-3.2.1.dist-info → zwarm-3.6.0.dist-info}/METADATA +73 -21
- {zwarm-3.2.1.dist-info → zwarm-3.6.0.dist-info}/RECORD +17 -21
- zwarm/adapters/__init__.py +0 -21
- zwarm/adapters/base.py +0 -109
- zwarm/adapters/claude_code.py +0 -357
- zwarm/adapters/codex_mcp.py +0 -1262
- zwarm/adapters/registry.py +0 -69
- zwarm/adapters/test_codex_mcp.py +0 -274
- zwarm/adapters/test_registry.py +0 -68
- {zwarm-3.2.1.dist-info → zwarm-3.6.0.dist-info}/WHEEL +0 -0
- {zwarm-3.2.1.dist-info → zwarm-3.6.0.dist-info}/entry_points.txt +0 -0
zwarm/cli/main.py
CHANGED
|
@@ -122,16 +122,6 @@ Manage zwarm configurations.
|
|
|
122
122
|
app.add_typer(configs_app, name="configs")
|
|
123
123
|
|
|
124
124
|
|
|
125
|
-
class AdapterType(str, Enum):
|
|
126
|
-
codex_mcp = "codex_mcp"
|
|
127
|
-
claude_code = "claude_code"
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
class ModeType(str, Enum):
|
|
131
|
-
sync = "sync"
|
|
132
|
-
async_ = "async"
|
|
133
|
-
|
|
134
|
-
|
|
135
125
|
@app.command()
|
|
136
126
|
def orchestrate(
|
|
137
127
|
task: Annotated[Optional[str], typer.Option("--task", "-t", help="The task to accomplish")] = None,
|
|
@@ -228,6 +218,26 @@ def orchestrate(
|
|
|
228
218
|
if orchestrator.instance_id and not instance:
|
|
229
219
|
console.print(f" [dim]Instance: {orchestrator.instance_id[:8]}[/]")
|
|
230
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
|
+
|
|
231
241
|
# Run the orchestrator loop
|
|
232
242
|
console.print("[bold]--- Orchestrator running ---[/]\n")
|
|
233
243
|
result = orchestrator.run(task=task)
|
|
@@ -289,7 +299,8 @@ def pilot(
|
|
|
289
299
|
config: Annotated[Optional[Path], typer.Option("--config", "-c", help="Path to config YAML")] = None,
|
|
290
300
|
overrides: Annotated[Optional[list[str]], typer.Option("--set", help="Override config (key=value)")] = None,
|
|
291
301
|
working_dir: Annotated[Path, typer.Option("--working-dir", "-w", help="Working directory")] = Path("."),
|
|
292
|
-
|
|
302
|
+
resume: Annotated[bool, typer.Option("--resume", help="Resume from previous state")] = False,
|
|
303
|
+
instance: Annotated[Optional[str], typer.Option("--instance", "-i", help="Instance ID (for isolation/resume)")] = None,
|
|
293
304
|
instance_name: Annotated[Optional[str], typer.Option("--name", "-n", help="Human-readable instance name")] = None,
|
|
294
305
|
model: Annotated[PilotLM, typer.Option("--model", "-m", help="LM to use")] = PilotLM.gpt5_verbose,
|
|
295
306
|
):
|
|
@@ -331,19 +342,30 @@ def pilot(
|
|
|
331
342
|
|
|
332
343
|
[dim]# Named instance[/]
|
|
333
344
|
$ zwarm pilot --name my-feature
|
|
345
|
+
|
|
346
|
+
[dim]# Resume a previous session[/]
|
|
347
|
+
$ zwarm pilot --resume --instance abc123
|
|
334
348
|
"""
|
|
335
349
|
from zwarm.cli.pilot import run_pilot, build_pilot_orchestrator
|
|
336
350
|
|
|
337
351
|
# Resolve task (optional for pilot)
|
|
338
352
|
resolved_task = _resolve_task(task, task_file)
|
|
339
353
|
|
|
340
|
-
|
|
354
|
+
# Validate resume requirements
|
|
355
|
+
if resume and not instance:
|
|
356
|
+
console.print("[red]Error:[/] --resume requires --instance to specify which session to resume")
|
|
357
|
+
console.print(" [dim]Use 'zwarm instances' to list available instances[/]")
|
|
358
|
+
raise typer.Exit(1)
|
|
359
|
+
|
|
360
|
+
console.print(f"[bold]{'Resuming' if resume else 'Starting'} pilot session...[/]")
|
|
341
361
|
console.print(f" Working dir: {working_dir.absolute()}")
|
|
342
362
|
console.print(f" Model: {model.value}")
|
|
343
363
|
if resolved_task:
|
|
344
364
|
console.print(f" Initial task: {resolved_task[:60]}...")
|
|
345
365
|
if instance:
|
|
346
366
|
console.print(f" Instance: {instance}" + (f" ({instance_name})" if instance_name else ""))
|
|
367
|
+
if resume:
|
|
368
|
+
console.print(f" [yellow]Resuming from saved state...[/]")
|
|
347
369
|
console.print()
|
|
348
370
|
|
|
349
371
|
orchestrator = None
|
|
@@ -361,6 +383,12 @@ def pilot(
|
|
|
361
383
|
if orchestrator.instance_id and not instance:
|
|
362
384
|
console.print(f" [dim]Instance: {orchestrator.instance_id[:8]}[/]")
|
|
363
385
|
|
|
386
|
+
# Resume from saved state if requested
|
|
387
|
+
if resume:
|
|
388
|
+
orchestrator.load_state()
|
|
389
|
+
msg_count = len(orchestrator.messages)
|
|
390
|
+
console.print(f" [green]✓[/] Resumed with {msg_count} messages")
|
|
391
|
+
|
|
364
392
|
# Run the pilot REPL
|
|
365
393
|
run_pilot(orchestrator, initial_task=resolved_task)
|
|
366
394
|
|
|
@@ -384,78 +412,68 @@ def pilot(
|
|
|
384
412
|
@app.command()
|
|
385
413
|
def exec(
|
|
386
414
|
task: Annotated[str, typer.Option("--task", "-t", help="Task to execute")],
|
|
387
|
-
adapter: Annotated[AdapterType, typer.Option("--adapter", "-a", help="Executor adapter")] = AdapterType.codex_mcp,
|
|
388
|
-
mode: Annotated[ModeType, typer.Option("--mode", "-m", help="Execution mode")] = ModeType.sync,
|
|
389
415
|
working_dir: Annotated[Path, typer.Option("--working-dir", "-w", help="Working directory")] = Path("."),
|
|
390
416
|
model: Annotated[Optional[str], typer.Option("--model", help="Model override")] = None,
|
|
417
|
+
wait: Annotated[bool, typer.Option("--wait", help="Wait for completion and show result")] = False,
|
|
391
418
|
):
|
|
392
419
|
"""
|
|
393
|
-
Run a single
|
|
420
|
+
Run a single Codex session directly (for testing).
|
|
394
421
|
|
|
395
|
-
|
|
422
|
+
Spawns a session using CodexSessionManager - same as interactive/pilot.
|
|
423
|
+
Web search is always enabled via .codex/config.toml (set up by `zwarm init`).
|
|
396
424
|
|
|
397
425
|
[bold]Examples:[/]
|
|
398
|
-
[dim]#
|
|
399
|
-
$ zwarm exec --task "What is 2+2?"
|
|
426
|
+
[dim]# Quick test[/]
|
|
427
|
+
$ zwarm exec --task "What is 2+2?" --wait
|
|
400
428
|
|
|
401
|
-
[dim]#
|
|
402
|
-
$ zwarm exec
|
|
429
|
+
[dim]# Run in background[/]
|
|
430
|
+
$ zwarm exec --task "Build feature"
|
|
403
431
|
|
|
404
|
-
[dim]#
|
|
405
|
-
$ zwarm exec --task "
|
|
432
|
+
[dim]# Web search is always available[/]
|
|
433
|
+
$ zwarm exec --task "Find latest FastAPI docs" --wait
|
|
406
434
|
"""
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
console.print(f"[bold]Running executor directly...[/]")
|
|
410
|
-
console.print(f" Adapter: [cyan]{adapter.value}[/]")
|
|
411
|
-
console.print(f" Mode: {mode.value}")
|
|
412
|
-
console.print(f" Task: {task}")
|
|
413
|
-
|
|
414
|
-
# Use isolated codex config if available
|
|
415
|
-
config_path = working_dir / ".zwarm" / "codex.toml"
|
|
416
|
-
if not config_path.exists():
|
|
417
|
-
config_path = None
|
|
418
|
-
|
|
419
|
-
try:
|
|
420
|
-
executor = get_adapter(adapter.value, model=model, config_path=config_path)
|
|
421
|
-
except ValueError as e:
|
|
422
|
-
console.print(f"[red]Error:[/] {e}")
|
|
423
|
-
sys.exit(1)
|
|
424
|
-
|
|
425
|
-
async def run():
|
|
426
|
-
try:
|
|
427
|
-
session = await executor.start_session(
|
|
428
|
-
task=task,
|
|
429
|
-
working_dir=working_dir.absolute(),
|
|
430
|
-
mode=mode.value,
|
|
431
|
-
model=model,
|
|
432
|
-
)
|
|
433
|
-
|
|
434
|
-
console.print(f"\n[green]Session started:[/] {session.id[:8]}")
|
|
435
|
-
|
|
436
|
-
if mode == ModeType.sync:
|
|
437
|
-
response = session.messages[-1].content if session.messages else "(no response)"
|
|
438
|
-
console.print(f"\n[bold]Response:[/]\n{response}")
|
|
435
|
+
import time
|
|
436
|
+
from zwarm.sessions import CodexSessionManager, SessionStatus
|
|
439
437
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
if user_input.lower() == "exit" or not user_input:
|
|
445
|
-
break
|
|
438
|
+
console.print(f"[bold]Running Codex session...[/]")
|
|
439
|
+
console.print(f" Task: {task[:60]}{'...' if len(task) > 60 else ''}")
|
|
440
|
+
if model:
|
|
441
|
+
console.print(f" Model: {model}")
|
|
446
442
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
except KeyboardInterrupt:
|
|
450
|
-
break
|
|
451
|
-
else:
|
|
452
|
-
console.print("[dim]Async mode - session running in background.[/]")
|
|
453
|
-
console.print("Use 'zwarm status' to check progress.")
|
|
443
|
+
manager = CodexSessionManager(working_dir / ".zwarm")
|
|
444
|
+
effective_model = model or "gpt-5.1-codex-mini"
|
|
454
445
|
|
|
455
|
-
|
|
456
|
-
|
|
446
|
+
session = manager.start_session(
|
|
447
|
+
task=task,
|
|
448
|
+
working_dir=working_dir.absolute(),
|
|
449
|
+
model=effective_model,
|
|
450
|
+
)
|
|
457
451
|
|
|
458
|
-
|
|
452
|
+
console.print(f"\n[green]Session started:[/] {session.short_id}")
|
|
453
|
+
|
|
454
|
+
if wait:
|
|
455
|
+
console.print("[dim]Waiting for completion...[/]")
|
|
456
|
+
while True:
|
|
457
|
+
time.sleep(2)
|
|
458
|
+
session = manager.get_session(session.id)
|
|
459
|
+
if session.status != SessionStatus.RUNNING:
|
|
460
|
+
break
|
|
461
|
+
|
|
462
|
+
if session.status == SessionStatus.COMPLETED:
|
|
463
|
+
console.print(f"\n[green]✓ Completed[/]")
|
|
464
|
+
# Show last assistant message
|
|
465
|
+
for msg in reversed(session.messages):
|
|
466
|
+
if msg.role == "assistant":
|
|
467
|
+
console.print(f"\n[bold]Response:[/]\n{msg.content}")
|
|
468
|
+
break
|
|
469
|
+
else:
|
|
470
|
+
console.print(f"\n[red]Status:[/] {session.status.value}")
|
|
471
|
+
if session.error:
|
|
472
|
+
console.print(f"[red]Error:[/] {session.error}")
|
|
473
|
+
else:
|
|
474
|
+
console.print("[dim]Running in background. Check with:[/]")
|
|
475
|
+
console.print(f" zwarm sessions")
|
|
476
|
+
console.print(f" zwarm session show {session.short_id}")
|
|
459
477
|
|
|
460
478
|
|
|
461
479
|
@app.command()
|
|
@@ -740,12 +758,12 @@ def init(
|
|
|
740
758
|
[bold]Creates:[/]
|
|
741
759
|
[cyan].zwarm/[/] State directory for sessions and events
|
|
742
760
|
[cyan].zwarm/config.toml[/] Runtime settings (weave, adapter, watchers)
|
|
743
|
-
[cyan].zwarm/codex.toml[/] Codex CLI settings (model,
|
|
761
|
+
[cyan].zwarm/codex.toml[/] Codex CLI settings (model, web search, etc.)
|
|
744
762
|
[cyan]zwarm.yaml[/] Project config (optional, with --with-project)
|
|
745
763
|
|
|
746
764
|
[bold]Configuration relationship:[/]
|
|
747
765
|
config.toml → Controls zwarm itself (tracing, which watchers run)
|
|
748
|
-
codex.toml →
|
|
766
|
+
codex.toml → Codex settings, parsed by zwarm and passed via -c overrides
|
|
749
767
|
zwarm.yaml → Project-specific context injected into orchestrator
|
|
750
768
|
|
|
751
769
|
[bold]Examples:[/]
|
|
@@ -917,6 +935,23 @@ def init(
|
|
|
917
935
|
codex_toml_path.write_text(codex_content)
|
|
918
936
|
console.print(f" [green]✓[/] Created .zwarm/codex.toml")
|
|
919
937
|
|
|
938
|
+
# Create claude.toml for isolated Claude Code configuration
|
|
939
|
+
claude_toml_path = state_dir / "claude.toml"
|
|
940
|
+
write_claude_toml = True
|
|
941
|
+
if claude_toml_path.exists():
|
|
942
|
+
if not non_interactive:
|
|
943
|
+
overwrite_claude = typer.confirm(" .zwarm/claude.toml exists. Overwrite?", default=False)
|
|
944
|
+
if not overwrite_claude:
|
|
945
|
+
write_claude_toml = False
|
|
946
|
+
console.print(" [dim]Skipping claude.toml[/]")
|
|
947
|
+
else:
|
|
948
|
+
write_claude_toml = False # Don't overwrite in non-interactive mode
|
|
949
|
+
|
|
950
|
+
if write_claude_toml:
|
|
951
|
+
claude_content = _generate_claude_toml(model="sonnet")
|
|
952
|
+
claude_toml_path.write_text(claude_content)
|
|
953
|
+
console.print(f" [green]✓[/] Created .zwarm/claude.toml")
|
|
954
|
+
|
|
920
955
|
# Create zwarm.yaml
|
|
921
956
|
if create_project_config:
|
|
922
957
|
if zwarm_yaml_path.exists() and not non_interactive:
|
|
@@ -939,7 +974,7 @@ def init(
|
|
|
939
974
|
# Explain config files
|
|
940
975
|
console.print("[bold]Configuration files:[/]")
|
|
941
976
|
console.print(" [cyan].zwarm/config.toml[/] - Runtime settings (Weave tracing, watchers)")
|
|
942
|
-
console.print(" [cyan].zwarm/codex.toml[/] - Codex CLI settings (model,
|
|
977
|
+
console.print(" [cyan].zwarm/codex.toml[/] - Codex CLI settings (model, web search, sandbox)")
|
|
943
978
|
if create_project_config:
|
|
944
979
|
console.print(" [cyan]zwarm.yaml[/] - Project context and constraints")
|
|
945
980
|
console.print()
|
|
@@ -959,40 +994,87 @@ def _generate_config_toml(
|
|
|
959
994
|
adapter: str = "codex_mcp",
|
|
960
995
|
watchers: list[str] | None = None,
|
|
961
996
|
) -> str:
|
|
962
|
-
"""Generate config.toml content."""
|
|
997
|
+
"""Generate config.toml content with all options at their defaults."""
|
|
963
998
|
watchers = watchers or []
|
|
964
999
|
|
|
965
1000
|
lines = [
|
|
966
1001
|
"# zwarm configuration",
|
|
967
1002
|
"# Generated by 'zwarm init'",
|
|
1003
|
+
"# All values shown are defaults - uncomment and modify as needed",
|
|
968
1004
|
"",
|
|
1005
|
+
"# ============================================================================",
|
|
1006
|
+
"# Weave Integration (optional tracing/observability)",
|
|
1007
|
+
"# ============================================================================",
|
|
969
1008
|
"[weave]",
|
|
970
1009
|
]
|
|
971
1010
|
|
|
972
1011
|
if weave_project:
|
|
973
1012
|
lines.append(f'project = "{weave_project}"')
|
|
974
1013
|
else:
|
|
975
|
-
lines.append(
|
|
1014
|
+
lines.append('# project = "your-entity/your-project" # Uncomment to enable Weave tracing')
|
|
976
1015
|
|
|
977
1016
|
lines.extend([
|
|
1017
|
+
"enabled = true",
|
|
978
1018
|
"",
|
|
1019
|
+
"# ============================================================================",
|
|
1020
|
+
"# Orchestrator Settings",
|
|
1021
|
+
"# ============================================================================",
|
|
979
1022
|
"[orchestrator]",
|
|
980
|
-
|
|
1023
|
+
'# lm = "gpt-5-mini" # LLM for orchestrator (gpt-5-mini, gpt-5, claude-sonnet-4)',
|
|
1024
|
+
"max_steps = 50 # Max steps for orchestrate command",
|
|
1025
|
+
"max_steps_per_turn = 60 # Max steps per turn in pilot mode",
|
|
1026
|
+
"parallel_delegations = 4 # Max concurrent delegations",
|
|
1027
|
+
'# prompt = "path/to/prompt.yaml" # Custom prompt file (optional)',
|
|
1028
|
+
'# allowed_dirs = ["*"] # Directories agent can delegate to (default: working_dir only)',
|
|
1029
|
+
"",
|
|
1030
|
+
"# Context window compaction (prevents overflow on long tasks)",
|
|
1031
|
+
"[orchestrator.compaction]",
|
|
1032
|
+
"enabled = true",
|
|
1033
|
+
"max_tokens = 100000 # Trigger compaction above this",
|
|
1034
|
+
"threshold_pct = 0.85 # Compact when at this % of max_tokens",
|
|
1035
|
+
"target_pct = 0.7 # Target this % after compaction",
|
|
1036
|
+
"keep_first_n = 2 # Always keep first N messages (system + task)",
|
|
1037
|
+
"keep_last_n = 10 # Always keep last N messages (recent context)",
|
|
981
1038
|
"",
|
|
1039
|
+
"# ============================================================================",
|
|
1040
|
+
"# Executor Settings (codex agent configuration)",
|
|
1041
|
+
"# ============================================================================",
|
|
982
1042
|
"[executor]",
|
|
983
|
-
f'adapter = "{adapter}"',
|
|
984
|
-
|
|
1043
|
+
f'adapter = "{adapter}" # codex_mcp | codex_exec | claude_code',
|
|
1044
|
+
'# model = "gpt-5.1-codex-mini" # Model for delegated sessions (uses codex.toml default if not set)',
|
|
1045
|
+
'sandbox = "workspace-write" # read-only | workspace-write | danger-full-access',
|
|
1046
|
+
"timeout = 3600 # Session timeout in seconds",
|
|
1047
|
+
'reasoning_effort = "high" # low | medium | high',
|
|
985
1048
|
"",
|
|
1049
|
+
"# ============================================================================",
|
|
1050
|
+
"# Watchers (automated monitoring and nudges)",
|
|
1051
|
+
"# ============================================================================",
|
|
986
1052
|
"[watchers]",
|
|
987
|
-
f"enabled = {watchers}",
|
|
1053
|
+
f"enabled = {str(bool(watchers)).lower()}",
|
|
1054
|
+
'message_role = "user" # Role for nudge messages: user | assistant | system',
|
|
988
1055
|
"",
|
|
989
|
-
"#
|
|
990
|
-
"#
|
|
991
|
-
"
|
|
1056
|
+
"# Default watchers: progress, budget, delegation_reminder",
|
|
1057
|
+
"# Uncomment below to customize:",
|
|
1058
|
+
"",
|
|
1059
|
+
"# [[watchers.watchers]]",
|
|
1060
|
+
'# name = "progress"',
|
|
1061
|
+
"# enabled = true",
|
|
1062
|
+
"",
|
|
1063
|
+
"# [[watchers.watchers]]",
|
|
1064
|
+
'# name = "budget"',
|
|
1065
|
+
"# enabled = true",
|
|
1066
|
+
"# [watchers.watchers.config]",
|
|
1067
|
+
"# max_sessions = 10",
|
|
992
1068
|
"# warn_at_percent = 80",
|
|
993
1069
|
"",
|
|
994
|
-
"# [watchers.
|
|
995
|
-
|
|
1070
|
+
"# [[watchers.watchers]]",
|
|
1071
|
+
'# name = "delegation_reminder"',
|
|
1072
|
+
"# enabled = true",
|
|
1073
|
+
"",
|
|
1074
|
+
"# ============================================================================",
|
|
1075
|
+
"# State Directory",
|
|
1076
|
+
"# ============================================================================",
|
|
1077
|
+
'# state_dir = ".zwarm" # Where to store session data',
|
|
996
1078
|
"",
|
|
997
1079
|
])
|
|
998
1080
|
|
|
@@ -1006,28 +1088,73 @@ def _generate_codex_toml(
|
|
|
1006
1088
|
"""
|
|
1007
1089
|
Generate codex.toml for isolated codex configuration.
|
|
1008
1090
|
|
|
1009
|
-
This file is
|
|
1010
|
-
|
|
1091
|
+
This file is parsed by zwarm and settings are passed to codex via -c overrides.
|
|
1092
|
+
Each .zwarm directory has its own codex config, independent of ~/.codex/config.toml.
|
|
1011
1093
|
"""
|
|
1012
1094
|
lines = [
|
|
1013
1095
|
"# Codex configuration for zwarm",
|
|
1014
|
-
"#
|
|
1096
|
+
"# zwarm parses this file and passes settings to codex via -c overrides",
|
|
1097
|
+
"# Each .zwarm dir has its own config, independent of ~/.codex/config.toml",
|
|
1015
1098
|
"# Generated by 'zwarm init'",
|
|
1016
1099
|
"",
|
|
1017
1100
|
"# Model settings",
|
|
1018
1101
|
f'model = "{model}"',
|
|
1019
1102
|
f'model_reasoning_effort = "{reasoning_effort}" # low | medium | high',
|
|
1020
1103
|
"",
|
|
1021
|
-
"#
|
|
1022
|
-
"#
|
|
1104
|
+
"# DANGER MODE - bypasses all safety controls",
|
|
1105
|
+
"# Set to true to use --dangerously-bypass-approvals-and-sandbox",
|
|
1106
|
+
"full_danger = true",
|
|
1107
|
+
"",
|
|
1108
|
+
"# Web search - enables web_search tool for agents",
|
|
1109
|
+
"[features]",
|
|
1110
|
+
"web_search_request = true",
|
|
1023
1111
|
"",
|
|
1024
|
-
"#
|
|
1112
|
+
"# Sandbox settings - network access required for web search",
|
|
1113
|
+
"[sandbox_workspace_write]",
|
|
1114
|
+
"network_access = true",
|
|
1115
|
+
"",
|
|
1116
|
+
"# Approval policy - 'never' means no human approval needed",
|
|
1117
|
+
"# approval_policy = \"never\"",
|
|
1118
|
+
"",
|
|
1119
|
+
"# You can add any codex config key here",
|
|
1025
1120
|
"# See: https://github.com/openai/codex#configuration",
|
|
1026
1121
|
"",
|
|
1027
1122
|
]
|
|
1028
1123
|
return "\n".join(lines)
|
|
1029
1124
|
|
|
1030
1125
|
|
|
1126
|
+
def _generate_claude_toml(
|
|
1127
|
+
model: str = "sonnet",
|
|
1128
|
+
) -> str:
|
|
1129
|
+
"""
|
|
1130
|
+
Generate claude.toml for isolated Claude Code configuration.
|
|
1131
|
+
|
|
1132
|
+
This file is parsed by zwarm and settings are passed to claude via CLI flags.
|
|
1133
|
+
Each .zwarm directory has its own claude config.
|
|
1134
|
+
"""
|
|
1135
|
+
lines = [
|
|
1136
|
+
"# Claude Code configuration for zwarm",
|
|
1137
|
+
"# zwarm parses this file and passes settings to claude via CLI flags",
|
|
1138
|
+
"# Each .zwarm dir has its own config",
|
|
1139
|
+
"# Generated by 'zwarm init'",
|
|
1140
|
+
"",
|
|
1141
|
+
"# Model settings",
|
|
1142
|
+
f'model = "{model}" # sonnet | opus | haiku',
|
|
1143
|
+
"",
|
|
1144
|
+
"# DANGER MODE - bypasses all permission checks",
|
|
1145
|
+
"# Set to true to use --dangerously-skip-permissions",
|
|
1146
|
+
"full_danger = true",
|
|
1147
|
+
"",
|
|
1148
|
+
"# Note: Claude Code uses different CLI flags than Codex",
|
|
1149
|
+
"# Common options:",
|
|
1150
|
+
"# --model <model> Model to use (sonnet, opus, haiku)",
|
|
1151
|
+
"# --add-dir <path> Additional directories to allow",
|
|
1152
|
+
"# --allowed-tools <tools> Restrict available tools",
|
|
1153
|
+
"",
|
|
1154
|
+
]
|
|
1155
|
+
return "\n".join(lines)
|
|
1156
|
+
|
|
1157
|
+
|
|
1031
1158
|
def _generate_zwarm_yaml(
|
|
1032
1159
|
description: str = "",
|
|
1033
1160
|
context: str = "",
|
|
@@ -1534,6 +1661,7 @@ def session_start(
|
|
|
1534
1661
|
Start a new Codex session in the background.
|
|
1535
1662
|
|
|
1536
1663
|
The session runs independently and you can check on it later.
|
|
1664
|
+
Web search is always enabled via .codex/config.toml (set up by `zwarm init`).
|
|
1537
1665
|
|
|
1538
1666
|
[bold]Examples:[/]
|
|
1539
1667
|
[dim]# Simple task[/]
|
|
@@ -1541,6 +1669,9 @@ def session_start(
|
|
|
1541
1669
|
|
|
1542
1670
|
[dim]# With specific model[/]
|
|
1543
1671
|
$ zwarm session start "Refactor the API" --model gpt-5.1-codex-max
|
|
1672
|
+
|
|
1673
|
+
[dim]# Web search is always available[/]
|
|
1674
|
+
$ zwarm session start "Research latest OAuth2 best practices"
|
|
1544
1675
|
"""
|
|
1545
1676
|
from zwarm.sessions import CodexSessionManager
|
|
1546
1677
|
|