Commit Graph

774 Commits

Author SHA1 Message Date
Peter Steinberger
568ea29d2e refactor(plugins): inherit shared package boundary settings (#117474) 2026-08-01 09:11:09 -07:00
Peter Steinberger
9e4381eb1c refactor(channels): unify bundled channel setup contracts (#117106)
* refactor(channels): canonicalize bundled setup contracts

* test(matrix): use scoped environment fixtures
2026-07-31 16:57:29 -07:00
Peter Steinberger
a39f0b7ea3 fix(channels): preserve accepted LINE and Mattermost delivery (#117024)
* fix(channels): preserve accepted delivery identities

* fix(channels): satisfy accepted-delivery owner boundaries

---------

Co-authored-by: Peter Steinberger <steipete@macos.shared>
2026-07-31 14:28:55 -07:00
Peter Steinberger
c572982556 fix(channels): preserve actionable presentation fallbacks (#116990)
* fix(channels): preserve actionable presentation fallbacks

* test(interactive): isolate presentation fallback regression coverage

---------

Co-authored-by: Peter Steinberger <steipete@macos.shared>
2026-07-31 12:37:30 -07:00
Peter Steinberger
f18d619c02 fix(channels): preserve preview and presentation delivery (#116884)
Co-authored-by: Peter Steinberger <steipete@macos.shared>
2026-07-31 07:58:56 -07:00
Ayaan Zaidi
c63241d3ce fix(channels): resolve tool progress against the caller's stream mode
resolveChannelStreamingPreviewToolProgress guessed a mode when streaming.mode
was unset, so it could never be right for every channel: guessing "partial"
dropped Discord and Telegram's explicit progress.toolProgress opt-out, and my
earlier switch to "progress" broke it the other way for Slack, Mattermost, and
Teams, whose real default is partial. With both keys configured and disagreeing,
the wrong one won.

The helper has no channel identity, so it stops guessing and takes the mode the
caller already resolved. All nine call sites pass theirs. Omitting it keeps the
previous configured-mode reading, so untouched callers behave exactly as before.

Reported by ClawSweeper on #116143.
2026-07-30 13:36:21 +09:00
Vincent Koc
d9565bf677 fix(mattermost): honor configured envelope timezone (#116145) 2026-07-30 09:14:40 +08:00
joshavant
545350488a fix(mattermost): preserve outbound delivery settlement 2026-07-29 19:56:34 -05:00
Peter Steinberger
986e5d1758 refactor(channels): share ingress retention defaults (#115824) 2026-07-29 06:24:30 -04:00
Peter Steinberger
bde6b166c0 refactor(channels): deduplicate ingress monitor shells (#115792)
* refactor(channels): deduplicate ingress monitor shells

* fix(channels): preserve ingress error overload types

* fix(channels): type ingress error overload implementation

* chore(plugin-sdk): ratchet ingress error surface
2026-07-29 05:33:00 -04:00
Peter Steinberger
20764dae6d fix(mattermost): preserve private-channel origin identity (#115726)
Preserve the authoritative inbound group origin for private Mattermost channels without changing public channels, direct messages, thread keys, or the native channel delivery target. Prove both Bot API channel types over real HTTP, gateway discovery, and SQLite-backed outbound persistence.

Credit the original group-origin observation in #95669.

Co-authored-by: hansraj <hansraj136@gmail.com>
2026-07-29 05:18:17 -04:00
Peter Steinberger
13563478fb fix(mattermost): preserve safe denied-sender group history (#115773)
* fix(mattermost): record pending history for non-allowlisted group senders

When `groupPolicy` is `"allowlist"`, messages from senders not in
`groupAllowFrom` were silently dropped before reaching
`recordPendingHistoryEntryIfEnabled()`. This meant the bot had no
context of what non-allowlisted users said, breaking use cases like
conversation summarization.

This change introduces a `senderBlockedByGroupAllowlist` flag so that
non-allowlisted messages continue through the history-recording code
path before returning without generating a response.

Changes:
1. Add `senderBlockedByGroupAllowlist` flag before access check
2. `GROUP_POLICY_NOT_ALLOWLISTED` sets flag instead of returning
3. Skip `commandGate` check for blocked senders
4. Call `recordPendingHistory()` then return before response processing

Fixes openclaw/openclaw#57607

* test(mattermost): verify denied history over real transport

Co-authored-by: Jason Wang <jasonwang@barkingdog.ai>

---------

Co-authored-by: Jason Wang <jasonwang@barkingdog.ai>
2026-07-29 05:08:22 -04:00
Peter Steinberger
c402688894 feat(media): probe duration and dimensions for playback metadata (#115728)
* feat(media): probe playback metadata

* fix(media): satisfy CI gates

* fix(media): satisfy lint rules
2026-07-29 04:52:17 -04:00
Peter Steinberger
382ea7b74e refactor: centralize channel streaming config hints (#115739)
* refactor: centralize channel streaming config hints

* fix: repair code mode static checks

* fix: restore QA evidence validator import
2026-07-29 04:42:23 -04:00
Peter Steinberger
302f262e6b refactor: deduplicate extension normalization primitives (#115650)
* refactor(plugins): reuse SDK normalization primitives

* fix(ci): repair code-mode matrix checks

* fix(ci): satisfy code-mode matrix gates

* fix(ci): use matrix evidence export

* fix(ci): validate matrix evidence artifact
2026-07-29 04:10:06 -04:00
iloveleon19
c193ffd554 fix(mattermost): key private channels as group on outbound routing (#96645)
* fix(mattermost): key private channels as group on outbound routing

A Mattermost private channel (server type `P`) is authoritatively chat_type
`group`, but it is addressed as the delivery target `channel:<id>` — the same
prefix as a public channel. Inbound classified it correctly as `group`, while
outbound/session reconstruction re-derived `channel` from the target string, so
one conversation was keyed under two session namespaces
(`...:mattermost:group:<id>:thread` inbound vs a phantom `...:channel:<id>:thread`
on delivery). Threaded/scheduled deliveries bound to one then failed to match the
other (fail-closed delivery, or a conversation split across two session keys).

The Mattermost outbound path could not represent `group` at all:
resolveMattermostOutboundSessionRoute only produced direct/channel, and
resolveMattermostOpaqueTarget only classified user/channel.

- session-route: key a conversation as `group` from an authoritative signal — the
  resolved target kind, an explicit `group:` prefix, or the inbound
  currentSessionKey peer kind — so outbound shares the inbound `group:<id>`
  namespace instead of forking `channel:<id>`.
- target-resolution: classify a bare channel id by its real channel type
  (P/G -> group, O -> channel), cached per id.

The wire target stays `channel:<id>` (Mattermost posts to the channel id either
way; parseMattermostTarget only accepts channel:/user:) — the group distinction
lives in the session key. Adds unit coverage for both paths.

Resolves #95646.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(cron): route bound-session cron deliveries under the bound namespace

An isolated cron run executes under an ephemeral agentSessionKey that does not
carry the source conversation's namespace. resolveDirectCronDeliverySessionKey
resolved the outbound delivery route from that isolated key, so for a job bound
to a real conversation thread (e.g. the gitlab-pipeline-watch recheck bound to
agent:...:mattermost:group:<id>🧵<root>) the lossy channel:<id> target was
re-derived as `channel`, forking a phantom channel:<id> session and splitting the
private-channel thread across two namespaces (#95646).

Prefer the job's bound conversation identity as the currentSessionKey used to
resolve the route (new selectCronRouteCurrentSessionKey helper), so the existing
currentSessionKey-based namespace resolution keeps group:<id>. No channel-type
cache is introduced — which is what made the cache-based attempts brittle on cold
restart (a sibling PR documented exactly that failure mode). Falls back to the
isolated key for unbound jobs and cron-namespace bindings. Adds unit coverage.

Refs #95646.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(mattermost): key public channels as channel in directory listing

listMattermostDirectoryGroups labeled every joined channel — public `O`
and private `P` — as kind `group`. A name-resolved public channel could
then be keyed as `mattermost:group:<id>` on outbound routing, forking a
phantom group session and splitting the transcript from the inbound
`channel:<id>` one. Derive the kind from the authoritative Mattermost
channel type (`O` -> channel, `P`/`G` -> group) and add a regression
test. This closes the public-channel regression path flagged in review
for #95646 while keeping private channels keyed as `group`.

* fix(mattermost): harden private channel routing

* test: expose cron route selection through production module

* fix. scope cron session reuse to Mattermost delivery

* fix(cron): validate bound delivery peer and channel authority

* fix(cron): capture validated delivery destination peer

---------

Co-authored-by: leon <leon@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-29 02:43:21 -04:00
Peter Steinberger
a24c1ceb3a fix(channels): release inbound debounce at admission (#115603)
Track full dispatch completion separately for error handling and shutdown drain while allowing same-session follow-ups to steer active runs. Fixes #113180.

Co-authored-by: Taksh <takshkothari09@gmail.com>
2026-07-29 01:38:15 -04:00
metaforismo
cc8fd57a01 feat(mattermost): read channel history with guarded permissions (#110875)
* feat(mattermost): add guarded channel history reads

* chore: refresh Mattermost generated metadata

* fix(channels): scope Mattermost read authorization

* test(mattermost): redact fixture credentials

* test(mattermost): use safe credential placeholders

* fix(mattermost): report directional pagination

* fix(mattermost): authorize current DM reads

* test(mattermost): cover named read policy inheritance

* fix(mattermost): make history reads opt in

* style(mattermost): align channel import ordering
2026-07-28 19:54:39 -05:00
Harjoth Khara
f4da74a0af fix: preserve finalized Mattermost replies after tool warnings (#109555)
* fix(mattermost): preserve finalized preview after warnings

* test(mattermost): cover finalized preview warning trace

* docs(mattermost): clarify finalized preview invariant

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-28 08:17:20 -04:00
joshavant
817aec8445 fix(mattermost): prevent hook-controlled preview leaks 2026-07-28 01:04:51 -05:00
wangmiao0668000666
9dbfeb7e61 fix(channels): keep healthy accounts' message actions when one credential SecretRef fails (#110329)
* fix(channels): isolate unavailable account discovery

Co-authored-by: wangmiao0668000666 <wang.miao86@xydigit.com>

* refactor(channels): remove stale discovery exports

* test(pr): provide ripgrep fixture command

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-28 01:50:51 -04:00
Peter Steinberger
4301ca9831 refactor(channels): mattermost event-plan builder and whatsapp durable admission (#114763)
* refactor(mattermost): consolidate event planning

* refactor(whatsapp): use shared durable admission

* refactor(channels): keep ingress helpers private

* fix(whatsapp): preserve receive-time ingress identity

* style(whatsapp): format durable receive helper
2026-07-27 19:43:56 -04:00
Peter Steinberger
08b1ff73e2 refactor(channels): fix account resolution and setup isolation (#114395)
* refactor(channels): fix account resolution and setup isolation

* fix(channels): keep account resolvers safely destructurable
2026-07-27 03:49:37 -04:00
joshavant
d8de71258a refactor(channels): route bundled command replies 2026-07-26 23:15:06 -05:00
Peter Steinberger
f6131a4fbf build(deps): remove npm shrinkwrap; mirror pnpm lock into transient package locks (#114006)
* build(deps): remove npm shrinkwrap; mirror pnpm lock into transient package locks

npm 12 removed shrinkwrap (command + tarball/root loading). Delete all 82
committed npm-shrinkwrap.json files and stop publishing lockfiles; keep
pnpm-lock.yaml as the single reviewed dependency boundary. The generator
becomes scripts/generate-npm-package-lock.mjs and feeds plugin bundling via
a transient package-lock.json + npm ci (works on npm 11 and 12). Tarball
validation treats the published 2026.7.2 beta train as a shrinkwrap
transition; self-update npm detection now uses install topology instead of
the shipped shrinkwrap.

* fix(deps): repair lint, deadcode, and test-type lanes for the npm 12 migration

- sort integrity comparisons with an explicit comparator (oxlint)
- keep resolveBunGlobalNodeModules module-local (knip unused-export gate)
- model npm pack --json as npm<=11 array / npm 12 name-keyed object
- default calver destructuring in the tarball test fixture
2026-07-26 01:29:55 -04:00
Peter Steinberger
0ff2c82033 refactor(mattermost): split inbound monitor (#113775)
* refactor(mattermost): split inbound monitor

* test(channels): follow mattermost history split
2026-07-25 10:56:58 -07:00
Peter Steinberger
b06f40a821 refactor(plugin-sdk): share ingress lifecycle fan-in (#113648)
* refactor(channels): share ingress lifecycle fan-in

* chore(plugin-sdk): refresh ingress API baseline
2026-07-25 05:34:35 -07:00
Peter Steinberger
332006bc1f test: consolidate OpenClaw test state fixtures (#113576)
* test: consolidate OpenClaw test state fixtures

* test(plugin-sdk): expose isolated test state

Promote the isolated OpenClaw test-state lifecycle through a narrow published Plugin SDK subpath so extension tests no longer import private core helpers. This intentional SDK surface addition is maintainer-approved.

* test: use SDK test-state seam in extensions

Route bundled extension suites through the focused repo-local Plugin SDK test-state entrypoint and remove the Codex projector harness exports made stale by fixture consolidation. Keep the seam out of production builds and published package artifacts while auditing its real consumers in the full-tree deadcode scan.

* test(plugins): map test-state in package boundaries
2026-07-25 05:30:52 -07:00
Peter Steinberger
d2b6573690 refactor(channels): remove v2026.7.2-gated compat (flat streaming keys, group intro hint) (#113533)
* refactor(channels): remove flat streaming compat

maintainer-approved early removal of v2026.7.2-gated compat

* refactor(channels): remove group intro hint adapter

maintainer-approved early removal of v2026.7.2-gated compat

Plugin SDK surface baseline update is maintainer-approved for this intentional removal.
2026-07-25 01:09:25 -07:00
Peter Steinberger
92a35b599a refactor: share channel probe runner (#113540) 2026-07-25 00:24:31 -07:00
Peter Steinberger
1e9d918037 feat(sdk): always persist media facts and ship facts-first replacements for legacy Media* surfaces (#113355)
* feat(sdk): always persist media facts and ship facts-first replacements for legacy Media* surfaces

PR 1 of the media legacy retirement program (audit-frozen, 4 PRs).

- Every media-bearing user turn now persists normalized __openclaw.media
  facts unconditionally while continuing to emit the legacy top-level
  Media* projection byte-identically (dual-write bridge; the conditional
  shouldPersistStructuredMediaEntries gate now always includes media).
- New replacement APIs, shipped before any removal: typed hook media
  facts (media[], originalMedia[], mediaStagingPending) on message
  events; {{AttachmentPath}}/{{AttachmentUrl}}/{{AttachmentContentType}}/
  {{AttachmentDir}}/{{AttachmentIndex}} template variables; focused
  openclaw/plugin-sdk/media-local-roots subpath split out of the
  deprecated agent-media-payload facade.
- Every legacy surface carries @deprecated naming its replacement, under
  one named compatibility record media-legacy-projection with the
  operator-approved removeAfter 2026-10-01 (two release trains; deletion
  additionally gates on a clean published-plugin artifact sweep).
- Generic transcript append invariant documented; SDK migration, hooks,
  and configuration docs updated to the facts-first path.

Writer golden matrix proves legacy bytes and model prompt bytes are
unchanged while nested facts become unconditional. 2,189 broad media
tests green; SDK api-baseline regenerated on fresh-env Testbox.

* feat(sdk): register media-local-roots subpath exports and deprecation metadata

Completes PR 1: package export map for openclaw/plugin-sdk/media-local-roots
plus the deprecated-subpath inventory and doc metadata entries for the
media-legacy-projection record.

* chore(sdk): track media-local-roots entrypoint and deprecated-export budgets

* fix(sdk): keep deprecated MSTeams buildMediaPayload re-export through the compat window

Deleting shipped runtime-api re-exports belongs to retirement PR 4 after
the media-legacy-projection window; PR 1 only deprecates. Also formats
the migration-guide schedule table.

* docs: regenerate docs map for media migration additions
2026-07-24 10:42:17 -07:00
Peter Steinberger
73bba03e4c refactor: canonicalize session delivery state (#113225)
* refactor: canonicalize session delivery state

* test: canonicalize reply persistence fixtures

* test: canonicalize talk delivery fixtures

* test: canonicalize voice session routes

* test: canonicalize attachment delivery fixtures

* test: migrate gateway delivery fixtures

* fix: skip invalid session delivery rows

* test: align delivery SDK surface gates

* fix: preserve legacy delivery precedence

* test: canonicalize heartbeat delivery fixtures

* fix: preserve delivery route prompt identity

* test: canonicalize session delivery fixtures

* fix: preserve recoverable legacy delivery routes

* fix: canonicalize remaining session state

* fix: preserve canonical session classification

* style: format delivery state changes

* test: refresh plugin SDK delivery baseline

* test: avoid mutating session fixture input

* style: simplify delivery identity check

* style: simplify delivery origin spread

* fix: preserve fresh delivery route metadata

* test: assert canonical surface route switch

* fix: canonicalize doctor file-store imports

* fix: preserve transitional delivery migration state

* fix: satisfy canonical delivery CI gates

* ci: scope GitHub App token permissions

* test: infer canonical delivery projections

* test: canonicalize ACP requester delivery fixtures

* test: canonicalize harness rollback fixture

* style: apply pinned formatter
2026-07-24 01:01:19 -07:00
Peter Steinberger
aae10cade5 refactor(markdown): unify format profile declarations (#113254)
* refactor(markdown): unify format profile declarations

* test(markdown): avoid suppression inventory drift
2026-07-24 00:37:59 -07:00
Peter Steinberger
acd92f6a3d chore(deps): refresh repository dependencies (#112453)
* build(deps): update QA broker dependency

* build(deps): refresh repository dependencies

* build(deps): reconcile rebased shrinkwraps

* test(plugins): remove stale loader test state

* test(deps): stabilize updated dependency coverage

* fix(swift): use caller-isolated TaskLocal overload

* build(deps): regenerate rebased shrinkwraps

* test(msteams): preserve DNS validation in fetch helper

* fix(deps): avoid vulnerable optional image stack

* test(deps): validate generated LRU override

* refactor(ui): extract chat resizable divider

* test(ui): update divider ownership path

* fix(matrix): retain restart-compatible SDK

* style(cron): format update test
2026-07-23 16:17:13 +00:00
Peter Steinberger
8108b9637b fix: preserve Discord bold and Mattermost tables (#113037)
* refactor(channels): unify Discord and Mattermost markdown

* build(discord): refresh shrinkwrap

* refactor(discord): split markdown formatter
2026-07-23 06:42:22 -07:00
Jesse Merhi
4a2a600809 feat(channels): add channel-owned setup contracts (#112176)
* feat(channels): add channel-owned setup contracts

* test(channels): align legacy setup fixtures

* chore(channels): regenerate config and SDK baselines after rebase

* fix(update): run fresh doctor after current-process core changes

* fix(channels): align add pre-scan with execution precedence

* style(cli): format channels-cli test additions

* fix(channels): restore option-before-positional channel resolution via metadata arity scan

* fix(channels): keep help flags out of metadata arity escalation

* test(update): mock fresh post-update doctor in current-process suites

* style: format review fixes and correct entrypoint mock type

* fix(channels): register only modern contract options for dual-publishing plugins

* test(update): align downgrade suites with fresh-doctor child invocation

* docs(channels): record empty-contract and input-forwarding invariants

* fix(line): keep the shipped --token switch as a channel access token alias

* fix(signal): stop treating exact cross-family loopback endpoints as bind-aligned

* chore(config): regenerate docs config baselines after second rebase

* style: format rebased channels add tests

* fix(channels): enforce field-key and flag-name agreement in setup contracts

* fix(signal): detect container endpoints for bare --http-url setup

* fix(signal): ignore unconfigured accounts in transport collision checks

* fix(channels): validate negated setup flags in contract and normalizer

* fix(signal): preserve existing transport kind when setup detection is unreachable

* style(signal): use direct boolean check in collision guard

* style(signal): type test config literals

* docs(update): record two-read design of fresh-doctor validation gate

* fix(channels): satisfy post-rebase architecture gates

* docs: refresh channel setup map

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-22 19:57:42 -04:00
Peter Steinberger
f236e83de7 refactor(channels): shrink ChannelSetupInput to a generic envelope with a deprecated compatibility tier (#112319)
* refactor(channels)!: shrink ChannelSetupInput to a generic envelope with a deprecated compatibility tier

* fix(channels): keep ChannelSetupInput structurally assignable without an index signature

* docs: regenerate docs map
2026-07-21 22:17:03 -07:00
Peter Steinberger
edecdbd05e refactor(config): config-surface reduction tranche 3 — product consolidations (review request) (#111527)
* refactor(config): consolidate media model lists

* refactor(config): unify memory configuration

* refactor(config): consolidate TTS ownership

* refactor(config): move typing policy to agents

* refactor(config): retire product-level config surfaces

* refactor(config): share scoped tool policy type

* chore(config): refresh generated baselines

* fix(config): honor agent typing overrides

* fix(config): migrate sibling config consumers

* refactor(infra): keep base64url decoder private

* fix(config): strip invalid legacy TTS values

* chore(config): refresh rebased baseline hash

* fix(doctor): route legacy messages.tts.realtime voice to talk during tts move

* refactor(config): polish final layout names

* refactor(config): freeze retired tuning defaults

* feat(config): add fast mode default symmetry

* refactor(config): key agent entries by id

* docs(config): update final layout reference

* test(config): cover final layout migrations

* chore(config): refresh final layout baselines

* fix(config): align final layout runtime readers

* fix(config): align remaining readers

* fix(config): stabilize final layout migrations

* fix(config): finalize config projection proof

* fix(config): address final layout review

* docs(release): preserve historical config names

* fix(config): complete keyed agent migration

* fix(config): close final migration gaps

* fix(config): finish full-branch review

* fix(config): complete runtime secret detection

* fix(config): close final review findings

* fix(config): finish canonical docs and heartbeat migration

* fix(config): integrate latest main after rebase

* refactor(env): isolate test-only controls

* refactor(env): isolate build and development controls

* refactor(env): collapse process identity indirection

* refactor(env): remove duplicate config and temp aliases

* docs(env): define the operator-facing allowlist

* ci(env): ratchet production variable count

* fix(env): remove stale provider helper import

* fix(env): make ratchet sorting explicit

* test(env): keep test seam in dead-code audit

* test(env): cover ratchet growth and boundary; document surface budgets

* docs(config): document tier-eval consolidations

* docs(config): clarify speech preference ownership

* test(memory): align retired tuning fixtures

* refactor(memory): freeze engine heuristics

* refactor(config): apply tier-eval tranche

* refactor(tts): move persona shaping to providers

* refactor(compaction): move prompt policy to providers

* test(config): align hookified prompt fixtures

* chore(deadcode): classify test-only exports

* chore(github): remove unused spawn helper

* chore(deadcode): classify queue diagnostics

* chore(deadcode): remove unused lane snapshot export

* chore(plugin-sdk): ratchet consolidated surface

* fix(config): integrate latest main after rebase
2026-07-21 20:28:43 -07:00
Peter Steinberger
d725632ee7 refactor(channels): move channel-flavored setup flags into plugin manifests (#112239)
* refactor(channels): move channel-flavored setup flags into plugin manifests

* refactor(channels): normalize manifest cliAddOptions formatting, drop changelog entry

* fix(channels): dedupe channels add options by commander switch identity

* fix(channels): let the selected channel's cliAddOptions win switch dedupe
2026-07-21 02:50:19 -07:00
Peter Steinberger
0adc2cf606 refactor(media): consolidate parallel media-kind unions onto canonical MediaKind (#112063)
* refactor(media): consolidate parallel media-kind unions onto canonical MediaKind

One canonical MediaKind union (media-core constants) replaces ~40
duplicate/parallel kind declarations across core and channel plugins;
channel-specific narrower contracts derive via Extract/Exclude. Also
fixes a review-caught fallback bug where a stored "unknown" reply-chain
kind preempted MIME inference and relabeled images as documents.

* refactor(ui): derive attachment kinds from MediaKind

* fix(telegram): drop type-dead unknown guard in reply-context kind fallback

* style(telegram): format media kind fallback
2026-07-20 22:27:22 -07:00
NIO
94a2494570 fix(mattermost): probe hangs past deadline when DNS preflight stalls (#111314)
* fix(mattermost): bound probe preflight with guard timeoutMs

* test(mattermost): tighten preflight timeout proof

Co-authored-by: hugenshen <hu.genshen@xydigit.com>

* test(mattermost): isolate preflight timeout path

Co-authored-by: hugenshen <hu.genshen@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-19 21:31:05 -07:00
Peter Steinberger
c7e7ac2728 refactor: remove expired plugin compatibility surfaces (#111451)
* docs(secrets): remove retired web credential paths

* refactor(web): remove retired provider compatibility paths

* refactor(providers): delete retired compatibility routes

* refactor(secrets): remove retired credential aliases

* refactor(plugin-sdk): delete retired compatibility surfaces

* docs(plugin-sdk): remove retired migration guidance

* chore(plugin-sdk): refresh rebased surface budgets

* chore(plugin-sdk): refresh API removal baseline

* refactor(compat): migrate retired internal callers

* chore(plugin-sdk): refresh current-main baselines

* test(config): migrate plugin-owned secret assertions

* test(gateway): narrow plugin secret refs

* fix(plugin-sdk): preserve private boundary type identity

* chore(compat): remove stale sweep references

* chore(lint): lower max-lines budget

* refactor(secrets): remove unused web helper

* build(plugin-sdk): drop removed compat entries

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): use Linux API baseline hash

* fix(plugin-sdk): preserve private bundled build entries

* fix(plugin-sdk): package private runtime facades

* fix(plugins): preserve external credential contracts
2026-07-19 11:04:48 -07:00
Peter Steinberger
83303f1ba8 feat(channels): batch 1 producers drop media placeholder bodies (#111447)
* feat(channels): batch 1 producers drop media placeholder bodies

Media-placeholder program batch 1: Google Chat, Zalo, LINE, and
Mattermost stop minting <media:kind> placeholder bodies. Media-only
messages carry an empty caption plus one structured fact per native
attachment (type-only when a download fails or is rejected, so payload
positions and kind signals stay aligned). The shared
formatMediaPlaceholderText SDK formatter renders text-only carriers
(Mattermost pending-room lines) from structured facts; per-channel
placeholder builders and the expected-count side channel are deleted.

* fix(mattermost): satisfy type, deadcode, and SDK manifest gates
2026-07-19 10:32:09 -07:00
Peter Steinberger
c163daa4ed refactor(channels): share durable ingress monitor (#111249)
* refactor(mattermost): share ingress monitor

* refactor(nextcloud-talk): share ingress monitor

* refactor(msteams): share ingress monitor

* refactor(zalo): share ingress monitor

* refactor(sms): share ingress monitor

* refactor(line): share ingress monitor
2026-07-19 00:26:28 -07:00
Chris Eckert
42be965eac improve(mattermost): restart only the changed account on config reload (#99312)
* 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>
2026-07-18 09:33:06 +01:00
Peter Steinberger
d4d23fe954 fix(mattermost): preserve websocket posts across restarts (#110386)
* fix(mattermost): adopt durable ingress drain at the websocket chokepoint

Posted events processed detached from the websocket receive with only a
5-minute in-memory guard; a crash lost the post and reconnect never replays
it. Raw posted envelopes now journal durably (event_id = post.id per the
upstream Post model, lane per channel_id, one row per post) before handler
scheduling; dispatch runs through the core drain with deferred claims through
debounce, merged-flush fan-out adoption, gated-turn settlement, and 30d/20k
tombstones covering the old 5min/2k guard, which is deleted after its parity
test. post_edited stays excluded and cannot be swallowed by posted
tombstones. Cold-gap limitation stated: Mattermost cannot replay posts missed
while disconnected.

Autoreview blocked by codex sandbox network in the build stage; full manual
review performed (updated one websocket test asserting the pre-adoption
parsed-post contract to the raw-envelope contract). Part of #109657 wave 2.

* style(mattermost): keep ingress monitor type internal

* fix(mattermost): retry then loudly escalate a failed durable append

Landing autoreview caught a real loss path: a durable enqueue failure at the
websocket chokepoint was logged and swallowed — the raw envelope discarded,
the connection kept running against a broken store, and reconnect never
replays, so a transient SQLite failure silently lost the post. The append now
retries with short backoff for transient blips; a persistent failure
propagates and the websocket terminates loudly so the outage is
operator-visible instead of silently dropping every subsequent post.
Regression test covers both the absorbed-transient and escalation paths.

* style(mattermost): format rebased ingress handler

* docs(mattermost): document bounded auth-failure retries under deferred claims

* fix(mattermost): guard the drain pump against stop racing the async prune

stop() disposing before the startup pump finished pruning let the pump
lazily create a fresh undisposed drain and dispatch after shutdown. The pump
now re-checks running after the prune, and stop() disposes again after
awaiting the pump so a drain created mid-race is torn down. Regression test
blocks the prune across stop and asserts no dispatch.

* fix(mattermost): serialize durable admissions to preserve lane order

Concurrent websocket callbacks let a post in append-retry backoff be
overtaken by its successor, inverting same-channel arrival order in the
queue. Admissions now chain (order over latency, mirroring the iMessage
admission tail); regression proves a retried post still lands ahead of a
concurrently received one.

* test(mattermost): assert lane order via dispatch sequence

* fix(mattermost): stop() awaits in-flight admissions before disposal

* fix(mattermost): satisfy ingress lint checks

* fix(mattermost): honor envelope-level channel ids in the durable inspector

Posts can carry their channel id on the post, the event data, or the
broadcast envelope — the monitor dispatch honors all three, but the ingress
inspector and claim-side validator required the nested field, rejecting valid
posts as permanent and (via the storage-failure escalation) tearing down the
socket for a failure that never happened, losing posts reconnect cannot
replay. Both sites accept the three shapes; regression proves an
envelope-level post dispatches.
2026-07-18 06:43:27 +01:00
Peter Steinberger
31ed59bdf6 feat: keep main agents aware of group activity (#110332)
* feat(sessions): auto-watch group activity from main

* feat(sessions): allow watchers to read watched sessions

* fix(sessions): skip group watches for system turns

* fix(sessions): preserve watched session semantics

* fix(channels): propagate effective dm scope

* fix(sessions): canonicalize main watch identity

* fix(sessions): distinguish ambient group watches

* fix(sessions): revoke ineligible ambient watches

* chore: drop CHANGELOG edit (release generation owns it)
2026-07-18 04:06:58 +01:00
Peter Steinberger
601ffa2530 refactor(channels): move inbound turn execution into core (#110095)
* refactor(channels): centralize turn execution lifecycle

* refactor(extensions): adopt core channel turn plans

* test(channels): ratchet modern dispatch ownership

* chore(channels): drop stale max-lines exemption

* fix(channels): allow async delivery error cleanup

* test(channels): await async delivery cleanup

* test(channels): await delivery error callbacks

* fix(channels): preserve delivery error contract

* fix(plugin-sdk): preserve channel inbound declarations

* test(channels): exercise core delivery plans

* fix(channels): await delivery error cleanup

* fix(channels): preserve async cleanup contract

* refactor(channels): trim dispatch boilerplate

* fix(channels): mask observe-only dispatch results

* refactor(channels): delete unused dispatch exports

* fix(msteams): preserve resolver config overrides

* refactor(channels): keep Teams turn config internal

* test(plugins): drop retired Teams runtime export guard
2026-07-18 00:55:46 +01:00
ooiuuii
9769642e7f fix: reject unsafe Mattermost API path segments (#98390)
* Harden Mattermost API paths

* fix(mattermost): guard direct media file paths

* refactor(mattermost): streamline API path validation

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-17 11:36:51 +01:00
Peter Steinberger
0e792b6de3 refactor(channels): centralize inbound orchestration and remove internal compat (#109716)
* refactor(channels): centralize inbound turn orchestration

* refactor(runtime): remove stale compatibility paths

* chore(guards): reject internal deprecated API use

* refactor(channels): simplify core turn planning

* chore(guards): keep deprecated checks boundary-focused

* refactor(memory): keep modern config off compat barrel

* fix(msteams): preserve feedback learning

* test(channels): align modern inbound fixtures

* refactor(channels): finish modern inbound migration

* refactor(channels): tighten core inbound kernel

* fix(channels): preserve turn assembly narrowing

* test(sdk): keep runtime mock binding immutable

* test(matrix): isolate read policy runtime

* test(msteams): mock canonical reply factory

* test(slack): mock core inbound turn dispatch

* test(telegram): inject core session recorder

* test(signal): inject core session recorder

* test(googlechat): assert canonical inbound routing

* test(synology-chat): align core turn fixture

* fix(sdk): preserve direct DM runtime compat

* refactor(channels): own inbound envelope compat in core

* refactor(channels): trim inbound dispatch seams

* refactor(channels): remove redundant async wrappers

* test(synology-chat): type canonical dispatcher mock

* refactor(channels): remove remaining dead compat seams

* chore(sdk): refresh API baseline after rebase

* fix(channels): preserve direct DM identity metadata
2026-07-17 00:56:46 -07:00