Fake the harness. No provider, no queue worker, no network, but the real coordinator, event store, approvals, and artifacts:
use Clutch\Laravel\Facades\Clutch;
use Clutch\Laravel\Runtime\ClutchResult;
Clutch::fake([
'Draft a brief' => ClutchResult::text('The proposed brief...'),
]);
$session = Clutch::agent(ResearchAgent::class)->for($user)->create();
$session->queue('Draft a brief');
Clutch::assertSessionCreated(ResearchAgent::class);
Clutch::assertRunQueued('Draft a brief');
Clutch::assertRunCompleted();
Clutch::assertNothingAwaitingApproval();
Test a paused run:
Clutch::fake([
ClutchResult::awaitingApproval(
tool: 'publish_article',
arguments: ['article_id' => 123],
),
]);
$run = $session->queue('Publish the approved article.');
Clutch::assertApprovalRequested('publish_article');
Script richer ones:
Clutch::fake([
ClutchResult::text('Report ready.')
->withToolCall('search_web', ['q' => 'pricing'], '12 results')
->withArtifact(Artifact::fromContents($pdf, 'report.pdf')->name('Report')),
ClutchResult::structured(['score' => 87]),
ClutchResult::failure('The provider is down.'),
]);
The assertions available: assertSessionCreated, assertNoSessionCreated, assertRunQueued, assertRunCompleted, assertRunFailed, assertRunCancelled, assertRunExceededBudget, assertRunAwaitingApproval, assertApprovalRequested, assertApproved, assertRejected, assertNothingAwaitingApproval, assertArtifactCreated, assertEventRecorded, assertPromptedTimes, assertNothingPrompted.
Failure messages say what actually happened:
Expected a harness run to have reached [completed] for a prompt containing [Draft a brief].
Runs recorded: awaiting_approval ("Draft a brief")