Skip to content

Sync upstream updates: AI tool failure recovery, raw responses and Inertia QUERY redirects - #100

Merged
binaryfire merged 16 commits into
0.4from
upstream-sync-2026-11-03-3
Nov 3, 2026
Merged

binaryfire merged 16 commits into
0.4from
upstream-sync-2026-11-03-3

Conversation

@binaryfire

@binaryfire binaryfire commented Nov 3, 2026 •

Copy link
Copy Markdown
Member

Upstream Updates

  • Expose the provider's raw HTTP response on AI embeddings, transcription, image and reranking responses, so provider-specific data such as OpenRouter's usage cost is available. Individually cached embeddings carry the fresh request's response. Bedrock and the fakes return null, and serialization still drops the response. Document the new response types.
  • Send array criteria on AI boolean questions to OpenAI as JSON text. They were interpolated into the prompt as "Array" with a conversion warning.
  • Keep the text a streamed AI turn produced before pausing for approval when the turn resumes.
  • Let agent fakes resume tool approvals. Approved tools run and rejected tools get a denied result before the next faked response. Hypervel's claiming approval store is attached through RunContext::withApprovalStore() only when a run resumes approvals, so runContextFor() keeps upstream's signature. Document faked resumes in the testing section.
  • Run Anthropic tool calls when the message ends with end_turn.
  • Stop failing an agent run when a tool throws. The exception is reported and the model receives a failed tool result; listener exceptions still fail the run. Document it, and correct the concurrent tools section, which said a tool failure cancels the other tools.
  • Join streamed text across Anthropic pause_turn continuations, starting a new paragraph only after a step that ran tools. The streamed response's single pass follows the same rule.
  • Run conversation title generation as its own agent run, linked to the turn by parentInvocationId, so agent events report it. A failed title run still falls back to the start of the prompt. Document it.
  • Report empty file uploads in Telescope client request entries as 0KB instead of a null size.
  • Sandbox Telescope's HTML mail preview, so an email's scripts no longer run in the dashboard's origin. The response sends a sandbox Content-Security-Policy, text/html and nosniff, and the preview iframe is sandboxed.
  • The Telescope dashboard already runs on Vue 3, Vue Router 4 and Bootstrap 5, but lacked upstream's back-navigation fix. Changing an entry's tab now goes through the router, so going back to the entry reopens the selected tab. Rebuild the dashboard.
  • Override glob and minimatch with patched versions. js-beautify, used by the frontend tests, still requires versions affected by GHSA-3g3m-2fjr-6pw4 and GHSA-g3hv-75wg-m249. Neither package is in the dashboard bundles. Telescope's hypervel/guzzle ^8.3 requirement already covers upstream's raised Guzzle floor.
  • Explain why Pennant needs no listeners to flush feature values after queued jobs: values are memoized per coroutine, and each queued job runs in its own coroutine.
  • Convert redirects after QUERY requests to 303, as the Inertia middleware already does for PUT, PATCH and DELETE.
  • Document Inertia QUERY visits and the SSR --no-stop-before-start option, and adopt the protocol sentence in How It Works. Correct the Svelte prefetching example's Link import.
  • Return 404 for .php files in the Nginx deployment example, which otherwise served their source as plain text.
  • Document Redis key prefixes for several instances of an application sharing one Redis server, with the per-connection database alternative.
  • Omit cache.ttl from Sentry cache.put spans for entries that never expire.
  • Add upstream's Guzzle 8 transfer failure tests for Saloon, including connector retries. No source change is needed: Hypervel's HTTP client turns these failures into ConnectionException, which Saloon raises and retries as FatalRequestException.

Additional Hypervel changes

  • Run Bedrock Converse tool calls when a message stops with end_turn, in both buffered and streamed responses, matching the Anthropic fix.
  • Join a streamed resume's text to the paused content instead of rejoining every stored step. Rejoining split a continuation without tools, such as a pause_turn, that the stream had shown as one paragraph.
  • Leave coroutine cancellation unwrapped when a tool is interrupted, so a canceled run is never recovered as a tool failure.
  • Report a tool that throws while an approval is being resumed, as other tool failures are. On that path, a tool event listener that throws now ends the run, as it does elsewhere. It used to be returned to the model as a tool failure, even when the tool had already run.
  • Leave exception messages out of tool, sub-agent and file storage tool failure results unless app.debug is enabled. The messages can carry query bindings and server paths to the provider, the conversation and the client. The exceptions are still reported.
  • Report sub-agent failures and the exceptions caught by the copy, move and file URL tools. The read and metadata tools now report a failed lookup of an existing file instead of describing it as missing, and a failed read is no longer described as binary content.
  • Keep paragraph breaks between sub-agent steps that ran tools in live sub-agent output, matching the final result.
  • Type classification instructions, criteria, option descriptions and score levels to accept lists as well as keyed arrays. Lists were already sent to providers correctly, but static analysis rejected them.
  • Convert redirects after QUERY requests to 303 in the global EnsureGetOnRedirect middleware too. It handles redirects issued outside the Inertia middleware, such as login redirects, which stayed 302.
  • Type Telescope's MailEmlController response as Response instead of mixed.
  • Lower pnpm's minimumReleaseAge from 7 days to 3.

Affected tests, formatting, and source analysis pass, as do the Horizon and Telescope frontend tests. CI runs the full suite and supported service matrix.

Note

Sync upstream: AI tool failure recovery, raw provider responses, and QUERY redirect support

  • Tool and sub-agent failures no longer end a run: non-cancellation exceptions in InvokesTools.php are wrapped in ToolExecutionException and returned to the model as failed tool results. Exception details reach the model only when app.debug is enabled; the exception is always reported.
  • All AI gateways now attach the raw provider HTTP response to embeddings, image, transcription, and reranking results via withRawResponse() on EmbeddingsResponse.php and the other response models.
  • TextDelta::combine no longer inserts a blank line between consecutive streamed steps that ran no tools; the boundary remains after a ToolResult. RunContext moves approval-store and conversation-ID setup out of the constructor into a withApprovalStore() fluent setter.
  • Anthropic and Bedrock responses with tool calls now map a Stop finish reason to ToolCalls, so the tool-call loop runs.
  • Inertia QUERY requests get 302-to-303 redirect conversion in Middleware.php and EnsureGetOnRedirect.php; Telescope mail previews are sandboxed; RunContext constructor signature changed.
  • Risk: RunContext no longer accepts the approval store and conversation ID as constructor parameters — out-of-tree callers constructing it that way break; use withApprovalStore(). Tool exceptions no longer escape a run, so code relying on a throwing tool to fail the run must rethrow via a ToolFailed listener.

