Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions swe_af/execution/dag_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ async def _setup_worktrees(
artifacts_dir=dag_state.artifacts_dir,
level=dag_state.current_level,
model=config.git_model,
permission_mode=config.permission_mode,
ai_provider=config.ai_provider,
build_id=build_id,
)
Expand Down Expand Up @@ -258,6 +259,7 @@ async def _merge_level_branches(
artifacts_dir=dag_state.artifacts_dir,
level=level_result.level_index,
model=config.merger_model,
permission_mode=config.permission_mode,
ai_provider=config.ai_provider,
)

Expand Down Expand Up @@ -348,6 +350,7 @@ async def _call_merger_for_repo(
artifacts_dir=dag_state.artifacts_dir,
level=level_result.level_index,
model=config.merger_model,
permission_mode=config.permission_mode,
ai_provider=config.ai_provider,
)
return result
Expand Down Expand Up @@ -459,6 +462,7 @@ async def _run_integration_tests(
artifacts_dir=dag_state.artifacts_dir,
level=level_result.level_index,
model=config.integration_tester_model,
permission_mode=config.permission_mode,
ai_provider=config.ai_provider,
workspace_manifest=dag_state.workspace_manifest,
)
Expand Down Expand Up @@ -491,6 +495,7 @@ async def _cleanup_worktrees(
level: int = 0,
model: str = "sonnet",
ai_provider: str = "claude",
permission_mode: str = "",
completed_results: list | None = None,
) -> None:
"""Remove worktrees and clean up branches after merge.
Expand Down Expand Up @@ -527,15 +532,15 @@ async def _cleanup_worktrees(
await _cleanup_single_repo(
call_fn, node_id, ws_repo.absolute_path, repo_worktrees_dir,
repo_branches, dag_state.artifacts_dir, level, model, ai_provider,
note_fn,
note_fn, permission_mode,
)
return

# --- Single-repo path: unchanged ---
await _cleanup_single_repo(
call_fn, node_id, dag_state.repo_path, dag_state.worktrees_dir,
branches_to_clean, dag_state.artifacts_dir, level, model, ai_provider,
note_fn,
note_fn, permission_mode,
)


Expand All @@ -550,6 +555,7 @@ async def _cleanup_single_repo(
model: str,
ai_provider: str,
note_fn: Callable | None = None,
permission_mode: str = "",
) -> None:
"""Clean up worktrees for a single repo. Retries once on failure."""
for attempt in range(2): # up to 1 retry
Expand All @@ -562,6 +568,7 @@ async def _cleanup_single_repo(
artifacts_dir=artifacts_dir,
level=level,
model=model,
permission_mode=permission_mode,
ai_provider=ai_provider,
)
if result.get("success"):
Expand Down Expand Up @@ -1591,6 +1598,7 @@ async def _memory_fn(action: str, key: str, value=None):
level=dag_state.current_level,
model=config.git_model,
ai_provider=config.ai_provider,
permission_mode=config.permission_mode,
completed_results=level_result.completed,
)
)
Expand Down
22 changes: 15 additions & 7 deletions swe_af/hitl/ask_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ def approval_webhook_url(app: Any) -> str | None:
]


class AskUserFormOption(BaseModel):
"""One selectable option for select, radio, or checkbox_group fields."""

value: str = Field(description="Submitted value for this option.")
label: str = Field(description="Human-readable label shown to the user.")


class AskUserFormField(BaseModel):
"""One field in a form the agent is constructing for the user."""

Expand Down Expand Up @@ -103,7 +110,7 @@ class AskUserFormField(BaseModel):
default=None,
description="Pre-filled value if the user submits without changing it.",
)
options: list[dict[str, str]] | None = Field(
options: list[AskUserFormOption] | None = Field(
default=None,
description=(
"Required for 'select', 'radio', 'checkbox_group'. Each entry is "
Expand Down Expand Up @@ -217,6 +224,7 @@ def _field_to_form_builder_call(form: Any, field: AskUserFormField) -> None:
common["default_value"] = field.default_value

ftype = field.type
options = [option.model_dump() for option in field.options or []]

if ftype == "input":
form.input(field.id, **common)
Expand All @@ -243,19 +251,19 @@ def _field_to_form_builder_call(form: Any, field: AskUserFormField) -> None:
kwargs["step"] = field.step
form.slider(field.id, **kwargs)
elif ftype == "select":
if not field.options:
if not options:
raise ValueError(f"select field '{field.id}' requires options")
form.select(field.id, options=field.options, **common)
form.select(field.id, options=options, **common)
elif ftype == "radio":
if not field.options:
if not options:
raise ValueError(f"radio field '{field.id}' requires options")
form.radio_group(field.id, options=field.options, **common)
form.radio_group(field.id, options=options, **common)
elif ftype == "checkbox_group":
if not field.options:
if not options:
raise ValueError(
f"checkbox_group field '{field.id}' requires options"
)
form.checkbox_group(field.id, options=field.options, **common)
form.checkbox_group(field.id, options=options, **common)
elif ftype == "checkbox":
common.pop("placeholder", None)
form.checkbox(field.id, checkbox_label=field.label, **common)
Expand Down
12 changes: 10 additions & 2 deletions swe_af/reasoners/execution_agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -1250,13 +1250,21 @@ async def run_qa_synthesizer(
workspace_manifest=ws_manifest,
)

provider = runtime_to_harness_adapter(ai_provider)
cwd = worktree_path or target_repo or "."

try:
result = await router.ai(
result = await router.harness(
task_prompt,
system=QA_SYNTHESIZER_SYSTEM_PROMPT,
system_prompt=QA_SYNTHESIZER_SYSTEM_PROMPT,
schema=QASynthesisResult,
model=model,
provider=provider,
cwd=cwd,
max_turns=DEFAULT_AGENT_MAX_TURNS,
permission_mode=permission_mode or None,
)
check_fatal_harness_error(result)
if result.parsed is not None:
router.note(
f"QA synthesizer complete: action={result.parsed.action.value}, "
Expand Down
Loading