Promote main to release - #2425
Merged
Merged
Conversation
## Summary - push the implicit current branch to `origin` inside the confirmed Create PR operation - require committed changes, preserve explicit/fork head behavior, and retry transient GitHub ref propagation - disclose the push in the dialog and permission prompt, with focused unit and visual coverage - reject diverged remote branches without force-pushing ## Validation - `pnpm test -- git-service pr-create-service permission-gate follow-up-create-pr` — 233/233 passed - full host-side `pnpm test` — 8,550/8,550 passed - typecheck, lint, formatting, demo-site sync, dead-code, oracle, and E2E syntax checks passed - focused `create-pr-dialog.e2e.ts` Electron run — 1/1 passed - updated dialog/completed-state screenshots visually reviewed The sandboxed full test command reached the unit phase but its loopback and fixed-temp-path tests were denied with `EPERM`; the host-side retry passed the complete unit suite. Co-Authored-By: Copse <noreply@copse.dev> Copse-Models: acp:codex-acp#gpt-5.6-sol, acp:claude-acp#opus[1m]
Bumps the npm-minor-patch group with 8 updates:
| Package | From | To |
| --- | --- | --- |
|
[@anthropic-ai/sdk](https://github.com/anthropics/anthropic-sdk-typescript)
| `0.117.1` | `0.122.0` |
|
[@wdio/mocha-framework](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-mocha-framework)
| `9.31.2` | `9.31.5` |
| [eslint](https://github.com/eslint/eslint) | `10.8.1` | `10.9.1` |
| [happy-dom](https://github.com/capricorn86/happy-dom) | `20.11.8` |
`20.12.0` |
| [openai](https://github.com/openai/openai-node) | `7.6.0` | `7.8.0` |
|
[webdriverio](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/webdriverio)
| `9.31.3` | `9.31.5` |
| [zod](https://github.com/colinhacks/zod) | `4.4.3` | `4.5.4` |
|
[@electron-internal/extract-zip](https://github.com/electron/extract-zip)
| `1.0.4` | `1.0.5` |
Updates `@anthropic-ai/sdk` from 0.117.1 to 0.122.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/anthropic-sdk-typescript/releases">@anthropic-ai/sdk's
releases</a>.</em></p>
<blockquote>
<h2>sdk: v0.122.0</h2>
<h2>0.122.0 (2026-08-27)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.121.0...sdk-v0.122.0">sdk-v0.121.0...sdk-v0.122.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> beta files/skills namespaces use GA shapes;
drop dated beta header pins (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/45d693a66bc7fb1af66d9c4e3625f6d6f64bae59">45d693a</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>client:</strong> classify cross-realm DOMException
abort/timeout errors correctly (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/1bd639515e9948c1dc45047ce5cb4fcfc5c436b8">1bd6395</a>)</li>
<li><strong>client:</strong> use configured logger for SSE parse errors
(<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/80eeaf7b01b49ed83abd38bb6841b4ad25b06fe6">80eeaf7</a>)</li>
<li><strong>sessions:</strong> make event accumulator forward-compatible
with new event types (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/410">#410</a>)
(<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/2d084fdaeb6b51a1613f321cd65e2184d55d8d1e">2d084fd</a>)</li>
<li><strong>tools:</strong> let read return a view_range of a file over
the size cap (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/427">#427</a>)
(<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/56b32601f6b97a0d56450f2eba19e58d1bb0202e">56b3260</a>)</li>
<li><strong>uploads:</strong> default bare Blob filename on skills
endpoints (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/422">#422</a>)
(<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/e1116491dd4021d0237d1bb440237677a03045f5">e111649</a>)</li>
<li><strong>webhooks:</strong> require headers to be passed to
<code>unwrap()</code> (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/3230804e63371f535a850d1df6752284643a01fe">3230804</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>internal:</strong> codegen related update (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/c2f327a30d14a4b1ee4cc777a4d0b37f5d44f0af">c2f327a</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li><strong>api:</strong> clarify pagination on the organization
rate-limit list endpoints (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/d6015b90b3e895cd173b7d71038ec71bf467a69b">d6015b9</a>)</li>
<li>document TypeScript 5.0 as the minimum supported version (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/788e7217937471e07af3800ab65172eafbf32e2a">788e721</a>)</li>
</ul>
<h2>sdk: v0.121.0</h2>
<h2>0.121.0 (2026-08-26)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.120.0...sdk-v0.121.0">sdk-v0.120.0...sdk-v0.121.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add <code>updates</code> thinking display mode
(beta) (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/22274eaf968edc62a8b9e1f39a1913f5e04b2ada">22274ea</a>)</li>
<li><strong>api:</strong> add missing anthropic-beta values (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/0779f02666cfc6088c1555ee25e8cd8677e421da">0779f02</a>)</li>
<li><strong>api:</strong> add support for Organization API endpoints (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/8b61e7a4d441d662cd121c94c6d27dbffa5c294a">8b61e7a</a>)</li>
<li><strong>helpers:</strong> support Standard Schema for structured
outputs and tools (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/788007889c048e18f6bdf19d4903f1ac96f66cb0">7880078</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>tools:</strong> keep the tool runner going on pause_turn (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/288">#288</a>)
(<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/f040dc9d39a3ec471a87ac8eb93387fb7d9960c3">f040dc9</a>)</li>
<li><strong>types:</strong> don't reference NodeJS.ProcessEnv from
published types (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/367">#367</a>)
(<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/fd38dfdc48beec5d0d3640b65ac3e467bc73ea4e">fd38dfd</a>)</li>
</ul>
<h3>Chores</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/anthropic-sdk-typescript/blob/main/CHANGELOG.md">@anthropic-ai/sdk's
changelog</a>.</em></p>
<blockquote>
<h2>0.122.0 (2026-08-27)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.121.0...sdk-v0.122.0">sdk-v0.121.0...sdk-v0.122.0</a></p>
<h3>Features</h3>
<ul>
<li>
<p><strong>api:</strong> beta files/skills namespaces use GA shapes;
drop dated beta header pins (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/45d693a66bc7fb1af66d9c4e3625f6d6f64bae59">45d693a</a>)</p>
<p>The beta Files and Skills namespaces (<code>client.beta.files</code>,
<code>client.beta.skills</code>) no longer send the
<code>files-api-2025-04-14</code> / <code>skills-2025-10-02</code>
headers and return the same shapes as <code>client.files</code> /
<code>client.skills</code> (with <code>Beta</code>-prefixed type names).
Requests that still send those headers on raw HTTP keep receiving the
beta shapes.</p>
<p>Changes in the beta namespaces:</p>
<ul>
<li><code>client.beta.skills.delete()</code> now deletes a Skill
together with all of its versions (previously refused while any version
existed). It returns <code>BetaDeletedSkill</code> (was
<code>SkillDeleteResponse</code>).</li>
<li>Beta Messages type <code>BetaSkill</code> (the <code>{type,
skill_id, version}</code> entry in <code>BetaContainer.skills</code>) is
renamed <code>BetaContainerSkill</code>; the request-side
<code>BetaSkillParams</code> keeps its name. <code>BetaSkill</code> now
names the Skill object returned by
<code>client.beta.skills.create()</code> / <code>retrieve()</code> /
<code>list()</code> (replacing <code>SkillCreateResponse</code> /
<code>SkillRetrieveResponse</code> / <code>SkillListResponse</code>),
and skill versions are <code>BetaSkillVersion</code> /
<code>BetaDeletedSkillVersion</code> (replacing
<code>Version*Response</code>).</li>
<li><code>client.beta.files.list()</code> returns a
<code>BetaFileMetadataPageCursor</code>
(<code>PageCursor<BetaFileMetadata></code> with <code>data</code>
/ <code>next_page</code>) and <code>FileListParams</code> paginates with
<code>page</code> / <code>ids</code> (was
<code>BetaFileMetadataPage</code>, a
<code>Page<BetaFileMetadata></code> with <code>data</code>,
<code>has_more</code>, <code>first_id</code>, <code>last_id</code> and
<code>before_id</code> / <code>after_id</code>); <code>for await</code>
auto-pagination is unchanged. <code>BetaSkill</code> uses
<code>display_name</code> (was <code>display_title</code>, also in
<code>SkillCreateParams</code>) and <code>latest_version_id</code> (was
<code>latest_version</code>), and <code>BetaSkillVersion</code> is
addressed by its <code>skver_…</code> <code>id</code> (the
Unix-timestamp <code>version</code> field is gone).</li>
</ul>
<p>Migration guides: <a
href="https://platform.claude.com/docs/en/build-with-claude/files#migrate-from-files-api-2025-04-14">Migrate
from <code>files-api-2025-04-14</code></a> · <a
href="https://platform.claude.com/docs/en/build-with-claude/skills-guide#migrate-from-skills-2025-10-02">Migrate
from <code>skills-2025-10-02</code></a></p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>client:</strong> classify cross-realm DOMException
abort/timeout errors correctly (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/1bd639515e9948c1dc45047ce5cb4fcfc5c436b8">1bd6395</a>)</li>
<li><strong>client:</strong> use configured logger for SSE parse errors
(<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/80eeaf7b01b49ed83abd38bb6841b4ad25b06fe6">80eeaf7</a>)</li>
<li><strong>sessions:</strong> make event accumulator forward-compatible
with new event types (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/410">#410</a>)
(<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/2d084fdaeb6b51a1613f321cd65e2184d55d8d1e">2d084fd</a>)</li>
<li><strong>tools:</strong> let read return a view_range of a file over
the size cap (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/427">#427</a>)
(<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/56b32601f6b97a0d56450f2eba19e58d1bb0202e">56b3260</a>)</li>
<li><strong>uploads:</strong> default bare Blob filename on skills
endpoints (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/422">#422</a>)
(<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/e1116491dd4021d0237d1bb440237677a03045f5">e111649</a>)</li>
<li><strong>webhooks:</strong> require headers to be passed to
<code>unwrap()</code> (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/3230804e63371f535a850d1df6752284643a01fe">3230804</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>internal:</strong> codegen related update (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/c2f327a30d14a4b1ee4cc777a4d0b37f5d44f0af">c2f327a</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li><strong>api:</strong> clarify pagination on the organization
rate-limit list endpoints (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/d6015b90b3e895cd173b7d71038ec71bf467a69b">d6015b9</a>)</li>
<li>document TypeScript 5.0 as the minimum supported version (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/788e7217937471e07af3800ab65172eafbf32e2a">788e721</a>)</li>
</ul>
<h2>0.121.0 (2026-08-26)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.120.0...sdk-v0.121.0">sdk-v0.120.0...sdk-v0.121.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add <code>updates</code> thinking display mode
(beta) (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/22274eaf968edc62a8b9e1f39a1913f5e04b2ada">22274ea</a>)</li>
<li><strong>api:</strong> add missing anthropic-beta values (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/0779f02666cfc6088c1555ee25e8cd8677e421da">0779f02</a>)</li>
<li><strong>api:</strong> add support for Organization API endpoints (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/8b61e7a4d441d662cd121c94c6d27dbffa5c294a">8b61e7a</a>)</li>
<li><strong>helpers:</strong> support Standard Schema for structured
outputs and tools (<a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/788007889c048e18f6bdf19d4903f1ac96f66cb0">7880078</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/7f3898c525b1a1486c85b3808f03935418fed0b8"><code>7f3898c</code></a>
chore: release main</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/956ccb55b69dd087eee2eb892f9056b5455d123a"><code>956ccb5</code></a>
docs(api): clarify pagination on the organization rate-limit list
endpoints</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/2131d80a5e6fda409d3b81fda6a7b4572970ab5d"><code>2131d80</code></a>
fix(tools): let read return a view_range of a file over the size cap (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/427">#427</a>)</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/f830c8f561e6e7d2aca348ae75adeb12416b4597"><code>f830c8f</code></a>
fix(aws,bedrock): prefer env credentials over AWS_PROFILE (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/436">#436</a>)</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/4d50ddfb4f4dcc3baa37ecb0130053fde336d2cd"><code>4d50ddf</code></a>
test(ecosystem): expect cross-realm timeout errors to classify
correctly</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/acdd669d75026add9cfb2568cba80c1e80be838b"><code>acdd669</code></a>
fix(client): classify cross-realm DOMException abort/timeout errors
correctly</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/0a14622ea99bcd1e14d9eae83252adf0bb90276b"><code>0a14622</code></a>
test(tools): pin that read, edit and the memory tool preserve CRLF and
CR lin...</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/17035dea97538f1071dc341de93397934e12dbc6"><code>17035de</code></a>
docs: document TypeScript 5.0 as the minimum supported version</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/2f95d8a6f7774fc1b98fdb59e1881d1f8204e589"><code>2f95d8a</code></a>
fix(client): use configured logger for SSE parse errors</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-typescript/commit/33f510300959ce8ce2fdd8cc1b6192a189b209f7"><code>33f5103</code></a>
feat(api): beta files/skills namespaces use GA shapes; drop dated beta
header...</li>
<li>Additional commits viewable in <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.117.1...sdk-v0.122.0">compare
view</a></li>
</ul>
</details>
<br />
Updates `@wdio/mocha-framework` from 9.31.2 to 9.31.5
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/webdriverio/webdriverio/releases">@wdio/mocha-framework's
releases</a>.</em></p>
<blockquote>
<h2>v9.31.5 (2026-08-29)</h2>
<h4>:rocket: New Feature</h4>
<ul>
<li><code>wdio-utils</code>, <code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15538">#15538</a>
feat: UNSTABLE Review Multi-remote <code>select</code> implementations
and remove filter (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
</ul>
</li>
</ul>
<h4>:bug: Bug Fix</h4>
<ul>
<li><code>wdio-utils</code>, <code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15538">#15538</a>
feat: UNSTABLE Review Multi-remote <code>select</code> implementations
and remove filter (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
</ul>
</li>
<li><code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15535">#15535</a>
fix(webdriverio): handle BiDi no such node errors as stale (<a
href="https://github.com/Fnine59"><code>@Fnine59</code></a>)</li>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15537">#15537</a>
fix(webdriverio): require all classic cookie filter attributes (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
</ul>
</li>
</ul>
<h4>:memo: Documentation</h4>
<ul>
<li><code>wdio-protocols</code>, <code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15536">#15536</a>
Docs/actions api guidance (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
</ul>
</li>
</ul>
<h4>Committers: 3</h4>
<ul>
<li>David Prevost (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
<li>Fnine59 (<a
href="https://github.com/Fnine59"><code>@Fnine59</code></a>)</li>
<li>Martsin Lazouski (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
</ul>
<h2>v9.31.4 (2026-08-27)</h2>
<h4>:rocket: New Feature</h4>
<ul>
<li><code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15532">#15532</a>
feat: Beta ElementArray under feature flag for
<code>multiRemote.$$()</code> e.g. MultiRemoteElement[] (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
</ul>
</li>
<li><code>wdio-allure-reporter</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15278">#15278</a>
feat(wdio-allure-reporter): add support for global errors and global
attachments (<a
href="https://github.com/todti"><code>@todti</code></a>)</li>
</ul>
</li>
</ul>
<h4>:bug: Bug Fix</h4>
<ul>
<li><code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15478">#15478</a>
fix(webdriverio): track document element per context (<a
href="https://github.com/Badisi"><code>@Badisi</code></a>)</li>
</ul>
</li>
<li><code>webdriver</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15516">#15516</a>
fix(webdriver): serialize IPv6 BiDi WebSocket candidates correctly (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
</ul>
</li>
</ul>
<h4>:memo: Documentation</h4>
<ul>
<li><code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15531">#15531</a>
docs(webdriverio): document the $$ async iterator methods (<a
href="https://github.com/devangpratap"><code>@devangpratap</code></a>)</li>
</ul>
</li>
</ul>
<h4>Committers: 5</h4>
<ul>
<li>Alex (<a
href="https://github.com/todti"><code>@todti</code></a>)</li>
<li>David Prevost (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
<li>Martsin Lazouski (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
<li><a href="https://github.com/Badisi"><code>@Badisi</code></a></li>
<li><a
href="https://github.com/devangpratap"><code>@devangpratap</code></a></li>
</ul>
<h2>v9.31.3 (2026-08-25)</h2>
<h4>:bug: Bug Fix</h4>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md">@wdio/mocha-framework's
changelog</a>.</em></p>
<blockquote>
<h2>v9.31.5 (2026-08-29)</h2>
<h4>:rocket: New Feature</h4>
<ul>
<li><code>wdio-utils</code>, <code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15538">#15538</a>
feat: UNSTABLE Review Multi-remote <code>select</code> implementations
and remove filter (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
</ul>
</li>
</ul>
<h4>:bug: Bug Fix</h4>
<ul>
<li><code>wdio-utils</code>, <code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15538">#15538</a>
feat: UNSTABLE Review Multi-remote <code>select</code> implementations
and remove filter (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
</ul>
</li>
<li><code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15535">#15535</a>
fix(webdriverio): handle BiDi no such node errors as stale (<a
href="https://github.com/Fnine59"><code>@Fnine59</code></a>)</li>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15537">#15537</a>
fix(webdriverio): require all classic cookie filter attributes (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
</ul>
</li>
</ul>
<h4>:memo: Documentation</h4>
<ul>
<li><code>wdio-protocols</code>, <code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15536">#15536</a>
Docs/actions api guidance (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
</ul>
</li>
</ul>
<h4>Committers: 3</h4>
<ul>
<li>David Prevost (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
<li>Fnine59 (<a
href="https://github.com/Fnine59"><code>@Fnine59</code></a>)</li>
<li>Martsin Lazouski (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
</ul>
<h2>v9.31.4 (2026-08-27)</h2>
<h4>:rocket: New Feature</h4>
<ul>
<li><code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15532">#15532</a>
feat: Beta ElementArray under feature flag for
<code>multiRemote.$()</code> e.g. MultiRemoteElement[] (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
</ul>
</li>
<li><code>wdio-allure-reporter</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15278">#15278</a>
feat(wdio-allure-reporter): add support for global errors and global
attachments (<a
href="https://github.com/todti"><code>@todti</code></a>)</li>
</ul>
</li>
</ul>
<h4>:bug: Bug Fix</h4>
<ul>
<li><code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15478">#15478</a>
fix(webdriverio): track document element per context (<a
href="https://github.com/Badisi"><code>@Badisi</code></a>)</li>
</ul>
</li>
<li><code>webdriver</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15516">#15516</a>
fix(webdriver): serialize IPv6 BiDi WebSocket candidates correctly (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
</ul>
</li>
</ul>
<h4>:memo: Documentation</h4>
<ul>
<li><code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15531">#15531</a>
docs(webdriverio): document the $ async iterator methods (<a
href="https://github.com/devangpratap"><code>@devangpratap</code></a>)</li>
</ul>
</li>
</ul>
<h4>Committers: 5</h4>
<ul>
<li>Alex (<a
href="https://github.com/todti"><code>@todti</code></a>)</li>
<li>David Prevost (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
<li>Martsin Lazouski (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
<li><a href="https://github.com/Badisi"><code>@Badisi</code></a></li>
<li><a
href="https://github.com/devangpratap"><code>@devangpratap</code></a></li>
</ul>
<h2>v9.31.3 (2026-08-25)</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/webdriverio/webdriverio/commit/4f39140405a3a54a44c391f97a6de1fc3af2d33d"><code>4f39140</code></a>
v9.31.5</li>
<li>See full diff in <a
href="https://github.com/webdriverio/webdriverio/commits/v9.31.5/packages/wdio-mocha-framework">compare
view</a></li>
</ul>
</details>
<br />
Updates `eslint` from 10.8.1 to 10.9.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/eslint/eslint/releases">eslint's
releases</a>.</em></p>
<blockquote>
<h2>v10.9.1</h2>
<h2>Bug Fixes</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/1e641c919fc1421493bf913feb607896982451a3"><code>1e641c9</code></a>
fix: no-loss-of-precision false positive with trailing decimal point (<a
href="https://redirect.github.com/eslint/eslint/issues/21251">#21251</a>)
(Aleksandr Shoronov)</li>
</ul>
<h2>Documentation</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/ad74a8dada2aaa17bfd0b8cc7b4119ff7a8ac04b"><code>ad74a8d</code></a>
docs: add deprecation steps for EOL package versions (<a
href="https://redirect.github.com/eslint/eslint/issues/21248">#21248</a>)
(Francesco Trotta)</li>
</ul>
<h2>Chores</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/3c3ae53a43721162f0db76c69665ebd9d752ea52"><code>3c3ae53</code></a>
chore: update ecosystem plugins (<a
href="https://redirect.github.com/eslint/eslint/issues/21249">#21249</a>)
(ESLint Bot)</li>
</ul>
<h2>v10.9.0</h2>
<h2>Features</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/08de88e50294c4e01f6cae97eceeb578da55792b"><code>08de88e</code></a>
feat: handle underflow in no-loss-of-precision (<a
href="https://redirect.github.com/eslint/eslint/issues/21218">#21218</a>)
(Rithish S)</li>
<li><a
href="https://github.com/eslint/eslint/commit/55db4791120ae591d88089c43127b7b0e16866d4"><code>55db479</code></a>
feat: add checkConditionalExpressions to
<code>no-unmodified-loop-condition</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21175">#21175</a>)
(sethamus)</li>
</ul>
<h2>Bug Fixes</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/2ba302554e7a24e9909bbdd026fd0c2d1d0d8638"><code>2ba3025</code></a>
fix: prevent unsafe <code>no-var</code> autofix with hoisted functions
(<a
href="https://redirect.github.com/eslint/eslint/issues/21213">#21213</a>)
(sethamus)</li>
<li><a
href="https://github.com/eslint/eslint/commit/8e6962219a605c5f5add10953aa31027da839194"><code>8e69622</code></a>
fix: Prevent no-var autofix when var is shadowed by catch parameter (<a
href="https://redirect.github.com/eslint/eslint/issues/21204">#21204</a>)
(Yang Hyeonjong)</li>
<li><a
href="https://github.com/eslint/eslint/commit/684b57972e1ddf25e076fb36189c60bbcacee635"><code>684b579</code></a>
fix: prefer-template invalid autofix creates a tagged template call (<a
href="https://redirect.github.com/eslint/eslint/issues/21207">#21207</a>)
(김채영)</li>
</ul>
<h2>Documentation</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/9ef407a3b051e74f50dc7fb8914e2bd89b3e5e53"><code>9ef407a</code></a>
docs: use eslint.config.* wherever config file names are listed (<a
href="https://redirect.github.com/eslint/eslint/issues/21216">#21216</a>)
(Marry (Subin Yang))</li>
<li><a
href="https://github.com/eslint/eslint/commit/87f66f4435c4df7f4f6815c939d153196ec03e3c"><code>87f66f4</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/585ef37516c0dc29ddb91ce2a2cdcc46fdbbd610"><code>585ef37</code></a>
docs: update architecture documentation (<a
href="https://redirect.github.com/eslint/eslint/issues/21112">#21112</a>)
(Francesco Trotta)</li>
<li><a
href="https://github.com/eslint/eslint/commit/f3993b0547bace7370e9728ee7408af49d367d76"><code>f3993b0</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/ffc87d6234b2aa4335eec069e5c4d6ac04832b9e"><code>ffc87d6</code></a>
docs: fix broken links in Further Reading sections (<a
href="https://redirect.github.com/eslint/eslint/issues/21203">#21203</a>)
(Minsu)</li>
<li><a
href="https://github.com/eslint/eslint/commit/1a761e1d11b011fcb6bee181231a51010c500e4d"><code>1a761e1</code></a>
docs: update moved JSX specification links (<a
href="https://redirect.github.com/eslint/eslint/issues/21198">#21198</a>)
(Imran Mustafa)</li>
<li><a
href="https://github.com/eslint/eslint/commit/4d00ca4064ae0d1a75b604a16c68ab9f386ad388"><code>4d00ca4</code></a>
docs: update ESLint peer dependency to <code>^10.0.0</code> in shareable
configs (<a
href="https://redirect.github.com/eslint/eslint/issues/21202">#21202</a>)
(lumir)</li>
<li><a
href="https://github.com/eslint/eslint/commit/510d1a2e87bc197219f42e195ddb638d2b183a5a"><code>510d1a2</code></a>
docs: Update README (GitHub Actions Bot)</li>
</ul>
<h2>Chores</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/899dbf131ce12a194b394bb8d67307df23509d17"><code>899dbf1</code></a>
chore: update github/codeql-action action to v4.37.7 (<a
href="https://redirect.github.com/eslint/eslint/issues/21243">#21243</a>)
(renovate[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/9aa38732177935bd1d7f1493732c0b67666be28a"><code>9aa3873</code></a>
chore: update ecosystem plugins (<a
href="https://redirect.github.com/eslint/eslint/issues/21235">#21235</a>)
(ESLint Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/dc1e7a8416937edefe04cf836ee202a6fc03bedd"><code>dc1e7a8</code></a>
chore: update ecosystem plugins (<a
href="https://redirect.github.com/eslint/eslint/issues/21208">#21208</a>)
(ESLint Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/f878d212e9622da9513bcd60d2aedb2e8bb4fc8b"><code>f878d21</code></a>
ci: bump pnpm/action-setup from 6.0.9 to 6.0.10 (<a
href="https://redirect.github.com/eslint/eslint/issues/21200">#21200</a>)
(dependabot[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/4891e50aceadb0e886ad7d8ab5ae2beab563de85"><code>4891e50</code></a>
ci: bump github/codeql-action from 4.37.4 to 4.37.6 (<a
href="https://redirect.github.com/eslint/eslint/issues/21199">#21199</a>)
(dependabot[bot])</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/5c8c2417b9ff462f2dc4e54a062c59135b45b845"><code>5c8c241</code></a>
10.9.1</li>
<li><a
href="https://github.com/eslint/eslint/commit/a7f3b7ddca7de8464995707d1bbac3ca91090015"><code>a7f3b7d</code></a>
Build: changelog update for 10.9.1</li>
<li><a
href="https://github.com/eslint/eslint/commit/1e641c919fc1421493bf913feb607896982451a3"><code>1e641c9</code></a>
fix: no-loss-of-precision false positive with trailing decimal point (<a
href="https://redirect.github.com/eslint/eslint/issues/21251">#21251</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/ad74a8dada2aaa17bfd0b8cc7b4119ff7a8ac04b"><code>ad74a8d</code></a>
docs: add deprecation steps for EOL package versions (<a
href="https://redirect.github.com/eslint/eslint/issues/21248">#21248</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/3c3ae53a43721162f0db76c69665ebd9d752ea52"><code>3c3ae53</code></a>
chore: update ecosystem plugins (<a
href="https://redirect.github.com/eslint/eslint/issues/21249">#21249</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/c27bc926e496985eb7911c09eb60914b2e4b5d0f"><code>c27bc92</code></a>
10.9.0</li>
<li><a
href="https://github.com/eslint/eslint/commit/fa831d95b326e6d23671d9b2df1ea5dbc64f6f34"><code>fa831d9</code></a>
Build: changelog update for 10.9.0</li>
<li><a
href="https://github.com/eslint/eslint/commit/899dbf131ce12a194b394bb8d67307df23509d17"><code>899dbf1</code></a>
chore: update github/codeql-action action to v4.37.7 (<a
href="https://redirect.github.com/eslint/eslint/issues/21243">#21243</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/08de88e50294c4e01f6cae97eceeb578da55792b"><code>08de88e</code></a>
feat: handle underflow in no-loss-of-precision (<a
href="https://redirect.github.com/eslint/eslint/issues/21218">#21218</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/9ef407a3b051e74f50dc7fb8914e2bd89b3e5e53"><code>9ef407a</code></a>
docs: use eslint.config.* wherever config file names are listed (<a
href="https://redirect.github.com/eslint/eslint/issues/21216">#21216</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/eslint/eslint/compare/v10.8.1...v10.9.1">compare
view</a></li>
</ul>
</details>
<br />
Updates `happy-dom` from 20.11.8 to 20.12.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/capricorn86/happy-dom/releases">happy-dom's
releases</a>.</em></p>
<blockquote>
<h2>v20.12.0</h2>
<h3>:art: Features</h3>
<ul>
<li>Adds support for <code>Animation</code>,
<code>AnimationTimeline</code>, <code>DocumentTimeline</code> and
<code>KeyframeEffect</code> - By <strong><a
href="https://github.com/capricorn86"><code>@capricorn86</code></a></strong></li>
<li>Adds support for <code>Element.animate()</code> - By <strong><a
href="https://github.com/capricorn86"><code>@capricorn86</code></a></strong></li>
</ul>
<h2>v20.11.15</h2>
<h3>:construction_worker_man: Patch fixes</h3>
<ul>
<li>Fixes issue with upgrading custom element caused by previous release
- By <strong><a
href="https://github.com/capricorn86"><code>@capricorn86</code></a></strong>
in task <a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2331">#2331</a></li>
</ul>
<h2>v20.11.14</h2>
<h3>:construction_worker_man: Patch fixes</h3>
<ul>
<li>Change prototype of element to the custom element when upgraded to
keep the reference - By <strong><a
href="https://github.com/capricorn86"><code>@capricorn86</code></a></strong>
in task <a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2331">#2331</a>
<ul>
<li>The previous solution replaced the element with the custom element
and copied it's properties, but this made any reference to the original
element invalid</li>
</ul>
</li>
</ul>
<h2>v20.11.13</h2>
<h3>:construction_worker_man: Patch fixes</h3>
<ul>
<li>Copy event listeners when upgrading a custom element - By <strong><a
href="https://github.com/capricorn86"><code>@capricorn86</code></a></strong>
in task <a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2329">#2329</a></li>
</ul>
<h2>v20.11.12</h2>
<h3>:construction_worker_man: Patch fixes</h3>
<ul>
<li>Add check for if the node is still a parent during disconnect - By
<strong><a
href="https://github.com/capricorn86"><code>@capricorn86</code></a></strong>
in task <a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2322">#2322</a></li>
</ul>
<h2>v20.11.11</h2>
<h3>:construction_worker_man: Patch fixes</h3>
<ul>
<li>Use proxy as parent node in Node.connectedToNode - By <strong><a
href="https://github.com/diegohaz"><code>@diegohaz</code></a></strong>
in task <a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2253">#2253</a></li>
</ul>
<h2>v20.11.10</h2>
<h3>:construction_worker_man: Patch fixes</h3>
<ul>
<li>Return default validationMessage for built-in constraint violations
- By <strong><a
href="https://github.com/mixelburg"><code>@mixelburg</code></a></strong>
in task <a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2187">#2187</a></li>
</ul>
<h2>v20.11.9</h2>
<h3>:construction_worker_man: Patch fixes</h3>
<ul>
<li>Make sure that the element has been upgraded before invoking
reaction callback in custom elements - By <strong><a
href="https://github.com/capricorn86"><code>@capricorn86</code></a></strong>
in task <a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2317">#2317</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/capricorn86/happy-dom/commit/4b0af0ce51268e897bf129c43d29644555b7bb48"><code>4b0af0c</code></a>
feat: Adds support for Element.animate (<a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2335">#2335</a>)</li>
<li><a
href="https://github.com/capricorn86/happy-dom/commit/e1a8af5cb664bf76d72404b86c499e739a4e4ed2"><code>e1a8af5</code></a>
fix: <a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2331">#2331</a>
Fixes issue with upgrading custom element caused by previous rel...</li>
<li><a
href="https://github.com/capricorn86/happy-dom/commit/096317b80d642c919f6641b23e4154a143e3c9cf"><code>096317b</code></a>
fix: <a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2331">#2331</a>
Change prototype of element to the custom element when upgraded ...</li>
<li><a
href="https://github.com/capricorn86/happy-dom/commit/9ff4b4752c2c00b5bb03b309d40fa82f05c15de7"><code>9ff4b47</code></a>
fix: <a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2329">#2329</a>
Copy event listeners when upgrading a custom element (<a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2330">#2330</a>)</li>
<li><a
href="https://github.com/capricorn86/happy-dom/commit/3d282e5ba993b33649d2afe9d3367b0a10cbff5b"><code>3d282e5</code></a>
fix: <a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2322">#2322</a>
Add check for if the node is still a parent during disconnect (#...</li>
<li><a
href="https://github.com/capricorn86/happy-dom/commit/64e2c774cadbb8eda5416c1e2bcca5006d1b5df9"><code>64e2c77</code></a>
fix: <a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2253">#2253</a>
Use proxy as parent node in Node.connectedToNode() (<a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2254">#2254</a>)</li>
<li><a
href="https://github.com/capricorn86/happy-dom/commit/a00ec699ffcaaabdb6e6b7707df1ce832e39d00f"><code>a00ec69</code></a>
fix: <a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2319">#2319</a>
Removing a child incorrectly calls flow for connecting node (<a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2321">#2321</a>)</li>
<li><a
href="https://github.com/capricorn86/happy-dom/commit/152b5d8a9e2690ba2039867f42e3e2a8e9b7c274"><code>152b5d8</code></a>
fix: <a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2319">#2319</a>
Removing a child incorrectly calls flow for connecting node (<a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2320">#2320</a>)</li>
<li><a
href="https://github.com/capricorn86/happy-dom/commit/1100b9c30182b63121dfa826b2a7fc7a8c76dd6e"><code>1100b9c</code></a>
fix: <a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2187">#2187</a>
Return default validationMessage for built-in constraint violati...</li>
<li><a
href="https://github.com/capricorn86/happy-dom/commit/189379545b74351a2f00c88a37c4732a3e185e41"><code>1893795</code></a>
fix: <a
href="https://redirect.github.com/capricorn86/happy-dom/issues/2317">#2317</a>
Make sure that the element has been upgraded before invoking rea...</li>
<li>See full diff in <a
href="https://github.com/capricorn86/happy-dom/compare/v20.11.8...v20.12.0">compare
view</a></li>
</ul>
</details>
<br />
Updates `openai` from 7.6.0 to 7.8.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/openai/openai-node/releases">openai's
releases</a>.</em></p>
<blockquote>
<h2>v7.8.0</h2>
<h2><a
href="https://github.com/openai/openai-node/compare/v7.7.0...v7.8.0">7.8.0</a>
(2026-08-27)</h2>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add compute_units to Responses and Chat
Completions usage (<a
href="https://redirect.github.com/openai/openai-node/issues/2505">#2505</a>)
(<a
href="https://github.com/openai/openai-node/commit/f9b131388a270451eec7242aa0d302e2135b1d3e">f9b1313</a>)</li>
<li><strong>api:</strong> add default WebSocket User-Agent and audit log
events (<a
href="https://redirect.github.com/openai/openai-node/issues/2504">#2504</a>)
(<a
href="https://github.com/openai/openai-node/commit/9b2f0892c9fde399163bea59d4aa9bc4ba0ae03e">9b2f089</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>auth:</strong> clamp the workload-identity refresh buffer to
the token lifetime (<a
href="https://redirect.github.com/openai/openai-node/issues/2490">#2490</a>)
(<a
href="https://github.com/openai/openai-node/commit/76b73a9c73aafe11c60a425a82898705f1db966d">76b73a9</a>)</li>
<li><strong>auth:</strong> secure first-class X.509 workload credentials
(<a
href="https://redirect.github.com/openai/openai-node/issues/2479">#2479</a>)
(<a
href="https://github.com/openai/openai-node/commit/1b36c19c011bcb9775a5cecb0d8a1bd072b74cd6">1b36c19</a>)</li>
<li><strong>deps:</strong> harden dependency and release boundaries (<a
href="https://redirect.github.com/openai/openai-node/issues/2484">#2484</a>)
(<a
href="https://github.com/openai/openai-node/commit/b21ff456e7bd578547c456e5f7abd1d494a02090">b21ff45</a>)</li>
<li><strong>events:</strong> settle WebSocket waiters when listeners
throw (<a
href="https://redirect.github.com/openai/openai-node/issues/2491">#2491</a>)
(<a
href="https://github.com/openai/openai-node/commit/b5a13a7dc8c95669440ec87662bcae8227b398d9">b5a13a7</a>)</li>
<li>restore native browser ESM imports (<a
href="https://redirect.github.com/openai/openai-node/issues/2495">#2495</a>)
(<a
href="https://github.com/openai/openai-node/commit/c8cab1e90eb5daf05d17d5c90492e6728afa91b7">c8cab1e</a>)</li>
<li><strong>uploads:</strong> detect multipart bodies from own
properties only (<a
href="https://redirect.github.com/openai/openai-node/issues/2492">#2492</a>)
(<a
href="https://github.com/openai/openai-node/commit/559ffc8079d0ae716bbf7915305e7db6ecc016a1">559ffc8</a>)</li>
</ul>
<h2>v7.7.0</h2>
<h2><a
href="https://github.com/openai/openai-node/compare/v7.6.0...v7.7.0">7.7.0</a>
(2026-08-26)</h2>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> make function call output call IDs optional
(<a
href="https://redirect.github.com/openai/openai-node/issues/2488">#2488</a>)
(<a
href="https://github.com/openai/openai-node/commit/b932ae62bc4054da760c1f82329b1cc24bc05fbe">b932ae6</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>streaming:</strong> honor cancellation while decoding SSE
responses (<a
href="https://redirect.github.com/openai/openai-node/issues/2487">#2487</a>)
(<a
href="https://github.com/openai/openai-node/commit/ababe129507a48d6c4d2be12bbef2ae442c1632f">ababe12</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/openai/openai-node/blob/main/CHANGELOG.md">openai's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/openai/openai-node/compare/v7.7.0...v7.8.0">7.8.0</a>
(2026-08-27)</h2>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add compute_units to Responses and Chat
Completions usage (<a
href="https://redirect.github.com/openai/openai-node/issues/2505">#2505</a>)
(<a
href="https://github.com/openai/openai-node/commit/f9b131388a270451eec7242aa0d302e2135b1d3e">f9b1313</a>)</li>
<li><strong>api:</strong> add default WebSocket User-Agent and audit log
events (<a
href="https://redirect.github.com/openai/openai-node/issues/2504">#2504</a>)
(<a
href="https://github.com/openai/openai-node/commit/9b2f0892c9fde399163bea59d4aa9bc4ba0ae03e">9b2f089</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>auth:</strong> clamp the workload-identity refresh buffer to
the token lifetime (<a
href="https://redirect.github.com/openai/openai-node/issues/2490">#2490</a>)
(<a
href="https://github.com/openai/openai-node/commit/76b73a9c73aafe11c60a425a82898705f1db966d">76b73a9</a>)</li>
<li><strong>auth:</strong> secure first-class X.509 workload credentials
(<a
href="https://redirect.github.com/openai/openai-node/issues/2479">#2479</a>)
(<a
href="https://github.com/openai/openai-node/commit/1b36c19c011bcb9775a5cecb0d8a1bd072b74cd6">1b36c19</a>)</li>
<li><strong>deps:</strong> harden dependency and release boundaries (<a
href="https://redirect.github.com/openai/openai-node/issues/2484">#2484</a>)
(<a
href="https://github.com/openai/openai-node/commit/b21ff456e7bd578547c456e5f7abd1d494a02090">b21ff45</a>)</li>
<li><strong>events:</strong> settle WebSocket waiters when listeners
throw (<a
href="https://redirect.github.com/openai/openai-node/issues/2491">#2491</a>)
(<a
href="https://github.com/openai/openai-node/commit/b5a13a7dc8c95669440ec87662bcae8227b398d9">b5a13a7</a>)</li>
<li>restore native browser ESM imports (<a
href="https://redirect.github.com/openai/openai-node/issues/2495">#2495</a>)
(<a
href="https://github.com/openai/openai-node/commit/c8cab1e90eb5daf05d17d5c90492e6728afa91b7">c8cab1e</a>)</li>
<li><strong>uploads:</strong> detect multipart bodies from own
properties only (<a
href="https://redirect.github.com/openai/openai-node/issues/2492">#2492</a>)
(<a
href="https://github.com/openai/openai-node/commit/559ffc8079d0ae716bbf7915305e7db6ecc016a1">559ffc8</a>)</li>
</ul>
<h2><a
href="https://github.com/openai/openai-node/compare/v7.6.0...v7.7.0">7.7.0</a>
(2026-08-26)</h2>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> make function call output call IDs optional
(<a
href="https://redirect.github.com/openai/openai-node/issues/2488">#2488</a>)
(<a
href="https://github.com/openai/openai-node/commit/b932ae62bc4054da760c1f82329b1cc24bc05fbe">b932ae6</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>streaming:</strong> honor cancellation while decoding SSE
responses (<a
href="https://redirect.github.com/openai/openai-node/issues/2487">#2487</a>)
(<a
href="https://github.com/openai/openai-node/commit/ababe129507a48d6c4d2be12bbef2ae442c1632f">ababe12</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/openai/openai-node/commit/31fae5367989ec503a4369a7ad7cb959b25b2878"><code>31fae53</code></a>
release: 7.8.0 (<a
href="https://redirect.github.com/openai/openai-node/issues/2493">#2493</a>)</li>
<li><a
href="https://github.com/openai/openai-node/commit/f9b131388a270451eec7242aa0d302e2135b1d3e"><code>f9b1313</code></a>
feat(api): add compute_units to Responses and Chat Completions usage (<a
href="https://redirect.github.com/openai/openai-node/issues/2505">#2505</a>)</li>
<li><a
href="https://github.com/openai/openai-node/commit/b21ff456e7bd578547c456e5f7abd1d494a02090"><code>b21ff45</code></a>
fix(deps): harden dependency and release boundaries (<a
href="https://redirect.github.com/openai/openai-node/issues/2484">#2484</a>)</li>
<li><a
href="https://github.com/openai/openai-node/commit/a4a80ebbb7491b015de63c2f4f07348d570faf9b"><code>a4a80eb</code></a>
test: exercise direct browser imports without credentials (<a
href="https://redirect.github.com/openai/openai-node/issues/2501">#2501</a>)</li>
<li><a
href="https://github.com/openai/openai-node/commit/c8cab1e90eb5daf05d17d5c90492e6728afa91b7"><code>c8cab1e</code></a>
fix: restore native browser ESM imports (<a
href="https://redirect.github.com/openai/openai-node/issues/2495">#2495</a>)</li>
<li><a
href="https://github.com/openai/openai-node/commit/d691a1067630680eb00e9c9e04b65a709ae6a4b0"><code>d691a10</code></a>
test: execute webpack browser bundle without live credentials (<a
href="https://redirect.github.com/openai/openai-node/issues/2498">#2498</a>)</li>
<li><a
href="https://github.com/openai/openai-node/commit/7ef99c48f76146a409763bb28f388837a2002bbd"><code>7ef99c4</code></a>
test: verify packed browser bundles target ES2020 (<a
href="https://redirect.github.com/openai/openai-node/issues/2496">#2496</a>)</li>
<li><a
href="https://github.com/openai/openai-node/commit/b86efacacbe1fa8374e99188161884cfd25b594e"><code>b86efac</code></a>
ci: require keyless ecosystem checks on release PRs (<a
href="https://redirect.github.com/openai/openai-node/issues/2497">#2497</a>)</li>
<li><a
href="https://github.com/openai/openai-node/commit/08f201c55556bc1c8086295d8e76842997f7fea5"><code>08f201c</code></a>
ci: gate npm publication on browser-compatible imports (<a
href="https://redirect.github.com/openai/openai-node/issues/2500">#2500</a>)</li>
<li><a
href="https://github.com/openai/openai-node/commit/44af438bf0680e0a89a2f4697a854bfb6c18a046"><code>44af438</code></a>
test: cover webpack ESM and CommonJS package entrypoints (<a
href="https://redirect.github.com/openai/openai-node/issues/2499">#2499</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/openai/openai-node/compare/v7.6.0...v7.8.0">compare
view</a></li>
</ul>
</details>
<br />
Updates `webdriverio` from 9.31.3 to 9.31.5
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/webdriverio/webdriverio/releases">webdriverio's
releases</a>.</em></p>
<blockquote>
<h2>v9.31.5 (2026-08-29)</h2>
<h4>:rocket: New Feature</h4>
<ul>
<li><code>wdio-utils</code>, <code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15538">#15538</a>
feat: UNSTABLE Review Multi-remote <code>select</code> implementations
and remove filter (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
</ul>
</li>
</ul>
<h4>:bug: Bug Fix</h4>
<ul>
<li><code>wdio-utils</code>, <code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15538">#15538</a>
feat: UNSTABLE Review Multi-remote <code>select</code> implementations
and remove filter (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
</ul>
</li>
<li><code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15535">#15535</a>
fix(webdriverio): handle BiDi no such node errors as stale (<a
href="https://github.com/Fnine59"><code>@Fnine59</code></a>)</li>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15537">#15537</a>
fix(webdriverio): require all classic cookie filter attributes (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
</ul>
</li>
</ul>
<h4>:memo: Documentation</h4>
<ul>
<li><code>wdio-protocols</code>, <code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15536">#15536</a>
Docs/actions api guidance (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
</ul>
</li>
</ul>
<h4>Committers: 3</h4>
<ul>
<li>David Prevost (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
<li>Fnine59 (<a
href="https://github.com/Fnine59"><code>@Fnine59</code></a>)</li>
<li>Martsin Lazouski (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
</ul>
<h2>v9.31.4 (2026-08-27)</h2>
<h4>:rocket: New Feature</h4>
<ul>
<li><code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15532">#15532</a>
feat: Beta ElementArray under feature flag for
<code>multiRemote.$$()</code> e.g. MultiRemoteElement[] (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
</ul>
</li>
<li><code>wdio-allure-reporter</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15278">#15278</a>
feat(wdio-allure-reporter): add support for global errors and global
attachments (<a
href="https://github.com/todti"><code>@todti</code></a>)</li>
</ul>
</li>
</ul>
<h4>:bug: Bug Fix</h4>
<ul>
<li><code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15478">#15478</a>
fix(webdriverio): track document element per context (<a
href="https://github.com/Badisi"><code>@Badisi</code></a>)</li>
</ul>
</li>
<li><code>webdriver</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15516">#15516</a>
fix(webdriver): serialize IPv6 BiDi WebSocket candidates correctly (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
</ul>
</li>
</ul>
<h4>:memo: Documentation</h4>
<ul>
<li><code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15531">#15531</a>
docs(webdriverio): document the $$ async iterator methods (<a
href="https://github.com/devangpratap"><code>@devangpratap</code></a>)</li>
</ul>
</li>
</ul>
<h4>Committers: 5</h4>
<ul>
<li>Alex (<a
href="https://github.com/todti"><code>@todti</code></a>)</li>
<li>David Prevost (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
<li>Martsin Lazouski (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
<li><a href="https://github.com/Badisi"><code>@Badisi</code></a></li>
<li><a
href="https://github.com/devangpratap"><code>@devangpratap</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md">webdriverio's
changelog</a>.</em></p>
<blockquote>
<h2>v9.31.5 (2026-08-29)</h2>
<h4>:rocket: New Feature</h4>
<ul>
<li><code>wdio-utils</code>, <code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15538">#15538</a>
feat: UNSTABLE Review Multi-remote <code>select</code> implementations
and remove filter (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
</ul>
</li>
</ul>
<h4>:bug: Bug Fix</h4>
<ul>
<li><code>wdio-utils</code>, <code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15538">#15538</a>
feat: UNSTABLE Review Multi-remote <code>select</code> implementations
and remove filter (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
</ul>
</li>
<li><code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15535">#15535</a>
fix(webdriverio): handle BiDi no such node errors as stale (<a
href="https://github.com/Fnine59"><code>@Fnine59</code></a>)</li>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15537">#15537</a>
fix(webdriverio): require all classic cookie filter attributes (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
</ul>
</li>
</ul>
<h4>:memo: Documentation</h4>
<ul>
<li><code>wdio-protocols</code>, <code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15536">#15536</a>
Docs/actions api guidance (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
</ul>
</li>
</ul>
<h4>Committers: 3</h4>
<ul>
<li>David Prevost (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
<li>Fnine59 (<a
href="https://github.com/Fnine59"><code>@Fnine59</code></a>)</li>
<li>Martsin Lazouski (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
</ul>
<h2>v9.31.4 (2026-08-27)</h2>
<h4>:rocket: New Feature</h4>
<ul>
<li><code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15532">#15532</a>
feat: Beta ElementArray under feature flag for
<code>multiRemote.$()</code> e.g. MultiRemoteElement[] (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
</ul>
</li>
<li><code>wdio-allure-reporter</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15278">#15278</a>
feat(wdio-allure-reporter): add support for global errors and global
attachments (<a
href="https://github.com/todti"><code>@todti</code></a>)</li>
</ul>
</li>
</ul>
<h4>:bug: Bug Fix</h4>
<ul>
<li><code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15478">#15478</a>
fix(webdriverio): track document element per context (<a
href="https://github.com/Badisi"><code>@Badisi</code></a>)</li>
</ul>
</li>
<li><code>webdriver</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15516">#15516</a>
fix(webdriver): serialize IPv6 BiDi WebSocket candidates correctly (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
</ul>
</li>
</ul>
<h4>:memo: Documentation</h4>
<ul>
<li><code>webdriverio</code>
<ul>
<li><a
href="https://redirect.github.com/webdriverio/webdriverio/pull/15531">#15531</a>
docs(webdriverio): document the $ async iterator methods (<a
href="https://github.com/devangpratap"><code>@devangpratap</code></a>)</li>
</ul>
</li>
</ul>
<h4>Committers: 5</h4>
<ul>
<li>Alex (<a
href="https://github.com/todti"><code>@todti</code></a>)</li>
<li>David Prevost (<a
href="https://github.com/dprevost-LMI"><code>@dprevost-LMI</code></a>)</li>
<li>Martsin Lazouski (<a
href="https://github.com/ML642"><code>@ML642</code></a>)</li>
<li><a href="https://github.com/Badisi"><code>@Badisi</code></a></li>
<li><a
href="https://github.com/devangpratap"><code>@devangpratap</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/webdriverio/webdriverio/commit/4f39140405a3a54a44c391f97a6de1fc3af2d33d"><code>4f39140</code></a>
v9.31.5</li>
<li><a
href="https://github.com/webdriverio/webdriverio/commit/71d86bcb9fb7f6eeee72515b1475fb511e2b61a1"><code>71d86bc</code></a>
feat: UNSTABLE Review Multi-remote <code>select</code> implementations
and remove filter...</li>
<li><a
href="https://github.com/webdriverio/webdriverio/commit/8850df025e9a8c9007e712f439a9167684065b66"><code>8850df0</code></a>
fix(webdriverio): handle BiDi no such node errors as stale (<a
href="https://github.com/webdriverio/webdriverio/tree/HEAD/packages/webdriverio/issues/15535">#15535</a>)</li>
<li><a
href="https://github.com/webdriverio/webdriverio/commit/8958aba3b13a4439995cc4f818eec2cdfade7282"><code>8958aba</code></a>
Docs/actions api guidance (<a
href="https://github.com/webdriverio/webdriverio/tree/HEAD/packages/webdriverio/issues/15536">#15536</a>)</li>
<li><a
href="https://github.com/webdriverio/webdriverio/commit/719be34eda44cd26985a5d1cbe1c6d43572e713e"><code>719be34</code></a>
fix(webdriverio): require all classic cookie filter attributes (<a
href="https://github.com/webdriverio/webdriverio/tree/HEAD/packages/webdriverio/issues/15537">#15537</a>)</li>
<li><a
href="https://github.com/webdriverio/webdriverio/commit/69c45e316f75ba2e40e83ef7475106ece26cf2e2"><code>69c45e3</code></a>
v9.31.4</li>
<li><a
href="https://github.com/webdriverio/webdriverio/commit/662093ba165cefeb62ab4be57d66f5f545ddcfc1"><code>662093b</code></a>
feat: Beta ElementArray under feature flag for
<code>multiRemote.$$()</code> e.g. MultiR...</li>
<li><a
href="https://github.com/webdriverio/webdriverio/commit/dad1df66d0c19f43a4aff427fbc1761393a28d9d"><code>dad1df6</code></a>
docs(webdriverio): document the $$ async iterator methods (<a
href="https://github.com/webdriverio/webdriverio/tree/HEAD/packages/webdriverio/issues/15531">#15531</a>)</li>
<li><a
href="https://github.com/webdriverio/webdriverio/commit/de2c6a8b19d5eccaf6007e50af91aaa9b8545fd9"><code>de2c6a8</code></a>
Fix test since instances order is no garantee (<a
href="https://github.com/webdriverio/webdriverio/tree/HEAD/packages/webdriverio/issues/15530">#15530</a>)</li>
<li><a
href="https://github.com/webdriverio/webdriverio/commit/c457b129c90b586701e53ac7b98ae4309941df8a"><code>c457b12</code></a>
fix(webdriverio): track document element per context (<a
href="https://github.com/webdriverio/webdriverio/tree/HEAD/packages/webdriverio/issues/15478">#15478</a>)</li>
<li>See full diff in <a
href="https://github.com/webdriverio/webdriverio/commits/v9.31.5/packages/webdriverio">compare
view</a></li>
</ul>
</details>
<br />
Updates `zod` from 4.4.3 to 4.5.4
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/colinhacks/zod/releases">zod's
releases</a>.</em></p>
<blockquote>
<h2>v4.5.4</h2>
<h2>Commits:</h2>
<ul>
<li>84e416fbf4740527bbc8f319634f4e1b065bb42c fix(v4): stop the cycle
walk from firing a default factory (<a
href="https://redirect.github.com/colinhacks/zod/issues/6500">#6500</a>)</li>
<li>e8e206fa33ac5fe7ce20a2beb12d57b1cb3df653 4.5.4</li>
</ul>
<h2>v4.5.3</h2>
<h2>Commits:</h2>
<ul>
<li>e6b6ab347675cd2bd54b1bdbed16f98c59be82a9 docs(blog): widen the
z.compile example to a 20-property schema</li>
<li>87d6464418582bb96fc665a01f852ca6da324ad0 fix(docs): drop the OG
description when the title wraps past two lines</li>
<li>99fce394a026823e602b9c30d8d5d9f5f1932ce7 bench(v4): z.compile()
against zod-compiler (<a
href="https://redirect.github.com/colinhacks/zod/issues/6499">#6499</a>)</li>
<li>e3a695b6bf3f0d591ea682816e3cdaea04b0f967 docs(v4): record the email
regex and container output-shape findings under Open</li>
<li>7e24a24288183ce02554f1ded7775d0650a7b7e6 docs(blog): drop the
reading time and put a GitHub link in the navbar</li>
<li>eab51ff3592b2d11d863f4ee4d5452f31a3de1b6 fix(v4): emit record
numeric keys as strings in toJSONSchema (<a
href="https://redirect.github.com/colinhacks/zod/issues/6497">#6497</a>)</li>
</ul>
<h2>v4.5.2</h2>
<h2>Commits:</h2>
<ul>
<li>a354314ac04fdd5484aa62dd5c3a4b553211a0e4 fix(docs): keep blog posts
out of the docs collection (<a
href="https://redirect.github.com/colinhacks/zod/issues/6484">#6484</a>)</li>
<li>d378c42aff6869f0929058a7923cd775880f5c4c ci: drop canary publishing
from the release workflow (<a
href="https://redirect.github.com/colinhacks/zod/issues/6487">#6487</a>)</li>
<li>212b941791e7faae078e17645eb612824fd8f79a fix(v4): let a prototype
method getter answer a bare call so vi.spyOn works (<a
href="https://redirect.github.com/colinhacks/zod/issues/6488">#6488</a>)</li>
<li>e7576f542a7bc7ef3cc5eeec237714fd0e6b6e98 docs(blog): let the page
show through the navbar in dark mode (<a
href="https://redirect.github.com/colinhacks/zod/issues/6489">#6489</a>)</li>
<li>fedb06fafe33a66ce0b5c236ad2557e0a5a170fe fix(docs): match the blog
TOC hover bar to the 2px active indicator</li>
<li>6c932fcb2eea6eb671710ea058ca9fdc382ada89 chore: bump devcontainer
image to Node 24 (<a
href="https://redirect.github.com/colinhacks/zod/issues/6470">#6470</a>)</li>
<li>6635d9dd367a664109de83c021995821f48efa29 docs(blog): soften the
"method memoization" attribution</li>
<li>019ae299cc75daa132bf1acf59086a520abf6b85 fix(docs): drop ISR on the
docs route so the home page hydrates</li>
<li>652bb438aa4c626c1cd7948c6849c4691239fca7 chore(docs): drop the
scroll log from the route-change scroller</li>
<li>571c8e8a3d73b4305f4abfdd6977773cc12f2bf5 fix(docs): render blog tabs
with the stock fumadocs tab card</li>
<li>9a193aa24b4efa3b315b91d4c56c8bc385b8513f 4.5.2</li>
</ul>
<h2>v4.5.1</h2>
<h2>Commits:</h2>
<ul>
<li>2e862dbf89da2835e5206a8fd3d3be61afe3cf7f ci: gate the GitHub release
and JSR publish on the version being live on npm</li>
<li>8e03380510db36fa6fda979fc78a375fdea8021c 4.5.1</li>
</ul>
<h2>v4.5.0</h2>
<p>Zod 4.5 is now available.</p>
<pre lang="sh"><code>npm install zod@latest
</code></pre>
<p>At a glance:</p>
<ul>
<li><a
href="https://github.com/colinhacks/zod/blob/HEAD%5Bhttps://github.com/colinhacks/zod/blob/HEAD%60https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEAD.https://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADohttps://github.com/colinhacks/zod/blob/HEADmhttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD%60https://github.com/colinhacks/zod/blob/HEAD%5Dhttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD#https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADohttps://github.com/colinhacks/zod/blob/HEADmhttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD">https://github.com/colinhacks/zod/blob/HEAD[https://github.com/colinhacks/zod/blob/HEAD`https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEAD.https://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADohttps://github.com/colinhacks/zod/blob/HEADmhttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD`https://github.com/colinhacks/zod/blob/HEAD]https://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD#https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADohttps://github.com/colinhacks/zod/blob/HEADmhttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD</a>
— the flagship feature of Zod 4.5</li>
<li><a
href="https://github.com/colinhacks/zod/blob/HEAD%5Bhttps://github.com/colinhacks/zod/blob/HEAD%60https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEAD.https://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADChttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD%60https://github.com/colinhacks/zod/blob/HEAD%5Dhttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD#https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADehttps://gith…
## Problem WebDriver reference captures inherit the host display's device-pixel ratio. Retina macOS runs therefore write 2x PNGs while Linux CI rewrites the same references at 1x, leaving mixed baselines and periodic screenshot-only churn. ## Fix - launch Electron with a fixed 2x device scale factor on every E2E host - fail fast at session startup if Chromium does not report the expected DPR - run headless Linux E2E under a 3200x2048 Xvfb framebuffer, preserving the existing 1200x800 and 1600x800 CSS layouts at 2x - route the local, CI, and markdown E2E scripts through the same cross-platform launcher ## Validation - `pnpm run check` — 8,550/8,550 tests passed, including typecheck, lint, formatting, dead-code, oracle, and E2E syntax gates; repeated after rebasing onto latest `origin/main` - `pnpm run build` - `pnpm run test:e2e -- --spec tests/e2e/mcp-tool-labels.e2e.ts` — passed; generated PNG verified at 2400x1600 and visually inspected - `pnpm run test:e2e -- --spec tests/e2e/roadmap-thread-reopen.e2e.ts` — passed at 2400x1600 after preserving the logical viewport The `update-screenshots` label runs the complete screenshot suite and publishes the normalized 2x PNGs through the repository's immutable child-PR review workflow. --------- Co-authored-by: Jonathan Kingston <KingstonMailBox@gmail.com> Co-authored-by: copse-release-bot[bot] <304038887+copse-release-bot[bot]@users.noreply.github.com> Co-authored-by: jonathanKingston <338988+jonathanKingston@users.noreply.github.com>
Lever 1 of 3 from a DevTools trace of a live agent turn. **This is the biggest single win of the three.** The set, biggest win first: **this PR** (~19 % of main-process work) · #2395 (~9 %, no dependency patch) · #2397 (~3 %, weakest). >⚠️ Conflicts with #2397 — both add `patchedDependencies` for the same package. Whichever lands second needs a trivial rebase to merge the two patch hunks. ## The problem ```js export function whichSync(bin) { if (typeof globalThis.Bun !== 'undefined') return globalThis.Bun.which(bin); const result = spawnSync('which', [bin], { … }); // ← the Node/Electron path ``` `wrapCommandWithSandboxMacOS` (and its Linux counterpart) call `whichSync(binShell || 'bash')` on **every sandboxed command**. Under Bun that's an in-process `Bun.which`. Under Node it **forks `/usr/bin/which`** — synchronously, on the Electron main thread. From the trace: | | | | --- | --- | | `whichSync` | **6.35 ms/call** — 72 % of the entire 8.85 ms sandbox wrap | | `spawnInProjectSandbox` | 56 calls in 5.5 s (~10/second) | So Copse blocks its main thread with a fork/exec roughly ten times a second, purely to look up a path that doesn't move. **There is no way to avoid this through the package's API.** `whichSync` is called unconditionally, and passing an absolute path doesn't help — `which /bin/bash` still spawns a process. ## The change A `pnpm patch` memoizing the Node fallback on `(binary, PATH)`. The Bun branch is untouched. Measured standalone: **3.05 ms → 0.074 ms per call, 41×.** **Only successful resolutions are cached** — see below for why that matters. ## A real bug, caught by e2e (commit 2) The first version of this patch cached misses too. That was wrong, and e2e caught it: ``` tests/e2e/close-confirm.e2e.ts ✖ "before all" hook element (".prompt-input") still not existing after 30000ms ``` `spawnSync` carries `timeout: 1000`, so on a loaded machine `which` can be killed before it answers and return null for a binary that is plainly present. Upstream re-spawns next call and recovers. Caching that null made **one transient timeout permanent for the life of the process** — every later sandboxed command threw `Shell 'bash' not found in PATH`, so the app could never reach a usable state. It was this branch's bug, not CI's — the same shard passed on #2395 (no dependency patch) and #2397 (patches this package but not `whichSync`), and it failed on both attempts here. Reproduced before fixing: look a binary up while absent, create it at the same PATH, look again. Before, the second lookup still returned null; after, it returns the path. A miss now costs one spawn next call, exactly as upstream, and the hit path — the whole point of the memo — is untouched. Worth noting the unit tests could not have caught this on their own; only e2e under real load did. That's an argument for the `e2e`-on-ready-for-review gate doing its job. ## The patch is guarded by tests `sandbox-which-cache.test.ts` pins both halves: 1. a resolved binary stays resolved after it is deleted (the memo exists — fails if a version bump drops the patch), 2. **a failed lookup is not cached** (the regression above). Both guards were verified to fail against the code they guard: (1) against unpatched upstream, (2) against the previous commit's patch, with `a failed lookup was cached`. ## On `pnpm patch-commit` It rewrites `pnpm-workspace.yaml` — reordering `overrides`, **dropping the `supportedArchitectures` block entirely**, and stripping every explanatory comment. I reverted its output and hand-added just the `patchedDependencies` block. Worth knowing before anyone runs it again. ## Safety No change to permission policy, the platform matrix, or what the sandbox allows or denies (`docs/shell-permissions.md`). The resolved shell path is identical — it's just not re-derived per command. The cache has no TTL, unlike #2395. `PATH` is part of the key and only successes are cached, so the remaining exposure is "a binary was replaced at the same PATH mid-session". Say the word if you'd rather it were TTL-bounded for symmetry. ## Ideally this goes upstream A small cache in `sandbox-runtime` itself would help every Node embedder — including the miss-handling subtlety above — and the patch should be dropped when it lands. I can't open that PR from here; this session is scoped to `copse-dev/agent-pane`. ## Validation - `tsc --noEmit -p tsconfig.node.json` — clean - `eslint`, `oxfmt --check .` (2770 files) — clean - `check-dead-code` — clean - `project-sandbox` + `pnpm-workspace-invariants` — 41/41 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01GdDtinDf7MGCnSuktjPM2M --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Lever 2 of 3 from a DevTools trace of a live agent turn. **This is the only one of the three entirely in Copse's own code** — no dependency patch, no carrying cost. The set, biggest win first: #2396 (whichSync, ~19 % of main-process work) · **this PR** (~9 %) · #2397 (~3 %, weakest). All three are independent except #2396/#2397, which both patch the same dependency. ## What the trace showed The Electron main process did **401 ms of real work in 5.8 s**, essentially all of it in the sandboxed-spawn path. `spawnInProjectSandbox` ran **56 times in 5.5 s (~10 sandboxed processes/second)**. `spawn.ts` rebuilds the whole filesystem rule set per spawn via `workspaceSandboxOverlay`, and `canonicalizeWorkspaceRoot` runs a blocking `realpathSync.native` on each rule path so seatbelt/bwrap rules match the kernel's symlink-free view. `uncoveredSiblingDenyPaths` re-resolves `homedir()` **plus every sibling worktree root**, every single command. | function | cost on the traced machine | | --- | --- | | `uncoveredSiblingDenyPaths` | 3.20 ms/command | | `canonicalizeWorkspaceRoot` | 3.08 ms/command | `realpath` blocks the Electron main thread, so this lands directly on IPC latency — the trace shows it as part of 45–65 ms main-thread stalls while tool calls fire. ## The change Route `canonicalizeWorkspaceRoot` through a short-TTL, size-bounded memo in a new `canonical-path-cache.ts`. The cache is **deliberately time-bounded rather than permanent**. Canonicalization is a filesystem fact and these rules are a security boundary: a cached answer that outlived a symlink change would emit rules naming the old target. A 2 s window collapses a burst of tool calls into one syscall per path while keeping any staleness window far shorter than the redundancy it removes. Size is bounded so a session touching many projects/worktrees cannot grow it without limit. Clock and resolver are injected at the boundary so the TTL and eviction are testable without sleeping or touching the filesystem — no test-only product API. ## Measured effect Replaying the traced workload (56 spawns, 12-worktree project): ``` before: 0.2431 ms/spawn, 728 realpath syscalls after : 0.0764 ms/spawn, ~39 realpath syscalls (~95% fewer) ``` ## Rules are unchanged The memo wraps the same `resolve` → `realpathSync.native` → fallback sequence. Sibling-deny output is verified **byte-identical** before and after. No change to permission policy, the platform matrix, or what the sandbox allows or denies (`docs/shell-permissions.md`). ## Validation - `tsc --noEmit -p tsconfig.node.json` — clean - `eslint`, `oxfmt --check` — clean - `check-dead-code` — clean (839 modules reachable) - `node scripts/run-tests.mts project-sandbox/canonical-path-cache project-sandbox/config` — **40/40 pass** (34 existing + 6 new) `sandbox-fs-client` / `sandbox-fs-server` fail in my container, but they fail identically on a clean tree — `node-pty` has no prebuilt Linux binary and I installed with `--ignore-scripts`. Unrelated to this change; please confirm they are green in CI. ## Not in scope `canonicalizeReadTarget` (external read grants) has the same shape but is a rarer path and did not appear hot in the trace. Left alone deliberately. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01GdDtinDf7MGCnSuktjPM2M Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…age) and add shared isDefined/isNonNull (#2407) Closes #1332. Three mechanical passes, one commit each, in the order the issue suggests. ### 1. `isDefined` / `isNonNull` — the highest-leverage addition There was no shared presence predicate. 34 sites across 19 files wrote one inline: ```ts .filter((change): change is GitChange => change !== null) ``` TypeScript never checks that `change is GitChange` follows from the body, so the annotation and the condition can drift apart silently, and an anonymous predicate cannot be tested. Both helpers now live in `packages/std/src/nullish.ts` — dependency-free, so the extracted packages can use them too — re-exported as `@shared/nullish.ts` for app code, with a test that pins the runtime behaviour *and* the narrowing (`const nonNull: number[] = values.filter(isNonNull)` only compiles if the predicate is right). Per `docs/type-safety.md`, the test was checked against a `return true` body: all 6 go red. The three call sites with an extra clause (`x !== undefined && x.trim().length > 0`) were left alone — they are not presence checks. ### 2. `isRecord` — 22 local copies deleted The issue scoped this to "~6 in `src/**`, not 10", on the grounds that the `packages/**` copies cannot import from `@shared`. That caveat is now stale: `@copse/std` exists precisely as the dependency-free leaf both sides share, and `hooks-dialects` / `plugin-sdk` already import `isRecord` from it. So all 22 go, leaving one definition. Two behavioural notes: - **Five copies omitted `!Array.isArray(value)`** and so accepted arrays as records (`stream-retry`, `spine-schema`, and three test files). Every use of those five is `isRecord(v) && typeof v['field'] === '…'`, which an array fails anyway — the canonical predicate is a narrowing with no reachable behaviour change. - `tool-args-format` (`!!value && …`) and `lm-studio-provider` (reordered clauses) spelled the identical three conditions differently. Also adds a short section to `docs/type-safety.md` pointing at the shared predicates, so the next boundary parser reaches for one instead of writing a 23rd. ### 3. `hasLastUsage` — five copies into `@copse/llm` One was exported and tested in `src/main/services/providers/provider-usage.ts`; three were verbatim copies; `redacting-provider` had a variant narrowing `LLMProvider` rather than `unknown`. Two live in extracted packages and so could not import the tested one. `LLMProvider` and `ModelUsage` both live in `@copse/llm`, so the predicate moved there with its test. `ProviderWithUsage.lastUsage` is now `ModelUsage | null` — the shape `llm-complete-text` already declared, and a superset of the two-field shape the others used, so no caller loses a field. `src/main/services/providers/provider-usage.ts` is gone; its three importers take the package path. ### Out of scope Step 4 of the issue ("revisit #1330's export question against whatever is left") is a hand-off to #1330, which stays open. ### Verification `pnpm run check` — typecheck (both projects), lint, format, dead-code, oracle, e2e-syntax, demo-site, and the 8556-test unit suite (0 failures). No visual eval: the four renderer files touched (`tool-args-format`, `model-options`, `composer-editor`, `roadmap-pane`) are predicate substitutions with identical results and unchanged DOM. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: copse-release-bot[bot] <304038887+copse-release-bot[bot]@users.noreply.github.com> Co-authored-by: jonathanKingston <338988+jonathanKingston@users.noreply.github.com>
## The cancel `Demo preview` runs keep ending `cancelled`, and it isn't the build being superseded — it's the deploy. `demo-preview.yml`'s `deploy` job was `uses: ./.github/workflows/pages.yml`. A called workflow's jobs run **inside the caller's run**, so the deploy took the PR's own run into the shared `pages` concurrency group. That group keeps a single pending slot, so a newer deploy arriving while ours waited cancelled ours — and a cancelled job marks the whole run cancelled, leaving a grey ✗ on the PR. Across the last 40 `Demo preview` runs: | conclusion | runs | | --- | --- | | cancelled | 23 | | success | 14 | | failure | 1 | All 23 were on **different branches**, so none of them was same-branch supersession — the per-branch `concurrency` group can't reach across branches. Picking one apart ([run 34024334865](https://github.com/copse-dev/agent-pane/actions/runs/34024334865)): ``` publish success comment success deploy/deploy cancelled (created 09:21:45, cancelled 09:21:55 — never started) → run: cancelled ``` The preview was built, published and commented. Only the queue slot was lost. One push to `main` fans this workflow out into a dozen runs whose deploys all reach the queue within seconds, so everything past the deploying run and the one pending behind it is cancelled. ## The skip **Dispatch `pages.yml` instead of calling it.** The supersession still happens — `pages.yml`'s own note explains why `queue: max` was worse — but it now cancels a standalone `Deploy site` run that no PR is watching. Nothing is lost by not waiting, for the same reason queued deploys were interchangeable in the first place: the assembler reads whatever is on the `demo-previews` tip, and `publish` has already pushed there. Whichever deploy runs next carries this target — which is also why the sticky `comment` job already didn't sit behind `needs: deploy`. **And skip the dispatch outright when a deploy is already queued.** A *queued* deploy hasn't assembled yet, so it will carry this build; a second dispatch would only supersede it to publish the same tree. Deliberately **not** "in progress" — such a deploy may have fetched the tip before this push landed, so skipping on it would drop the preview. Two runs can still race the check and both dispatch; the loser is superseded on a run nobody watches, so this is an optimisation, never a correctness condition. In a burst of a dozen pushes it collapses ~12 deploy runs to ~2. **Warn rather than fail if the dispatch doesn't go out**, carrying over the `tolerate-deploy-failure: true` the call used to pass. The concrete case is a PR that merges while its preview is still building: auto-delete takes the head branch with it and the dispatch ref 404s — `ci.yml`'s autoformat job guards the same trap. Failing there would put a red ✗ on an already-merged PR, which is worse than the grey one this PR removes. ## Deliberately unchanged - **`concurrency: cancel-in-progress: true`** on `demo-preview.yml` stays. That cancel is the *right* one: it kills a build on a commit that is no longer the head, saving several minutes of runner time on bytes nobody will publish, and it never shows on the PR's current head. Turning it into a skip would mean paying for the stale build and then discarding it. - **`demo-preview-cleanup.yml` keeps `workflow_call`.** Its deploy can be cancelled the same way, but it fires on a *closed* PR where the cancelled run is genuinely invisible — and keeping that caller keeps `tolerate-deploy-failure` honest rather than dead config. ## Also - Drops `pages: write` / `id-token: write` from `demo-preview.yml` — nothing in it deploys any more, and the dispatched run mints those from `pages.yml`'s own `permissions:` block. `actions: write` is declared on the dispatching job alone, so `publish` (the one job running a shell script against a write token) doesn't carry it. - Updates the `pages.yml` concurrency note and the `demo-links-per-pr-previews.md` plan, both of which documented the old `workflow_call` choice explicitly. - Four new pins in `ci-workflow-invariants.test.ts`: no `uses:` back into `pages.yml`, the dispatch is on the pushed branch, the queued-only guard never widens to `in_progress`, and the dispatch warns rather than fails. ## Testing `ci-workflow-invariants.test.ts` — 60/60 pass; `oxfmt --check` clean on all four files. `npm run lint` and `typecheck` were not run here: the container has no `node_modules` and the full install (~590 MB plus an electron rebuild) isn't available, so CI is the first place they run. The change is three YAML/Markdown files plus one test file that uses nothing the file didn't already import. The dispatch path itself can only be exercised on a real push to a branch with an open PR, which this PR is — so `Demo preview` on this PR is the test: it should end **green** with either `Dispatched the Pages deploy on …` or `Deploy run … is already queued …` in the `deploy` job log. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_019JeFWg8CQL5JHK852n6YjX --- _Generated by [Claude Code](https://claude.ai/code/session_019JeFWg8CQL5JHK852n6YjX)_ Co-authored-by: Claude <noreply@anthropic.com>
…2397) Lever 3 of 3 from a DevTools trace of a live agent turn. > **This is the weakest of the three — land #2396 and #2395 first.** It buys the least (~3 % of main-process work vs ~19 % and ~9 %), and it is the only one that patches a security-critical dependency *and* cannot be verified black-box. I'd understand a "not worth the carrying cost" verdict; opening it because it was the third lever I measured. >⚠️ Conflicts with #2396 (lever 1) — both add `patchedDependencies` for the same package. Whichever lands second needs a trivial rebase to merge the two patch hunks. ## The problem `generateSandboxProfile` pushes every read/write/deny entry through `normalizePathForSandbox`, one blocking `realpathSync` apiece. A Copse workspace overlay produces **~49 entries per sandboxed command**, and **~35 % re-resolve a directory another entry already resolved** — `<dir>` and `<dir>/**` are two entries naming one directory. Trace: **2.94 ms per profile build**, once per command, ~10 times a second. ## The change Memoize those lookups **for one synchronous profile build**, cleared on the next microtask. The scoping is the whole point. These paths become seatbelt/bwrap rules, so a resolution that outlived its build could emit a rule naming a stale symlink target. A profile is generated synchronously, so the next microtask is already the next build — nothing crosses that boundary. I measured a cross-command cache too (92 % saving vs 37 %) and **deliberately did not use it** for exactly this reason. ``` 49 realpath calls → 32 per build 1.93 ms → 1.22 ms (37%) ``` ## Why there is no behavioural test The memo is **invisible in the return value by design**. `isSymlinkOutsideBoundary` only accepts a resolution that is the path itself, its `/private` canonical form, or a path beneath it — every other resolution is discarded and the original spelling returned either way. I verified this against the unpatched module across symlink, glob, missing and plain-directory inputs: **outputs identical in every case.** That is the safety property, but it also means no black-box test can see the cache. So the guard pins what can actually regress: 1. the patch is present in the installed dependency, 2. the microtask clear is present (the security-relevant half), 3. normalization still produces the right answers. Verified by reverting `node_modules` to upstream — the guard fails with `patch missing from node_modules — a version bump likely dropped patches/…`. A source-string assertion is not lovely. It is the honest option here, and it is what would actually catch a version bump silently dropping the patch. ## Also worth knowing `pnpm patch-commit` rewrites `pnpm-workspace.yaml` — reordering `overrides`, dropping the `supportedArchitectures` block, and stripping every comment. I reverted its output and hand-added only the `patchedDependencies` block. ## Safety No change to permission policy, the platform matrix, or what the sandbox allows or denies (`docs/shell-permissions.md`). ## Validation - `tsc --noEmit -p tsconfig.node.json` — clean - `eslint`, `oxfmt --check .` (2770 files) — clean - `check-dead-code` — clean - `project-sandbox` + `pnpm-workspace-invariants` — **41/41**, plus the negative test above 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01GdDtinDf7MGCnSuktjPM2M --- _Generated by [Claude Code](https://claude.ai/code/session_01GdDtinDf7MGCnSuktjPM2M)_ Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Retires `src/shared/types/ipc.ts`. The hand-written `IpcInvokeMap` / `IpcEventMap` were a second, drifting description of a surface that #2352 already froze as a generated protocol. ## Why it is dead - **Nothing imported it.** Those two interfaces were its only exports. They were re-exported through the `@shared/types` barrel, but no file in the repo names either type. - **Its private types were dead too.** `Provider`, `CloudProvider` and `AvailableProviders` were never exported at all, and the renderer already derives its own `AvailableProviders` from the real contract (`ApiClient['settings']['availableProviders']`, `src/renderer/views/model-options.ts`). - **It was a partial mirror, and drifting.** It named 149 channels against the protocol's 241 invoke + 2 send + 54 event. Coverage had *decreased* since #2352 measured 111/237 and 36/53, because new channels land in `api.d.ts` and nobody updates this file. - **It was already on the books to go.** #2352 explicitly rejected it as a generator source (rationale in `scripts/lib/api-protocol.mts`), and `docs/api-protocol.md` listed retiring it under "Known gaps". The source of truth is unchanged: `ApiClient` in `src/preload/api.d.ts` plus the preload bindings, generated into `schemas/api-protocol.manifest.json`. ## What changed - Deleted `src/shared/types/ipc.ts` and its `export *` line in `src/shared/types/index.ts`. - Removed the invariants test `ipc.ts only names channels the protocol has`. That guard existed solely to stop the stale maps naming channels that no longer exist "until it is retired" — it has no subject now. - Updated the generator's rationale comment, the "Known gaps" section of `docs/api-protocol.md`, and the step-1 findings in `docs/plans/client-server-split.md`. ## Notes for a reviewer - **The generated manifest is byte-identical.** `ipc.ts` was never a generator input, so the protocol surface does not move and no version bump applies. The manifest drift test passing is the proof. - **Type-only deletion.** The file contained no runtime exports, so the compiled barrel loses nothing. - Three older plan docs still mention `ipc.ts` (`thread-referencing.md`, `managed-agent-environments.md`, `ssh-remote-repo.md` — the last with already-stale line numbers). They read as point-in-time snapshots of unrelated plans rather than live pointers, so they are left alone; happy to scrub them if preferred. ## Verification `typecheck`, `lint`, `format:check` and `check:dead-code` (838 modules reachable) all pass. `pnpm test api-protocol` is 19/19. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
) Closes #1330. **183 hand-written predicates → 118**, and a ratchet so the count can only go one way. Six commits plus a merge, in the order the issue lays the work out. > Rebased onto #2407, which landed while this was open. The two do not overlap by design — that PR took the 26 presence checks this one deliberately left alone; this one took the membership and narrowing ones. All six conflicts were import lines and one docs section, no logic. Base figures below are against `main` **with** #2407. ## 1. The blocker isn't one #1330 says the remedy is unavailable until `@typescript-eslint/explicit-function-return-type` is exempted, because obtaining inference means deleting the return annotation. Measured against this repo's config, in both directions: | | lints? | | --- | --- | | `function isFoo(v: unknown) { return typeof v === 'string' }` | ✗ `explicit-function-return-type` **and** `explicit-module-boundary-types` | | `const isFoo: (v: unknown) => v is Foo = (v) => typeof v === 'string'` | ✓ clean | | `xs.filter((x) => typeof x === 'string')` | ✓ clean | So only the route the issue proposed violates the rule. Moving the annotation to the **binding** keeps the signature written down and gets the check for free — `allowTypedFunctionExpressions` is on by default — and an unannotated arrow in argument position never triggered the rule at all. The annotated form is also the *better* remedy, not just the available one. Inference by deletion degrades silently: a body that stops narrowing yields plain `boolean` and the caller quietly gets a wider type. The annotated form is `TS2677` instead — `return true` fails to compile, which is exactly the case the issue opens with. **No lint config changes in this PR.** ## 2. Membership: 28 assertions → 1 `.includes()` / `.some()` / `.has()` don't narrow, so these can be neither checked nor inferred — the issue's "at least 14 that genuinely need the assertion". They don't; they need to stop being written by hand. ```ts export const isThemePreference = memberOf(THEME_PREFERENCES) ``` `memberOf` (`packages/std/src/member-of.ts`) holds the codebase's single membership `is` assertion. Its test checks the exact contract — `memberOf(list)(value) === list.includes(value)` — over a cross-product of 12 member lists and a hostile corpus (prototype keys, near-misses, symbols, bigints, `NaN`, functions with a matching `toString`), plus compile-time narrowing assertions. Per `docs/type-safety.md` I checked it can fail: a `return true` body turns **6 of its 10 cases red**. Two things fell out: - **`decision-log.ts` and `deferred-approval.ts` spelled their member lists twice** — once as a union, once as a `Set` literal — so the predicate could silently disagree with the type it claimed. The list is now the source and the type derives from it, which makes that drift unrepresentable rather than merely tested. - Three sites keep an explicit annotation because their declared input is narrower than `unknown` (`isCursorPermissionHookEvent`, `isServiceTier`, `isHookDialect`). The factory's return type is checked against it. ## 3. Convert what's cheap — measured, not guessed I converted **all 100** function-declaration predicates to the annotated form and kept only what `tsc` accepted: **18**. That is the honest headline, and it is much less than the issue anticipates. What survives is `instanceof`, `Array.isArray`, `in` over an object union, a discriminant comparison, and a literal-union disjunction. What doesn't is the bulk of this codebase's predicates: **structural boundary parsers**, where indexed access doesn't narrow the object. `isRecord` fails twice over — the negated `Array.isArray` stops inference producing a predicate at all, and even without it the most the compiler concludes is `v is object`, which has no index signature. There is no way to write it that the compiler checks. For the inline `.filter((x): x is T => …)` arrows — the one shape that can *never* satisfy the "exported predicates must be tested" rule, because an anonymous predicate has no test surface — I stripped all 73 non-presence annotations and **diffed the resolved type of every call expression in every touched file**. 24 had moved: - `Boolean(part)` doesn't narrow at all — `(string | undefined)[]`, not `string[]`; - in `cursor-adapter.ts` two filters lost their `wireEvent` narrowing and started reporting a *different* event set, still compiling; - where the input array is `any[]` (a `storageGet` read), the annotation was the only thing pinning the element type, and removing it trips `no-unsafe-*`. So the batch that landed is the 21 sites where **nothing moves**: every call-expression type across the 17 files is byte-identical before and after. The 52 excluded sites keep their annotation and stay in the inventory. ## 4. The ratchet The follow-up comment on #1330 is the real finding: 161 → 212 in four months, with 15 covered. Counting by hand can't keep up, and nothing in the build objects — `no-unsafe-type-assertion` doesn't flag a predicate, and the suppression baseline is empty, so an unverified `x is T` passes every gate we have. `scripts/type-predicate-inventory.test.ts` lists the 118 that remain and fails **both ways**, the same shape as `module-boundaries.test.ts`: a predicate not on the list fails, and a list entry whose predicate is gone fails. Converting one forces its line out in the same change, so the list can only shrink. I verified both directions by adding a predicate and by removing one. Only the **asserted** form is counted — `memberOf(TUPLE)`, an annotated binding and an unannotated `.filter()` arrow are all absent by construction, so the cheapest predicate to add is now also the honest one. That classification is load-bearing, so `scripts/lib/type-predicates.mts` has its own unit test for the ten shapes it has to tell apart. `memberOf`'s own assertion is *in* the list, as are `isDefined` / `isNonNull` from #2407. That is the point: the shared ones are the audited ones, and the list says which they are. ## 5. Docs `docs/type-safety.md` gains the three checked forms, the lint finding above, and a measured table of **what TypeScript can and cannot infer a predicate from** — including that a preceding `const` is fine but an early `return false` is not, and the two traps that make a blanket conversion unsafe. The point is that nobody re-derives this. `memberOf` is folded into #2407's "reach for the shared predicate" list rather than competing with it. ## Numbers Counted by `scripts/lib/type-predicates.mts` over tracked non-test `src/` + `packages/` + `scripts/`, which is why the base is 183 rather than the issue's 212. | | main (incl. #2407) | here | | --- | --- | --- | | asserted predicates | 183 | **118** | | files carrying one | 113 | 74 | | membership predicates written by hand | 28 | 0 | The remaining 118 are overwhelmingly structural boundary parsers. Driving those to zero is a decoder-at-the-boundary job, not a predicate job — `docs/type-safety.md` now says so and points at `safeJsonParse(text, decodeWithSchema(schema))`. ## Verification `pnpm run check` end to end on the merged tree: typecheck (both projects), lint, format, demo-site, dead-code, oracle, e2e-syntax, and **8,587 passing / 0 failing** unit tests. No visual eval. The seven renderer files touched are predicate-form substitutions with identical runtime semantics and unchanged DOM — `main.ts`'s `Set.has` and `memberOf`'s internal `Set.has` are the same call, and every other change is type-level. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01J9mJfbsrvEeTMUdxTKf8xZ --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Daily automated promotion of
mainto the release-readyreleasebranch.The repository ruleset requires the full
CI Passedgate. This PR willmerge automatically when that gate is green; failures leave it open
until the underlying issue is fixed on
main.