Macroscope summarized 3d724a7.

…reranking responses

These responses kept no reference to the provider's HTTP response, so
provider-specific data such as OpenRouter's usage cost was out of reach.

Each response now uses HasRawResponse, and every HTTP gateway for these
operations attaches the response it parsed. Individually cached
embeddings carry the fresh request's response. Bedrock (AWS SDK) and the
fakes leave it null, as upstream does, and serialization still drops it.

Ports laravel/ai#1181, laravel/ai#1180, laravel/ai#1179 and
laravel/ai#1182 at c8495608a1. They share the gateways and
ResponseSerializationTest. Their ai-sdk.md change lands with the later
agent-run commit in this batch, which shares that page. Ported the
OpenAI, OpenRouter, Cohere, Jina and Voyage AI raw-response tests,
EmbeddingsCacheTest's raw-response test in both cache modes and the four
serialization tests. tests/Ai passes.
Boolean questions accept array instructions, but an array criterion was
interpolated into the OpenAI decision prompt as "Array" with a
conversion warning.

predicateInstructions() now passes each criterion through toText(), so
arrays are sent as JSON text, and the Boolean docblock allows array
criteria.

Port of laravel/ai#1202 at c8495608a1. Ported the OpenAI
ClassificationTest array-criteria test and the integration
testBooleanCriteriaMayBeGivenAsArrays cases. The docs already allow
array criteria. The OpenAI ClassificationTest file passes.
…urn text

These upstream changes share the text generation loop, the Anthropic
stream, the conversation store, src/docs/ai-sdk.md and their tests, so
they land together. Ported from laravel/ai at c8495608a1.

laravel/ai#1162: a streamed turn that paused for approval lost its
pre-pause text on resume. Ported TextDelta::join(). Unlike upstream,
resumePausedRow() joins the resumed text to the paused content instead
of rejoining every stored step. Rejoining split a continuation without
tools, such as a pause_turn, that the stream had shown as one paragraph.
Prompted resumes are unchanged. Ported the prompted/streamed resume
cases and the double-pause test, and added a regression test for a
continued step.

laravel/ai#1190: agent fakes can resume tool approvals, so approved
tools run and rejected tools get a denied result before the next faked
response. Ported pausedForApproval(), resumesApproval() and the upstream
resumableApprovalFor()/approvalResultRecorderFor() signatures. Hypervel
records approvals through the claiming store, so prepareRunContext()
attaches it with RunContext::withApprovalStore() only for resumed
approvals. runContextFor() keeps its upstream signature, and the store
and conversation ID leave the RunContext constructor. Ported the six AgentFakeTest
resume tests and their fixture.

laravel/ai#1188: Anthropic messages that end with end_turn but contain
tool calls now run those calls. Bedrock Converse can do the same, so its
buffered and streamed paths get the same fix. Ported the Anthropic
streaming and tool-loop tests and added the Bedrock equivalents.

laravel/ai#1191: a tool that throws no longer fails the run. The
exception is reported and the model receives a failed tool result;
listener exceptions still fail the run. Unlike upstream, cancellation
is not wrapped in ToolExecutionException, so it is never recovered as
the tool's own failure. Ported ToolFailureRecoveryTest and the event,
failed-turn, InvokesTools and integration test updates.

laravel/ai#1186: streamed text joins across Anthropic pause_turn
continuations, starting a new paragraph only after a step that ran
tools. StreamedAgentResponse's single pass follows the same rule. Ported
the pause_turn streaming tests and the TextDelta test updates.

laravel/ai#1203: conversation title generation runs as its own agent
run with the turn's parentInvocationId, so agent events report it. A
failed title run still falls back to the prompt. Ported the five
title-event tests and the step-attribution update.

ai-sdk.md also gains the raw-response types from laravel/ai#1181,
laravel/ai#1180, laravel/ai#1179 and laravel/ai#1182, and the
concurrent-tools failure sentence is corrected. tests/Ai passes, and
composer analyse is clean.
A multipart upload of an empty file was recorded with a null size,
because the size check treated 0 as missing. It now checks for null,
as upstream does, so an empty attachment reports 0KB.

Port of laravel/telescope#1782 at a5e100fd5c. Hypervel already reads
the size of attached resources through their streams. Added
ClientRequestWatcherTest::testClientRequestWatcherReportsTheSizeOfAnEmptyResourceFileUpload.
Upstream's ViewWatcherTest realpath() change does not apply: the
Hypervel test uses a temp directory path with no relative segments.
The Telescope suite passes.
…navigation

These upstream changes share the rebuilt dashboard bundle, so they land
together. Ported from laravel/telescope at a5e100fd5c.

laravel/telescope#1781: the HTML mail preview ran the email's scripts
in the dashboard's origin. MailHtmlController now returns the HTML with
a sandbox Content-Security-Policy, an explicit text/html content type
and nosniff, and the preview iframe gets the matching sandbox
attribute. MailEmlController's return type is corrected from mixed to
Response. Ported Http/RouteTest::testMailPreviewIsSandboxed.

laravel/telescope#1783: Hypervel's dashboard already runs on Vue 3,
Vue Router 4 and Bootstrap 5, but upstream's back-navigation fix was
missing. RelatedEntries::activateTab() replaced the URL hash outside the
router, so the router's stored location lost the hash and going back to
an entry reopened its first tab. It now uses $router.replace({ hash }).
Templates use {{ title }} as upstream. Ported the RelatedEntries
"restores the selected tab when going back to the entry" test, the four
routes.test.js tests and the queries preview stub's optional default
slot. The overflow-tab test now mounts a router and checks the
router-managed history state, and the Hypervel route test keeps only
its per-section named-route coverage.

Dist is rebuilt. The Telescope PHPUnit suite and the Telescope Vitest
project pass, and vp check is clean.
Upstream now flushes memoized feature values when a queued job fails,
as it already did after processed jobs. Hypervel memoizes feature
values per coroutine in PennantState, and the queue worker runs each
job in a new coroutine without copied context, so no job can read
another job's values and no listeners are needed.

