Skip to content

Releases: claude-php/claude-php-agent

v1.5.0

08 Feb 13:09

Choose a tag to compare

Cross-Vendor Dynamic Model Fusion (DMF)

Adds multi-vendor LLM support, enabling Claude to orchestrate OpenAI and Google Gemini as callable tools during agent execution. Claude remains the primary orchestrator while delegating to vendor-specific capabilities via the existing ToolInterface.

New Features

  • VendorRegistry — API key management for Anthropic, OpenAI, and Google Gemini with auto-detection from environment variables
  • ModelRegistry — Catalog of 30+ models across all three vendors with capability metadata (model IDs, descriptions, defaults)
  • Capability enum — Typed capabilities: chat, web_search, image_generation, text_to_speech, speech_to_text, code_execution, grounding, deep_research
  • CrossVendorToolFactory — Auto-creates vendor tools from available API keys; supports per-capability tool creation

Vendor Tools

Tool Vendor Capability
vendor_chat OpenAI / Gemini Cross-vendor chat delegation
openai_web_search OpenAI Real-time web search via Responses API
openai_image_generation OpenAI GPT Image 1.5 generation
openai_text_to_speech OpenAI TTS with voice styles and instructions
gemini_grounding Google Google Search grounded responses with citations
gemini_code_execution Google Server-side Python execution in sandbox
gemini_image_generation Google Nano Banana image generation and editing

Architecture

  • Lightweight cURL-based HTTP adapters (no vendor SDK dependencies)
  • Retry logic with exponential backoff
  • Per-vendor configuration overrides (custom models, timeouts, base URLs)
  • Full compatibility with existing ToolInterface — vendor tools integrate seamlessly with any agent type

Testing

  • 138 tests with 562 assertions (126 unit + 12 live integration)
  • Live integration tests verify all vendor APIs (chat, web search, grounding, code execution, cross-vendor delegation)

Usage

$vendorRegistry = VendorRegistry::fromEnvironment();
$factory = new CrossVendorToolFactory($vendorRegistry);
$tools = $factory->createAllTools();

$agent = new ReactAgent($client, [
    'tools' => $tools,
]);

Set API keys in .env:

OPENAI_API_KEY=your_key
GEMINI_API_KEY=your_key

See examples/cross_vendor_dmf.php for a complete walkthrough.

v1.4.8

08 Feb 10:04

Choose a tag to compare

Bug Fix

  • Skill relevance scoring: Filter out common English stop words and short tokens (< 3 chars) before scoring to prevent false positive skill matches (e.g. "in" matching "guidel-in-es", "f-in-ancial")

v1.4.7

08 Feb 09:20

Choose a tag to compare

Enhancement

Dynamic tool management at runtime via AgentContext

Added addTool() and removeTool() methods to AgentContext, allowing tools to be added or removed dynamically during agent execution. This enables more flexible workflows where the available toolset can change based on context or intermediate results.

Changes

  • AgentContext::addTool() — register a new tool so it is included in subsequent API calls
  • AgentContext::removeTool() — remove a tool so it is excluded from future API calls
  • Added type annotations to the tools property for better IDE support
  • Updated ToolExecutionTrait to source tools from the context's current tool list

Upgrade

composer update claude-php/agent

No breaking changes. Drop-in replacement for v1.4.6.

v1.4.6

06 Feb 03:25

Choose a tag to compare

Bug Fix

Fix orphaned tool_use blocks causing API validation errors

The agent loops (ReactLoop, PlanExecuteLoop, ReflectionLoop) previously relied on stop_reason === 'tool_use' to decide whether to add tool_result blocks. When the Claude API returned tool_use blocks with a different stop_reason (e.g. max_tokens), no tool_result was added, leaving orphaned tool_use blocks in the message array. The next API call then failed with:

messages.N: tool_use ids were found without tool_result blocks immediately after

Changes

  • Added contentHasToolUse() helper to ToolExecutionTrait that inspects response content directly
  • ReactLoop now always adds tool_result blocks when content contains tool_use, regardless of stop_reason
  • Same fix applied to PlanExecuteLoop and ReflectionLoop
  • Added regression test testToolUseWithMaxTokensStopReasonStillAddsToolResults

Upgrade

composer update claude-php/agent

No breaking changes. Drop-in replacement for v1.4.5.

v1.4.5

06 Feb 01:57

Choose a tag to compare

Bug Fix

fix: prevent context compaction from corrupting tool_use/tool_result pairing

Fixed a critical bug where long-running agent workflows (15+ iterations) would fail with tool_use ids were found without tool_result blocks immediately after API errors.

Root Cause

Three interacting issues in the context compaction system:

  1. Compaction fired between tool_use and tool_result additionAgentContext::addMessage() triggered auto-compaction after adding the assistant's tool_use response but before the tool_result was added, orphaning the tool_use block.

  2. Initial user message dropped during compaction — The compaction algorithm preserved system messages but not the initial user task message, causing compacted messages to start with an assistant role and violating API requirements.

  3. Compaction algorithm misordered preserved messages — The array_unshift approach inserted recent messages before preserved messages, breaking message ordering.

