Skip to content

Releases: laragentic/agents

v0.8.1

Choose a tag to compare

@dalehurley dalehurley released this 18 Mar 08:04

Bug Fix

String-based PausesLoop detection for SDK-resolved tool results

The Prism gateway stringifies all tool results before they reach the agentic loop, so instanceof PausesLoop checks never matched for SDK-resolved tools. This caused loops to continue iterating instead of pausing when a tool returned a PausesLoop signal.

Changes

  • Added MARKER_KEY constant to PausesLoop contract — implementations must include this key in their __toString() output
  • Created PauseSignal::isSignal() static helper for string-based detection (mirrors AskHumanSignal::isSignal())
  • Updated both ReActLoop and PlanExecuteLoop to detect pauses via instanceof || PauseSignal::isSignal()
  • LoopResult::$pauseSignal and PlanResult::$pauseSignal are now ?string (stringified JSON) instead of ?PausesLoop
  • onPause callbacks now receive string $signal as the first argument

Migration

If you have onPause callbacks, update the first parameter type from PausesLoop to string:

// Before
->onPause(function (PausesLoop $signal, ...) { ... })

// After
->onPause(function (string $signal, ...) { ... })

If you have custom PausesLoop implementations, include the marker in __toString():

public function __toString(): string
{
    return json_encode([
        self::MARKER_KEY => true,
        // ... your payload
    ]);
}

v0.8.0

Choose a tag to compare

@dalehurley dalehurley released this 18 Mar 07:32

What's New

PausesLoop Support in PlanExecuteLoop

The PlanExecuteLoop now correctly detects and handles PausesLoop tool results during step execution. Previously, only the ReActLoop supported pausing when a tool required human interaction (e.g. an SDK app rendering an interactive iframe). This caused the plan-execute loop to keep running through remaining steps instead of pausing and waiting for user input.

The Problem

When using Plan & Execute mode with SDK app tools (like a Stamp Duty Calculator), the loop would:

  1. Execute the SDK app tool in step 1 — returning an iframe render payload
  2. Not pause — continue to steps 2, 3, 4... repeating the same instructions
  3. Eventually hit max steps, never rendering the interactive tool for the user

The Fix

Both planExecute() and planExecuteStream() now scan toolResults for PausesLoop instances (alongside the existing AskHumanSignal scan). When detected, the loop:

  • Records the completed step and fires afterStep callback
  • Calculates deferred steps (remaining plan steps not yet executed)
  • Fires the pause callback with the signal, deferred steps, step number, and response
  • Returns a PlanResult with pauseSignal and deferredSteps

New APIs on PlanResult

  • pauseSignal — the PausesLoop instance that caused the pause (null when not paused)
  • deferredSteps — list<string> of plan step descriptions not yet executed
  • paused() — returns true when the loop was paused by a PausesLoop tool result
  • completed() — now returns false when paused (in addition to askedHuman and reachedMaxSteps)

Usage

No code changes required — if your agent already uses PlanExecuteLoop with tools that implement PausesLoop, the loop will now correctly pause and return the signal.

$result = $agent->planExecute('Calculate stamp duty for NSW');

if ($result->paused()) {
    // Emit the SDK render event to the frontend
    $payload = json_decode((string) $result->pauseSignal, true);
    
    // Remaining steps the agent needs to complete after user interaction
    $deferred = $result->deferredSteps; // ['Record result', 'Advise client']
}

Full Changelog: v0.7.0...v0.8.0

v0.7.0

Choose a tag to compare

@dalehurley dalehurley released this 18 Mar 05:30

What's New

Skill Tool Restrictions

Skills can now specify which tools an agent should use via the toolIds parameter on Skill. This enables agents to filter their available tools based on active skill restrictions.