Covers laravel/pennant#190 at cec5f8d3c3. The PennantServiceProvider
comment now names all three queue events. Upstream's
Feature/QueueEventsTest is not ported; a REMOVED note on
CoroutineIsolationTest::testValuesAreIsolatedBetweenConcurrentCoroutines
points to the isolation it relies on. The Pennant suite passes.
A redirect answering a QUERY request kept its 302 status, so browsers
could repeat the QUERY against the redirect target. The Inertia
middleware now converts these redirects to 303, as it already does for
PUT, PATCH and DELETE.

Port of inertiajs/inertia-laravel#927 at e68dadfc16. Unlike upstream,
the global EnsureGetOnRedirect middleware also includes QUERY, with a
comment. It converts redirects issued outside the Inertia middleware,
such as a login redirect, and otherwise kept them as 302s. Ported
MiddlewareTest::testRedirectsFromQueryRequestsAreConvertedTo303 and
added InertiaServiceProviderTest::testRedirectsFromQueryRequestsOutsideTheInertiaMiddlewareAreConvertedTo303.
The Inertia suite passes.
Ported from inertiajs/docs at 8cfece18fd.

inertiajs/docs#98: documents the QUERY method with its server
counterpart, inertia-laravel#927. inertia.md gains the Manual Visits
QUERY Method section, the link and prefetching query examples, query in
the router, form helper and useHttp method lists and in the state
preservation and validation sentences, the 303 redirect wording now
including QUERY, and the --no-stop-before-start paragraph, which
StartSsr already supports. Client badges become v3.10.0+, and the
Laravel adapter badge is dropped. The prefetching Svelte example
imports Link, correcting upstream's inertia import. The protocol page
is excluded, and the CSRF page's sentence for other frameworks does not
apply to Hypervel's own CSRF text.

inertiajs/docs#94: the only applicable change is upstream's protocol
sentence in How It Works. Its adapter-building, community adapter,
DevTools protocol and protocol pages cover other backends or protocol
implementation, which Hypervel's docs exclude.
The example's try_files $uri would serve any .php file under public as
plain text, exposing its source. A location block now returns 404 for
them. The WebSocket example serves no files.

Port of laravel/docs#11425 at 625d1dfb61.
Applications sharing a Redis server can overwrite each other's cache,
session and queue keys unless each uses its own prefix or database.
redis.md gains a Key Prefixes section, and queues.md points to it from
the Redis driver.

Port of laravel/docs#11428 at 625d1dfb61. Adapted: the default prefix
comes from the application ID, the example uses Hypervel's colon prefix
style, and the database alternative names REDIS_DB and the per-connection
REDIS_CACHE_DB, REDIS_SESSION_DB, REDIS_QUEUE_DB and REDIS_REVERB_DB
overrides.
Entries stored forever were recorded with a null cache.ttl. The
cache.put span now adds cache.ttl only when the entry has an
expiry, for single and many writes.

Port of getsentry/sentry-laravel#1205 at fc6c60a9b5, with upstream's
comment. Hypervel's integration records no session spans. Ported
Features/CacheIntegrationTest::testCachePutSpanOmitsTtlWhenEntryNeverExpires
without upstream's tracing-availability skip. The CacheIntegration
tests pass.
Upstream's GuzzleSender missed Guzzle 8's NetworkException family, so
those failures escaped as raw Guzzle exceptions and skipped retries.
Hypervel needs no source change: requests go through Hypervel's HTTP
client, which turns every Guzzle TransferException into a
ConnectionException, and SaloonManager::send() raises and retries it as
a FatalRequestException.

Port of saloonphp/saloon#569 at 6e6932a635. Ported
Feature/Guzzle8SenderExceptionTest: the four failure types through
Http::fake() and the connector retry case through RetryConnector(3),
without upstream's Guzzle 7 skip. The sendAsync() case is REMOVED,
since coroutine pools replace promises. The test file passes.
…se age

js-beautify, used by @vue/test-utils in the frontend tests, still
requires glob 13 and minimatch 10, which are affected by
GHSA-3g3m-2fjr-6pw4 and GHSA-g3hv-75wg-m249. Workspace overrides now
require the patched glob ^14.0.2 and minimatch ^11.0.1. The lock
resolves glob 14.0.3, minimatch 11.0.3, path-scurry 3.0.0 and
@isaacs/nonrecursive-glob-match 1.0.3; nothing else changes, and
neither package is in the dashboard bundles.

Port of laravel/telescope#1788 at a5e100fd5c. Its Guzzle constraint is
already covered by Telescope's hypervel/guzzle ^8.3 requirement.

The pnpm minimumReleaseAge drops from 7 days to 3 (4320 minutes). The
Horizon and Telescope Vitest projects pass.
…rors

When an approved tool threw while a paused turn was resumed, the model
received a failed tool result, but the exception was never reported,
unlike the same failure on the normal tool path. The resume path also
caught every Throwable, so an exception thrown by an InvokingTool or
ToolInvoked listener was returned to the model as a tool failure, even
when the tool had already run.

The resume path now catches only ToolExecutionException, as
executedToolResult() does. The tool's own exception is reported and the
model receives a failed result; listener exceptions and coroutine
cancellation end the run. Upstream laravel/ai at c8495608a1 catches
Throwable here without reporting, and a comment records the difference.

AgentEventsTest now asserts that a tool failing during a resume is
reported once, and covers a throwing ToolInvoked listener failing the
resume without the tool running again or another request being sent.
Both tests fail without the change and pass with it.
Classification instructions, Boolean criteria, Choice option
descriptions and Score levels may be arrays when one sentence is not
enough, and the gateways already send lists correctly: OpenAI encodes
any array as JSON text, and the other gateways pass it through. The
annotations only allowed string-keyed arrays, so static analysis
rejected lists such as ['Money lost.', 'Customer blocked.'].

The free-form values now use array<array-key, mixed> in Boolean,
Choice, Score, CollectionChoice (options, decide() text and resolved
descriptions), OpenAiClassificationGateway::mapLevel(),
FakeClassificationGateway::fakeScoreAnswer() and ScoreAnswer's legend
and label(). The outer shapes and record-shaped arrays such as provider
options are unchanged. Upstream laravel/ai at c8495608a1 uses
array<string, mixed> for these values.

The Classification type fixture now passes list inputs to each question
type and to CollectionChoice::decide(). It reported the narrowed types
before the change. Static analysis and the OpenAI and fake
classification tests pass.
@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.

@binaryfire

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Action not completed

