Module: Brute

Defined in:
lib/brute/patches/buffer_nil_guard.rb,
lib/brute.rb,
lib/brute.rb,
lib/brute/diff.rb,
lib/brute/hooks.rb,
lib/brute/skill.rb,
lib/brute/session.rb,
lib/brute/version.rb,
lib/brute/pipeline.rb,
lib/brute/compactor.rb,
lib/brute/doom_loop.rb,
lib/brute/todo_store.rb,
lib/brute/tools/shell.rb,
lib/brute/agent_stream.rb,
lib/brute/orchestrator.rb,
lib/brute/prompts/base.rb,
lib/brute/message_store.rb,
lib/brute/system_prompt.rb,
lib/brute/tools/fs_read.rb,
lib/brute/tools/fs_undo.rb,
lib/brute/prompts/skills.rb,
lib/brute/snapshot_store.rb,
lib/brute/tools/delegate.rb,
lib/brute/tools/fs_patch.rb,
lib/brute/tools/fs_write.rb,
lib/brute/tools/question.rb,
lib/brute/middleware/base.rb,
lib/brute/middleware/otel.rb,
lib/brute/providers/shell.rb,
lib/brute/tools/fs_remove.rb,
lib/brute/tools/fs_search.rb,
lib/brute/tools/net_fetch.rb,
lib/brute/tools/todo_read.rb,
lib/brute/middleware/retry.rb,
lib/brute/prompts/autonomy.rb,
lib/brute/prompts/identity.rb,
lib/brute/tools/todo_write.rb,
lib/brute/prompts/max_steps.rb,
lib/brute/middleware/tracing.rb,
lib/brute/prompts/code_style.rb,
lib/brute/prompts/git_safety.rb,
lib/brute/prompts/tool_usage.rb,
lib/brute/file_mutation_queue.rb,
lib/brute/middleware/llm_call.rb,
lib/brute/prompts/conventions.rb,
lib/brute/prompts/doing_tasks.rb,
lib/brute/prompts/environment.rb,
lib/brute/prompts/objectivity.rb,
lib/brute/middleware/otel/span.rb,
lib/brute/prompts/build_switch.rb,
lib/brute/prompts/instructions.rb,
lib/brute/providers/models_dev.rb,
lib/brute/prompts/plan_reminder.rb,
lib/brute/prompts/proactiveness.rb,
lib/brute/prompts/frontend_tasks.rb,
lib/brute/prompts/tone_and_style.rb,
lib/brute/prompts/code_references.rb,
lib/brute/prompts/task_management.rb,
lib/brute/prompts/editing_approach.rb,
lib/brute/providers/shell_response.rb,
lib/brute/middleware/token_tracking.rb,
lib/brute/middleware/tool_use_guard.rb,
lib/brute/middleware/otel/tool_calls.rb,
lib/brute/middleware/compaction_check.rb,
lib/brute/middleware/message_tracking.rb,
lib/brute/middleware/otel/token_usage.rb,
lib/brute/patches/anthropic_tool_role.rb,
lib/brute/prompts/editing_constraints.rb,
lib/brute/prompts/security_and_safety.rb,
lib/brute/middleware/otel/tool_results.rb,
lib/brute/middleware/doom_loop_detection.rb,
lib/brute/middleware/session_persistence.rb,
lib/brute/middleware/tool_error_tracking.rb,
lib/brute/middleware/reasoning_normalizer.rb

Overview

Monkey-patch: Fix Anthropic tool result message role.

llm.rb stores tool results as messages with role=“tool” (via @llm.tool_role). Anthropic’s API requires tool result messages to have role=“user” with tool_result content blocks. The Completion adapter already correctly formats the content (Function::Return -> “tool_result”, …), but passes through the “tool” role unchanged — which Anthropic rejects.

This patch overrides adapt_message to set role=“user” when the message content contains tool returns.

Defined Under Namespace

Modules: Diff, FileMutationQueue, Hooks, Middleware, Patches, Prompts, Providers, Skill, SnapshotStore, TodoStore, Tools Classes: AgentStream, Compactor, DoomLoopDetector, MessageStore, Orchestrator, Pipeline, Session, SystemPrompt

Constant Summary collapse

TOOLS =

The complete set of tools available to the agent.

[
  Tools::FSRead,
  Tools::FSWrite,
  Tools::FSPatch,
  Tools::FSRemove,
  Tools::FSSearch,
  Tools::FSUndo,
  Tools::Shell,
  Tools::NetFetch,
  Tools::TodoWrite,
  Tools::TodoRead,
  Tools::Delegate,
  Tools::Question
].freeze
PROVIDERS =
{
  'anthropic' => ->(key) { LLM.anthropic(key: key).tap { Patches::AnthropicToolRole.apply! } },
  'openai' => ->(key) { LLM.openai(key: key) },
  'google' => ->(key) { LLM.google(key: key) },
  'deepseek' => ->(key) { LLM.deepseek(key: key) },
  'ollama' => ->(key) { LLM.ollama(key: key) },
  'xai' => ->(key) { LLM.xai(key: key) },
  'opencode_zen' => ->(key) { LLM::OpencodeZen.new(key: key) },
  'opencode_go' => ->(key) { LLM::OpencodeGo.new(key: key) },
  'shell' => ->(_key) { Providers::Shell.new },
}.freeze
VERSION =
"0.4.1"

Class Method Summary collapse

Class Method Details

.agent(cwd: Dir.pwd, model: nil, tools: TOOLS, session: nil, reasoning: {}, agent_name: nil, **callbacks) ⇒ Object

Create a new orchestrator with sensible defaults.



130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/brute.rb', line 130

def self.agent(cwd: Dir.pwd, model: nil, tools: TOOLS, session: nil, reasoning: {}, agent_name: nil, **callbacks)
  Orchestrator.new(
    provider: provider,
    model: model,
    tools: tools,
    cwd: cwd,
    session: session,
    reasoning: reasoning,
    agent_name: agent_name,
    **callbacks
  )
end

.api_key_for(name) ⇒ Object

Look up the API key for a given provider name.



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/brute.rb', line 174

def self.api_key_for(name)
  # Shell provider needs no key.
  return "none" if name == "shell"

  # OpenCode providers: check OPENCODE_API_KEY, fall back to "public" for anonymous access.
  if name == "opencode_zen" || name == "opencode_go"
    return ENV["OPENCODE_API_KEY"] || "public"
  end

  # Explicit generic key always works
  return ENV["LLM_API_KEY"] if ENV["LLM_API_KEY"]

  case name
  when "anthropic" then ENV["ANTHROPIC_API_KEY"]
  when "openai"    then ENV["OPENAI_API_KEY"]
  when "google"    then ENV["GOOGLE_API_KEY"]
  end
end

.configured_providersObject

List provider names that have API keys configured in the environment. The shell provider is always available (no key needed).



157
158
159
# File 'lib/brute.rb', line 157

def self.configured_providers
  PROVIDERS.keys.select { |name| api_key_for(name) }
end

.providerObject

Default provider, resolved from environment.



121
122
123
# File 'lib/brute.rb', line 121

def self.provider
  @provider ||= resolve_provider
end

.provider=(p) ⇒ Object



125
126
127
# File 'lib/brute.rb', line 125

def self.provider=(p)
  @provider = p
end

.provider_for(name) ⇒ Object

Build a provider instance for the given name using available API keys. Returns nil if no key is found.



163
164
165
166
167
168
169
170
171
# File 'lib/brute.rb', line 163

def self.provider_for(name)
  key = api_key_for(name)
  return nil unless key

  factory = PROVIDERS[name]
  return nil unless factory

  factory.call(key)
end