openclaw - ✅(Solved) Fix Feature: global default for reasoning visibility (reasoningLevel) [2 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#44513Fetched 2026-04-08 00:45:52
View on GitHub
Comments
1
Participants
2
Timeline
3
Reactions
0
Author
Participants
Timeline (top)
cross-referenced ×2commented ×1

Fix Action

Fixed

PR fix notes

PR #56616: feat(openresponses): return reasoning/thinking content in /v1/responses output

Description (problem / solution / changelog)

Summary

  • Problem: The /v1/responses endpoint does not return model reasoning/thinking content. When models produce chain-of-thought reasoning (Anthropic Claude, OpenAI o-series, Gemini thinking, Ollama thinking models), the reasoning output is silently discarded by the HTTP API. Callers have no way to observe model reasoning through the OpenResponses interface.
  • Why it matters: The Open Responses spec defines type: "reasoning" as a first-class output item (ReasoningBody) and response.reasoning.delta/response.reasoning.done as streaming events. Not implementing these means OpenClaw's OpenResponses endpoint is incomplete relative to the spec, and API callers (dashboards, orchestrators, observability tools) cannot access reasoning data.
  • What changed: The endpoint now captures reasoning content from agent thinking events and returns it as type: "reasoning" output items (non-stream) or response.reasoning.delta/response.reasoning.done SSE events (stream). When no assistant text is produced but reasoning exists, the reasoning text is used as a fallback response to avoid empty replies from thinking-only models.
  • What did NOT change (scope boundary): No config changes required. No new dependencies. Existing behavior for non-reasoning models is unchanged. The onReasoningStream callback path for channel-specific reasoning delivery (Discord, Telegram, etc.) is unmodified. This change only adds reasoning to the HTTP API surface.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Related #54496
  • Related #44513
  • Related #8364
  • This PR fixes a bug or regression

Root Cause / Regression History (if applicable)

N/A — this is a new feature implementing an existing spec item.

Regression Test Plan (if applicable)

N/A

User-visible / Behavior Changes

Non-streaming responses

When reasoning is produced, the response output array now includes a type: "reasoning" item before the assistant message:

{
  "output": [
    {
      "type": "reasoning",
      "id": "reasoning_abc123",
      "content": "Let me think about this step by step..."
    },
    {
      "type": "message",
      "role": "assistant",
      "content": [{ "type": "output_text", "text": "The answer is 42." }]
    }
  ]
}

Streaming responses

Two new SSE event types are emitted during streaming:

  • response.reasoning.delta — cumulative reasoning text as it streams
  • response.reasoning.done — final reasoning text when complete

Fallback behavior

When a model produces reasoning but no assistant text (common with thinking-only model configurations), the reasoning content is used as the response text instead of returning "No response from OpenClaw."

Diagram (if applicable)

Model produces reasoning + answer:

  [thinking events] ──→ collect reasoning ──→ output[0]: reasoning item
  [assistant events] ──→ collect text     ──→ output[1]: assistant message

Model produces reasoning only (no assistant text):

  [thinking events] ──→ collect reasoning ──→ output[0]: reasoning item
  (no assistant text)                     ──→ output[1]: assistant = reasoning fallback

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No — reasoning content was already produced by the model, it was just being discarded

Repro + Verification

Environment

  • OS: macOS 15.4 (Apple Silicon)
  • Runtime: Node 22, Bun 1.2
  • Model: any reasoning-capable model (Claude, o-series, Gemini thinking)

Steps

  1. Send a POST to /v1/responses with a reasoning-capable model
  2. Observe the response output array

Expected

Reasoning content appears as type: "reasoning" output item.

Actual

Same as expected.

Evidence

  • pnpm build green
  • pnpm check green (lint, format, types)
  • src/gateway/openresponses-http.test.ts — 12/12 tests pass
  • src/agents/pi-embedded-subscribe.* — 21/30 pass (9 pre-existing upstream failures, verified same count on clean upstream/main)

Human Verification (required)

  • Verified: non-stream reasoning collection, stream reasoning events, fallback from thinking to text, cleanup on error
  • Edge cases: no reasoning produced (no-op), reasoning-only response (fallback), tool-call + reasoning combined output
  • Not verified: production load, all provider-specific thinking formats

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes — adds new output items, does not remove or change existing ones
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: reasoning content could be large (10k+ chars) for complex prompts, increasing response payload size
    • Mitigation: This mirrors the model's actual output; callers can ignore the reasoning item if not needed
  • Risk: double emitAgentEvent calls (raw + formatted) for channel-streaming reasoning mode
    • Mitigation: The raw-only event fires first for HTTP capture; the formatted event fires only when streamReasoning is active. HTTP consumers filter by rawText presence.

Made with Cursor

Changed files

  • src/agents/pi-embedded-subscribe.handlers.messages.ts (modified, +9/-10)
  • src/agents/pi-embedded-subscribe.handlers.types.ts (modified, +1/-0)
  • src/agents/pi-embedded-subscribe.ts (modified, +25/-9)
  • src/gateway/open-responses.schema.ts (modified, +15/-1)
  • src/gateway/openresponses-http.ts (modified, +132/-12)

PR #56674: feat(openresponses): return reasoning/thinking content in /v1/responses output

Description (problem / solution / changelog)

Summary

  • Problem: The /v1/responses endpoint does not return model reasoning/thinking content. When models produce chain-of-thought reasoning (Anthropic Claude, OpenAI o-series, Gemini thinking, Ollama thinking models), the reasoning output is silently discarded by the HTTP API. Callers have no way to observe model reasoning through the OpenResponses interface.
  • Why it matters: The Open Responses spec defines type: "reasoning" as a first-class output item (ReasoningBody) and response.reasoning.delta/response.reasoning.done as streaming events. Not implementing these means OpenClaw's OpenResponses endpoint is incomplete relative to the spec, and API callers (dashboards, orchestrators, observability tools) cannot access reasoning data.
  • What changed: The endpoint now captures reasoning content from agent thinking events and returns it as type: "reasoning" output items (non-stream) or response.reasoning.delta/response.reasoning.done SSE events (stream). When no assistant text is produced but reasoning exists, the reasoning text is used as a fallback response to avoid empty replies from thinking-only models.
  • What did NOT change (scope boundary): No config changes required. No new dependencies. Existing behavior for non-reasoning models is unchanged. The onReasoningStream callback path for channel-specific reasoning delivery (Discord, Telegram, etc.) is unmodified. This change only adds reasoning to the HTTP API surface.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Related #54496
  • Related #44513
  • Related #8364
  • Partially addresses #31449 (OpenResponses reasoning output/events only; full tool I/O/context parity still pending)
  • This PR fixes a bug or regression

Root Cause / Regression History (if applicable)

N/A — this is a new feature implementing an existing spec item.

Regression Test Plan (if applicable)

N/A

User-visible / Behavior Changes

Non-streaming responses

When reasoning is produced, the response output array now includes a type: "reasoning" item before the assistant message:

{
  "output": [
    {
      "type": "reasoning",
      "id": "reasoning_abc123",
      "content": "Let me think about this step by step..."
    },
    {
      "type": "message",
      "role": "assistant",
      "content": [{ "type": "output_text", "text": "The answer is 42." }]
    }
  ]
}

Streaming responses

Two new SSE event types are emitted during streaming:

  • response.reasoning.delta — cumulative reasoning text as it streams
  • response.reasoning.done — final reasoning text when complete

Fallback behavior

When a model produces reasoning but no assistant text (common with thinking-only model configurations), the reasoning content is used as the response text instead of returning "No response from OpenClaw."

Diagram (if applicable)

Model produces reasoning + answer:

  [thinking events] ──→ collect reasoning ──→ output[0]: reasoning item
  [assistant events] ──→ collect text     ──→ output[1]: assistant message

Model produces reasoning only (no assistant text):

  [thinking events] ──→ collect reasoning ──→ output[0]: reasoning item
  (no assistant text)                     ──→ output[1]: assistant = reasoning fallback

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No — reasoning content was already produced by the model, it was just being discarded

Repro + Verification

Environment

  • OS: macOS 15.4 (Apple Silicon)
  • Runtime: Node 22, Bun 1.2
  • Model: any reasoning-capable model (Claude, o-series, Gemini thinking)

Steps

  1. Send a POST to /v1/responses with a reasoning-capable model
  2. Observe the response output array

Expected

Reasoning content appears as type: "reasoning" output item.

Actual

Same as expected.

Evidence

  • pnpm build green
  • pnpm check green (lint, format, types)
  • src/gateway/openresponses-http.test.ts — 12/12 tests pass
  • src/agents/pi-embedded-subscribe.* — 21/30 pass (9 pre-existing upstream failures, verified same count on clean upstream/main)

Human Verification (required)

  • Verified: non-stream reasoning collection, stream reasoning events, fallback from thinking to text, cleanup on error
  • Edge cases: no reasoning produced (no-op), reasoning-only response (fallback), tool-call + reasoning combined output
  • Not verified: production load, all provider-specific thinking formats

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes — adds new output items, does not remove or change existing ones
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: reasoning content could be large (10k+ chars) for complex prompts, increasing response payload size
    • Mitigation: This mirrors the model's actual output; callers can ignore the reasoning item if not needed
  • Risk: double emitAgentEvent calls (raw + formatted) for channel-streaming reasoning mode
    • Mitigation: The raw-only event fires first for HTTP capture; the formatted event fires only when streamReasoning is active. HTTP consumers filter by rawText presence.

Made with Cursor

Changed files

  • src/agents/pi-embedded-subscribe.handlers.messages.ts (modified, +16/-8)
  • src/agents/pi-embedded-subscribe.handlers.types.ts (modified, +1/-0)
  • src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.subscribeembeddedpisession.test.ts (modified, +45/-0)
  • src/agents/pi-embedded-subscribe.ts (modified, +26/-9)
  • src/gateway/open-responses.schema.ts (modified, +15/-1)
  • src/gateway/openresponses-http.test.ts (modified, +131/-0)
  • src/gateway/openresponses-http.ts (modified, +182/-15)
  • src/gateway/server-chat.agent-events.test.ts (modified, +41/-0)
  • src/gateway/server-chat.ts (modified, +3/-2)

Code Example

{
  "agents": {
    "defaults": {
      "reasoningDefault": "off"
    }
  }
}

---

{
  "reasoning": {
    "default": "off"
  }
}
RAW_BUFFERClick to expand / collapse

Problem

When a model is reasoning-capable (e.g. has reasoning: true in the catalog or provider metadata), the default reasoningLevel resolves to "on" via resolveReasoningDefault(). This causes every reasoning-capable model (Anthropic Sonnet/Opus, local models with reasoning: true) to send thinking/reasoning output as a separate follow-up message in every session — with no way to disable it globally.

Current behavior

  • resolveReasoningDefault() in model-selection returns "on" if the model supports reasoning
  • Every new session with a reasoning-capable model broadcasts thinking as a separate message
  • The only way to suppress it is:
    • /reasoning off per session (doesn't persist to new sessions)
    • Remove reasoning: true from custom model configs (prevents the model from thinking at all)
    • For Anthropic models, there's no override since they're in the built-in catalog

Expected behavior

There should be a global config key to set the default reasoning visibility, e.g.:

{
  "agents": {
    "defaults": {
      "reasoningDefault": "off"
    }
  }
}

Or at the top level:

{
  "reasoning": {
    "default": "off"
  }
}

This would allow users to keep models thinking internally (thinking: low/medium/high) without broadcasting the reasoning output as messages — which is the desired behavior for most chat-based setups (Slack, Telegram, etc.).

Impact

Without this, every reasoning-capable model spams follow-up messages with thinking output across all channels. Users have to play whack-a-mole with /reasoning off on every new session.

Environment

  • OpenClaw version: latest (as of March 2026)
  • Affected models: All reasoning-capable models (Anthropic Claude Sonnet/Opus, local Qwen with reasoning: true)
  • Channels: Slack (likely affects all channels)

extent analysis

Fix Plan

To address the issue, we need to introduce a global configuration option to control the default reasoning visibility. Here are the steps:

  • Add a new configuration key reasoningDefault to the agents.defaults or top-level reasoning section in the config file.
  • Update the resolveReasoningDefault() function in model-selection to check for the new config key and return its value if set.
  • If the config key is not set, the function can still return "on" as the default value.

Example code changes:

// config.json
{
  "agents": {
    "defaults": {
      "reasoningDefault": "off"
    }
  }
}

// model-selection.js
function resolveReasoningDefault() {
  const config = getConfig();
  return config.agents.defaults.reasoningDefault || "on";
}

Alternatively, you can add the config key at the top level:

{
  "reasoning": {
    "default": "off"
  }
}

And update the resolveReasoningDefault() function accordingly:

function resolveReasoningDefault() {
  const config = getConfig();
  return config.reasoning.default || "on";
}

Verification

To verify the fix, you can:

  • Set the reasoningDefault config key to "off" and create a new session with a reasoning-capable model.
  • Check that the model does not send thinking/reasoning output as a separate message.
  • Try setting the config key to "on" and verify that the model sends thinking/reasoning output as a separate message.

Extra Tips

  • Make sure to update the documentation to reflect the new configuration option.
  • Consider adding a warning or notice to the config file to inform users about the new option and its effects.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

There should be a global config key to set the default reasoning visibility, e.g.:

{
  "agents": {
    "defaults": {
      "reasoningDefault": "off"
    }
  }
}

Or at the top level:

{
  "reasoning": {
    "default": "off"
  }
}

This would allow users to keep models thinking internally (thinking: low/medium/high) without broadcasting the reasoning output as messages — which is the desired behavior for most chat-based setups (Slack, Telegram, etc.).

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix Feature: global default for reasoning visibility (reasoningLevel) [2 pull requests, 1 comments, 2 participants]