Review skipped: 103 files exceed the limit of 100.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Sync AI run recovery, raw responses, and Inertia QUERY redirects

🐞 Bug fix ✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Expose raw AI provider responses and improve tool-failure recovery, approval resumes, and streamed
 text continuity.
• Convert Inertia QUERY redirects to 303 and secure Telescope mail previews and navigation.
• Add regression coverage, update documentation, and override vulnerable frontend dependencies.
Diagram

graph TD
  A["Agent prompt"] --> B["Run context"] --> C["Generation loop"] --> D["Provider gateway"] --> E["Agent response"]
  C --> F["Tool execution"] --> C
  D --> G["Raw HTTP response"] --> E
Loading
High-Level Assessment

The targeted changes fit an upstream sync: recover only failures originating in tools, attach raw responses at the gateways that receive them, and convert redirects in both Inertia paths. A broad rewrite or shared abstraction would add risk without resolving these independent behaviors.

Files changed (106) +2112 / -284

Enhancement (27) +142 / -98
Boolean.phpAccept list-based boolean criteria +2/-2

Accept list-based boolean criteria

• Broadens classification type documentation to accept arrays with numeric as well as string keys.

src/ai/src/Classification/Boolean.php

Choice.phpAccept list-based choice descriptions +2/-2

Accept list-based choice descriptions

• Broadens choice classification array types so list-shaped instructions and descriptions pass static analysis.

src/ai/src/Classification/Choice.php

CollectionChoice.phpType list-shaped collection choices +3/-3

Type list-shaped collection choices

• Allows list-shaped option descriptions and classification inputs in type documentation.

src/ai/src/Classification/CollectionChoice.php

Score.phpAccept list-shaped score levels +2/-2

Accept list-shaped score levels

• Broadens score classification array types to cover list-shaped inputs and level descriptions.

src/ai/src/Classification/Score.php

AzureOpenAiGateway.phpRetain Azure AI HTTP responses +4/-4

Retain Azure AI HTTP responses

• Attaches parsed provider HTTP responses to supported non-text AI results.

src/ai/src/Gateway/AzureOpenAi/AzureOpenAiGateway.php

CohereGateway.phpExpose Cohere raw responses +4/-4

Expose Cohere raw responses

• Retains the HTTP response for Cohere embedding and reranking results.

src/ai/src/Gateway/CohereGateway.php

ElevenLabsGateway.phpExpose ElevenLabs transcription responses +6/-6

Expose ElevenLabs transcription responses

• Attaches raw HTTP responses to parsed transcription results.

src/ai/src/Gateway/ElevenLabsGateway.php

FakeClassificationGateway.phpAlign fake classification array types +1/-1

Align fake classification array types

• Broadens fake gateway classification annotations to allow list-shaped values.

src/ai/src/Gateway/FakeClassificationGateway.php

GeminiGateway.phpExpose Gemini operation responses +8/-8

Expose Gemini operation responses

• Attaches raw HTTP responses to supported Gemini non-text results.

src/ai/src/Gateway/Gemini/GeminiGateway.php

GroqGateway.phpExpose Groq transcription responses +2/-2

Expose Groq transcription responses

• Preserves the provider HTTP response on parsed transcription results.

src/ai/src/Gateway/Groq/GroqGateway.php

JinaGateway.phpExpose Jina embeddings and reranking responses +4/-4

Expose Jina embeddings and reranking responses

• Retains the original HTTP response on supported Jina results.

src/ai/src/Gateway/JinaGateway.php

MistralGateway.phpExpose Mistral operation responses +4/-4

Expose Mistral operation responses

• Attaches raw provider HTTP responses to supported non-text results.

src/ai/src/Gateway/Mistral/MistralGateway.php

OllamaGateway.phpExpose Ollama embedding responses +2/-2

Expose Ollama embedding responses

• Retains the parsed request's HTTP response on embedding results.

src/ai/src/Gateway/Ollama/OllamaGateway.php

OpenAiGateway.phpExpose OpenAI non-text responses +6/-6

Expose OpenAI non-text responses

• Attaches raw HTTP responses to supported embedding, transcription, and image results.

src/ai/src/Gateway/OpenAi/OpenAiGateway.php

OpenAiCompatibleGateway.phpRetain OpenAI-compatible HTTP responses +4/-4

Retain OpenAI-compatible HTTP responses

• Exposes raw provider responses on supported non-text operations.

src/ai/src/Gateway/OpenAiCompatible/OpenAiCompatibleGateway.php

OpenRouterGateway.phpExpose OpenRouter raw responses +8/-8

Expose OpenRouter raw responses

• Retains HTTP responses on supported non-text results, making provider-specific metadata accessible.

src/ai/src/Gateway/OpenRouter/OpenRouterGateway.php

MapsEmbeddingInputs.phpRetain Voyage multimodal embedding response +6/-4

Retain Voyage multimodal embedding response

• Keeps the HTTP response available after parsing multimodal embeddings and attaches it to the result.

src/ai/src/Gateway/VoyageAi/Concerns/MapsEmbeddingInputs.php

VoyageAiGateway.phpExpose Voyage AI raw responses +12/-8

Expose Voyage AI raw responses

• Attaches HTTP responses to supported Voyage embedding and reranking results.

src/ai/src/Gateway/VoyageAi/VoyageAiGateway.php

XaiImageGateway.phpExpose xAI image HTTP responses +4/-4

Expose xAI image HTTP responses

• Retains the provider response on generated image results.

src/ai/src/Gateway/Xai/XaiImageGateway.php

RememberConversation.phpReport conversation titles as agent runs +16/-9

Report conversation titles as agent runs

• Generates titles through a separate agent prompt linked to its parent turn, preserving fallback titles when generation fails.

src/ai/src/Middleware/RememberConversation.php

PendingEmbeddingsGeneration.phpRetain fresh response for individually cached embeddings +2/-1

Retain fresh response for individually cached embeddings

• Propagates the fresh embedding request's raw response into the combined cached result.

src/ai/src/PendingResponses/PendingEmbeddingsGeneration.php

ResumesToolApprovals.phpResume genuine faked approval pauses +26/-8

Resume genuine faked approval pauses

• Allows fake gateways to execute approval decisions when their history contains a valid paused tool call, while retaining ordinary fake responses otherwise.

src/ai/src/Providers/Concerns/ResumesToolApprovals.php

