Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ The format is based on Keep a Changelog, and this project currently tracks chang

- `todo_write` tool now updates an existing unchecked item in-place when `checked=True` instead of appending a duplicate `[x]` line.

- Built-in `Explore` and `claude-code-guide` agents no longer hard-code `model="haiku"`, which caused them to fail for users on non-Anthropic providers (OpenAI, Bedrock, custom base URLs, etc.). Both agents now use `model="inherit"` so they run with whatever model the parent session is using. `build_inherited_cli_flags` is also fixed to skip the `--model` flag entirely when the value is `"inherit"`, letting the subprocess correctly inherit the parent model via the `OPENHARNESS_MODEL` environment variable instead of receiving the literal string `"inherit"` as a model name.

- React TUI spinner now stays visible throughout the entire agent turn: `assistant_complete` no longer resets `busy` state prematurely, and `tool_started` explicitly sets `busy=true` so the status bar remains active even when tool calls follow an assistant message. `line_complete` is the sole signal that ends the turn and clears the spinner.
- Skill loader now uses `yaml.safe_load` to parse SKILL.md frontmatter, correctly handling YAML block scalars (`>`, `|`), quoted values, and other standard YAML constructs instead of naive line-by-line splitting.
- `BackendHostConfig` was missing the `cwd` field, causing `AttributeError: 'BackendHostConfig' object has no attribute 'cwd'` on startup when `oh` was run after the runtime refactor that added `cwd` support to `build_runtime`.
Expand Down
4 changes: 2 additions & 2 deletions src/openharness/coordinator/agent_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ class AgentDefinition(BaseModel):
),
tools=["Glob", "Grep", "Read", "WebFetch", "WebSearch"],
system_prompt=_CLAUDE_CODE_GUIDE_SYSTEM_PROMPT,
model="haiku",
model="inherit",
permission_mode="dontAsk",
subagent_type="claude-code-guide",
source="builtin",
Expand All @@ -565,7 +565,7 @@ class AgentDefinition(BaseModel):
),
disallowed_tools=["agent", "exit_plan_mode", "file_edit", "file_write", "notebook_edit"],
system_prompt=_EXPLORE_SYSTEM_PROMPT,
model="haiku",
model="inherit",
omit_claude_md=True,
subagent_type="Explore",
source="builtin",
Expand Down
3 changes: 2 additions & 1 deletion src/openharness/swarm/spawn_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ def build_inherited_cli_flags(
flags.extend(["--permission-mode", "acceptEdits"])

# --- Model override ----------------------------------------------------
if model:
# "inherit" means use the parent's model via the OPENHARNESS_MODEL env var.
if model and model != "inherit":
flags.extend(["--model", shlex.quote(model)])
Comment on lines +145 to 147

# --- Settings path propagation ----------------------------------------
Expand Down
37 changes: 37 additions & 0 deletions tests/test_coordinator/test_agent_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,43 @@ def test_builtin_general_purpose_has_all_tools():


# ---------------------------------------------------------------------------
# Model inheritance: provider-agnostic built-in agents
# ---------------------------------------------------------------------------

_PROVIDER_SPECIFIC_MODELS = {"haiku", "sonnet", "opus"}
"""Short model aliases that only exist for Anthropic's API."""


def test_builtin_explore_does_not_hardcode_provider_model():
"""Explore must use 'inherit' (or None) so it works with any API provider."""
builtins = get_builtin_agent_definitions()
explore = next(a for a in builtins if a.name == "Explore")
assert explore.model not in _PROVIDER_SPECIFIC_MODELS, (
f"Explore.model={explore.model!r} hard-codes a provider-specific model alias; "
"use 'inherit' so the agent works with non-Anthropic providers."
)


def test_builtin_claude_code_guide_does_not_hardcode_provider_model():
"""claude-code-guide must not hard-code an Anthropic-only model alias."""
builtins = get_builtin_agent_definitions()
guide = next(a for a in builtins if a.name == "claude-code-guide")
assert guide.model not in _PROVIDER_SPECIFIC_MODELS, (
f"claude-code-guide.model={guide.model!r} hard-codes a provider-specific model alias; "
"use 'inherit' so the agent works with non-Anthropic providers."
)


def test_builtin_provider_agnostic_agents_use_inherit_or_none():
"""Plan, verification, Explore, and claude-code-guide must not override the model."""
agnostic_agents = {"Plan", "verification", "Explore", "claude-code-guide"}
builtins = {a.name: a for a in get_builtin_agent_definitions()}
for name in agnostic_agents:
agent = builtins[name]
assert agent.model in {None, "inherit"}, (
f"Built-in agent {name!r} sets model={agent.model!r}; "
"provider-agnostic agents should use None or 'inherit'."
)
# _parse_agent_frontmatter
# ---------------------------------------------------------------------------

Expand Down
30 changes: 30 additions & 0 deletions tests/test_swarm/test_spawn_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from openharness.swarm.spawn_utils import (
TEAMMATE_COMMAND_ENV_VAR,
build_inherited_cli_flags,
build_inherited_env_vars,
get_teammate_command,
)
Expand All @@ -26,3 +27,32 @@ def test_build_inherited_env_vars_disables_coordinator_mode(monkeypatch):
env = build_inherited_env_vars()

assert env["CLAUDE_CODE_COORDINATOR_MODE"] == "0"


# ---------------------------------------------------------------------------
# build_inherited_cli_flags – model handling
# ---------------------------------------------------------------------------


def test_build_inherited_cli_flags_explicit_model_included():
flags = build_inherited_cli_flags(model="claude-opus-4-5")
assert "--model" in flags
idx = flags.index("--model")
assert "claude-opus-4-5" in flags[idx + 1]


def test_build_inherited_cli_flags_inherit_model_excluded():
"""model='inherit' must NOT produce a --model flag so the subprocess
picks up the parent's model from the OPENHARNESS_MODEL env var."""
flags = build_inherited_cli_flags(model="inherit")
assert "--model" not in flags


def test_build_inherited_cli_flags_none_model_excluded():
flags = build_inherited_cli_flags(model=None)
assert "--model" not in flags


def test_build_inherited_cli_flags_empty_string_model_excluded():
flags = build_inherited_cli_flags(model="")
assert "--model" not in flags
Loading