`reasoning_content` is a non-standard field that some thinking-model
providers (Kimi k2.5 on Anthropic-format) require on every assistant
message with tool calls — even when empty. Other OpenAI-compatible
providers (Cerebras, NVIDIA NIM, OpenAI direct, etc.) reject the field
outright with a 400 ``wrong_api_format`` validation error.
Until now, ``_convert_assistant_message`` unconditionally emitted
``reasoning_content: ''`` for tool-using assistant messages. That hard-
breaks every strict-OpenAI provider on the very first tool turn.
Behaviour change:
- Captured non-empty reasoning is still always replayed.
- The empty-string fallback is now opt-in via
``OPENHARNESS_REQUIRE_EMPTY_REASONING_CONTENT=1``.
- Default behaviour matches strict-OpenAI providers, which is the
larger / standards-compliant population. Kimi-on-Anthropic users
set the env var in their dotfiles or settings.
Verified end-to-end against Cerebras gpt-oss-120b (previously 100%
failure rate from this field) — zero ``wrong_api_format`` errors.
Tests added: 6 cases covering captured-replay, opt-in matrix
(truthy/falsy values), and the no-tool-calls invariant.
Some providers (e.g. DeepSeek, MiniMax) embed chain-of-thought content
inside <think>…</think> tags in delta.content rather than using a dedicated
reasoning_content field. Without filtering, these internal reasoning blocks
leak into the user-visible output.
Add _strip_think_blocks() which:
- Removes fully-closed <think>…</think> pairs via regex (DOTALL).
- Holds back any unclosed opening tag in a per-stream buffer so cross-chunk
tags are handled correctly.
Tests: 9 new unit tests in TestStripThinkBlocks covering no-op passthrough,
single/multiple complete blocks, multiline blocks, unclosed tags, and the
cross-chunk scenario.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add _token_limit_param_for_model to map max_tokens vs max_completion_tokens for models that require the latter (e.g. gpt-5 and reasoning-model families).
Add OpenAICompatibleClient that implements SupportsStreamingMessages,
enabling any provider using the OpenAI /v1/chat/completions format:
DashScope, DeepSeek, GitHub Models, Groq, Ollama, etc.
- New openai_client.py with streaming, tool calling, retry logic
- --api-format openai CLI flag and OPENHARNESS_API_FORMAT env var
- OPENAI_API_KEY fallback in resolve_api_key()
- Provider detection for dashscope/qwen and github models
- Wired through cli → app → runtime → client selection
- 11 unit tests for format conversion functions
- Updated README provider compatibility section
- CHANGELOG entries
A lightweight open-source Python implementation of the Agent Harness architecture.
44x lighter than Claude Code (11K vs 512K lines), 98% core tool coverage.
- 43 tools with Pydantic validation and parallel execution
- Skills system compatible with anthropics/skills (17+ tested)
- Plugin system compatible with claude-code/plugins (12+ tested)
- API retry with exponential backoff
- Multi-level permissions with path rules
- React/Ink TUI with "Oh my Harness!" branding
- 114 unit tests + 6 E2E test suites
- MIT License