ScoreAnswer.phpType list-shaped score answers +2/-2

Type list-shaped score answers

• Broadens score answer annotations to accept numeric-keyed arrays.

src/ai/src/Responses/Data/ScoreAnswer.php

EmbeddingsResponse.phpExpose raw embedding response +3/-0

Expose raw embedding response

• Adds the existing raw-response behavior to embedding results.

src/ai/src/Responses/EmbeddingsResponse.php

ImageResponse.phpExpose raw image response +3/-0

Expose raw image response

• Adds the existing raw-response behavior to image results.

src/ai/src/Responses/ImageResponse.php

RerankingResponse.phpExpose raw reranking response +3/-0

Expose raw reranking response

• Adds the existing raw-response behavior to reranking results.

src/ai/src/Responses/RerankingResponse.php

TranscriptionResponse.phpExpose raw transcription response +3/-0

Expose raw transcription response

• Adds the existing raw-response behavior to transcription results.

src/ai/src/Responses/TranscriptionResponse.php

Bug fix (22) +231 / -69
ToolExecutionException.phpIdentify exceptions thrown by tools +21/-0

Identify exceptions thrown by tools

• Adds an internal wrapper that distinguishes a tool's own exception from listener failures and cancellation.

src/ai/src/Exceptions/ToolExecutionException.php

AnthropicGateway.phpPass paused content into streaming +1/-0

Pass paused content into streaming

• Supplies prior paused message content when continuing an Anthropic stream.

src/ai/src/Gateway/Anthropic/AnthropicGateway.php

HandlesTextStreaming.phpPreserve Anthropic pause-turn text continuity +35/-0

Preserve Anthropic pause-turn text continuity

• Reads paused replay blocks to decide whether resumed streamed text continues directly or follows a tool boundary.

src/ai/src/Gateway/Anthropic/Concerns/HandlesTextStreaming.php

ParsesTextResponses.phpRun Anthropic end-turn tool calls +5/-0

Run Anthropic end-turn tool calls

• Treats end-turn messages containing real tool calls as tool-call steps rather than final answers.

src/ai/src/Gateway/Anthropic/Concerns/ParsesTextResponses.php

BedrockTextGateway.phpRun Bedrock end-turn tool calls +10/-0

Run Bedrock end-turn tool calls

• Recognizes tool calls in buffered and streamed Converse messages even when their stop reason is end_turn.

src/ai/src/Gateway/Bedrock/BedrockTextGateway.php

InvokesTools.phpSeparate tool failures from cancellation +4/-1

Separate tool failures from cancellation

• Wraps tool-originated exceptions for recovery while leaving coroutine cancellation and listener exceptions able to fail the run.

src/ai/src/Gateway/Concerns/InvokesTools.php

OpenAiClassificationGateway.phpEncode array-valued boolean criteria +2/-2

Encode array-valued boolean criteria

• Uses text conversion for OpenAI boolean criteria so array criteria become JSON rather than an interpolation warning; broadens score-level typing.

src/ai/src/Gateway/OpenAi/OpenAiClassificationGateway.php

TextGenerationLoop.phpRecover tool failures and recognize fake pauses +56/-17

Recover tool failures and recognize fake pauses

• Reports tool exceptions as failed model-visible results without swallowing listener failures or cancellation. Adds paused-approval detection for fake resumes.

src/ai/src/Gateway/TextGenerationLoop.php

GeneratesText.phpPrepare approval-aware run contexts +29/-16

Prepare approval-aware run contexts

• Detects resumable approvals before generation and attaches claiming stores only to runs that resume stored approvals.

src/ai/src/Providers/Concerns/GeneratesText.php

StreamsText.phpApply approval context to streamed runs +3/-3

Apply approval context to streamed runs

• Passes messages and tools into resume detection and prepares an approval-aware context before streaming.

src/ai/src/Providers/Concerns/StreamsText.php

StreamedAgentResponse.phpJoin streamed text only across tool boundaries +11/-5

Join streamed text only across tool boundaries

• Keeps successive text-only streaming steps contiguous and inserts a paragraph break after a step that ran tools.

src/ai/src/Responses/StreamedAgentResponse.php

DatabaseConversationStore.phpPreserve text when storing streamed resumes +13/-1

Preserve text when storing streamed resumes

• Appends a streamed continuation to the paused row's displayed text instead of reconstructing and potentially splitting prior steps.

src/ai/src/Storage/DatabaseConversationStore.php

TextDelta.phpCombine stream text across text-only steps +15/-11

Combine stream text across text-only steps

• Adds a text-joining helper and inserts paragraph breaks only when a preceding streaming step produced tool results.

src/ai/src/Streaming/Events/TextDelta.php

Middleware.phpConvert Inertia QUERY redirects to 303 +1/-1

Convert Inertia QUERY redirects to 303

• Includes QUERY among methods whose 302 redirect must become a GET-following 303.

src/inertia/src/Middleware.php

EnsureGetOnRedirect.phpConvert global QUERY redirects to 303 +3/-2

Convert global QUERY redirects to 303

• Also upgrades redirects outside Inertia page middleware, including login redirects, after Inertia QUERY requests.

src/inertia/src/Middleware/EnsureGetOnRedirect.php

CacheIntegration.phpOmit TTL on nonexpiring cache spans +10/-4

Omit TTL on nonexpiring cache spans

• Sets cache.ttl only when a cache event supplies a non-null expiration.

src/sentry/src/Features/CacheIntegration.php

IndexScreen.vueAlign index navigation with router state +1/-1

Align index navigation with router state

• Adjusts dashboard index navigation as part of the tab and back-navigation fix.

src/telescope/resources/js/components/IndexScreen.vue

PreviewScreen.vueAlign preview navigation with router state +1/-1

Align preview navigation with router state

• Adjusts entry preview navigation so the selected tab is restored correctly.

src/telescope/resources/js/components/PreviewScreen.vue

RelatedEntries.vueRoute Telescope tab changes through Vue Router +1/-1

Route Telescope tab changes through Vue Router

• Replaces direct history mutation with router hash replacement so navigation restores the selected tab.

src/telescope/resources/js/components/RelatedEntries.vue

preview.vueSandbox HTML mail iframe +1/-0

Sandbox HTML mail iframe

• Adds an iframe sandbox boundary for rendered email previews.

src/telescope/resources/js/screens/mail/preview.vue

MailHtmlController.phpSandbox HTML mail responses +7/-2

