Repository navigation
Sync framework, Testbench, MCP and AI updates and support PHPUnit 13.4 - #64
Conversation
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.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
PR Summary by QodoSync upstream packages and add PHPUnit 13.4 support
AI Description
Diagram
High-Level Assessment
Files changed (86)
|
Code Review by Qodo
1.
|
[High impact] This appears safe to merge; the remaining
|
| $disk = $this->disk(); | ||
|
|
||
| $from = (string) $request->string('from'); | ||
| $to = (string) $request->string('to'); | ||
|
|
||
| if (! $this->fileExists($disk, $from)) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Upstream Updates
12abcto12, which would resolve the wrong model, and models with string keys already needkeyType: 'string'for eager loading,whereKey()andforeignIdFor(). Explain the check, record the skipped upstream test and add a regression test that rejects12abcwithout running a query.Queueableuse the owner recorded in their payload.QueueFake's pending results. Hypervel already tracks pending, delayed and reserved jobs separately. Fold the existing oldest-pending test into upstream's stronger version.sortByDesc()receives a comparison callback. Reverse the callback's result with<=>instead of negating it, which overflows forPHP_INT_MIN.PackageManifest::getManifestPath()and use it in the manifest tests. Each parallel worker already builds its manifest in its own runtime copy.#[WithCachedViews]attribute, which runsview:cachebefore a test andview:clearafter 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 checkedfalseinstead of the compiled view's path once the file was gone. Document the attribute.Hypervel\Testbench\PHPUnit\AssertwithmarkTestSkippedUnless()andmarkTestSkippedWhen(), and delegateHandlesAssertionsto it. Add thein_parallel_testing()helper, which also works without an application, such as in data providers. Upstream's per-token.envfiles aren't needed, because each worker's runtime copy has its own.env.~13.4.3, matching upstream's conflict with untested 13.5 releases, and ParaTest^7.27.2, and update the testing package's suggestions.^3.5.43and source-map-js to 1.2.2, and rebuild the dashboard assets.CopyFiletool 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 inMoveFileandGetFileUrl.Stopmiddleware, withStop::when()andStop::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.Retry-Afteron rate-limited or overloaded responses before failing over. The wait uses the fakeableSleepand releases idle database connections first. Document the retry.X-Hypervel-Scoutheader with the framework version on Turbopuffer requests.User-Agentheader.Additional Hypervel Fixes
"0"in OpenRouter image requests. Upstream's filter dropped every falsy value; the required fields now drop only nulls."0"when creating OpenAI and Azure OpenAI stores. Empty optional fields are still omitted.uniqueFor()as no expiry instead of throwing aTypeError.retry()'s sleep closure to return milliseconds as an integer or float, matching what it accepts at runtime.truefrom mockedPort::set()calls and test a failed port configuration, and expect the exception Swoole now throws for malformed SNI settings.#[Test]attribute from the method docblock rule, astest*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
Stopmiddleware, OpenAI-compatible classification, and PHPUnit 13.4 supportStopmiddleware (when/unlessfactories, serializable conditions) that halts agent runs before a model call;TextGenerationLoopreturns an empty response for stopped runs and ends streams withFinishReason::CancelledOpenAiCompatibleClassificationGateway, posting to the/decisionsendpoint with the configured default modelRetry-Afterdelay of 10 seconds or less before failing over, inHandlesFailoverErrorsaspect_ratio/resolutionfields; fixes structured-output decoding for unescaped quotes in object keysWithCachedViewsattribute, conditional skip helpers, andin_parallel_testing()functionFinishReason::Unknowninstead ofLength;CallQueuedHandlerfailed-job cleanup releases unique locks only when ownership is established;Collection::sortBywith descending callable comparators now inverts comparator results; MCP excludes protocol revision 2025-03-26Macroscope summarized 7778e15.