Changes

  • AgentContext::addMessage() now defers compaction when the last message is a dangling tool_use (no matching tool_result yet)
  • ContextManager::compactMessages() preserves both the system message and initial user task message
  • Rewrote compaction to correctly build preserved + recent message sets, keeping the maximum number of recent message units that fit
  • Added early return after clearToolResults if that alone brings context within limits
  • Added 7 regression tests covering multi-iteration compaction, tool_use pairing integrity, and message ordering

Impact

Fixes production failures for:

  • Document analysis (PDFs, large files)
  • Complex multi-step automation workflows
  • Data extraction requiring 15+ agent iterations
  • Any workflow using context management with auto_compact enabled

v1.4.4

06 Feb 01:14

Choose a tag to compare

Full Changelog: v1.4.3...v1.4.4

v1.4.3

05 Feb 21:20

Choose a tag to compare

Bug Fix: Always Add tool_results After tool_use

Removes the if (! empty($toolResults)) guard that could skip adding a tool_result message after the assistant's tool_use blocks. The Anthropic API requires every tool_use block to have a corresponding tool_result in the immediately following user message — skipping it leaves the conversation in an invalid state.

Changes

  • ReactLoop — always add tool_results message
  • ReflectionLoop — same fix in both generate() and refine() methods
  • PlanExecuteLoop — same fix in executeStep()
  • StreamingLoop — same fix

Full Changelog

v1.4.2...v1.4.3

v1.4.2

05 Feb 21:14

Choose a tag to compare

Bug Fix: Tool Exceptions Always Produce a tool_result

Fixes an issue where a tool that throws an exception would leave the conversation in an invalid state. The Anthropic API requires every tool_use block in an assistant message to have a corresponding tool_result block in the following user message. Previously, if $tool->execute() threw, the exception would bubble up without adding a tool_result, breaking the conversation.

Changes

  • ToolExecutionTrait — wrapped tool lookup and execution in try-catch; exceptions now produce an error ToolResult instead of propagating
  • StreamingLoop::executeStreamingTools() — same fix applied
  • ReactLoopTest — added testToolExceptionProducesErrorToolResult to verify throwing tools still produce a valid error tool_result

Full Changelog

v1.4.1...v1.4.2

v1.4.1

05 Feb 21:04

Choose a tag to compare

Bug Fix: JSON Object Encoding for Tool Inputs

Fixes an issue where PHP's json_decode($json, true) converts empty JSON objects {} to empty arrays [], which then re-encode as [] instead of {}. This caused the Anthropic API to reject tool_use requests with "Input should be a valid dictionary".

Changes

  • New ToolExecutionTrait — shared trait with normalizeContentBlocks(), executeTools(), and extractTextContent() methods, eliminating code duplication across all loop implementations
  • ReactLoop, ReflectionLoop, PlanExecuteLoop, StreamingLoop — all now use the trait and normalize tool_use.input to stdClass before adding to message history
  • Tool::toDefinition() — casts properties to (object) for correct API serialization
  • Unit tests — added tests for empty and non-empty input normalization
  • .gitignore — added runtime storage directories

Full Changelog

v1.4.0...v1.4.1

v1.3.0 - Error Handling Service 🎯

04 Feb 19:48

Choose a tag to compare

Error Handling Service with User-Friendly Error Messages

Inspired by Langflow's user-friendly error conversion, this comprehensive error handling service converts technical API errors into actionable user messages.

Key Features

  • 🎯 User-friendly error messages for all Claude API errors
  • 🔄 Smart retry logic with exponential backoff
  • 📊 Detailed error context extraction for debugging
  • ⚙️ 9 configurable default error patterns (rate limit, auth, timeout, etc.)
  • 🏢 Full service layer integration with ServiceManager
  • 📝 Comprehensive PSR-3 logging support
  • 🛠️ Safe tool execution helpers with error handling

Error Pattern Coverage

  • Rate limit errors (429)
  • Authentication errors (401)
  • Permission errors (403)
  • Timeout errors
  • Connection errors
  • Overloaded errors (529)
  • Bad request errors (400)
  • Server errors (500)
  • Validation errors (422)

Statistics

  • New Code: ~600 lines (service + factory)
  • Tests: 45+ tests across 4 test files
  • Documentation: ~1,200 lines
  • Examples: 9 working examples (~2,400 lines)
  • Total: ~4,200+ lines of production-ready code

Documentation

  • Complete service guide at docs/services/error-handling.md
  • Comprehensive tutorial at docs/tutorials/ErrorHandling_Tutorial.md
  • 9 example files in examples/Services/ and examples/tutorials/error-handling/