New APIs

  • Skill::$toolIds — array<string> constructor parameter specifying allowed tool identifiers
  • Skill::hasToolRestrictions() — returns true when the skill restricts tool usage
  • HasAgentSkills::allowedToolIds() — returns the union of tool IDs from all loaded skills (empty when no restrictions apply)

Prompt Enhancement

When a loaded skill has toolIds set, the enhanced prompt now includes a "Preferred tools for this skill" section listing the allowed tools, guiding the LLM toward the correct tool set.

Usage

$skill = new Skill(
    metadata: $metadata,
    instructions: 'Analyse contracts...',
    path: '',
    toolIds: ['sdk_contract_review', 'sdk_llm', 'sdk_docs_create'],
);

// In your agent's tools() method:
$allowedIds = $this->allowedToolIds();
// Filter tools based on $allowedIds when non-empty

Full Changelog: v0.6.2...v0.7.0

v0.6.2

Choose a tag to compare

@dalehurley dalehurley released this 17 Mar 01:43

Bug Fix

  • Fixed PausesLoop detection in SDK-resolved tool results: When the Laravel AI SDK resolves tool calls internally (via the Prism gateway), the ReAct loop's ACTION phase is skipped. Previously, only AskHumanSignal was scanned in these SDK-resolved results — PausesLoop signals were missed entirely. This caused both SDK tools (e.g. stamp duty calculator + email composer) to be executed within a single prompt() call, instead of pausing after the first interactive tool.

What Changed

  • Added scanSdkResolvedToolResults() and scanSdkResolvedToolResultsStream() methods to the ReActLoop trait
  • These methods scan $response->toolResults for PausesLoop instances, build pause observations with deferred tool context, fire onPause callbacks, and return a LoopResult with the pause signal and deferred tools
  • Both the sync reactLoop() and streaming reactLoopStream() paths now correctly handle PausesLoop in SDK-resolved results

v0.6.1

Choose a tag to compare

@dalehurley dalehurley released this 16 Mar 06:59

Added

  • onPause() callback — fires when a tool returns a PausesLoop result, receiving the pause signal, deferred tool names, iteration, and response
  • LoopResult::$deferredTools — array of tool names the LLM requested but were not executed due to the pause
  • Pause observation — when the loop pauses, an observation is built and stored in the conversation history, explicitly listing which tools were executed and which were deferred. This ensures the LLM picks up remaining tools when the conversation resumes.

Changed

  • The onPause callback replaces onLoopComplete when the loop pauses (previously loopComplete fired on pause, now pause fires instead)
  • CoTResult now includes pauseSignal and deferredTools properties (consistent with LoopResult)

Fixed

  • When the loop paused after executing an interactive tool (e.g. SDK app), the LLM had no context about deferred tools when the conversation resumed. Now the observation explicitly tells the LLM which tools still need to be called.

v0.6.0

Choose a tag to compare

@dalehurley dalehurley released this 13 Mar 05:18

What's New

PausesLoop contract for tools requiring human interaction

Tools that return a PausesLoop result (e.g. SDK app iframes) now cause the loop to stop executing further tools and terminate gracefully. This prevents multiple interactive tools from launching simultaneously when the LLM requests them in a single iteration.

Changes

  • Add PausesLoop marker interface in Contracts
  • Detect PausesLoop results in ExecutesLoopTools alongside AskHumanSignal
  • Handle pause termination in ReActLoop and ChainOfThoughtLoop (sync + stream)
  • Add pauseSignal and paused() to LoopResult

Full Changelog: v0.5.1...v0.6.0

v0.4.0 — AskHuman: Human-in-the-Loop Clarification

Choose a tag to compare

@dalehurley dalehurley released this 19 Feb 21:00

What's New

AskHuman Signal System

Agents can now pause an agentic loop to ask the human for clarification before proceeding. When the LLM decides it needs more information, it calls the built-in ask_human tool — the loop terminates immediately (no second LLM iteration) and fires the onAskHuman callback.

