Commit Graph

39566 Commits

Author SHA1 Message Date
wahaha1223
0e85bfa632 fix(gmail): preserve bind errors across watcher output chunks (#106314)
* fix(gmail): preserve bind errors across watcher output chunks

Carry a bounded stderr tail across data chunks so the address-in-use
marker is detected even when split across chunk boundaries. Without
this, the exit handler misses the bind failure and enters an endless
5-second respawn loop instead of stopping restarts.

* fix(gmail): decide watcher restarts after stderr close

Restart policy waited on exit while Node can still deliver the final
stderr fragment afterward, so a split bind marker could still respawn.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(gmail): classify untruncated combined stderr before bounded tail retention

* fix(gmail): avoid retries after watcher spawn failures

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-20 21:01:50 -07:00
zengLingbiao
e679566a1c fix(sessions): reject non-UTF-8 session-state notice context keys (#111152)
* fix(sessions): reject non-UTF-8 session-state notice context keys

* fix(sessions): preserve leading U+FEFF in notice context keys

* fix(state): reject corrupt legacy watch markers

Co-authored-by: zenglingbiao <zeng.lingbiao@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-20 20:54:32 -07:00
Jason (Json)
8968e955f7 fix(plugins): restore external runtime imports (#112021)
* fix(plugins): restore external runtime imports

* fix(plugins): support synthetic runtime builds

* test(plugins): type synthetic runtime plan
2026-07-20 21:45:23 -06:00
Alix-007
3c10513c72 fix(doctor): bound legacy launchd cleanup (#109116)
* fix(doctor): bound legacy launchd cleanup

Bound both legacy launchctl cleanup attempts to five seconds while keeping the existing bootout-then-unload order. Only move the legacy plist after a successful command or an explicit already-unloaded result; leave it in place when timeout or another failure prevents confirmation so a later doctor run can retry. Report filesystem cleanup failures as skipped instead of claiming removal.

* fix(doctor): verify launchd cleanup state before moving plist

Poll a bounded launchctl print postcondition after legacy bootout and unload. Treat loaded, unknown, and timed-out probes as unconfirmed so doctor leaves the plist available for retry.

* test(doctor): clarify launchd cleanup postcondition

* fix(doctor): reject timed-out launchd probes

Preserve timeout evidence from the command runner, including sanitized timeout messages, so partial not-loaded output cannot authorize plist removal.

* fix(doctor): classify no-output launchd timeouts

* test(doctor): trim launchd cleanup cases

Co-authored-by: Alix-007 <li.long15@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-20 20:36:04 -07:00
Peter Steinberger
afb0d77fd1 fix(agents): give cold prepared-model-runtime builds a 120s startup budget (#111983) 2026-07-20 20:33:55 -07:00
juyaohuidt
9061aa8ea8 fix(plugin-sdk): bound Anthropic Vertex ADC credential read (#111120)
* fix(plugin-sdk): bound Anthropic Vertex ADC credential read

canReadAnthropicVertexAdc read the entire GOOGLE_APPLICATION_CREDENTIALS
file via unbounded fs.readFileSync solely to check readability (the
contents are discarded). The provider-local copy of this logic in
extensions/anthropic-vertex/region.ts was already bounded with
tryReadSecretFileSync + a 1 MiB limit in #109260 ("reject oversized
credential files in remaining readers"); this standalone plugin-sdk
preflight helper is a duplicate that sweep missed.

Mirror the region.ts bound: replace the unbounded readFileSync with
tryReadSecretFileSync(..., { maxBytes: 1 MiB, rejectHardlinks: false }),
wrapped in try/catch (the helper throws FsSafeError on oversize) so an
oversized credential file is rejected instead of slurped into memory.
Presence-check semantics are unchanged for normal-sized files.

* refactor(plugin-sdk): remove orphaned vertex auth helper

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-20 20:24:35 -07:00
Peter Steinberger
67a15e63e2 feat(dashboard): widget presentation contract, shared frame inset, and intrinsic auto height (#111977)
* feat(boards): widget presentation contract and height mode in protocol, store, and show_widget

* feat(dashboard): card inset, full-bleed and frameless presentations plus intrinsic auto height

* fix(boards): preserve frame prefs on re-pin, pin legacy resizes, reserve touch chrome in auto height

* fix(dashboard): pass pointer events through the overlay bar to widget content

* fix(dashboard): re-layout auto-height rows when pointer capability changes

* refactor(dashboard): extract widget sizing policy module and split oversized board tests

* test(dashboard): complete frame lifecycle host stubs for content-height callback

* fix(dashboard): fold sizing into grid module, scope bar interactivity, drop useless fixture escape

* fix(dashboard): inline widget-size literal and drop orphan sizing module for startup budget
2026-07-20 20:18:27 -07:00
Drew Meyer
51b31fc131 fix(codex): timed-out native hooks exhaust memory (#109446)
* fix(codex): prevent native hook relay orphans

* test(codex): prove native relay timeout ownership

* fix(codex): keep source hook relay pid stable

* test: auto-clean compile-cache fixtures

Co-authored-by: Drew Meyer <dr00-eth@users.noreply.github.com>

---------

Co-authored-by: Drew Meyer <dr00-eth@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-20 20:07:11 -07:00
ClawSweeper
5dd7780b1d fix(gateway): retry catalog reads after config reload (#112026)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-20 20:04:21 -07:00
cxbAsDev
831499e069 fix(doctor): bound legacy session store read with size cap and stat-failure fix (#110772)
* fix(doctor): bound legacy session store read with size cap

* fix(doctor): handle ENOTDIR in legacy store stat preflight

statSync with throwIfNoEntry: false returns undefined for paths whose
parent is a regular file (ENOTDIR) rather than throwing. When a store
appears missing but its parent directory is not a directory, report
store_unreadable instead of silently treating it as an absent store.

* fix(doctor): use descriptor-bound read for legacy session store files

Replace statSync-then-readFileSync with openSync/fstatSync/readFileSync on the
same file descriptor to eliminate the TOCTOU race where a file can change
between size validation and read.

* fix(doctor): bound descriptor read to validated byte count in session store migration

Replace readFileSync(fd, 'utf-8') — which reads to EOF — with a
descriptor-bound loop that reads at most the byte count validated by
fstatSync. This prevents same-inode growth after fstat from bypassing
the 50 MiB per-store cap and causing OOM.

* style(doctor): add braces for curly lint rule

* fix(doctor): remove hard 50 MiB cap to preserve legacy migration compatibility

* fix(doctor): open legacy session store with O_NONBLOCK to prevent FIFO blocking

* refactor(doctor): reuse bounded descriptor reader

* fix(doctor): report descriptor stat failures

---------

Co-authored-by: OpenClaw Agent <agent@openclaw.ai>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-20 19:55:09 -07:00
mikasa
b6865e9bce fix(gateway): forward tool abort signals (#104093)
* fix(gateway): forward tool abort signals

Forward the tool execution cancellation signal to gateway config reads.

Refs #104093; credit @mikasa0818.

Co-authored-by: mikasa0818 <0668001030@xydigit.com>

* style(gateway): format abort signal test

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-20 19:52:17 -07:00
YangManBOBO
f8380a8324 fix: route bindings return the wrong agent when route identifiers contain separators (#110001)
* fix(routing): replace delimiter-concatenated route cache key with JSON-serialized tuple to prevent identifier collisions

* fix(routing): restore dmScope input contract alongside JSON cache key fix

* fix(routing): restore max-lines exemption on resolve-route.ts

* test(routing): correct cache collision regression

Co-authored-by: YangManBOBO <152153397+YangManBOBO@users.noreply.github.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: YangManBOBO <152153397+YangManBOBO@users.noreply.github.com>
2026-07-20 19:45:11 -07:00
Peter Steinberger
2bb6b870aa fix: preserve Codex-bound conversation history across session rotation (#112016)
* fix: preserve Codex conversation bindings

* test: harden Codex live gateway stress

* chore: refresh plugin SDK API baseline

* test: tighten restart recovery fixtures

* test: complete restart recovery recorder fixture

* fix: keep plugin command authorization fail closed

* fix: restore private queue source imports

* fix: keep private SDK imports inside core

* fix: keep speech core on private SDK paths

* fix: stage private SDK exports for live tests

* fix: preserve plugin-bound gateway history

* fix: block transcript writes after binding races

* test: complete live stage helper contracts

* test: align binding mocks with current types

* fix: preserve compiler OOM diagnostics

* chore: move release notes to PR context
2026-07-20 19:42:56 -07:00
Peter Steinberger
4ff2cdd215 fix(sessions): reconcile client-abandoned running sessions on restart (#111699) 2026-07-20 19:39:41 -07:00
Yuval Dinodia
83ca537778 fix(shared): block prototype-polluting keys in JSON schema default hydration (#107978)
A plugin manifest configSchema declaring a __proto__ property polluted
Object.prototype process-wide during ordinary plugin config validation.
applyObjectPropertyDefaults iterated schema-controlled property keys and
wrote into value[key] with no blocked-key guard; because manifests are
parsed JSON, __proto__ is an own key and value["__proto__"] dereferences
the inherited Object.prototype, onto which nested defaults were written.

Skip __proto__, prototype, and constructor at each write site in
applyObjectPropertyDefaults, reusing the existing isBlockedObjectKey
helper already used at other untrusted object-write boundaries.
2026-07-20 19:33:31 -07:00
Peter Steinberger
b61619e03c fix(gateway): dev mode suppresses ambient channel env auto-configuration (#112011)
Channel plugins declare package env triggers in manifests, and ambient
environment variables count as "channel configured". A dev gateway
(gateway run --dev) inherits the operator's shell, so real channel
credentials silently configured channels and could connect development
instances to live services (observed with reef and a Telegram-range
connection during isolated stress testing).

Dev mode now drops presence signals whose only sources are env or
manifest-env across activation planning, auto-enable, autostart, health
recovery, readiness, reloads, and startup warnings. Explicit
channels.<id> config still works, --dev-ambient-channels restores the
old behavior, and startup logs the suppressed channel ids once. Non-dev
gateways are unchanged.
2026-07-20 19:28:07 -07:00
Peter Steinberger
64607ba63d feat: show cloud workspace conflicts in the Control UI (#111329)
* feat(ui): surface cloud workspace conflicts with staged-ref guidance

* fix(ui): satisfy workspace conflict CI checks

* fix(ui): reject terminal controls in conflict paths

* chore: keep release notes out of the PR

* fix(ui): satisfy conflict path lint

* fix(ui): retain cloud conflicts after reclaim

* fix(ui): keep child conflict badges visible

* test(ui): cover cloud conflict recovery in browser

* fix(ci): rotate poisoned dependency snapshot

* fix(ui): keep nested cloud conflicts discoverable

* docs(changelog): note cloud conflict UI

* fix(ui): restore conflict UI budget headroom
2026-07-20 19:27:19 -07:00
sunlit-deng
562babf2aa fix(agents): keep session tool stderr tails UTF-8 safe (#108012)
* fix(agents): keep session tool stderr tails UTF-8 safe

* refactor(agents): reuse UTF-8 tail truncation

Co-authored-by: sunlit-deng <yang.jiajun1@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-20 19:22:41 -07:00
Peter Steinberger
e3488e1d73 fix(setup): preserve invalid config during baseline setup (#112010) 2026-07-20 19:15:51 -07:00
chengzhichao-xydt
2a20555a1c fix(plugins): accept npm 12 json shapes in install metadata reads (#109699)
npm 12 wraps `npm view --json` results in a singleton array and keys
`npm pack --json` results by package name instead of returning an entry
array. The install metadata readers assumed the npm <=11 shapes, so on
npm 12 plugin install/update metadata reads failed closed with
incomplete-metadata errors or silently dropped resolved metadata, and
worker release verification rejected published packages.

Normalize both shapes in one shared resolver and use it for plugin
install pack reads and worker bundle release verification.

Co-authored-by: chengzhichao-xydt <264300353+chengzhichao-xydt@users.noreply.github.com>
2026-07-20 19:10:47 -07:00
wahaha1223
80c16ecc80 fix(tui): prevent memory growth during repeated selector searches (#109451)
* fix(tui): bound searchable select regex cache

* fix(tui): discard stale selector regexes

Co-authored-by: wahaha1223 <0668001153@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-20 19:06:58 -07:00
Peter Steinberger
3d841472bd fix(cli): align configure and channel wizard behavior (#111720) 2026-07-20 18:22:57 -07:00
Josh Avant
33720886c8 fix(agents): keep sender-scoped tools in delegated runs (#110345)
* fix(agents): preserve requester policy across delegation

* fix(agents): keep policy types internal

* test(codex): cover delegated policy forwarding

* fix(agents): harden delegated policy contract

* fix(agents): preserve split-owner completion policy

* fix: version delegated requester snapshots

* fix: refresh delegated policy protocol artifacts
2026-07-20 19:42:06 -05:00
Dallin Romney
cde2e71d43 fix(deps): update axios past proxy advisory (#111984) 2026-07-21 09:21:14 +09:00
Peter Steinberger
49ff6a4497 fix(protocol): classify system agents in rosters (#111920)
* fix(protocol): classify system agents in rosters

* fix(ci): align agent kind client gates

* fix(ci): keep roster filtering in startup bundle

* chore(ui): remove superseded roster module

* style(android): satisfy agent kind test formatting

* style(linux): format merged agent capability assertion

* fix(config): reject reserved system agent ids

* chore(i18n): refresh native source inventory

* style(android): format agent kind surfaces

* fix(gateway): preserve configured agent ownership
2026-07-20 16:57:04 -07:00
Peter Steinberger
d5a8233e76 feat(gateway,ui): agent-scoped model provider credentials (#111796)
* feat: scope model provider credentials by agent

* fix: reject unknown model auth agent ids

* fix: discard stale model provider preload

* fix(gateway): reject whitespace-only explicit agent ids

* fix: scope model provider probes by agent

* fix: complete model provider agent scoping

* fix(ui): always clear the providers refresh flag on completion

* fix(ui): keep probe epochs monotonic across agent switches

* fix: derive agent scope for provider aborts

* fix: satisfy model provider CI checks
2026-07-20 16:22:09 -07:00
Peter Steinberger
7cafe35bd5 fix(onboard): never silently remap an existing fleet's workspaces (#111787)
* fix(onboard): preserve agent workspaces on rerun

* fix(onboard): preserve first-run workspace setup
2026-07-20 16:16:04 -07:00
Josh Avant
e07de4c561 fix(agents): avoid synthetic overflow in tool-heavy sessions (#110297)
* fix(agents): defer context overflow to provider

* fix(agents): align provider pressure tests

* fix(agents): make overflow recovery authoritative

* test(agents): preserve generic compaction lock type

* test(agents): auto-clean SQLite compaction fixture

* test(agents): keep compaction guard private

* fix(agents): keep overflow recovery ownership internal

* fix(agents): preserve session sdk type exports
2026-07-20 17:49:12 -05:00
mushuiyu886
0e6561bc2c fix(docs): search live docs with unrelated invalid config (#111803) 2026-07-20 15:49:03 -07:00
Josh Lehman
a2a0e932c8 fix: preserve transcript cursors across embedded turns (#111949)
* fix(agents): preserve transcript generation on flush (clawdbot-85c)

* fix(agents): preserve SQLite transcript parents (clawdbot-85c.5)

* test(agents): prove cursor continuity through Gateway (clawdbot-85c.7)
2026-07-20 15:41:31 -07:00
Peter Steinberger
4aaea7b072 fix(ui): persist synchronized sidebar preferences (#111929) 2026-07-20 11:11:45 -07:00
Peter Steinberger
bf6961d33e fix(ui): actionable diagnostics for missing widget sandbox origin behind proxies (#111909)
* fix(ui): actionable error and doctor warning for missing widget sandbox origin

* fix(ui): phrase sandbox-origin diagnostics as conditional guidance

* test(ui): satisfy deadcode and mock-factory gates for sandbox diagnostics
2026-07-20 10:35:47 -07:00
Peter Steinberger
febeb6ac12 fix(dashboard): review follow-ups — provider lifecycle, scope-gated controls, pin robustness (#111907)
* fix(canvas): validate dashboard pins before materializing

* fix(ui): bound dashboard provider lifecycles

* fix(gateway): harden board widget descendant frames

* test(ui): clean up dashboard provider tests

* fix(ui): harden dashboard availability lifecycle

* fix(ui): invalidate stale board availability

* perf(ui): keep dashboard bundle within budget
2026-07-20 10:27:52 -07:00
Peter Steinberger
59bc78a5b2 fix(gateway): auto-approve trusted-proxy same-key device scope upgrades (#111916) 2026-07-20 10:25:25 -07:00
Peter Steinberger
c715d89de8 fix(ui): show newly created PRs in session sidebar (#111783)
* fix(ui): surface newly created session PRs

* test(ui): split sidebar PR state coverage
2026-07-20 10:04:41 -07:00
Josh Lehman
f0b18c1d74 fix(session): clear orphaned recovery fences (#111869) 2026-07-20 09:32:37 -07:00
Peter Steinberger
324fd4fe6e refactor(channels): delete all media-placeholder consumer awareness (#111891)
Final sweep of the media-placeholder program: with all eleven producers
migrated, the consumer regexes are dead input and are hard-deleted —
media-understanding token un-mixing (bodies are pure user text now),
the persisted-user-turn placeholder drop, the session-memory strip, and
the inbound-event mediaPlaceholder field with its replacement branch
(an intentional removal of shipped SDK surface per the program's
hard-delete decision: plugins still minting placeholder bodies render
them as literal text until updated). formatMediaPlaceholderText remains
the single owner of the <media:kind> vocabulary, used only for
text-only carriers. Legacy tokens in pre-migration history remain
ordinary text.
2026-07-20 09:31:51 -07:00
ooiuuii
570f8b4d9e fix(errors): recover from Z.AI token-limit errors (#111744)
* fix(errors): classify Z.AI token-limit overflow

* fix(zai): own token overflow classification

* fix(zai): classify live prompt overflow errors

---------

Co-authored-by: Altay <altay@hey.com>
2026-07-20 17:11:40 +03:00
Peter Steinberger
154a49a78b feat(dashboard): widget capabilities — net CSP, host tools, hardened shared sandbox (#111687)
* feat(boards): enforce widget capability grants

* feat(ui): expose dashboard widget capabilities

* test(dashboard): align rebased gateway contracts

* refactor(dashboard): finish capability alignment

* chore: internalize unused board sandbox exports

* test(dashboard): bind grants to widget instances

* fix(dashboard): close widget authority lifetime gaps

* refactor(ui): preserve modular widget frame ownership

* test(ui): preserve explicit MCP App lease refresh

* test(ui): use shared dashboard sandbox server

* fix(ui): settle widget requests on reconnect

* fix(ui): retry transient widget document loads

* refactor(dashboard): document WebRTC egress residual

* fix(dashboard): clear landing checks
2026-07-20 06:54:44 -07:00
Leon-SK668
8d4d02a3cf fix(usage): guard malformed Z.AI usage payloads (#110741)
* fix(usage): guard malformed Z.ai usage payloads

* fix(usage): normalize Z.ai usage payloads

* fix(usage): preserve empty Z.ai snapshots

---------

Co-authored-by: Leon-SK668 <17695126+Leon-SK668@users.noreply.github.com>
Co-authored-by: Altay <altay@hey.com>
2026-07-20 16:14:42 +03:00
xingzhou
8518603392 fix(tts): contract speech requests hang on stalled endpoints (#110725)
* fix(tts): time out stalled contract speech requests

* fix(tts): release discarded contract responses

* test(tts): reuse canonical HTTP server helper

* test(tts): mark fixture credential synthetic

---------

Co-authored-by: Mason Huang <masonxhuang@proton.me>
2026-07-20 21:02:39 +08:00
Peter Steinberger
0ac7b24d56 feat(channels): batch 3 producers drop media placeholder bodies (#111800)
* feat(channels): batch 3 producers drop media placeholder bodies

Media-placeholder program batch 3: iMessage, Signal, and WhatsApp stop
minting <media:kind> placeholder bodies. Primary bodies carry captions
only with one aligned structured fact per native attachment (type-only
for missing/rejected/oversized). Echo, quote, and pending-history
records gain structured media facts so dedupe and previews work without
rendered text. Signal media-only outbound sends drop the placeholder
body entirely (signal-cli accepts attachment-only sends; recipients now
see just the media). WhatsApp audio preflight de-stringed; qa-lab
sticker recognizer moved to structured facts; WhatsApp gif maps to
video (presentation mode, no behavioral consumer).

* test(whatsapp): keep media classifier private
2026-07-20 04:22:44 -07:00
Pavan Kumar Gondhi
8d5ad804a6 fix: require admin for keyed session model changes [AI] (#111651)
* fix: gate keyed session model creation

* fix: preserve fresh keyed session creation

* fix: cover existing keyed model adoption

* fix: keep keyed session create idempotent

* fix: resolve keyed session model aliases before gating

* fix: tighten keyed session gate comparison

* docs: clarify keyed session gate fail-closed cases

* docs: explain keyed session model error gating

* fix: clarify unscoped keyed session gate

* fix: share session model patch resolution

* fix: preserve existing auth profile no-op creates

* fix: resolve subagent defaults in keyed session gate
2026-07-20 16:33:33 +05:30
Peter Steinberger
8bec61bab1 fix(cli): reject unknown models in config set (#111571)
* fix(cli): validate configured model references

* fix(cli): cover dependent model references

* fix(cli): reject malformed model references

* test(cli): satisfy model validation gates

* fix(cli): harden model validation drafts

* fix(cli): preserve model runtime ownership

* fix(cli): validate expanded model references

* fix(cli): close model validation gaps

* fix(cli): preserve model alias ownership

* fix(cli): secure model validation context

* fix(cli): redact expanded model references

* fix(cli): validate inherited agent catalogs

* fix(cli): align canonical model resolution

* fix(cli): scope inherited catalog validation

* fix(cli): compare resolved model snapshots

* fix(cli): cover model dependency transitions

* fix(cli): isolate model env validation

* fix(cli): redact dependent model checks

* fix(cli): compare agent model identity

* fix(cli): validate expanded model removal

* fix(cli): defer unresolved fallback validation

* fix(cli): preserve authored model changes
2026-07-20 03:31:03 -07:00
Leon-SK668
fab745b82d fix(infra): guard malformed Claude usage responses (#111088)
* fix(infra): guard malformed Claude usage responses

* fix(infra): normalize Claude usage payloads

Co-authored-by: Leon-SK668 <17695126+Leon-SK668@users.noreply.github.com>

---------

Co-authored-by: Leon-SK668 <17695126+Leon-SK668@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-20 03:26:33 -07:00
Peter Steinberger
7fe1d70a50 fix(mcp): prevent custom server config races and parsing errors (#111761)
* fix(mcp): harden custom server management

* fix(ui): keep config patch types private
2026-07-20 02:34:13 -07:00
Peter Steinberger
7defd4a7b4 refactor(android): split offline client databases (#111710)
* refactor(android): split offline client databases

* chore(android): refresh native i18n inventory

* test(gateway): close databases before transcript cleanup
2026-07-20 02:28:02 -07:00
Peter Steinberger
473f2aca33 fix(gateway): honor session title model routing (#111757)
Route automatic dashboard titles through the effective session model and auth profile while preserving explicit cross-provider utility ownership and the existing generic title fallback.
2026-07-20 02:19:57 -07:00
Peter Steinberger
fd081d6521 fix(ui): keep Codex commentary visible after runs (#111648)
* fix(ui): keep Codex commentary visible

* fix(codex): preserve commentary transcript ordering

* fix(ui): preserve commentary retention opt-out
2026-07-20 02:10:56 -07:00
zengLingbiao
2c05365fbd fix(plugins): decode self-hosted discovery JSON with fatal UTF-8 validation (#111113)
* fix(plugins): decode self-hosted discovery JSON with fatal UTF-8 validation

* ci: retrigger checks

* refactor(plugins): share self-hosted JSON decoding

Co-authored-by: zenglingbiao <zeng.lingbiao@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-20 02:07:45 -07:00