Sandbox HTML mail responses

• Returns mail HTML with a sandbox Content-Security-Policy, HTML content type, and nosniff header.

src/telescope/src/Http/Controllers/MailHtmlController.php

ClientRequestWatcher.phpReport empty uploads as zero kilobytes +1/-1

Report empty uploads as zero kilobytes

• Displays a zero size rather than null for empty files in client request entries.

src/telescope/src/Watchers/ClientRequestWatcher.php

Refactor (2) +18 / -4
RunContext.phpAttach approval stores only for resumes +16/-3

Attach approval stores only for resumes

• Moves approval-store configuration into withApprovalStore(), keeping the ordinary run context constructor unchanged.

src/ai/src/Gateway/RunContext.php

MailEmlController.phpSpecify EML controller response type +2/-1

Specify EML controller response type

• Types the controller response as Response rather than mixed.

src/telescope/src/Http/Controllers/MailEmlController.php

Tests (45) +1537 / -78
RelatedEntries.test.jsTest related-entry tab restoration +61/-8

Test related-entry tab restoration

• Exercises router-backed tab selection, history state, and returning to a selected tab with browser back.

src/telescope/resources/js/components/RelatedEntries.test.js

routes.test.jsExpand Telescope route coverage +73/-27

Expand Telescope route coverage

• Checks sidebar links from the actual layout and validates active states and named routes across sections.

src/telescope/resources/js/routes.test.js

preview.test.jsAdjust query preview navigation test +1/-1

Adjust query preview navigation test

• Aligns the frontend expectation with router-based preview navigation.

src/telescope/resources/js/screens/queries/preview.test.js

ConcurrentToolsTest.phpCover recovery with concurrent tools +10/-2

Cover recovery with concurrent tools

• Updates assertions for tool failure results without cancelling other tool executions.

tests/Ai/ConcurrentToolsTest.php

AgentEventsTest.phpTest title-run and failure events +173/-9

Test title-run and failure events

• Checks parent-linked title events, fallback behavior, and tool-failure reporting without failing the agent run.

tests/Ai/Feature/AgentEventsTest.php

AgentFakeTest.phpTest approval resumes with agent fakes +186/-0

Test approval resumes with agent fakes

• Covers approved and rejected paused calls, stored results, mismatch validation, and fake histories that must remain ordinary responses.

tests/Ai/Feature/AgentFakeTest.php

AgentPromptTest.phpAdjust agent prompt expectation +1/-1

Adjust agent prompt expectation

• Aligns an agent prompt assertion with the updated run behavior.

tests/Ai/Feature/AgentPromptTest.php

EmbeddingsCacheTest.phpTest raw responses with cached embeddings +14/-0

Test raw responses with cached embeddings

• Verifies individually cached embeddings retain the fresh request's raw response in both cache modes.

tests/Ai/Feature/EmbeddingsCacheTest.php

FailedTurnRecordingTest.phpTest failed-turn recording after recovery +6/-0

Test failed-turn recording after recovery

• Updates coverage for recording turns under the revised tool failure behavior.

tests/Ai/Feature/FailedTurnRecordingTest.php

StreamingTest.phpTest Anthropic streamed continuations +179/-0

Test Anthropic streamed continuations

• Covers end-turn tool calls and text continuity or separation across pause_turn and server-tool steps.

tests/Ai/Feature/Providers/Anthropic/StreamingTest.php

ToolCallLoopTest.phpTest Anthropic end-turn tool execution +51/-0

Test Anthropic end-turn tool execution

• Verifies buffered Anthropic responses execute tool calls despite an end_turn stop reason.

tests/Ai/Feature/Providers/Anthropic/ToolCallLoopTest.php

StreamingTest.phpTest streamed Bedrock end-turn tools +39/-0

Test streamed Bedrock end-turn tools

• Ensures streamed Converse messages run tool calls when their stop reason is end_turn.

tests/Ai/Feature/Providers/Bedrock/StreamingTest.php

ToolCallLoopTest.phpTest buffered Bedrock end-turn tools +39/-0

Test buffered Bedrock end-turn tools

• Ensures buffered Converse messages run tool calls when their stop reason is end_turn.

tests/Ai/Feature/Providers/Bedrock/ToolCallLoopTest.php

RerankingTest.phpAssert Cohere raw reranking response +3/-0

Assert Cohere raw reranking response

• Checks access to the original Cohere HTTP response.

tests/Ai/Feature/Providers/Cohere/RerankingTest.php

RerankingTest.phpAssert Jina raw reranking response +3/-0

Assert Jina raw reranking response

• Checks access to the original Jina HTTP response.

tests/Ai/Feature/Providers/Jina/RerankingTest.php

ClassificationTest.phpTest array-valued OpenAI boolean criteria +15/-0

Test array-valued OpenAI boolean criteria

• Verifies array criteria are serialized into the OpenAI prompt as JSON text.

tests/Ai/Feature/Providers/OpenAi/ClassificationTest.php

EmbeddingTest.phpAssert OpenAI raw embedding response +11/-0

Assert OpenAI raw embedding response

• Verifies embedding results retain the parsed HTTP response.

tests/Ai/Feature/Providers/OpenAi/EmbeddingTest.php

ImageGenerationTest.phpAssert OpenAI raw image response +11/-0

Assert OpenAI raw image response

• Verifies image generation results retain the parsed HTTP response.

tests/Ai/Feature/Providers/OpenAi/ImageGenerationTest.php

TranscriptionTest.phpAssert OpenAI raw transcription response +12/-0

Assert OpenAI raw transcription response

• Verifies transcription results retain the parsed HTTP response.

tests/Ai/Feature/Providers/OpenAi/TranscriptionTest.php

EmbeddingsTest.phpAssert OpenRouter raw embedding response +13/-0

Assert OpenRouter raw embedding response

• Verifies provider-specific response data remains accessible on embeddings.

tests/Ai/Feature/Providers/OpenRouter/EmbeddingsTest.php

ImageGenerationTest.phpAssert OpenRouter raw image response +10/-0

Assert OpenRouter raw image response

• Verifies generated images expose their original provider response.

tests/Ai/Feature/Providers/OpenRouter/ImageGenerationTest.php

RerankingTest.phpAssert OpenRouter raw reranking response +4/-1

Assert OpenRouter raw reranking response

• Checks raw-response access alongside reranking results.