New Classes

  • AskHumanSignal — signal value object carrying the question payload
  • HumanQuestion — individual question with type, text, and options
  • QuestionType — enum: FreeText, SingleChoice, MultipleChoice
  • AskHumanTool — built-in tool the LLM calls; schema guides structured question construction

New Lifecycle Hook

$result = (new MyAgent)
    ->onAskHuman(function (AskHumanSignal $signal, int $iteration) {
        broadcast(new ClarificationRequested($signal->toArray()));
    })
    ->reactLoop('Help me build something...');

if ($result->askedHuman()) {
    return response()->json($result->askHumanSignal->toArray());
}

Two Question Modes

Free-text (default) — a single open question:

{ "mode": "free_text", "question": "What kind of project are you building?" }

Structured — typed questions the frontend renders natively:

{
  "mode": "structured",
  "questions": [
    { "type": "single_choice", "question": "What is your timeline?", "options": ["1 week", "1 month", "3+ months"] },
    { "type": "free_text", "question": "Describe the core feature.", "options": [] }
  ]
}

Works Across All Loops

onAskHuman is supported in ReActLoop, ChainOfThoughtLoop, and PlanExecuteLoop. Each result type (LoopResult, CoTResult, PlanResult) gains askedHuman() and askHumanSignal.

Full Changelog

v0.3.1...v0.4.0

v0.3.1 - Fix multi-loop trait collision

Choose a tag to compare

@dalehurley dalehurley released this 18 Feb 20:22

Bug Fix

Fatal error when composing multiple loop traits

Agents using more than one loop trait together (e.g. ReActLoop, ChainOfThoughtLoop, PlanExecuteLoop) would crash on instantiation with a PHP fatal error:

```
Trait method Laragentic\Loops\ChainOfThoughtLoop::onMaxIterationsReached has not been applied
because of collision with Laragentic\Loops\ReActLoop::onMaxIterationsReached
```

Root cause: ReActLoop and ChainOfThoughtLoop both defined 9 identical methods. PHP requires trait method collisions to be explicitly resolved in the consuming class, which isn't feasible to ask every agent author to do.

Fix: Shared methods have been extracted into two new concern traits:

  • Laragentic\Concerns\HasIterationCallbacks — shared iteration-phase callback registrations (onMaxIterationsReached, onIterationStart, onIterationEnd, onBeforeAction, onAfterAction)
  • Laragentic\Concerns\ExecutesLoopTools — shared tool execution helpers (executeLoopToolCalls, executeLoopTool, resolveToolMap, formatObservation)

PHP de-duplicates traits that appear via multiple paths, so any combination of loop traits now composes cleanly.

No breaking changes

All existing public APIs are unchanged. Agents using a single loop trait are unaffected. The formatObservation() method remains overridable. ReActLoop gains a new overridable buildReActObservation() method for customising the full ReAct observation prompt (guidance text + tool results).

Upgrading

No changes required. Update your composer.json constraint to ^0.3.1 and run composer update.

Release v0.2.0 - Agent Skills System

Choose a tag to compare

@dalehurley dalehurley released this 15 Feb 10:41
293b37c

v0.2.0 - Agent Skills System

Major release featuring the complete Agent Skills System following the
agentskills.io specification.

New Features

Agent Skills System

  • Progressive disclosure with dynamic skill loading
  • Manual skill loading via ->withSkill() and ->withSkills()
  • Auto-resolution with intelligent relevance scoring
  • Full integration with all loops (ReAct, Plan-Execute, Chain-of-Thought)
  • Callback system for skill loading and resolution events
  • Streaming support for real-time skill activation
  • Comprehensive configuration options

Example Skills (Production-Ready)

  • code-review: Security, performance, and best practices analysis
  • data-analysis: Statistical analysis and insight generation
  • api-testing: API validation and testing

Testing

  • 53 Skills tests with 154 assertions (100% passing)
  • Full integration test coverage
  • Comprehensive unit test suite

