* fix(auto-reply): don't block reply completion on transcript mirror
mirrorTranscriptAfterDispatcherDelivery() opens with
`await dispatcher.waitForIdle()` and is awaited from sendFinalPayload,
i.e. before the reply operation completes. When a follow-up is queued
(queueDepth>=2) the dispatcher cannot go idle until this operation
clears, and the operation cannot clear until sendFinalPayload returns —
a completion<->dispatcher-idle deadlock. The reply operation stays
phase=running and surfaces as stalled_agent_run (recovery=none) until
the ~15-minute stuck-session abort.
Run the transcript mirror as fire-and-forget: it is post-delivery
bookkeeping and does not need to gate completion. The operation
completes immediately, the queued follow-up proceeds, the dispatcher
goes idle, and the detached mirror then records the transcript.
Validated live across three backends (LMStudio openai-completions,
claude-cli, DeepSeek cloud): queueDepth 2 and 3, zero stalls.
* test(auto-reply): regression coverage for detached transcript mirror
Two focused tests on the queueDepth>=2 completion<->dispatcher-idle deadlock:
- final reply completes even when dispatcher.waitForIdle() never resolves
(pre-fix this awaited-mirror path deadlocked; the test hangs on the old code).
- a rejecting background mirror does not fail the reply (the detached .catch
contains it instead of surfacing an unhandled rejection).
* fix(auto-reply): settle transcript mirrors after delivery
Co-authored-by: Zaytsev Ivan <ivan@jad.ru>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(plugins): resolve public artifacts from installed plugin roots
Externalized official plugins ship gateway-auth/doctor/secret/message-tool
public artifacts in their installed npm package, but the public-surface
loader only searched bundled locations. On packaged installs this emptied
the gateway auth bypass set for /api/channels/mattermost/command, so every
Mattermost slash callback was rejected 401 before the plugin ran.
Fall back to the plugin's installed package root (install-record index)
when bundled resolution misses; bundled/source locations keep precedence.
Fixes#98740
* fix(gateway): resolve channel auth-bypass artifacts via activation-gated facade
Route gateway-auth bypass artifact loading through the activation-gated
facade seam so externalized (installed) channel plugins keep their
unauthenticated callback paths, while denied/disabled plugins contribute
nothing and never execute artifact code. Reject hardlinked artifacts for
installed plugin roots in the facade loader per hardlink-policy.
Replaces the earlier install-record fallback in the shared bundled
artifact loader, which broadened every artifact surface past its trust
boundary (see PR review); other externalized-plugin surfaces are #98842.
Fixes#98740
* fix(gateway): load facade activation runtime via async lazy import
The sync createRequire/jiti candidate loader for
facade-activation-check.runtime.js only resolves from built dist; under
vitest source runs it cannot follow .js->.ts specifiers or workspace
package subpaths, so the gateway auth-bypass path threw 'Unable to load
facade activation check runtime' (CI shard failure). Add an async
activated-load API backed by dynamic import (vitest- and dist-native),
warm the shared runtime memo, and make the bypass consumer async - its
single caller already awaits behind the config-snapshot promise cache.
* revert(plugins): drop install-record fallback from public-surface loader
Superseded by the activation-gated facade seam in gateway-auth-bypass;
review found the loader-level fallback broadened every artifact surface
past its trust boundary (see PR #98819 discussion).
* fix(plugin-sdk): drop unused oxlint disable on async facade loader
* test(plugin-sdk): cover facade hardlink policy for installed vs core roots
* test(plugin-sdk): cover facade hardlink policy for installed vs core roots
---------
Co-authored-by: Alex Knight <15041791+amknight@users.noreply.github.com>
Treat wrapped shell-profile permission failures as an optional doctor repair,
while preserving non-permission failures and the actual failing profile path.
Cover install and upgrade flows across EACCES, EPERM, and EROFS.
Fixes#99237
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(file-transfer): don't inline zero-byte files as image content blocks
file_fetch's remote mime detection falls back to the extension-derived
MIME type when content sniffing finds nothing to sniff (a zero-byte
buffer), so fetching an empty .png/.jpg/.webp/.gif produces
{type: "image", data: "", mimeType: "image/png"}. That payload-less
image block reached the model as an unrecoverable placeholder instead
of the existing "saved at <path>" text fallback used for every other
non-inlined file. Require a non-empty payload before treating a fetch
as an inline image. Refs #98673.
* test(file-transfer): prove empty image fetch fallback
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(build): forward default exports through stable runtime aliases
runtime-postbuild writes stable aliases (`X.runtime.js`) for hashed runtime
chunks as bare `export * from "./X-HASH.js"` — but `export * from` never
re-exports `default`. In the shipped 2026.6.10/2026.6.11 artifacts the
compaction runtime alias points at a chunk whose only export is default, so
its lazy consumer destructuring `{ default: reconcile }` gets undefined:
every successful auto-compaction logs "late compaction count reconcile
failed: TypeError: reconcile is not a function" and the persisted
compactionCount never updates. On local-model deployments the resulting
repeat compactions each cost a multi-minute full re-prefill (observed:
3 compactions in 25 minutes). Other stable aliases are latent instances of
the same generator defect for any target that gains a default export.
Fix: when the alias target has a default export, also emit
`export { default } from ...` (applies to both the stable-alias and
legacy-compat writers). Detection is an anchored export-statement pattern,
verified against all 7 shipped runtime chunks (no false positives; naive
text matching would both miss and over-match — an alias claiming a default
its target lacks is a hard SyntaxError at import). Adds a regression test
mirroring the real compaction-reconcile chunk shape.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H6Hz9UEpxQ4W3d8XecvupH
* fix(build): preserve defaults through legacy runtime aliases
---------
Co-authored-by: headbouyJB <23249268+headbouyJB@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>