55 Commits
Author SHA1 Message Date
Mohamed Boudra c67b7158b4 chore(release): cut 0.9.2 2026-09-24 12:05:08 +02:00
Mohamed Boudra 8186585201 chore(release): cut 0.9.1 2026-09-22 20:34:13 +02:00
Mohamed Boudra 7f7e60bcbb chore(release): cut 0.9.0 2026-09-22 12:25:40 +02:00
Mohamed Boudra e9d32a17d6 chore(release): cut 0.9.0-beta.2 2026-09-18 11:41:13 +02:00
Mohamed Boudra 7c1958f5b0 chore(release): cut 0.9.0-beta.1 2026-09-17 19:39:33 +02:00
Mohamed Boudra 047f40e623 Install npm plugins and review source updates (#4975)
* feat(plugins): unify source installation and simplify plugin settings

* feat(plugins): install npm sources and review CLI updates

Treat install selectors as one-time choices. Keep source identity separate from the installed revision, and resolve ordinary updates from the current upstream default. npm only acquires directories; plugin loading remains independent of npm.

* fix(plugins): preserve CLI diagnostics and exercise reviewed updates

Move new source capability coverage to real browser journeys. Update the requirements regression to exercise preview and apply, and preserve the existing duplicate-selector diagnostic across positional option scopes.

* test(plugins): remove unused pending helper

* test(plugins): pack registry fixtures with the Windows command runner

* test(plugins): assert failed client contribution setup

* docs(plugins): consolidate guides and document npm publishing

* docs(plugins): introduce publishing choices before npm instructions

* test(plugins): own browser journey cleanup in a fixture

* feat(plugins): prepare scaffolded packages for publishing
2026-09-17 10:22:20 +02:00
Mohamed Boudra c7db3c5a8c Let plugins open external links and workspace browsers (#4972)
* feat(plugins): open external links and workspace browsers

* fix(plugins): refuse invalid links and unavailable browser workspaces

* docs(plugins): clarify external links and browser navigation
2026-09-16 23:25:17 +02:00
Mohamed Boudra a8a8da0472 fix(plugins): preserve nested provider subagent links (#4970)
* fix(plugins): preserve nested provider subagent links

The plugin adapter routed descendants to their managed root but discarded their direct parent when publishing descriptors. Preserve runtime-to-public child identities and forward optional spawning tool IDs.

* test(plugins): separate opening and live subagent scenarios

* test(server): retry terminal fixture directory cleanup

Match the daemon harness cleanup policy so transient Windows EBUSY locks do not fail terminal ownership tests after their assertions complete.
2026-09-16 22:50:58 +02:00
Mohamed Boudra 6ce6f4c961 Let plugins discover hosts and use host-targeted SDK clients (#4971)
* feat(plugins): expose hosts and host-targeted SDK clients

* test(plugins): verify exact cross-host observation cleanup

* fix(plugins): reacquire explicitly disposed host clients

* docs(plugins): clarify host client usage and lifecycle

* docs(plugins): use theme colors in host discovery example
2026-09-16 22:45:18 +02:00
paseo-bot[bot] 72e3d6957a Let plugins navigate across hosts (#4942)
* feat(plugin): support cross-host navigation

Keep route construction inside Paseo while letting plugin surfaces target a host other than the currently selected one. Calls without a server ID retain selected-host behavior.

* test(plugin): cover cross-host navigation

Exercise workspace and agent navigation against a second live daemon so the SDK contract is verified through the caller-visible route instead of mocked helper forwarding.

* test(plugin): clarify cross-host navigation journey

Keep the scenario at the caller-visible intent level while shared helpers own route waits and visibility mechanics.

---------

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-09-16 20:27:15 +02:00
Lukas BuckandMohamed Boudra 4be34f05ef fix(plugin): continue one timeline item across ACP chunks without messageId (#4701)
* fix(plugin): continue one timeline item across ACP chunks without messageId

The plugin ACP reducer minted a new fallback id for every
agent_message_chunk / agent_thought_chunk that carried no messageId, so
a streamed reply rendered as one block per chunk, splitting words and
Markdown delimiters (#4699). Keep one fallback id per chunk kind and end
it on a chunk of the other kind, a tool call, a plan, a user message, or
a turn boundary, matching the server-side ACP provider.

* fix(plugin): end the other chunk kind's item on any chunk

A chunk of one kind now ends the other kind's fallback item before the
content or id is looked at, so an explicit-id text chunk between two
id-less thoughts yields three items instead of merging the thoughts.
The test declares the streamed updates through the connector harness
instead of handling protocol frames itself.

* test(plugin): verify ACP message boundaries and rendered Markdown

* test(app): synchronize rewind setup and menu selection

---------

Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-09-15 21:25:27 +02:00
Matt CowgerandMohamed Boudra 0c5f472da1 fix(plugins): expose server settings access (#4674)
* fix(plugins): expose server settings access

* fix(plugins): isolate settings subscriber state

* test(plugins): read persisted settings before clients connect

* test(app): reconnect seed client after old daemon restart

* test(server): drain file creation events before deletion assertions

* test(creation): expose errors when completion fails

---------

Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-09-14 23:03:20 +02:00
Mohamed Boudra b8e24677e1 chore(release): cut 0.8.0 2026-09-10 12:17:08 +02:00
Mohamed Boudra 0b3d82a469 feat(plugin): add shared header buttons and composer controls (#4577)
* feat(plugin): add shared header buttons and composer controls

* fix(plugin): isolate button registration imports and settle captures

* test(plugin): express button journeys through domain helpers
2026-09-09 15:39:12 +02:00
Mohamed Boudra 158525624f fix(acp): keep vendor configuration notifications atomic (#4479)
ACP SDK extension dispatch can finish after a later response, letting vendor config notifications escape commit or rollback depending on pipe chunking. Consume synchronous vendor transforms at the stream boundary before forwarding later responses, and reproduce coalesced single and burst notifications through a real subprocess.
2026-09-08 12:59:09 +02:00
Mohamed Boudra 4eab53e24e chore(release): cut 0.8.0-beta.1 2026-09-08 10:19:53 +02:00
1f5b6143d6 Enforce shared, client, and server plugin SDK boundaries (#4347)
* fix(plugins): keep React out of the server plugin host

* fix(plugins): align SDK guidance and tests with runtime boundaries

* fix(plugin): loosen the react peer to ~19.1.0

The plugin SDK pins its react peer to exactly 19.1.0, and that pin is
what a downstream `npm i -g @getpaseo/cli` resolves against. So every
user ends up with react 19.1.0 in their global node_modules, where
corporate dependency scanners flag it: the CVE-2025-55182 advisory range
covers 19.1.0 even though the vulnerability itself lives in
react-server-dom-webpack/-parcel/-turbopack, none of which the CLI
installs. The user is then left hand-patching node_modules or
uninstalling Paseo.

Widening the peer to ~19.1.0 lets a downstream install pick the latest
19.1.x patch (19.1.9 today) while this repo stays on 19.1.0, which the
root override still pins.

Keeping the repo itself on 19.1.0 is deliberate. react-native 0.81.5
embeds a 19.1.0 Paper renderer that hard-throws "Incompatible React
versions" on any exact mismatch, and it is reachable from Fabric builds
too: RendererImplementation.js requires the Paper shim unconditionally
in findNodeHandle, unstable_batchedUpdates, sendAccessibilityEvent and
three other entry points, and react-native-gesture-handler re-exports
findNodeHandle. Bumping the app's react is therefore not safe until
react-native moves.

The published CLI tree contains no react-native, so downstream installs
are not subject to that constraint.

Supersedes the earlier 19.1.5/19.1.9 version-bump approach on this
branch, which would have broken the mobile app.

* Enforce example import ownership including client types

* Update Nix dependency hash for plugin peer metadata

* Group plugin APIs under client and server entry trees

* Resolve plugin type dependencies through declarations

* Validate declarations for implicit plugin type imports

* Preserve optional imports and validate declaration references

* Refresh Nix hash for plugin declaration resolver

* Canonicalize plugin paths consistently on Windows

* Normalize plugin ownership paths across resolver boundaries

---------

Co-authored-by: liujin0506 <liujin0506@qq.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-09-07 23:42:28 +02:00
LiuJingandMohamed Boudra 56fd50988c fix(plugin): loosen the react peer to ~19.1.0 (#4100)
* fix(plugin): loosen the react peer to ~19.1.0

The plugin SDK pins its react peer to exactly 19.1.0, and that pin is
what a downstream `npm i -g @getpaseo/cli` resolves against. So every
user ends up with react 19.1.0 in their global node_modules, where
corporate dependency scanners flag it: the CVE-2025-55182 advisory range
covers 19.1.0 even though the vulnerability itself lives in
react-server-dom-webpack/-parcel/-turbopack, none of which the CLI
installs. The user is then left hand-patching node_modules or
uninstalling Paseo.

Widening the peer to ~19.1.0 lets a downstream install pick the latest
19.1.x patch (19.1.9 today) while this repo stays on 19.1.0, which the
root override still pins.

Keeping the repo itself on 19.1.0 is deliberate. react-native 0.81.5
embeds a 19.1.0 Paper renderer that hard-throws "Incompatible React
versions" on any exact mismatch, and it is reachable from Fabric builds
too: RendererImplementation.js requires the Paper shim unconditionally
in findNodeHandle, unstable_batchedUpdates, sendAccessibilityEvent and
three other entry points, and react-native-gesture-handler re-exports
findNodeHandle. Bumping the app's react is therefore not safe until
react-native moves.

The published CLI tree contains no react-native, so downstream installs
are not subject to that constraint.

Supersedes the earlier 19.1.5/19.1.9 version-bump approach on this
branch, which would have broken the mobile app.

* Update Nix dependency hash for plugin peer metadata

---------

Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-09-07 21:40:30 +02:00
Mohamed Boudra 9c8d775895 Let plugins customize agent creation and observe lifecycle events (#4435)
* feat(plugins): expose agent and workspace lifecycle hooks

* test(plugins): verify lifecycle examples with version requirements

* fix(plugins): publish archive hooks for closed agents

* test(plugins): remove diagnostic catch from lifecycle test

* refactor(plugins): define hook handlers in lifecycle module
2026-09-07 19:17:42 +02:00
Mohamed Boudra dd7671be20 Remember model selections across workspace creation (#4401)
* fix(providers): remember model selections across workspace creation

Keep saved model intent independent of catalogue readiness and use the caller-owned host and directory from the first render. Share discovery through provider-owned keys while preserving execution context.

Give each target one committed snapshot, publish actual transitions atomically, and share catalogue content across client directory associations. Regression coverage exercises successful repeated creation and provider lifecycle boundaries.

* fix(providers): preserve shared cache bodies during eviction

Admit directory references with their bodies, retain reference recency during eviction and recovery, and remove the unused persisted index. Refresh known workspace catalogues concurrently.

Make repeated-creation picker states explicit and seed mode tests from the installed provider catalogue, preserving assertions across Codex versions.

* fix(providers): bound discovery independently per provider

Settings refresh repairs all remembered targets, which otherwise starts an unbounded number of discoveries. Admit work after catalogue-key deduplication and keep each provider’s budget independent so a stalled provider cannot block another. Preserve active accounting across configuration changes and settle obsolete queued requests during replacement and shutdown.
2026-09-06 20:29:41 +02:00
Mohamed Boudra 38c22139bb Let plugins control modal layout, scrolling and clipboard actions (#4392)
* feat(plugins): support modal body layout, scrolling and clipboard actions

* fix(app): keep native scroll dependencies out of web imports

* fix(nix): sync dependency hash for plugin native types

* test(plugins): express modal QA as user journeys
2026-09-06 13:48:29 +02:00
Mohamed Boudra ae80ec4252 Let plugins contribute settings screens (#4357)
* feat(plugins): contribute settings screens and shared controls

* fix(plugins): localize settings and verify shared UI consumers

* test(composer): unmount draft test roots after each case

* test(app): keep launch terminals alive until cleanup

The fixed ten-second profile lifetime could expire before a busy CI browser attached or finished revisiting the composer. Keep the real process waiting for input until project teardown terminates it.

* test(server): await the initial snapshot before a buffered ref event

Watcher setup can read checkout facts before the initial refresh publishes a snapshot. Wait for publication before injecting the event so the test consistently exercises a second refresh.
2026-09-06 10:22:26 +02:00
Mohamed Boudra 35d2457d83 Let plugins provide coding agents (#4314)
Allow trusted local plugins to register complete coding-agent providers through the existing agent lifecycle. Includes direct and ACP implementations, SVG provider icons, custom timeline items, runnable examples, and public authoring documentation.

Closes #3547
Closes #3556
2026-09-04 19:56:57 +02:00
Mohamed Boudra 20ecdee72d docs(plugins): version breaking API guides (#4245)
Keep stable v0.7 guidance available while the client-entry layout is previewed for v0.8. Existing reference and migration URLs redirect to the release that owns their content.
2026-09-02 23:45:23 +02:00
Mohamed Boudra 53d824a320 feat(plugins): split client and server plugin entries (#4206)
* feat(plugins): phase 1 split runtime entries after green gate

The compiler now builds explicit client and server entries, so runtime boundaries are source-owned instead of registration-name filtering. Compiler and runtime tests prove the entry split, directory and suffix boundaries, client Node import errors, migration failure, and client-only operation without a subprocess.

* feat(plugins): phase 2 run client entries after green gate

Explicit client and server contexts replace the mixed plugin context. The app runner owns every client registration and its idempotent removal, including late contributions and composer pills; focused contribution tests prove the removal contract, and all five migrated examples reached running on the isolated worktree daemon.

* feat(cli): phase 3 scaffold runtime split after green gate

The generated project demonstrates the required shared RPC contract, server handler, client surface, and sidebar wiring. The scaffold test and an isolated init, typecheck, install, and running check prove the phase acceptance path.

* docs(plugins): phase 4 publish migration after green gate

Gate: current docs and the paseo-plugin skill describe only explicit client/server entries. The standalone migration guide maps every former registration, and migration-doc.test.ts proves every client add* method remains represented.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Gate: compiler 13, runtime 24, typecheck, lint, and format passed. The dependency fixture proved node_modules/client was incorrectly treated as a plugin boundary.

* fixup! feat(plugins): phase 2 run client entries after green gate

Gate: client runtime and registry tests, typecheck, lint, format

* fixup! docs(plugins): phase 4 publish migration after green gate

Gate: migration docs test, paragraph audit, typecheck, lint, format

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Gate: compiler organization tests, typecheck, lint, targeted format

* fixup! feat(plugins): phase 2 run client entries after green gate

Gate: shared SDK tests, runtime tests, typecheck, lint, targeted format

* fixup! feat(cli): phase 3 scaffold runtime split after green gate

* fixup! docs(plugins): phase 4 publish migration after green gate

* fixup! feat(plugins): phase 1 split runtime entries after green gate

* docs(plugins): rewrite public plugin docs for runtime entries and mobile guardrails

* fixup! feat(cli): phase 3 scaffold runtime split after green gate

The scaffold now keeps DOM globals out of the program and declares only window.open inside client/web.ts. The scaffold test invokes tsc in a fresh process, proves the generated project passes, and proves a stray document access fails.

* fixup! docs(plugins): phase 4 publish migration after green gate

The internal guide and plugin skill now forbid both the DOM lib and triple-slash DOM references. They direct web adapters to declare only the globals used by client/web.ts.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Removing the obsolete Babel parser changes the locked npm dependency graph. The macOS Nix desktop gate reported the new fixed-output hash, which this commit records.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Reject relative imports that escape the plugin root while continuing to skip resolved node_modules internals. The compiler regression test proves the escaped import is rejected and the dependency-internal fixture remains accepted.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Classify absolute imports and reject plugin-authored paths that escape into node_modules. Dependency internals remain exempt based on their importer path. Red-first compiler regressions cover both bypasses, and the existing dependency fixture remains green.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Classify canonical esbuild resolution results so dependency-relative and symlink imports cannot escape client/server boundaries. The two compiler regressions failed before the fix and pass afterward.

* test(ci): synchronize flaky state transitions

Wait for repository watcher registration before emitting buffered ref events, let sidebar order polling retry unlaid-out rows, and wait for the inactive browser parking state before screenshot capture. These changes directly address the three observed CI failures; the affected server, Playwright, and desktop browser tests pass locally.

* test(ci): synchronize Mermaid completion layout assertion

The Playwright streaming acceptance test exposed a completion remount between visibility and layout sampling. Check the existing completion promise around the measurement so that transition is not reported as diagram loss.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Resolve bare package imports before boundary classification so symlinked dependency entries cannot expose server modules to the client bundle. The focused regression proves the bypass and the compiler file passes 20/20.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Allow canonical paths only within a matching linked package root while preserving runtime and containment checks for imports that leave it. The linked-dependency regression fails before the fix and compiler tests pass 21/21 afterward.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Reject matching package manifests that contain the plugin or live inside it, so linked-dependency roots cannot exempt plugin or workspace files. The ancestor-manifest regression fails before the fix and compiler tests pass 22/22 afterward.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Validate plugin-local lexical boundaries before canonical linked-root exemptions and require remembered-root imports to originate inside that root. The linked-root bypass regression fails before the fix and compiler tests pass 23/23 afterward.
2026-09-02 19:11:45 +02:00
Mohamed Boudra e34aea206e feat(plugins): add live timeline rows and client slash commands
Plugins can transform live timeline source items at render time, append durable plugin rows from daemon handlers, and register client-only slash commands in the composer.

Related to #3940 and #3952.
2026-09-02 13:12:09 +02:00
Mohamed Boudra 9400a49af6 chore(release): cut 0.7.2 2026-09-02 02:07:34 +02:00
Mohamed Boudra 3e59adb4dc Add Claude Fable 5.1 to the model catalog (#4179)
* feat(claude): add Fable 5.1 model

Expose the new 1M-context model through the curated Claude catalog and normalize its dated and context-suffixed runtime IDs back to one canonical entry.

* fix(claude): group Fable models in catalog

* chore(release): cut 0.7.1

* fix(nix): refresh 0.7.1 dependency hash

* test(cli): follow package version in plugin scaffold

* test(cli): keep Claude catalog checks extensible
2026-09-01 21:51:51 +02:00
Mohamed Boudra c56638ea8c chore(release): cut 0.7.0 2026-08-31 19:25:32 +02:00
Mohamed Boudra 95f4eb7577 chore(release): cut 0.7.0-beta.3 2026-08-31 09:52:59 +02:00
Mohamed Boudra fcd81f591e chore(release): carry forward 0.7.0-beta.2 version 2026-08-31 09:46:56 +02:00
Omer Cohenandpaseo-bot[bot] 1d4c3a2722 feat(plugin): add host navigation capabilities for plugin surfaces (#3901)
* feat(plugin): add optional agent navigation capability

* feat(plugin): extend host navigation capability

* fix(plugin): preserve navigation tests after sdk rebase

* fix(plugin): scope host navigation contracts

---------

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-08-29 21:33:09 +02:00
Mohamed Boudra deb846eed6 Let plugins use Paseo modals, icons, and toasts (#3967)
* feat(plugins): add React Native UI primitives

Give client plugins host-owned adaptive UI without exposing app internals. Preserve plugin runtime contexts when modal content moves through the compact portal host.

* docs(plugins): clarify host UI reference

* refactor(plugins): harden host UI boundary

Replace mocked pass-through coverage with a real browser plugin journey, keep host runtime ownership inside the evaluator, preserve toast context across compact portals, and expose modal content as a compound component.

* fix(plugins): defer native UI presentation runtime

Loading the plugin evaluator initialized the modal and toast presentation stacks even when no plugin requested host UI. Keep the capability stable while delaying platform-heavy presentation code until Modal.Content renders.

* refactor(plugins): move modal metadata to root

Keep Modal.Content body-only while the top-level Modal owns the host-rendered title and leading icon. This leaves presentation metadata stable as the compound API gains future regions.
2026-08-28 11:43:30 +02:00
Mohamed Boudra fe803f6e70 Let plugins contribute contextual composer pills (#3956)
* feat(plugin): allow composer pill contributions

Keep pill presentation host-owned while plugin components render contextual content. Pill callbacks reuse the selected host's API, RPC, surface, and registered-panel navigation capabilities without exposing router or workspace layout internals.

* fix(plugin): place composer pills in track bar

* fix(plugin): make composer pills client-owned
2026-08-28 16:02:00 +08:00
Mohamed Boudra 1860a6f3af chore(release): cut 0.7.0-beta.1 2026-08-27 19:42:09 +02:00
Omer Cohen 8e0af11b66 feat(plugin): add host-rendered Icon component (#3903) 2026-08-27 10:50:20 +00:00
Mohamed Boudra 7ae5133ed2 feat(plugins): let plugins transform and render timeline items (#3940)
* feat(plugins): add timeline item contributions

Keep canonical daemon history unchanged while plugins project and render custom timeline items in clients. Live matches refresh authoritative projected history before replacement so lifecycle deltas stay collapsed.

* fix(nix): update npm dependency hash

The plugin SDK lockfile change invalidated the fixed-output npm dependency derivation. Use the hash calculated by the macOS Nix build for the current lockfile.
2026-08-27 11:40:17 +02:00
Omer Cohen f82886f284 feat(plugin): expose semantic theme tokens (#3898) 2026-08-27 08:15:22 +00:00
Mohamed Boudra 20d7efc46a chore(release): cut 0.6.1 2026-08-25 21:50:24 +02:00
Mohamed Boudra 6da9fa43fa chore(release): cut 0.6.0 2026-08-25 15:58:11 +02:00
Mohamed Boudra c914bcb44b feat(app): make Explorer a first-class pane host (#3826) 2026-08-25 13:25:57 +02:00
Mohamed Boudra 4092c29265 chore(release): cut 0.5.2 2026-08-24 14:30:57 +02:00
Mohamed Boudra f517493591 chore(release): cut 0.5.1 2026-08-23 19:37:03 +02:00
Mohamed Boudra b8a31034ca chore(release): cut 0.5.0 2026-08-23 15:42:11 +02:00
Mohamed Boudra acad9f6881 chore(release): cut 0.5.0-beta.5 2026-08-22 23:35:40 +02:00
Mohamed Boudra f660fcf2e5 chore(release): cut 0.5.0-beta.4 2026-08-21 16:35:11 +02:00
Okan YILDIRIM e1805ef542 feat(plugins): add theme contributions
Adds plugin-contributed light and dark app themes, including persistence, host-aware coalescing, fallback behavior, documentation, examples, and browser/iOS QA.
2026-08-21 11:48:12 +02:00
Mohamed Boudra dd8efba552 chore(release): cut 0.5.0-beta.3 2026-08-20 09:12:30 +02:00
Mohamed Boudra ccd991b3e3 chore(release): cut 0.5.0-beta.2 2026-08-19 16:30:43 +02:00
Mohamed Boudra 929d2b717e fix(plugins): publish the plugin SDK under the @getpaseo scope
The daemon imports the plugin SDK at runtime, but the workspace was named
@paseo/plugin and marked private, so it was never published. 0.5.0-beta.1
shipped @getpaseo/server depending on a package that does not exist and
`npm install @getpaseo/cli@beta` failed with a 404 for every user.

Two things hid it. sync-workspace-versions only rewrites deps matching
@getpaseo/*, so the "*" range on @paseo/plugin survived untouched into the
published tarball. And `npm pack --dry-run`, which is all release:check and
CI run, lists files without ever resolving the dependency graph.

Renaming to @getpaseo/plugin rather than publishing the old name: @paseo is
not our scope on npm, so that name could never have been published at all.
The rename also brings the package under the version-sync filter, which is
what pins it correctly from here on.

Plugins scaffolded against the old name still import @paseo/plugin. Both
spellings resolve through plugin-sdk-specifiers.ts, tagged COMPAT with a
removal date, so existing plugins keep loading.

This does not repair 0.5.0-beta.1 — a published tarball's dependencies
cannot be changed. The package publishes with the next release.
2026-08-19 15:52:51 +02:00