Documentation

  • Complete tutorial with examples and diagrams
  • Skills README with quick reference
  • Updated main README with Skills section
  • Three production-ready example skills

All Features

Agentic Loops

  • ReAct Loop (Reasoning + Acting)
  • Plan-Execute Loop (Planning + Execution + Synthesis)
  • Chain-of-Thought Loop (Iterative Self-Reflection)

Skills System

  • Dynamic skill loading and progressive disclosure
  • Auto-discovery and relevance scoring
  • Integration with all loop types

Developer Experience

  • Comprehensive callbacks for all loops
  • Streaming support (SSE)
  • Configurable limits and thresholds
  • Zero-config defaults
  • Full Laravel integration

For detailed usage and examples, see:

  • README.md - Complete documentation
  • tutorial/agent-skills-system.md - Skills tutorial
  • SKILLS_README.md - Skills quick reference

v0.1.0 - Chain-of-Thought Loop

Choose a tag to compare

@dalehurley dalehurley released this 15 Feb 06:14

🧠 Chain-of-Thought Loop

This release introduces a new agentic loop pattern focused on deep reasoning through iterative self-reflection.

What's New

Chain-of-Thought Loop

  • New trait: ChainOfThoughtLoop - Enables progressive reasoning with confidence evaluation
  • Streaming support: Real-time streaming of reasoning iterations via chainOfThoughtStream()
  • Self-reflection: Automatic confidence checks between reasoning cycles
  • Full transparency: Shows step-by-step thinking process to users

Supporting Classes

  • CoTResult - Enhanced result object with reasoning metadata
  • CoTStep - Value object for each reasoning iteration
  • CoTPrompts - Reusable prompt templates for CoT reasoning

Lifecycle Callbacks (6 new callbacks)

  • onIterationStart - When reasoning iteration begins
  • onBeforeReasoning - Before LLM reasons through problem
  • onAfterReasoning - After reasoning step completes
  • onReflection - When confidence evaluation occurs
  • onLoopComplete - When agent reaches confident answer
  • onMaxIterationsReached - When iteration limit is hit

Plus all standard action callbacks (onBeforeAction, onAfterAction)

Configuration

  • max_reasoning_iterations - Control iteration limits (default: 5)
  • throw_on_max_iterations - Configure error handling
  • Environment variable support via AGENTIC_COT_MAX_ITERATIONS

Documentation

  • New tutorial: Streaming Chain-of-Thought Loop
  • Complete code examples with backend routes and React frontend
  • Comparison tables: CoT vs ReAct vs Plan-Execute
  • Best practices and customization guide
  • Screenshots demonstrating the UI

Tests

  • Comprehensive unit tests with 20+ test cases
  • Integration tests with real API calls
  • 100% coverage of CoT loop logic

When to Use Chain-of-Thought

Use CoT when:

  • ✅ The reasoning process needs to be visible/auditable
  • ✅ You need to demonstrate understanding, not just results
  • ✅ The problem requires careful, step-by-step analysis
  • ✅ Users want to see "how" the agent thinks
  • ✅ Confidence in the answer is critical

Quick Example

```php
use Laragentic\Loops\ChainOfThoughtLoop;

class MathAgent implements Agent, HasTools
{
use Promptable, ChainOfThoughtLoop;

public function instructions(): string
{
    return 'Think step-by-step. Evaluate your confidence. '
         . 'Only answer when you're certain.';
}

}

$result = (new MathAgent)->chainOfThought(
'Which train is faster and by how much?'
);

echo $result->text();
echo "Reasoning iterations: {$result->reasoningIterations}";
```

Breaking Changes

None - fully backward compatible with v0.0.2

Updated Documentation

  • README updated with Chain-of-Thought section
  • Tutorial links updated
  • Configuration documented
  • All existing tutorials updated with CoT references

Full Changelog: v0.0.2...v0.1.0