Skip to content

Sync framework, Testbench, MCP and AI updates and support PHPUnit 13.4 - #64

Merged
binaryfire merged 43 commits into
0.4from
upstream-sync-2026-10-19
Oct 19, 2026
Merged

binaryfire merged 43 commits into
0.4from
upstream-sync-2026-10-19

Conversation

@binaryfire

@binaryfire binaryfire commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

Upstream Updates

  • Memoized tagged cache keys — Port the stronger test for forgetting memoized tagged cache keys, which checks that the values are memoized before they are forgotten. Hypervel's source already handled arrays of keys.
  • Non-incrementing route keys — Keep validating integer route keys for non-incrementing models. MySQL and MariaDB coerce a value such as 12abc to 12, which would resolve the wrong model, and models with string keys already need keyType: 'string' for eager loading, whereKey() and foreignIdFor(). Explain the check, record the skipped upstream test and add a regression test that rejects 12abc without running a query.
  • Unique job locks on failure — Release a unique-until-processing job's lock when the job fails before processing starts, such as after exhausting its attempts. The lock is only released while the failed job still owns it, so a replacement job's lock stays in place. Jobs without Queueable use the owner recorded in their payload.
  • Delayed fake jobs — Port the tests that keep delayed jobs out of QueueFake's pending results. Hypervel already tracks pending, delayed and reserved jobs separately. Fold the existing oldest-pending test into upstream's stronger version.
  • Descending callable sorts — Respect the descending direction when sortByDesc() receives a comparison callback. Reverse the callback's result with <=> instead of negating it, which overflows for PHP_INT_MIN.
  • Prompts inline style widths — Port the width tests for inline styles with hyphenated and hex colors, replacing weaker Hypervel cases. Hypervel already strips any Symfony inline style value.
  • Testbench manifest path — Add the public PackageManifest::getManifestPath() and use it in the manifest tests. Each parallel worker already builds its manifest in its own runtime copy.
  • Testbench cached views — Add the #[WithCachedViews] attribute, which runs view:cache before a test and view:clear after it, so packages can test their views compiled. Hypervel runs the attribute after each worker has selected its own compiled view path, so upstream's parallel-testing deferral isn't needed. Fix upstream's follow-up test, which checked false instead of the compiled view's path once the file was gone. Document the attribute.
  • Testbench mutable dates — Record the mutable-date test as removed. Hypervel's default date class is already immutable.
  • Testbench assertions and parallel helper — Add Hypervel\Testbench\PHPUnit\Assert with markTestSkippedUnless() and markTestSkippedWhen(), and delegate HandlesAssertions to it. Add the in_parallel_testing() helper, which also works without an application, such as in data providers. Upstream's per-token .env files aren't needed, because each worker's runtime copy has its own .env.
  • PHPUnit 13.4 — Support PHPUnit 13.4, which requires an event emitter for the PHP process handler used by parallel runs. Require PHPUnit ~13.4.3, matching upstream's conflict with untested 13.5 releases, and ParaTest ^7.27.2, and update the testing package's suggestions.
  • Horizon dependencies — Update Vue to ^3.5.43 and source-map-js to 1.2.2, and rebuild the dashboard assets.
  • Reverb client whispers — Port the test for client whispers when another application has subscribed to the same channel name. Hypervel already resolves the channel through the connection's application.
  • MCP legacy handshake — Port the legacy handshake tests and the client's version check. Hypervel doesn't negotiate 2025-03-26, which requires JSON-RPC batching the server doesn't support, so clients requesting it are offered 2025-11-25. Document the supported protocol versions.
  • AI file copying — Stop the CopyFile tool from copying directories or missing paths. Check the source inside the tool's error handling, so a path the disk rejects returns a message to the model instead of ending the run, and do the same in MoveFile and GetFileUrl.
  • AI Stop middleware — Add Stop middleware, with Stop::when() and Stop::unless(), to end an agent run before a step. Streamed runs finish with a cancelled reason. Stop conditions are serializable, so queued agents can use the middleware. Document stopping a run, since upstream has no documentation for it yet.
  • AI structured output keys — Decode structured output whose object keys contain unescaped double quotes. Valid JSON never reaches this fallback, and its scan is linear.
  • AI incomplete responses — Map incomplete Responses API streams by their reason, so output token limits, content filters and other reasons get distinct finish reasons.
  • AI OpenRouter images — Use OpenRouter's images endpoint for image generation, with aspect ratio, resolution and input references. Errors returned in a successful HTTP response still throw.
  • AI Ollama embeddings — Use Ollama's native embedding dimensions unless dimensions are configured.
  • AI Retry-After — Retry once after a short Retry-After on rate-limited or overloaded responses before failing over. The wait uses the fakeable Sleep and releases idle database connections first. Document the retry.
  • AI structured sub-agents — Run sub-agents with structured output unstreamed when the parent streams, so the parent receives the structured response.
  • AI OpenAI-compatible classification — Add classification to the OpenAI-compatible provider, with a configurable default model and attachment support. Image attachments resolve provider options for the OpenAI-compatible provider. Document it.
  • Scout Turbopuffer header — Send an X-Hypervel-Scout header with the framework version on Turbopuffer requests.
  • Scout Meilisearch client agent — Restore the Meilisearch client agent, which the SDK adds to the User-Agent header.
  • Scout Meilisearch documentation — Update the Meilisearch Scout documentation, including search modes, hosting options and syncing index settings for native embedders.
  • Reconcile the Sentry AI monitoring documentation with Sentry's current Laravel AI and agent tracing docs. Cover transactions for console commands, classification conversations, the transaction needed for conversation IDs and the data each setting sends.

Additional Hypervel Fixes

  • Keep a falsy prompt such as "0" in OpenRouter image requests. Upstream's filter dropped every falsy value; the required fields now drop only nulls.
  • Keep a falsy vector store name such as "0" when creating OpenAI and Azure OpenAI stores. Empty optional fields are still omitted.
  • Treat a queued listener's null uniqueFor() as no expiry instead of throwing a TypeError.
  • Allow retry()'s sleep closure to return milliseconds as an integer or float, matching what it accepts at runtime.
  • Adapt server tests to current Swoole. Read response headers from stream metadata inside coroutines, return true from mocked Port::set() calls and test a failed port configuration, and expect the exception Swoole now throws for malformed SNI settings.
  • Exempt methods marked with PHPUnit's #[Test] attribute from the method docblock rule, as test* methods already were.

The affected suites, Testbench package tests, formatting and static analysis pass. CI will run the full suite and supported service matrix.

Note

Add AI Stop middleware, OpenAI-compatible classification, and PHPUnit 13.4 support

  • Adds Stop middleware (when/unless factories, serializable conditions) that halts agent runs before a model call; TextGenerationLoop returns an empty response for stopped runs and ends streams with FinishReason::Cancelled
  • Makes the OpenAI-compatible provider a classification provider via OpenAiCompatibleClassificationGateway, posting to the /decisions endpoint with the configured default model
  • Retries rate-limited (429) or overloaded providers once after a Retry-After delay of 10 seconds or less before failing over, in HandlesFailoverErrors
  • Switches OpenRouter image generation from chat completions to the images endpoint with top-level aspect_ratio/resolution fields; fixes structured-output decoding for unescaped quotes in object keys
  • Raises PHPUnit constraints to ~13.4.3 and ParaTest to ^7.27.2 across manifests; adds Testbench WithCachedViews attribute, conditional skip helpers, and in_parallel_testing() function
  • Behavioral Change: OpenAI incomplete responses with unrecognized reasons now map to FinishReason::Unknown instead of Length; CallQueuedHandler failed-job cleanup releases unique locks only when ownership is established; Collection::sortBy with descending callable comparators now inverts comparator results; MCP excludes protocol revision 2025-03-26

Macroscope summarized 7778e15.

