# Task 1 Report: Fix `run_agent()` — crashes on every non-dispatcher call

## What was changed

### `claw_forge/agent/runner.py`

**Bug 1 (primary): String prompt crashes when `can_use_tool` is set**

The SDK's `_internal/client.py` raises `ValueError("can_use_tool callback requires streaming mode. Please provide prompt as an AsyncIterable instead of a string.")` whenever `can_use_tool` is set and the prompt is a plain string. `run_agent()` unconditionally sets `can_use_tool=make_can_use_tool(...)` and previously passed the raw string directly to `query()`.

Fix: Immediately before `raw_stream = query(...)`, detect whether `prompt` is a `str` and if so, wrap it in a single-item async generator that yields the SDK's streaming message format:
```python
{"type": "user", "message": {"role": "user", "content": _prompt_text}}
```
Non-string prompts (already an AsyncIterable) are passed through unchanged.

**Bug 2 (secondary): Wrong OAuth env var name**

Line 129 set `env["CLAUDE_OAUTH_TOKEN"]` — a name the CLI never reads. The correct var is `ANTHROPIC_AUTH_TOKEN` (as used in `orchestrator/task_handler.py:409`). Changed to `env["ANTHROPIC_AUTH_TOKEN"]`.

### `tests/agent/test_runner_streaming.py` (new file)

Test verifies that calling `run_agent("say hi", cwd=None, project_dir=None)` does not raise the SDK `ValueError` about streaming mode. Transport/auth failures are acceptable — only the `can_use_tool` validation error is the target.

## Test command and output

```
uv run pytest tests/agent/test_runner_streaming.py -v
```

**Before fix:**
```
FAILED tests/agent/test_runner_streaming.py::test_run_agent_string_prompt_does_not_raise_can_use_tool_valueerror
Failed: run_agent still passes a non-streaming prompt with can_use_tool
```

**After fix:**
```
PASSED tests/agent/test_runner_streaming.py::test_run_agent_string_prompt_does_not_raise_can_use_tool_valueerror
1 passed in 1.04s
```

**Lint:** `uv run ruff check claw_forge/ tests/` — All checks passed.

## Commit

SHA: `722b601`
Message: `fix(runner): stream string prompts so can_use_tool works; correct OAuth env var`

## Self-review notes

- The async generator closure captures `_prompt_text` (not `prompt`) to avoid a closure-over-loop-variable issue, consistent with the brief's guidance.
- The `stream_prompt: Any` type annotation allows the heterogeneous union type without mypy complaints.
- The non-str branch (`stream_prompt = prompt`) preserves the pre-existing non-string-prompt path unchanged, so dispatcher usage (which may already pass an AsyncIterable) is unaffected.
- The `_single_message_stream` generator return type is annotated `-> Any` rather than `-> AsyncIterator[dict[str, Any]]` to avoid a potential variance issue with the `yield` inside.

## Concerns

None. The fix is minimal and exactly scoped to the brief.

---

## Type Annotation Fix

**Change:** Widened the `run_agent()` parameter annotation from `prompt: str` to `prompt: str | AsyncIterable[Any]` to match the actual implementation (which branches on `isinstance(prompt, str)` and accepts both string and AsyncIterable inputs).

**File:** `claw_forge/agent/runner.py`
- Line 7: Added `AsyncIterable` to `collections.abc` import
- Line 33: Changed `prompt: str` to `prompt: str | AsyncIterable[Any]`

**Test command:** `uv run pytest tests/agent/test_runner_streaming.py -v`
**Test result:** `1 passed in 0.85s`

**Lint:** `uv run ruff check claw_forge/ tests/` — All checks passed.

**Mypy:** `uv run mypy claw_forge/agent/runner.py --ignore-missing-imports` — Success: no issues found.

This resolves the Pyright unreachable-code warning on the `else` branch at line 268.

---

## Test Updates

**Changes:** Updated two pre-existing tests in `tests/agent/test_agent_runner.py` to assert the corrected behaviour:

1. **`test_provider_config_oauth_token_sets_env` (line 223):**
   - Changed assertion from `CLAUDE_OAUTH_TOKEN` to `ANTHROPIC_AUTH_TOKEN` (the actual env var the CLI reads).

2. **`test_prompt_passed_to_query` (lines 261-262):**
   - Changed from asserting a raw string to consuming the async iterator and verifying the wrapped message structure.
   - The prompt is now wrapped in `_single_message_stream()` yielding: `{"type": "user", "message": {"role": "user", "content": "my special prompt"}}`.

**Test command:** `uv run pytest tests/agent/test_agent_runner.py::TestRunAgent::test_provider_config_oauth_token_sets_env tests/agent/test_agent_runner.py::TestRunAgent::test_prompt_passed_to_query -v`

**Result:** 2 passed in 0.45s

**Full agent suite:** `uv run pytest tests/agent/ -q` — 431 passed in 3.23s

**Lint:** `uv run ruff check claw_forge/ tests/` — All checks passed.