tests/Ai/Feature/Providers/OpenRouter/RerankingTest.php

TranscriptionTest.phpAssert OpenRouter raw transcription response +9/-0

Assert OpenRouter raw transcription response

• Verifies transcription results expose the provider HTTP response.

tests/Ai/Feature/Providers/OpenRouter/TranscriptionTest.php

RerankingTest.phpAssert Voyage raw reranking response +3/-0

Assert Voyage raw reranking response

• Checks that Voyage reranking results retain their provider response.

tests/Ai/Feature/Providers/VoyageAi/RerankingTest.php

RememberConversationTest.phpAdjust conversation title expectations +5/-7

Adjust conversation title expectations

• Aligns conversation-memory tests with title generation as a distinct agent run.

tests/Ai/Feature/RememberConversationTest.php

ResponseSerializationTest.phpTest raw-response serialization boundaries +86/-0

Test raw-response serialization boundaries

• Ensures embeddings, images, transcriptions, and rerankings omit raw HTTP responses when serialized.

tests/Ai/Feature/ResponseSerializationTest.php

DatabaseConversationStoreTest.phpTest streamed resume text persistence +99/-7

Test streamed resume text persistence

• Checks paused and resumed text across repeated pauses, including a text-only continuation that must remain one paragraph.

tests/Ai/Feature/Storage/DatabaseConversationStoreTest.php

ToolFailureRecoveryTest.phpTest tool-failure recovery boundaries +97/-0

Test tool-failure recovery boundaries

• Verifies prompted and streamed failures reach the model and exception handler while listener exceptions still fail the run.

tests/Ai/Feature/ToolFailureRecoveryTest.php

RememberingUngatedApprovableAgent.phpAdd ungated approval fixture +34/-0

Add ungated approval fixture

• Provides a remembered agent whose approvable tool does not currently require approval, for fake-resume tests.

tests/Ai/Fixtures/Agents/RememberingUngatedApprovableAgent.php

ThrowingToolAgent.phpAdd throwing-tool agent fixture +31/-0

Add throwing-tool agent fixture

• Provides a tool-using agent that throws to exercise recovery in prompted and streamed runs.

tests/Ai/Fixtures/Agents/ThrowingToolAgent.php

PartitionStoreTest.phpAlign partition-store test with run context +1/-1

Align partition-store test with run context

• Adjusts a test for approval-store attachment through the updated run context API.

tests/Ai/PartitionStoreTest.php

InvokesToolsTest.phpAssert tool exception wrapping +5/-3

Assert tool exception wrapping

• Updates invocation tests to distinguish tool-originated failures from cancellation and event-listener failures.

tests/Ai/Unit/Gateway/InvokesToolsTest.php

TextGenerationLoopTest.phpAssert failed tool results +3/-3

Assert failed tool results

• Adjusts loop expectations for recoverable tool execution exceptions.

tests/Ai/Unit/Gateway/TextGenerationLoopTest.php

StreamedAgentResponseTest.phpTest streamed response text joining +18/-0

Test streamed response text joining

• Checks that tool-free steps concatenate while tool boundaries separate paragraphs.

tests/Ai/Unit/Responses/StreamedAgentResponseTest.php

TextDeltaTest.phpTest text-delta paragraph boundaries +22/-0

Test text-delta paragraph boundaries

• Covers combining deltas and joining text across tool and non-tool step transitions.

tests/Ai/Unit/Streaming/TextDeltaTest.php

InertiaServiceProviderTest.phpTest global QUERY redirect conversion +9/-0

Test global QUERY redirect conversion

• Verifies the registered redirect middleware converts applicable Inertia QUERY redirects to 303.

tests/Inertia/InertiaServiceProviderTest.php

MiddlewareTest.phpTest Inertia QUERY redirect conversion +14/-0

Test Inertia QUERY redirect conversion

• Verifies QUERY redirects receive the same 303 treatment as other non-GET Inertia visits.

tests/Inertia/MiddlewareTest.php

AgentTest.phpAdapt integration agent failure expectations +9/-8

Adapt integration agent failure expectations

• Aligns end-to-end agent assertions with recoverable tool failures and updated turn behavior.

tests/Integration/Ai/AgentTest.php

ClassificationTest.phpTest list-shaped classification inputs +25/-0

Test list-shaped classification inputs

• Covers array instructions, criteria, descriptions, and score levels through classification integration paths.

tests/Integration/Ai/ClassificationTest.php

CoroutineIsolationTest.phpClarify Pennant isolation test +1/-0

Clarify Pennant isolation test

• Adds context to the coroutine-scoped feature-value behavior exercised by the test.

tests/Pennant/CoroutineIsolationTest.php

Guzzle8SenderExceptionTest.phpTest Guzzle 8 transfer failures in Saloon +89/-0

Test Guzzle 8 transfer failures in Saloon

• Verifies connection and timeout failures become FatalRequestException and network failures participate in connector retries.

tests/Saloon/Feature/Guzzle8SenderExceptionTest.php

CacheIntegrationTest.phpTest nonexpiring cache span metadata +11/-0

Test nonexpiring cache span metadata

• Asserts cache.put spans omit cache.ttl when the cache entry has no expiration.

tests/Sentry/Features/CacheIntegrationTest.php

RouteTest.phpTest sandboxed mail HTML response +15/-0

Test sandboxed mail HTML response

• Checks mail preview headers and response behavior on the Telescope route.

tests/Telescope/Http/RouteTest.php

ClientRequestWatcherTest.phpTest empty upload size display +13/-0

Test empty upload size display

• Verifies an empty uploaded file is reported as 0KB.

tests/Telescope/Watchers/ClientRequestWatcherTest.php

Classification.phpCheck list-shaped classification types +23/-0

Check list-shaped classification types

• Adds static-analysis assertions for list instructions, criteria, descriptions, score levels, and collection input.

types/Ai/Classification.php

Documentation (6) +153 / -16
ai-sdk.mdDocument raw results and agent recovery +8/-2

Document raw results and agent recovery

• Explains raw responses, title-run events, fake approval resumes, and continued execution after tool failures; corrects concurrent-tool guidance.

src/docs/ai-sdk.md

deployment.mdBlock PHP source in Nginx example +4/-0

Block PHP source in Nginx example

• Adds an Nginx rule returning 404 for PHP files instead of serving their source as text.

src/docs/deployment.md

inertia.mdDocument QUERY visits and SSR startup option +120/-13