The Turbopuffer client from laravel/scout#1007 sends an X-Laravel-Scout
header with the Scout version on every request, but the Hypervel port
left it out. Send it as X-Hypervel-Scout with the framework version,
which Hypervel's split packages share.

The upsert test now asserts the header, as upstream's does, against the
version constant rather than a literal (laravel/scout#1017).

Validation: composer lint:fix and composer analyse pass, and the Scout
suite passes under ParaTest.

Upstream reference: laravel/scout 11.x at d6d1774c4fa55035ac30b6312961edcd4155b9a7.
Upstream's Meilisearch client binding passes a "Meilisearch Laravel
Scout" client agent (laravel/scout#678), which the SDK adds to the
User-Agent header. The Hypervel binding dropped it while adding the
explicit Guzzle client. Pass "Meilisearch Hypervel Scout" with the
framework version, and add a test for the resulting header.

Validation: composer lint:fix and composer analyse pass, and the Scout
suite passes under ParaTest.

Upstream reference: laravel/scout 11.x at d6d1774c4fa55035ac30b6312961edcd4155b9a7.
Port laravel/docs#11398, a follow-up to the Meilisearch semantic search
docs: describe Meilisearch's full-text, semantic and hybrid search and
its hosting options, update the documentation link, drop the outdated
binary-compatibility and upgrade notes, and explain that native
embedders need scout:sync-index-settings after configuration changes,
with a link to Meilisearch's embedder documentation.

Upstream reference: laravel/docs 13.x at 12080c72699b99ec6573f7e79ff4834d9001b6bc.
Reconcile the AI monitoring section with Sentry's Laravel AI and agent
tracing docs:

- Console commands only start transactions as scheduled tasks, so AI
  calls from other commands need a custom transaction.
- Classifications join the conversation of an agent in the same trace,
  including those made from the agent's tools.
- Integration::setConversationId() needs an active transaction.
- System instructions and attachments follow the sensitive data
  settings, tool names and descriptions are always sent, and tool
  definitions are sent by default and count as inputs under
  data_collection.

Upstream reference: getsentry/sentry-docs master at
bce878cefbfd96633427a42a9dd68404c7da8af8, against
getsentry/sentry-laravel 0537a1d5bfd583beb89001887af9cadd3116bccb.
laravel/framework#61881 fixed forgetting an array of keys on the
memoized tagged cache. Hypervel's source already matched. Replace the
weaker body of testItCanForgetMultipleMemoizedValuesWithTags with
upstream's, which uses two tags and checks that the values are
memoized before they are forgotten.

Validation: composer lint:fix, composer analyse and the full suite
under composer test:parallel pass.

Upstream reference: laravel/framework master at
8c19f778147a38bdfb2550df20469b47ad93b6b4.
laravel/framework#61960 skips integer route key validation for
non-incrementing models. Hypervel keeps the validation: MySQL and
MariaDB coerce a value such as "12abc" to 12 and would resolve the
wrong model, and models with string keys already need
keyType: 'string' for eager loading, whereKey() and foreignIdFor().

Explain the check in resolveRouteBindingQuery(), record the skipped
upstream test with a REMOVED comment, and add a regression test that
rejects "12abc" without running a query.

Validation: composer lint:fix, composer analyse and the full suite
under composer test:parallel pass.

Upstream reference: laravel/framework master at
8c19f778147a38bdfb2550df20469b47ad93b6b4.
Port laravel/framework#61986. A job that is unique until processing
releases its lock when processing starts, so a job that failed before
then, such as one that exhausted its attempts, kept the lock until it
expired. CallQueuedHandler::failed() now releases the lock, but only
while this job still owns it, so it cannot release a lock taken by a
replacement job.

UniqueLock::release() and ensureUniqueJobLockIsReleased() take
upstream's onlyOwned flag as their second parameter, followed by
Hypervel's owner and queued-job parameters. Jobs without Queueable use
the owner carried in their payload.

Also fix a TypeError in UniqueLock when a queued listener's uniqueFor()
returns null; upstream treats null as no expiry.

Port upstream's seven CallQueuedHandler unit tests and both
integration tests, and add a regression test for a job without
Queueable that exhausts its attempts before processing.

Validation: composer lint:fix, composer analyse and the full suite
under composer test:parallel pass, with Redis.

Upstream reference: laravel/framework master at
8c19f778147a38bdfb2550df20469b47ad93b6b4.
laravel/framework#62004 stops QueueFake counting delayed jobs as
pending. Hypervel already records pending, delayed and reserved jobs
as separate states of one push history. Add upstream's three tests and
fold Hypervel's testDelayedJobsDoNotDetermineTheOldestPendingTime into
upstream's stronger testCreationTimeOfOldestPendingJobExcludesDelayedJobs.

Validation: composer lint:fix, composer analyse and the full suite
under composer test:parallel pass.

Upstream reference: laravel/framework master at
8c19f778147a38bdfb2550df20469b47ad93b6b4.
Port laravel/framework#62064. sortByMany() ignored the descending
direction when a comparison was a callable, so sortByDesc() with a
comparator sorted in ascending order. Negate the callable's result for
descending sorts, and port upstream's test.

Validation: composer lint:fix, composer analyse and the full suite
under composer test:parallel pass.

Upstream reference: laravel/framework master at
8c19f778147a38bdfb2550df20469b47ad93b6b4.
laravel/prompts#276 strips inline style tags with hyphenated or hex
colors when measuring width. Hypervel's Utils::stripEscapeSequences()
already accepts any Symfony inline style value. Add upstream's three
cases to the UtilsTest provider, replacing Hypervel's weaker hex and
bright-color entries.

Validation: composer lint:fix, composer analyse and the full suite
under composer test:parallel pass.

Upstream reference: laravel/prompts main at
d649ea63640598001e9183fdbcf4be425274f910.
orchestral/testbench-core#432 gives each parallel worker its own
package manifest path. Hypervel already does this, since each worker
builds its manifest in its own runtime skeleton copy. Port the public
PackageManifest::getManifestPath() and upstream's use of it in the
manifest tests, and move UsesVendorTest's WithConfig attribute to the
class, as upstream does.

Validation: composer lint:fix, composer analyse, composer
test:testbench and the full suite under composer test:parallel pass.

Upstream reference: orchestral/testbench-core 12.x at
7b55ff4c765be6b760ccd6708a0d9b30bd12b9d9.
Port orchestral/testbench-core#429 and its later version in #437.
#[WithCachedViews] runs view:cache before the test and view:clear
afterwards, so packages can test their views compiled, as an
application runs them after view:cache. Document the attribute in the
Testbench docs.

Hypervel runs BeforeEach attributes after the parallel setUpTestCase
callbacks, which have already selected the worker's compiled view
path, so upstream's parallel-testing deferral is not needed. Fix
upstream's dependent test, which passed realpath()'s false to
assertFileDoesNotExist() once the compiled view was gone.

Validation: composer lint:fix, composer analyse, composer
test:testbench and the full suite under composer test:parallel pass.

Upstream reference: orchestral/testbench-core 12.x at
7b55ff4c765be6b760ccd6708a0d9b30bd12b9d9.
orchestral/testbench-core#433 adds a test expecting mutable dates
after a #[WithImmutableDates] test. Hypervel's default date class is
already CarbonImmutable, so the test cannot apply. Record it with a
REMOVED comment at its upstream location.

Upstream reference: orchestral/testbench-core 12.x at
7b55ff4c765be6b760ccd6708a0d9b30bd12b9d9.
Port orchestral/testbench-core f503bf99af and the HandlesAssertions
change from #434. Hypervel\Testbench\PHPUnit\Assert provides the
public markTestSkippedUnless() and markTestSkippedWhen() helpers, and
HandlesAssertions now delegates to them. The existing
HandlesAssertionsTest covers the delegation.

Validation: composer lint:fix, composer analyse, composer
test:testbench and the full suite under composer test:parallel pass.

Upstream reference: orchestral/testbench-core 12.x at
7b55ff4c765be6b760ccd6708a0d9b30bd12b9d9.
Port the in_parallel_testing() helper from
orchestral/testbench-core#434, backed by ParallelTesting::inParallel(),
with a test. The rest of #434 writes a per-token .env file, which
Hypervel does not need because each worker's runtime copy owns its
.env.

Validation: composer lint:fix, composer analyse, composer
test:testbench and the full suite under composer test:parallel pass.

Upstream reference: orchestral/testbench-core 12.x at
7b55ff4c765be6b760ccd6708a0d9b30bd12b9d9.
Port orchestral/testbench-core#422. PHPUnit 13.4.0 made the internal
PhpHandler constructor require an event emitter, which RunsInParallel
did not pass. Pass EventFacade::emitter(), as PHPUnit's Application
does.

ParaTest 7.26+ supports PHPUnit 13.4, so lift the 13.3.* pin: the root
and dogfood manifests require PHPUnit ^13.4.3 and ParaTest ^7.27.2,
Testbench requires PHPUnit ^13.4.3, and the testing package's
suggestions and their metadata test match. Remove the resolved pin
entry from docs/todo.md, and note that the PHPUnit #7039 output
buffering hook arrives in PHPUnit 13.5.

The hypervel/hypervel skeleton's pin follows in a separate PR once
this change is split.

Validation: composer lint:fix, composer analyse, composer
test:testbench, composer test:dogfood and the full suite under
composer test:parallel pass.

Upstream reference: orchestral/testbench-core 12.x at
7b55ff4c765be6b760ccd6708a0d9b30bd12b9d9.
Swoole master changed three behaviors these tests relied on:

- http_get_last_response_headers() is rejected inside coroutines, so
  CommanderServeTest reads the response headers from the stream's
  metadata and closes the stream in a finally block.
- Swoole\Server\Port::set() now declares a bool return, so the
  secondary port mocks return true. A new test covers a false return,
  which stops configuration before the port's events are registered.
- Malformed SNI settings now throw Swoole\Exception instead of warning
  and returning false, so ServerNativeTest asserts the exception and
  its message.

Remove the Server comment that described set() as void and warning.

Validation: composer lint:fix, composer analyse, composer
test:testbench and the full suite under composer test:parallel pass
on Swoole master (6.3.0RC1).
Port laravel/horizon#1831 and #1830, which share the lockfile. Raise
vue to ^3.5.43 and update source-map-js to 1.2.2 with npm 11, then
rebuild dist/app.js. The lockfile matches upstream's target except for
Hypervel's existing newer sass 1.105.0, and the styles are unchanged.

Validation: the Horizon unit and Redis integration suites pass.

Upstream reference: laravel/horizon 5.x at
9de5ceb25c0a8288cf09f6c31dee8ccb91c0738e.
laravel/reverb#418 scopes the client event membership check to the
connection's application. Hypervel's ClientEvent already resolves the
channel through the connection's application. Port the test into the
real-server suite: a second application subscribes to the same private
channel name, and the whisper still reaches the first application's
receiver.

The second application is a per-worker parallel app, since the shared
fixture apps enforce connection limits. subscribe() takes an optional
key and secret for it, as the signed-request helpers do.

Validation: composer lint:fix and composer analyse pass, and the Reverb
integration suite passes against the test servers.

Upstream reference: laravel/reverb main at
94ccfe195094a0685b75c15d7eea62124f0d9dbc.
Port laravel/mcp#372 and cd20f0afe6. The legacy initialize handshake
now accepts 2025-03-26 clients, and the HTTP header middleware's
supported list includes it. The client still accepts only versions in
both its own and the initialize list, so it does not negotiate
2025-03-26 and its error message is unchanged.

Port upstream's tests: the handshake-then-tools flow for each legacy
version, with and without the version header, and the extended unit
handshake. The client rejection test also covers 2025-03-26, which
guards the client's version intersection.

Validation: composer lint:fix and composer analyse pass, and the MCP
suite and the AI MCP client tests pass.

Upstream reference: laravel/mcp main at
cd20f0afe62ca496678068643f4c09fa6a4e9e16.
Port laravel/ai#1109. CopyFile now checks that the source file exists
before copying, so a directory or missing path returns "File [...]
does not exist." Hypervel's cancellation rethrow is kept. Port
upstream's three tests; the copy-failure double mocks fileExists(),
which Hypervel's helper uses instead of size().

Validation: composer lint:fix, composer analyse and the AI suite pass.

Upstream reference: laravel/ai 1.x at
d44ae14d17fb966a55c142a94fdcff980570e6c9.
Port laravel/ai#1072. Stop::when() and Stop::unless() end an agent run
before any step for which their condition holds or fails. StepResult
gains stop() and stopped(), with a nullable source. generate() ends
the loop with an empty response when a step is stopped, and stream()
finishes with FinishReason::Cancelled. A stopped step never reaches
attempt(), so no step is started or recorded. The Vercel protocol maps
cancelled to other, as upstream does.

Port upstream's six tests. The documentation follows in a separate
commit.

Validation: composer lint:fix, composer analyse and the AI suite pass.

Upstream reference: laravel/ai 1.x at
d44ae14d17fb966a55c142a94fdcff980570e6c9.
Port laravel/ai#1111. When a provider returns structured output with
unescaped double quotes inside object keys, decoding now escapes them
as a last fallback, treating only a quote followed by a colon as the
end of a key. Valid JSON never reaches this path, and the scan is
linear and stops at the first unmatched string token. Port upstream's
two tests.

Validation: composer lint:fix, composer analyse and the AI suite pass.

Upstream reference: laravel/ai 1.x at
d44ae14d17fb966a55c142a94fdcff980570e6c9.
Port the rest of laravel/ai#1113. Failed and incomplete responses and
a missing terminal response were already handled. Now only
max_output_tokens maps to FinishReason::Length, content_filter maps to
ContentFilter, and other incomplete reasons map to Unknown.

Port upstream's incomplete tool-call test, where tool calls are
skipped and usage is kept, with its responseIncomplete() helper and
the failed-response error case. Rename the missing-terminal test to
upstream's name.

Validation: composer lint:fix, composer analyse and the AI suite pass,
including the OpenAI and Azure OpenAI tests.

Upstream reference: laravel/ai 1.x at
d44ae14d17fb966a55c142a94fdcff980570e6c9.
Port laravel/ai#1116. Image generation posts to OpenRouter's images
endpoint with prompt, aspect_ratio, resolution and input_references,
and reads data[].b64_json and media_type from the response.
image_config provider options become top-level fields, which size and
quality override, and defaultImageOptions() returns resolution.
Hypervel keeps checking the body for an error, so an error in an HTTP
200 response still throws.

Fix upstream's request filter, which dropped a falsy prompt such as
"0"; the required fields now drop only nulls, with a regression test.

Port upstream's feature tests, integration dataset entries and live
tests, which skip without keys. The Base64 and remote attachment tests
merge into upstream's single test, keeping the check that remote
images are not downloaded.

Validation: composer lint:fix, composer analyse and the AI suite pass.

Upstream reference: laravel/ai 1.x at
d44ae14d17fb966a55c142a94fdcff980570e6c9.
OpenAiStoreGateway::createStore() filtered the whole request by
truthiness, so a store named "0" was created without its name. Send
the name directly and filter only the optional fields, so empty file
IDs are still omitted. Azure OpenAI uses the same gateway. Add a
regression test for the exact request.

Found while checking the siblings of the OpenRouter prompt fix in the
laravel/ai#1116 port.

Validation: composer lint:fix and composer analyse pass, and the
OpenAI and Azure OpenAI store gateway tests pass.
Port laravel/ai#1115. OllamaProvider defaults to zero dimensions and
supports native dimensions, so embedding requests omit dimensions
unless they are configured. Port upstream's five tests.

Validation: composer lint:fix, composer analyse and the AI suite pass.

Upstream reference: laravel/ai 1.x at
d44ae14d17fb966a55c142a94fdcff980570e6c9.
retry()'s $sleepMilliseconds docblock typed a closure's return as
CarbonInterval only, although retry() also accepts an integer or float
number of milliseconds from it. PHPStan therefore rejected millisecond
closures, such as the Retry-After delay in laravel/ai#1112. Allow
CarbonInterval|float|int returns, and add a types fixture.

Validation: composer lint:fix and composer analyse pass, including the
types fixtures.
Port laravel/ai#1112. When a rate-limited or overloaded response
carries a whole-second Retry-After of 10 seconds or less,
withErrorHandling() retries the request once through retry() before
failing over. retry() sleeps with the fakeable, coroutine-hooked Sleep
and releases idle database connections first. Upstream's null-response
guard is dropped because Hypervel's RequestException::$response is not
nullable.

Port upstream's unit and feature tests, including the new
AgentRetryAfterTest and the streaming case. The documentation follows
in a separate commit.

Validation: composer lint:fix, composer analyse and the AI suite pass.

Upstream reference: laravel/ai 1.x at
d44ae14d17fb966a55c142a94fdcff980570e6c9.
Port laravel/ai#1117. AgentTool::stream() answers through handle() for
agents with structured output, so a streaming parent receives the
sub-agent's structured response. Port upstream's two tests; the loop
test also checks the parent invocation link and the ToolInvoked
result.

Validation: composer lint:fix, composer analyse and the AI suite pass.

Upstream reference: laravel/ai 1.x at
d44ae14d17fb966a55c142a94fdcff980570e6c9.
Port laravel/ai#1128. OpenAiCompatibleClassificationGateway is
OpenAI's decisions gateway with the OpenAI-compatible client. The
provider gains the classification gateway, a default classification
model that must be configured, and attachment support. Port upstream's
four tests. The documentation follows in a separate commit.

Validation: composer lint:fix, composer analyse and the AI suite pass.

Upstream reference: laravel/ai 1.x at
d44ae14d17fb966a55c142a94fdcff980570e6c9.
Upstream has no documentation for laravel/ai#1072, #1112 or #1128 at
the pinned docs revision, so add it to the AI SDK docs:

- A "Stopping a Run" section for Stop middleware, and a corrected
  middleware sentence that implied middleware could only stop a run by
  throwing.
- A failover sentence on the single retry after a short Retry-After.
- Classification in the OpenAI-compatible feature list, a
  configuration section, and the provider table entry.

Upstream reference: laravel/ai 1.x at
d44ae14d17fb966a55c142a94fdcff980570e6c9; laravel/docs 13.x at
12080c72699b99ec6573f7e79ff4834d9001b6bc.
The docblock rule exempted only test* methods, although methods marked
with PHPUnit's #[Test] attribute are test methods too, and none of
them in the repository has a title. Exempt test methods however
PHPUnit identifies them.
@coderabbitai

coderabbitai Bot commented Oct 19, 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
  • Configuration used: Repository: hypervel/components-backup/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 934c2e09-619f-4d30-a0a1-187c2a1200fa

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

Comment @coderabbitai help to get the list of available commands.

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

Copy link
Copy Markdown

PR Summary by Qodo

Sync upstream packages and add PHPUnit 13.4 support

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

Grey Divider

AI Description

• Sync AI, Testbench, MCP, Scout, and framework behavior with upstream changes.
• Fix unique-job lock cleanup, provider requests, route-key validation, and collection sorting.
• Support PHPUnit 13.4 and expand regression coverage across affected packages.
Diagram

graph TD
  A["Agent run"] --> B["Step middleware"] --> C{"Stop run?"}
  C -->|Yes| D["Final response"]
  C -->|No| E["Generation loop"] --> F["Provider gateway"] --> G["HTTP provider"]
  G -->|Retry or result| F
  F --> D
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Split the sync by package
  • ➕ Smaller reviews
  • ➕ Independent verification and rollback of AI, queue, Testbench, and protocol changes
  • ➖ More coordination and CI runs
  • ➖ Related dependency and Testbench changes may need careful sequencing

Recommendation: The package-local implementations generally reuse existing gateways, traits, and lifecycle hooks rather than adding parallel mechanisms. Given the breadth and independent risk of these changes, splitting future syncs by package would improve reviewability; for this PR, review and verify each subsystem separately.

Files changed (86) +1810 / -324

Enhancement (16) +282 / -61
HandlesFailoverErrors.phpRetry short provider rate limits before failover +27/-1

Retry short provider rate limits before failover

• Retries a rate-limited or overloaded request once when it supplies a numeric Retry-After of at most ten seconds. Existing provider-error mapping remains the final failure path.

src/ai/src/Gateway/Concerns/HandlesFailoverErrors.php

OpenAiCompatibleClassificationGateway.phpAdd OpenAI-compatible classification gateway +13/-0

Add OpenAI-compatible classification gateway

• Reuses OpenAI classification behavior with the OpenAI-compatible client's endpoint and authentication settings.

src/ai/src/Gateway/OpenAiCompatible/OpenAiCompatibleClassificationGateway.php

OpenRouterGateway.phpMove OpenRouter image generation to the images API +15/-46

Move OpenRouter image generation to the images API

• Sends image requests to the images endpoint with prompt, aspect ratio, resolution, and input references. Parses base64 image data and preserves falsy required fields.

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

StepResult.phpRepresent middleware-stopped AI steps +19/-3

Represent middleware-stopped AI steps

• Adds a stopped result with no model response so generation loops can end a run without treating the stop as a completed step.

src/ai/src/Gateway/StepResult.php

TextGenerationLoop.phpEnd buffered and streamed runs on Stop results +29/-5

End buffered and streamed runs on Stop results

• Stops before another model call, retains completed work, and emits a cancelled reason for streams. Returns an empty response when no step completed.

src/ai/src/Gateway/TextGenerationLoop.php

Stop.phpAdd conditional Stop middleware +51/-0

Add conditional Stop middleware

• Provides Stop::when() and Stop::unless() to end an agent run before a pending model step.

src/ai/src/Middleware/Stop.php

OpenAiCompatibleProvider.phpExpose OpenAI-compatible classification +34/-1

Expose OpenAI-compatible classification

• Implements classification support with a configurable default model, compatible gateway, and attachment support.

src/ai/src/Providers/OpenAiCompatibleProvider.php

OpenRouterProvider.phpMap OpenRouter image quality to resolution +1/-5

Map OpenRouter image quality to resolution

• Changes default image options to use the images API's resolution field instead of image_size.

src/ai/src/Providers/OpenRouterProvider.php

FinishReason.phpAdd cancelled AI finish reason +1/-0

Add cancelled AI finish reason

• Defines a finish reason for streams ended by Stop middleware.

src/ai/src/Responses/Data/FinishReason.php

ProtocolVersion.phpAccept 2025-03-26 in legacy MCP initialization +1/-0

Accept 2025-03-26 in legacy MCP initialization

• Adds the older version to the legacy initialize-supported list without adding it to client-negotiated versions.

src/mcp/src/Enums/ProtocolVersion.php

ScoutServiceProvider.phpIdentify Scout in Meilisearch requests +1/-0

Identify Scout in Meilisearch requests

• Passes a versioned Hypervel Scout client agent to the Meilisearch SDK for its User-Agent header.

src/scout/src/ScoutServiceProvider.php

TurbopufferClient.phpIdentify Scout in Turbopuffer requests +2/-0

Identify Scout in Turbopuffer requests

• Adds an X-Hypervel-Scout header containing the framework version.

src/scout/src/Services/Turbopuffer/TurbopufferClient.php

WithCachedViews.phpCompile and clear views around attributed tests +38/-0

Compile and clear views around attributed tests

• Adds a class- or method-level attribute that runs view:cache before a test and view:clear afterward.

src/testbench/src/Attributes/WithCachedViews.php

PackageManifest.phpExpose the Testbench manifest path +8/-0

Expose the Testbench manifest path

• Adds a public getter for the package manifest path.

src/testbench/src/Foundation/PackageManifest.php

Assert.phpAdd reusable conditional skip assertions +30/-0

Add reusable conditional skip assertions

• Introduces static markTestSkippedUnless() and markTestSkippedWhen() methods.

src/testbench/src/PHPUnit/Assert.php

functions.phpExpose parallel-testing status helper +12/-0

Expose parallel-testing status helper

• Adds in_parallel_testing() using Testbench's resolved ParallelTesting service.

src/testbench/src/functions.php

Bug fix (12) +110 / -27
DecodesStructuredOutput.phpRecover structured JSON with unescaped quotes in keys +47/-0

Recover structured JSON with unescaped quotes in keys

• Adds a fallback decoder that escapes stray quotes in object keys after normal JSON and fenced-JSON decoding fail.

src/ai/src/Gateway/Concerns/DecodesStructuredOutput.php

ParsesTextResponses.phpDistinguish incomplete response finish reasons +5/-3

Distinguish incomplete response finish reasons

• Maps output-token limits to length, content filters to content_filter, and other incomplete reasons to unknown.

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

OpenAiStoreGateway.phpPreserve falsy vector-store names +10/-8

Preserve falsy vector-store names

• Always sends the required store name, including "0", while filtering empty optional request fields.

src/ai/src/Gateway/OpenAi/OpenAiStoreGateway.php

OllamaProvider.phpDefault Ollama embeddings to native dimensions +9/-1

Default Ollama embeddings to native dimensions

• Omits embedding dimensions unless configured, allowing the selected Ollama model to use its native size.

src/ai/src/Providers/OllamaProvider.php

AgentTool.phpRun structured sub-agents without streaming +5/-0

Run structured sub-agents without streaming

• Uses a regular prompt for structured sub-agents even when the parent streams, preserving their structured tool result.

src/ai/src/Tools/AgentTool.php

CopyFile.phpReject missing files and directories as copy sources +8/-2

Reject missing files and directories as copy sources

• Checks that the source is a file before copying and distinguishes missing sources from copy failures.

src/ai/src/Tools/Filesystem/CopyFile.php

UniqueLock.phpGuard ownership-sensitive lock release +6/-2

Guard ownership-sensitive lock release

• Treats null uniqueFor() as no expiry and permits failure cleanup to release a lock only when its owner is known.

src/bus/src/UniqueLock.php

Collection.phpHonor descending callable comparisons +4/-0

Honor descending callable comparisons

• Reverses callable comparison results for descending multi-column sorts.

src/collections/src/Collection.php

InitializeResult.phpKeep MCP client negotiation within client-supported versions +3/-2

Keep MCP client negotiation within client-supported versions

• Validates an initialize response against versions supported by both the legacy handshake and this client.

src/mcp/src/Client/Schema/InitializeResult.php

CallQueuedHandler.phpRelease owned unique-until-processing locks on failure +10/-7

Release owned unique-until-processing locks on failure

• Cleans up locks when jobs fail before processing, using job or payload ownership to avoid removing a replacement job's lock.

src/queue/src/CallQueuedHandler.php

helpers.phpClarify retry sleep closure type +1/-1

Clarify retry sleep closure type

• Updates the retry() PHPDoc to express integer, float, and interval values returned by a sleep closure.

src/support/src/helpers.php

RunsInParallel.phpSupply PHPUnit event emitter to parallel runner +2/-1

Supply PHPUnit event emitter to parallel runner

• Constructs PHPUnit's PhpHandler with the event emitter required by PHPUnit 13.4.

src/testing/src/Concerns/RunsInParallel.php

Refactor (2) +7 / -8
Initialize.phpUse one legacy MCP version list during negotiation +4/-2

Use one legacy MCP version list during negotiation

• Reuses the supported-version list for matching and fallback initialize responses.

src/mcp/src/Server/Methods/Initialize.php

HandlesAssertions.phpDelegate conditional skips to Testbench Assert +3/-6

Delegate conditional skips to Testbench Assert

• Routes conditional test-skipping helpers through the new reusable assertion class.

src/testbench/src/Concerns/HandlesAssertions.php

Tests (41) +1288 / -158
AgentMiddlewareTest.phpCover stopped agent runs and remembered work +113/-0

Cover stopped agent runs and remembered work

• Tests first-step and later stops, cancelled streams, structured responses, and retained conversation messages.

tests/Ai/Feature/AgentMiddlewareTest.php

AgentRetryAfterTest.phpVerify buffered AI retry before failover +51/-0

Verify buffered AI retry before failover

• Checks that a short rate limit delay retries on the primary provider without dispatching failover.

tests/Ai/Feature/AgentRetryAfterTest.php

AgentStreamFailoverTest.phpVerify streamed AI retry before failover +30/-0

Verify streamed AI retry before failover

• Checks short Retry-After handling on a streamed request using a fakeable sleep.

tests/Ai/Feature/AgentStreamFailoverTest.php

FilesystemToolsTest.phpCover CopyFile source validation +23/-1

Cover CopyFile source validation

• Tests missing files, directory sources, and copy operations that return failure.

tests/Ai/Feature/FilesystemToolsTest.php

EmbeddingTest.phpCover native and explicit Ollama dimensions +60/-0

Cover native and explicit Ollama dimensions

• Verifies omitted, configured, and explicit dimensions, plus the behavior of automatic embedding fakes without a known size.

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

StoreGatewayTest.phpTest falsy vector-store names +10/-0

Test falsy vector-store names

• Asserts that a store named "0" is sent while empty optional fields are omitted.

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

StreamingTest.phpExpand incomplete Responses API stream coverage +35/-8

Expand incomplete Responses API stream coverage

• Tests finish reasons and usage for incomplete streams, including unknown reasons and completed tool-call items.

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

ClassificationTest.phpTest OpenAI-compatible classification requests +104/-0

Test OpenAI-compatible classification requests

• Covers endpoint and model selection, authentication, inline image attachments, and missing-model errors.

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

ImageGenerationTest.phpTest OpenRouter images API mapping +71/-85

Test OpenRouter images API mapping

• Reworks request and response assertions for the images endpoint, options, references, base64 results, falsy prompts, and errors.

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

OpenAiHelpers.phpAdd incomplete-response test fixture +14/-0

Add incomplete-response test fixture

• Builds terminal incomplete Responses API events with reason, usage, and optional output.

tests/Ai/Fixtures/OpenAiHelpers.php

DecodesStructuredOutputTest.phpTest recovery of quoted object keys +24/-0

Test recovery of quoted object keys

• Covers unescaped key quotes in nested and fenced JSON without changing quoted values.

tests/Ai/Unit/Gateway/Concerns/DecodesStructuredOutputTest.php

HandlesFailoverErrorsTest.phpTest Retry-After limits and failure paths +93/-2

Test Retry-After limits and failure paths

• Covers eligible statuses, one retry, persistent failures, and headers or statuses that must not trigger retries.

tests/Ai/Unit/Gateway/Concerns/HandlesFailoverErrorsTest.php

TextGenerationLoopTest.phpTest structured sub-agent execution in streams +44/-0

Test structured sub-agent execution in streams

• Verifies that a streamed parent receives a structured result from a non-streamed child prompt.

tests/Ai/Unit/Gateway/TextGenerationLoopTest.php

AgentToolTest.phpTest structured child-agent tool results +20/-0

Test structured child-agent tool results

• Asserts that the streaming tool path calls prompt rather than stream for structured agents.

tests/Ai/Unit/Tools/AgentToolTest.php

DatabaseEloquentRouteBindingTest.phpRegress malformed non-incrementing integer route keys +18/-0

Regress malformed non-incrementing integer route keys

• Asserts that a key such as 12abc is rejected before a query, and records the differing upstream test.

tests/Database/DatabaseEloquentRouteBindingTest.php

Providers.phpAdd OpenRouter image integration models +5/-0

Add OpenRouter image integration models

• Lists several OpenRouter image models for credential-gated integration tests.

tests/Integration/Ai/Fixtures/Providers.php

ImageTest.phpExercise OpenRouter image generation and editing +32/-0

Exercise OpenRouter image generation and editing

• Checks generated image base64 and adds reference-image editing cases across OpenRouter models.

tests/Integration/Ai/ImageTest.php

MemoizedTaggedCacheTest.phpStrengthen tagged-cache forget regression +10/-5

Strengthen tagged-cache forget regression

• Confirms tagged values are memoized before forgetting multiple keys, then checks both live and memoized results.

tests/Integration/Cache/MemoizedTaggedCacheTest.php

UniqueUntilProcessingJobTest.phpExercise failure-time unique lock ownership +71/-0

Exercise failure-time unique lock ownership

• Tests exhausted attempts, replacement locks, and jobs without Queueable state against the database queue and cache locks.

tests/Integration/Queue/UniqueUntilProcessingJobTest.php

ReverbIntegrationTestCase.phpAllow subscriptions with another Reverb app credential +5/-1

Allow subscriptions with another Reverb app credential

• Extends the subscription helper to sign channel requests with supplied application credentials.

tests/Integration/Reverb/ReverbIntegrationTestCase.php

ServerTest.phpTest whispers across identically named app channels +40/-0

Test whispers across identically named app channels

• Verifies a client whisper reaches a subscriber in its own application when another application uses the same channel name.

tests/Integration/Reverb/ServerTest.php

ValidateMcpHeadersTest.phpTest legacy MCP requests after initialization +32/-8

Test legacy MCP requests after initialization

• Exercises supported versions, including 2025-03-26, through initialization and tool calls with and without a version header.

tests/Mcp/Feature/Server/Middleware/ValidateMcpHeadersTest.php

ClientTest.phpGuard unsupported MCP client negotiation +5/-3

Guard unsupported MCP client negotiation

• Confirms the client rejects both older unsupported versions, even when the server accepts one for legacy initialization.

tests/Mcp/Unit/Client/ClientTest.php

ProtocolVersionTest.phpAssert separate MCP version sets +1/-1

Assert separate MCP version sets

• Updates expected legacy initialize versions without broadening the client's supported negotiation list.

tests/Mcp/Unit/Enums/ProtocolVersionTest.php

ServerTest.phpExercise each legacy MCP handshake +31/-3

Exercise each legacy MCP handshake

• Tests initialization, subsequent tool listing, and tool calls for all supported legacy versions.

tests/Mcp/Unit/ServerTest.php

UtilsTest.phpStrengthen inline-style width cases +3/-2

Strengthen inline-style width cases

• Tests stripping nested, hyphenated, and hexadecimal Symfony inline colors from width calculations.

tests/Prompts/UtilsTest.php

CallQueuedHandlerTest.phpTest terminal unique-lock cleanup and overrides +132/-0

Test terminal unique-lock cleanup and overrides

• Covers failed jobs and listeners, replacement ownership, legacy locks, and forwarding of the ownership requirement.

tests/Queue/CallQueuedHandlerTest.php

TurbopufferEngineTest.phpAssert Turbopuffer Scout identification +2/-0

Assert Turbopuffer Scout identification

• Checks that upsert requests include the versioned X-Hypervel-Scout header.

tests/Scout/Feature/Engines/TurbopufferEngineTest.php

ScoutServiceProviderTest.phpAssert Meilisearch Scout client agent +11/-0

Assert Meilisearch Scout client agent

• Checks the versioned client-agent prefix in the SDK's User-Agent header.

tests/Scout/Unit/ScoutServiceProviderTest.php

ServerNativeTest.phpExpect current Swoole malformed-SNI exception +4/-18

Expect current Swoole malformed-SNI exception

• Updates native server coverage to assert Swoole's exception instead of a warning followed by a ServerException.

tests/Server/ServerNativeTest.php

ServerTest.phpCover secondary port configuration failure +34/-2

Cover secondary port configuration failure

• Tests that a failed Port::set() prevents registration and makes successful port mocks return true.

tests/Server/ServerTest.php

SupportCollectionTest.phpTest descending callable sort combinations +20/-0

Test descending callable sort combinations

• Covers sortByDesc() callbacks and mixed-direction callable comparisons.

tests/Support/SupportCollectionTest.php

SupportTestingQueueFakeTest.phpVerify delayed fake jobs stay out of pending results +35/-12

Verify delayed fake jobs stay out of pending results

• Tests disjoint delayed and pending counts, single recording, and oldest-pending timestamps.

tests/Support/SupportTestingQueueFakeTest.php

UsesVendorTest.phpApply database test configuration at class scope +1/-1

Apply database test configuration at class scope

• Moves the testing database selection from one attributed method to its test class.

tests/Testbench/Attributes/UsesVendorTest.php

WithCachedViewsTest.phpTest cached views and post-test cleanup +60/-0

Test cached views and post-test cleanup

• Checks that a compiled view exists during an attributed test and is gone in a dependent test.

tests/Testbench/Attributes/WithCachedViewsTest.php

WithImmutableDatesTest.phpRecord incompatible mutable-date upstream case +3/-0

Record incompatible mutable-date upstream case

• Notes that the removed upstream expectation does not apply because Hypervel defaults to immutable dates.

tests/Testbench/Attributes/WithImmutableDatesTest.php

CommanderServeTest.phpRead served response headers from stream metadata +9/-2

Read served response headers from stream metadata

• Uses the opened HTTP stream's wrapper data to retrieve headers reliably inside coroutines.

tests/Testbench/CommanderServeTest.php

PackageManifestTest.phpUse the manifest's own path in tests +6/-2

Use the manifest's own path in tests

• Reads built manifests from PackageManifest::getManifestPath() rather than a test-held path.

tests/Testbench/Foundation/PackageManifestTest.php

HelpersTest.phpTest parallel-testing helper +22/-0

Test parallel-testing helper

• Verifies in_parallel_testing() with the parallel flag set and unset.

tests/Testbench/HelpersTest.php

PackageMetadataTest.phpAssert updated testing dependency suggestions +2/-2

Assert updated testing dependency suggestions

• Updates expected PHPUnit and ParaTest version guidance in package metadata.

tests/Testing/PackageMetadataTest.php

helpers.phpCheck retry sleep-closure static type +2/-0

Check retry sleep-closure static type

• Adds a PHPStan assertion for retry() with an integer-valued sleep closure.

types/Support/helpers.php

Documentation (8) +68 / -18
AGENTS.mdRecognize attributed PHPUnit tests in docblock guidance +1/-1

Recognize attributed PHPUnit tests in docblock guidance

• Exempts methods marked with #[Test] from the test-method docblock requirement, alongside test-prefixed methods.

AGENTS.md

todo.mdUpdate PHPUnit output-buffering follow-up +1/-2

Update PHPUnit output-buffering follow-up

• Removes the completed PHPUnit 13.4 pin task and tracks the PHPUnit 13.5 output-buffering integration.

docs/todo.md

Model.phpExplain validation of non-incrementing integer route keys +2/-0

Explain validation of non-incrementing integer route keys

• Documents why integer route keys remain validated even without auto-increment: database coercion could otherwise bind the wrong model.

src/database/src/Eloquent/Model.php

ai-sdk.mdDocument new AI provider and run behavior +41/-3

Document new AI provider and run behavior

• Documents OpenAI-compatible classification, Stop middleware, and short Retry-After retries.

src/docs/ai-sdk.md

scout.mdRefresh Meilisearch search and embedder guidance +5/-8

Refresh Meilisearch search and embedder guidance

• Describes search modes and hosting, updates links, and explains syncing index settings after native embedder changes.

src/docs/scout.md

sentry.mdClarify Sentry AI tracing and data collection +3/-3

Clarify Sentry AI tracing and data collection

• Explains console transaction requirements, classification conversations, conversation-ID context, and sensitive-data settings.

src/docs/sentry.md

testbench.mdDocument cached-view testing +15/-0

Document cached-view testing

• Shows how #[WithCachedViews] compiles views for a test and clears them afterward.

src/docs/testbench.md

Server.phpRemove obsolete Swoole SNI comment +0/-1

Remove obsolete Swoole SNI comment

• Drops a comment describing warning-and-false behavior that no longer matches current Swoole.

src/server/src/Server.php

Other (7) +55 / -52
composer.jsonRaise root PHPUnit and ParaTest requirements +2/-2

Raise root PHPUnit and ParaTest requirements

• Requires PHPUnit ^13.4.3 and ParaTest ^7.27.2 for the updated parallel-test integration.

composer.json

composer.jsonAllow PHPUnit 13.4 in AI dogfood tests +1/-1

Allow PHPUnit 13.4 in AI dogfood tests

• Raises the AI dogfood package's development PHPUnit requirement to ^13.4.3.

dogfood/ai/composer.json

composer.jsonRaise Testbench dogfood test dependencies +2/-2

Raise Testbench dogfood test dependencies

• Requires PHPUnit ^13.4.3 and ParaTest ^7.27.2 in the dogfood package.

dogfood/testbench-package/composer.json

app.jsRebuild Horizon dashboard bundle +46/-43

Rebuild Horizon dashboard bundle

• Updates the generated dashboard asset alongside the frontend dependency changes.

src/horizon/dist/app.js

package.jsonRaise Horizon Vue requirement +1/-1

Raise Horizon Vue requirement

• Updates the dashboard's Vue requirement to ^3.5.43.

src/horizon/package.json

composer.jsonRequire PHPUnit 13.4 in Testbench +1/-1

Require PHPUnit 13.4 in Testbench

• Raises Testbench's PHPUnit requirement to ^13.4.3.

src/testbench/composer.json

composer.jsonUpdate testing-package dependency suggestions +2/-2

Update testing-package dependency suggestions

• Advertises the newer ParaTest and PHPUnit versions required for parallel testing and PHPUnit integration.

src/testing/composer.json

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

qodo-free-for-open-source-projects Bot commented Oct 19, 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. File-check errors abort the copy tool ✓ Resolved
Description
CopyFile::handle() calls fileExists() before entering the try block that handles filesystem
failures. If the existence check throws because of a storage or path error, the exception escapes
instead of returning the tool's failure response, even though an error from copy() is handled.
Code

src/ai/src/Tools/Filesystem/CopyFile.php[R34-36]

+        if (! $this->fileExists($disk, $from)) {
+            return "File [{$from}] does not exist.";
+        }
Evidence
The new guard invokes an existence check before the catch block; the filesystem adapter forwards
that check to its driver without catching errors. The catch block currently covers only the
subsequent copy.

src/ai/src/Tools/Filesystem/CopyFile.php[29-44]
src/ai/src/Tools/Filesystem/FilesystemTool.php[27-33]
src/filesystem/src/FilesystemAdapter.php[232-237]

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 new source-file check can throw outside `CopyFile::handle()`'s filesystem error handler, aborting the tool instead of returning a failure response.
## Fix Focus Areas
- src/ai/src/Tools/Filesystem/CopyFile.php[34-44]
## Recommended Fix
Move the existence check inside the existing `try` block. Preserve the missing-file response and continue to rethrow cancellation exceptions while converting other filesystem exceptions to tool responses.

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


2. Existing lock-release calls can fail 🐞 Bug ≡ Correctness
Description
UniqueLock::release() inserts $onlyOwned before its existing $owner argument, changing what a
second positional argument means. Callers using the previously public release($job, $owner) form
now pass an owner string to a boolean parameter and fail under strict typing; the queue handler's
calls were updated, but that does not preserve the method's existing calling convention.
Code

src/bus/src/UniqueLock.php[128]

+    public function release(mixed $job, bool $onlyOwned = false, string $owner = ''): void
Evidence
The changed signature assigns the second argument to $onlyOwned, while the method still uses
$owner as the captured lock owner. The queue handler demonstrates that releasing a job without
Queueable state requires passing that owner and has had its call adapted to the new signature.

src/bus/src/UniqueLock.php[123-142]
src/queue/src/CallQueuedHandler.php[230-242]

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

## Issue description
Adding a boolean before `UniqueLock::release()`'s existing owner parameter breaks positional calls that supply an owner.
## Fix Focus Areas
- src/bus/src/UniqueLock.php[123-140]
- src/queue/src/CallQueuedHandler.php[234-241]
## Recommended Fix
Keep `$owner` as the second parameter and append `$onlyOwned` after it. Update the queue handler to pass both arguments by name, preserving the new ownership check without changing existing positional calls.

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


3. 2025-03-26 MCP clients can send batches that are rejected ✓ Resolved
Description
ProtocolVersion::initializeSupported() now includes 2025-03-26, so Initialize::handle echoes
that version back and ValidateMcpHeaders accepts it, but Server::handle still rejects any JSON
array body with Invalid Request: The message must be an object.. Revision 2025-03-26 says servers
MUST accept JSON-RPC batches (2025-06-18 removed batching). A client that negotiates this version
and then sends a batch, for example initialized plus tools/list, gets an error instead of
responses.
Code

src/mcp/src/Enums/ProtocolVersion.php[R59-63]

      return [
          self::V2025_11_25->value,
          self::V2025_06_18->value,
+            self::V2025_03_26->value,
      ];
Evidence
Initialize returns the requested version whenever it is in initializeSupported(), which now contains
2025-03-26. The server's message handler rejects array_is_list payloads (batches), and nothing under
src/mcp/src/Server handles batches.

src/mcp/src/Enums/ProtocolVersion.php[57-64]
src/mcp/src/Server/Methods/Initialize.php[20-28]
src/mcp/src/Server.php[217-235]

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 server now negotiates MCP protocol 2025-03-26 in the legacy initialize handshake. That revision requires servers to accept JSON-RPC batches, but `Server::handle` rejects any array payload as an invalid request.
## Fix Focus Areas
- src/mcp/src/Server.php[217-235]
- src/mcp/src/Enums/ProtocolVersion.php[57-64]
## Recommended Fix
When the session negotiated 2025-03-26, accept a JSON array body in `Server::handle`. Dispatch each element as a request or notification and return the responses as an array, leaving notifications out. Keep rejecting arrays for later versions. If batching is out of scope, document that 2025-03-26 is accepted without batch support, or leave it out of the initialize negotiation.

ⓘ 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 import rules and skills only from the repositories your team follows

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/ai/src/Tools/Filesystem/CopyFile.php Outdated
Comment thread src/bus/src/UniqueLock.php
Comment thread src/mcp/src/Enums/ProtocolVersion.php
@greptile-apps

greptile-apps Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5 Tier: plus

[High impact] This appears safe to merge; the remaining CopyFile error-handling issue is non-blocking.

Fix All in Claude CodeFindings

  1. P2 Copy errors end agent runs ▶

Summary

Syncs framework, Testbench, AI, Scout and Horizon updates, and adds PHPUnit 13.4 support.

  • Agent runs can stop before the next model call.
  • AI responses keep structured keys and explain incomplete streams.
  • Structured sub-agents return their result to streaming parents.
  • OpenAI-compatible providers can classify text and attachments.
  • Ollama embeddings use the model's native size by default.
  • OpenAI vector stores keep names such as "0".
  • OpenRouter image requests use its images endpoint and keep prompts and references.
  • AI calls retry once after a short Retry-After delay.
  • Filesystem tools reject paths that are missing or not files.
  • Failed unique jobs release only the lock they still own.
  • Callable sortByDesc comparisons put higher values first.
  • Non-incrementing models reject route keys that are not integers.
  • MCP clients and servers check the versions each side supports.
  • Testbench tests can cache views and use shared test helpers.
  • Parallel test runs support PHPUnit 13.4 and ParaTest 7.27.
  • Scout requests identify Hypervel and restore the Meilisearch client agent.
  • Horizon's dashboard dependencies move to updated Vue and source-map-js versions.
  • Server tests match current Swoole response and port behavior.
  • Ported tests and supporting notes keep existing behavior unchanged.

Tests were inspected but not run.

Reviews (2) · Last reviewed commit: "Tighten ported test assertions and add m..." · Reviewed by Greptile

Comment thread src/ai/src/Middleware/Stop.php
Comment on lines +29 to +34
$disk = $this->disk();

$from = (string) $request->string('from');
$to = (string) $request->string('to');

if (! $this->fileExists($disk, $from)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Copy errors end agent runs

CopyFile now resolves its disk and checks the source outside its error handler. A missing disk configuration throws from FilesystemManager and ends the agent run instead of returning the existing Unable to copy... result. Errors from the new fileExists() call escape the same way.

Move both operations inside the try, keeping cancellation errors separate.

Knowledge Base Used: Data access and state

Fix in Claude Code Fix in Codex

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in fc350fc for the existence check, which now shares the copy's error handling, so an error from fileExists() returns a tool message. MoveFile and GetFileUrl now match. Disk resolution stays outside the handler: a missing disk configuration is an application error, so it fails with the framework's own exception instead of being reported to the model.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification and fix. With fileExists() now inside the try block in CopyFile, MoveFile, and GetFileUrl, filesystem errors from those checks are converted into the tool’s error message. Keeping disk resolution outside the handler is appropriate when a missing disk configuration should surface as an application configuration exception. This resolves the concern.

Stop stored its condition closure directly, so queuing an agent with
Stop::when() or Stop::unless() in its runtime middleware failed with
"Serialization of 'Closure' is not allowed". Runtime middleware is
documented as serializable, and Promptable already wraps direct
closures for the same reason. Wrap a closure condition in
SerializableClosure and invoke it directly, since value() does not
call a SerializableClosure. Boolean conditions are unchanged.

Add a regression test that queues an agent with a Stop condition
through a serializing queue fake, and add the missing method title in
the structured-output test.

Validation: composer lint:fix, composer analyse, AgentMiddlewareTest
and the AI suite pass.

Follow-up to laravel/ai#1072.
The OpenAI-compatible classification gateway inherits image mapping
from the OpenAI gateway, which always resolved an image's provider
options for Lab::OpenAI. Options given for the OpenAI-compatible
provider were ignored. Add a providerOptionsKey() method, overridden
by the OpenAI-compatible gateway, following the pattern upstream uses
for Azure's file gateway. The protected mapInput() and mapImage()
signatures are unchanged.

Add a regression test for provider-specific image options.

Validation: composer lint:fix, composer analyse and the OpenAI and
OpenAI-compatible classification tests pass.

Follow-up to laravel/ai#1128.
CopyFile, MoveFile and GetFileUrl checked that the source exists
before entering the error handling around the disk operation. A path
the disk rejects, such as "../report.txt" on a local disk, threw
PathTraversalDetected from fileExists() and ended the agent run, while
the same error from copy() or move() returned a message the model
could act on. Before laravel/ai#1109, CopyFile returned that message.

Move the existence check inside each tool's existing try block.
Resolving the disk still fails fast, and coroutine cancellation still
passes through. Add a regression test covering all three tools on a
real local disk.

The other file tools are unchanged: ReadFile and GetFileMetadata
already handle these errors, and WriteFile, DeleteFile, ListFiles and
FileExists have no operation error handling upstream.

Validation: composer lint:fix, composer analyse and
FilesystemToolsTest pass.

Follow-up to laravel/ai#1109.
sortByMany() negated a callable comparison's result for descending
order. A comparator returning PHP_INT_MIN, such as $a - $b for
PHP_INT_MIN and 0, negates to a float beyond the integer range, which
uasort() treats as 0, so the pair stayed in its original order. Use
0 <=> $result, which reverses the sign for any result without
overflowing.

Add a regression test beside upstream's descending callable test.

Validation: composer lint:fix, composer analyse and
SupportCollectionTest pass.

Follow-up to laravel/framework#62064.
in_parallel_testing() resolved ParallelTesting from
Container::getInstance(). Without an application, such as in a data
provider, that is a bare container that cannot build ParallelTesting,
so the helper threw a BindingResolutionException. Build ParallelTesting
directly from the bare container in that case. With an application it
is still resolved from the container, so registered token resolvers
are honored.

Add a regression test that runs the helper with a bare container and
restores the parallel testing environment afterwards.

Validation: composer lint:fix, composer analyse, HelpersTest and
composer test:testbench pass.

Follow-up to orchestral/testbench-core f503bf9 and #434.
Testbench conflicts with PHPUnit 13.5 and later, so untested minor
releases cannot install. Hypervel's parallel runner builds PHPUnit's
internal PHP process handler, whose constructor changed in 13.4.0, so
an untested minor release can break it in the same way. Require
PHPUnit ~13.4.3 in the root, Testbench and dogfood manifests, and match
the testing package's suggestion and its metadata test.

Extend the PHPUnit #7039 todo entry to lift these constraints, and the
skeleton's, once Testbench's PHPUnit integration is verified on 13.5.
Also correct the method title todo entry: test methods are exempt
whether they use the test prefix or the #[Test] attribute, which
lowers its count.

Validation: composer lint:fix, composer analyse, PackageMetadataTest,
composer test:testbench, composer test:dogfood and the full suite under
composer test:parallel pass.

Follow-up to orchestral/testbench-core#422. Upstream reference:
orchestral/testbench-core 12.x at
7b55ff4c765be6b760ccd6708a0d9b30bd12b9d9.
laravel/mcp#372 added 2025-03-26 to the legacy initialize handshake.
That revision's base protocol requires servers to accept JSON-RPC
batches, which the server does not support. Hypervel supports the
2026-07-28 revision and the 2025-11-25 and 2025-06-18 initialize
handshakes instead, so remove 2025-03-26 from the initialize list.
Clients requesting it are offered 2025-11-25, and the HTTP header check
rejects it as an unsupported version, as before #372.

Keep upstream's other changes: Initialize reads the supported list
once, the client accepts only versions in both its own and the
initialize list, and the handshake-then-tools tests run for the
remaining legacy versions with and without the version header. Restore
the client rejection test to upstream's form, since its extra
2025-03-26 case no longer covers a separate branch.

Record the omission in a source comment, REMOVED comments in both
handshake data providers, the MCP README, a new protocol versions
section in the MCP documentation and the porting guide.

Validation: composer lint:fix, composer analyse, the MCP suite and the
AI MCP tool tests pass.

Upstream reference: laravel/mcp main at
cd20f0afe62ca496678068643f4c09fa6a4e9e16.
The AI SDK documentation said a streaming agent's sub-agents stream as
well, but sub-agents with structured output return their result once
complete. Say so.

The Sentry AI monitoring documentation said tool names and descriptions
are always sent. Only tools that run are recorded, so describe it that
way.
- Assert in the Reverb whisper test that the other application's
  connection receives nothing, so the test checks the isolation it
  describes.
- Assert that live image generation returns non-empty image data. A
  successful base64 decode of an empty string passed the previous
  check.
- Add missing method titles in the unique job and queued handler tests.

Validation: the Reverb server tests, CallQueuedHandlerTest and
UniqueUntilProcessingJobTest pass. The live image tests skip without
provider API keys.
@binaryfire
binaryfire merged commit 10dfaa3 into 0.4 Oct 19, 2026
53 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