Accepted Nextcloud Talk webhook and Nostr relay messages now use the shared durable channel-ingress queue. Claims stay owned until reply-lane terminal completion; bounded append and delivery retries fail closed into dead-letter handling; restart recovery owns redelivery; and Nostr cursors advance only after relay EOSE and durable progress.
Related: #109657
* fix(zalo): adopt durable ingress drain with ack gated on raw-envelope append
OA webhooks acked before detached processing behind a 5-minute in-memory
guard, and Zalo does not retry (silent loss). Raw webhook JSON now appends
durably (event_id = Bot API message.message_id, lane per chat.id) before the
200; failed appends answer 400/500 so nothing acks unpersisted. Dispatch runs
through the core drain with serialized admissions across append backoff,
deferred claims held through shutdown until adoption, retry/dead-letter
classification, and 30d/20k tombstones replacing the deleted 5min/5k guard
after its parity test. Webhook and polling transports stay mutually
exclusive; no twin scenario exists across the four received-event types.
Autoreview scanner false-positive on deleted-line token field references; full
manual review performed (ack ordering, dual-transport exclusivity, eleven-test
matrix). Part of the #109657 fleet program (wave 3).
* refactor(zalo): keep webhook runtime exports private
* test(zalo): use managed temp directories
* fix(vault): keep request deadline through response body
* test(vault): use generic stalled-body fixture
* test(vault): mark fixture token synthetic
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* improve(gateway): restart only the changed account on channel config reload
When a config change is scoped entirely to one channel account
(channels.{kind}.accounts.{accountId}[.*]), restart just that account
instead of the whole channel. Wholesale channel restarts disconnect every
account on the channel; on gateways running many accounts, each
account-scoped config write (adding an account, changing one account's
settings) briefly dropped every other account's connection.
The reload plan gains a restartChannelAccounts bucket populated only when
all of a channel's changed paths are account-scoped; any channel-global
path falls back to the existing wholesale restart, and a channel scheduled
for wholesale restart drops its per-account entries so each (channel,
account) pair restarts at most once. The executor runs per-account
restarts through the existing stopChannel/startChannel accountId parameter.
* fix(gateway): preserve account reload admission
* fix(gateway): scope surgical reloads to isolated plugins
* test(gateway): avoid preactivating reload snapshot
* docs(channels): define account reload isolation contract
* fix(gateway): preflight scoped account reloads
* fix(gateway): re-drain live scoped reload targets
* docs: refresh generated docs map
* fix(mattermost): align scoped reload with durable ingress
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(agents): use fetchWithSsrFGuard in minimax-vlm.ts instead of raw fetch
Replace raw fetch() with fetchWithSsrFGuard + withTrustedEnvProxyGuardedFetchMode
in minimaxUnderstandImage, aligning with all other agent network transport
paths (provider-transport-fetch, mcp-http-fetch, web-guarded-fetch, tools-manager).
This adds SSRF protection, redirect safety, debug capture, and proper
dispatcher lifecycle management. The ensureGlobalUndiciEnvProxyDispatcher
call is removed since guarded fetch handles proxy routing in trusted env-proxy
mode.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(agents): preserve configured MiniMax origins in guarded-fetch migration
* fix(agents): resolve lint and type-suppression regressions in MiniMax VLM guarded-fetch tests
Replace as-any casts in vi.mock vi.importActual calls with generic type
parameters, and replace the opts:any parameter in the fetch-guard bridge
with an inline type. Add SSRF policy assertions covering default hosts,
custom public hosts, and explicitly-authorized loopback origins.
* fix(minimax-vlm): thread allowPrivateNetwork into guarded-fetch policy
Honor the explicit provider request private-network opt-out in the MiniMax
VLM guarded-fetch path. When an operator sets
models.providers.<id>.request.allowPrivateNetwork: false, skip the origin
exception for non-default hosts so the explicit denial is authoritative.
Also fix an invalid Vitest fn generic (two-parameter form -> function
signature) that caused check-test-types to fail.
* fix(agents): use canonical provider SSRF policy for MiniMax VLM
* fix(media): harden MiniMax VLM transport
Co-authored-by: Peter Lee <li.xialong@xydigit.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Peter Steinberger <peter@steipete.me>
* improve(diagnostics): surface run startup phases as session-correlated events
The embedded runner reports ordered startup milestones (workspace, auth,
context_engine, ..., model_call_started) through the private
onExecutionPhase callback, so they reach typing indicators and cron
watchdogs but never the diagnostic bus. External status surfaces that
already consume run.attempt / run.progress / model.call.* cannot see
where a turn is during startup — exactly the window that matters when a
run hangs before its first model call.
Wrap params.onExecutionPhase once at the runner entry so every phase
transition also emits a session-correlated run.execution_phase event
(runId, sessionId, sessionKey, closed EmbeddedAgentExecutionPhase +
provider/model/tool context). The event rides the async diagnostic lane
like the other high-frequency lifecycle events, so ordering against
model.call.* is preserved and no subscriber work runs on the hot path.
The wrapper tracks session rotation via onSessionIdChanged so
post-compaction phases carry the current session id. Stability records
project the milestone into their dedicated phase/provider/model fields.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(diagnostics): preserve trusted phase observability
* chore(plugin-sdk): refresh diagnostic API baseline
* fix(diagnostics): publish execution phases to plugins
* refactor(logging): keep public diagnostic recorder path
* fix(plugin-sdk): keep deprecated budget exact
* fix(plugin-sdk): refresh diagnostic surface budget
* fix(diagnostics): preserve execution phase coverage
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>