Document QUERY visits and SSR startup option

• Adds QUERY examples and behavior across links, visits, forms, HTTP hooks, prefetching, and redirects. Documents --no-stop-before-start and corrects the Svelte import.

src/docs/inertia.md

queues.mdClarify queued Pennant feature isolation +2/-0

Clarify queued Pennant feature isolation

• Explains why per-coroutine memoization removes the need for queued-job feature-flush listeners.

src/docs/queues.md

redis.mdDocument shared-Redis key isolation +17/-0

Document shared-Redis key isolation

• Explains key prefixes for multiple application instances and the alternative of separate connection databases.

src/docs/redis.md

PennantServiceProvider.phpExplain Pennant's coroutine-scoped cache +2/-1

Explain Pennant's coroutine-scoped cache

• Clarifies in code why queued jobs need no feature-value flushing listeners.

src/pennant/src/PennantServiceProvider.php

Other (4) +31 / -19
pnpm-workspace.yamlOverride vulnerable glob dependencies +7/-3

Override vulnerable glob dependencies

• Overrides glob and minimatch with patched versions and reduces the minimum package release age to three days.

pnpm-workspace.yaml

pnpm-lock.yamlLock patched glob dependency tree +21/-13

Lock patched glob dependency tree

• Records patched glob and minimatch releases and their updated transitive dependencies.

pnpm-lock.yaml

app.cssRebuild Telescope stylesheet +1/-1

Rebuild Telescope stylesheet

• Updates the generated dashboard CSS artifact alongside the frontend rebuild.

src/telescope/dist/app.css

app.jsRebuild Telescope dashboard script +2/-2

Rebuild Telescope dashboard script

• Updates the generated dashboard bundle with the router and preview changes.

src/telescope/dist/app.js

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Live sub-agent output runs its steps together ✓ Resolved
Description
TextDelta::combine() now starts a new step only when the current chunk already holds a
ToolResult event. Its only production caller, TextGenerationLoop::preliminaryToolResults(),
passes it only StreamStart and TextDelta events, so no chunk ever contains a ToolResult. As a
result, every preliminary snapshot of a streamed sub-agent joins narration from separate steps with
no break (for example "Let me check.The answer is…"). The final result from StreamedAgentResponse
does put a paragraph break after tool steps, so the live text changes shape once the tool finishes.

2. Raw tool error text is sent to the model ✓ Resolved
Description
executedToolResult() and resolveApprovalResults() turn a tool exception’s unfiltered
getMessage() into model-facing failure text instead of ending the run. When a tool throws an error
containing SQL, bound values, credentials, hostnames or file paths, that text reaches the
third-party model provider, is stored in conversation history and may be repeated to the end user.


Informational

3. Supply-chain release delay cut from 7 to 3 days 🐞 Bug ⛨ Security
Description
minimumReleaseAge in pnpm-workspace.yaml drops from 10080 to 4320 minutes. Newly published npm
versions, including the new glob/minimatch overrides and anything the Telescope and Horizon builds
pull in, can now install after 3 days instead of 7. That leaves a shorter window for a compromised
release to be found and yanked before it reaches the committed dist bundles.
Code

pnpm-workspace.yaml[R24-28]

+# Supply-chain defense: require packages to be at least 3 days old before
# install. Compromised packages typically get yanked within hours, so this
# buffer dramatically reduces the chance of pulling a malicious release.
-# Value is in minutes (7 days × 24h × 60min = 10080).
-minimumReleaseAge: 10080
+# Value is in minutes (3 days × 24h × 60min = 4320).
+minimumReleaseAge: 4320
Evidence
The changed setting is the only gate on how fresh a package version may be at install time. The PR
doesn't say why it was lowered.

pnpm-workspace.yaml[16-28]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The pnpm minimum release age was lowered from 7 days to 3 days, which shortens the supply-chain defense window.
## Fix Focus Areas
- pnpm-workspace.yaml[24-28]
## Recommended Fix
Restore `minimumReleaseAge: 10080`. If certain packages need newer versions sooner, list them in `minimumReleaseAgeExclude` instead of lowering the limit for everything.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can tweak Display settings with a live preview to see your comment before it ships

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

…ailures

Tool failures were returned to the model as "The tool call failed: "
followed by the exception message. That message can carry query
bindings, server paths and other private details. It was sent to the
provider, stored in the conversation, and streamed to the client as the
tool's error text. Sub-agent failures ("Agent failed: ...") and the
CopyFile, MoveFile and GetFileUrl tools did the same.

With app.debug disabled, these results now omit the exception message:
the model receives "The tool call failed.", "Agent failed." or the
operation and paths, such as "Unable to copy [a.txt] to [b.txt]." Debug
mode keeps the detailed text. This applies to ordinary tool failures,
approval resumes, AgentTool and the three filesystem tools. Validation
feedback and other non-exception results are unchanged. Upstream
laravel/ai at c8495608a1 always includes the message; comments at each
site, the AI README and the porting guide record the difference.

Failures that were turned into results without reaching the exception
handler are now reported:

- AgentTool reports a failed sub-agent on both the prompt and streaming
  paths. AgentFailed only reaches listeners, and the parent run never
  sees the exception.
- CopyFile, MoveFile and GetFileUrl report the exceptions they catch.
- ReadFile and GetFileMetadata treated any size() failure as a missing
  file, so an S3 outage or permission error told the model that an
  existing file was missing. After a failed lookup they now check
  existence: a missing file keeps its result, and any other failure is
  reported and returned as "Unable to read [path]." or "Unable to get
  metadata for [path]." The success path makes no extra request.
- ReadFile returns "Unable to read [path]." when a non-throwing disk
  returns null, instead of describing the file as binary.

Live sub-agent snapshots also keep the sub-agent's ToolResult events, so
TextDelta::combine() starts a new paragraph after a step that ran tools,
as the final result does. Since the pause_turn change, snapshots had
joined every step together.

The AI docs cover tool, sub-agent and file storage failures.
ToolFailureRecoveryTest, AgentToolTest, AgentEventsTest and
FilesystemToolsTest cover both debug modes, the new reporting and the
lookup and null-read corrections. TextGenerationLoopTest covers a tool
step boundary in live snapshots. The new cases fail without the change,
and the affected tests, formatting and static analysis pass.
@binaryfire
binaryfire merged commit 6c96878 into 0.4 Nov 3, 2026
46 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant