channels: opt-in SDK-backed persistent state for slack, discord, msteams, matrix#74933
channels: opt-in SDK-backed persistent state for slack, discord, msteams, matrix#74933
Conversation
…ams, matrix
Adds plugins.entries.<id>.config.experimentalPersistentState (default
false) to slack/discord/msteams/matrix, mirroring four in-memory caches
into the SDK-backed plugin state store while keeping the existing
process-local fast paths as the default behavior:
- Slack thread participation (sent-thread-cache)
- Discord component & modal registries (components-registry)
- MS Teams sent-message dedupe (sent-message-cache)
- Matrix approval reaction targets (approval-reactions)
Errors from the persistent path are logged via the plugin runtime and
never break the synchronous record/lookup paths. Each plugin gets a
*ForConfig async variant used by handlers that already run in async
contexts; opt-in is gated by resolvePluginConfigObject(cfg, ...).
Tests: extends each plugin's cache test with no-op-when-disabled and
persistent register/lookup with a fake runtime store. Updates relevant
docs (channels/{slack,discord,msteams,matrix}.md, plugins/sdk-runtime.md)
and the changelog.
|
Codex review: needs maintainer review before merge. What this changes: The PR adds opt-in Maintainer follow-up before merge: Protected Security review: Security review cleared: Read-only diff review found no workflow, dependency, lockfile, lifecycle script, secret-handling, or third-party code execution changes; persisted data is existing channel metadata behind opt-in plugin config. Review detailsBest possible solution: Keep the PR open for maintainer review. If accepted, land the opt-in bundled-plugin migrations on top of the existing keyed-store SDK, preserve process-local defaults, and keep BlueBubbles/Feishu claim-style dedupe work deferred until the store has safe claim/check semantics. Do we have a high-confidence way to reproduce the issue? Not applicable as a feature migration PR. The motivating limitation is statically verifiable on current Is this the best way to solve the issue? Unclear pending maintainer review. The opt-in per-plugin config over the bundled-only keyed-store SDK appears narrow and consistent with #74190, but protected draft status means owner validation is still the right next step. What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against d117ed183aaa. |
Address draft PR review feedback for the opt-in SDK-backed channel state migration: - backfill Slack and MS Teams in-memory caches after persistent lookup hits so hot inbound paths only pay the SQLite lookup once per key after restart - reset cached persistent store handles from existing cache clear helpers so tests and runtime reset paths reopen stores from the current runtime - use a narrow Matrix plugins config lookup type instead of a double cast - document why Slack persists agentId even though current reads only check participation presence Validation: pnpm test extensions/slack/src/sent-thread-cache.test.ts extensions/discord/src/components.test.ts extensions/msteams/src/sent-message-cache.test.ts extensions/matrix/src/approval-reactions.test.ts; pnpm check:changed.
Summary
Follow-up to #74190 — wires the new SQLite-backed
api.runtime.state.openKeyedStoreinto the four in-memory caches the PR called out as migration candidates, while keeping the existing process-local fast paths as the default and gating the persistent variant behind a per-plugin opt-in.Each bundled channel plugin now declares
experimentalPersistentState: boolean(defaultfalse) in itsopenclaw.plugin.json#configSchema. When operators opt in viaplugins.entries.<id>.config.experimentalPersistentState: true, the in-memory cache is mirrored into a per-plugin namespace in the new state store so it survives restarts; otherwise the runtime behaves exactly as it does today.Migrations
sent-thread-cache)slack.thread-participationcomponents-registry)discord.components,discord.modalssent-message-cache)msteams.sent-messagesapproval-reactions)matrix.approval-reactionsFor each plugin:
*ForConfigasync variant is awaited by callers that already run in async contexts (prepare.ts,dispatch.ts, monitor handlers, reaction events, modal handlers). It checks the in-memory cache first and falls back to the persistent store on miss.runtime.logging.getChildLogger(...)and never throw into channel-side message handling.deleteon the persistent entry when an in-memory hit consumes a still-persisted entry, so consumed components/modals don't resurrect after restart.Migrations intentionally deferred
PR #74190 listed two more candidates (BlueBubbles inbound dedupe, Feishu persistent dedupe) — both want claim/check semantics that aren't safely expressible on the v1
register/lookup/consumeAPI yet, so they're left as follow-ups.Tests
extensions/slack/src/sent-thread-cache.test.ts— opt-in gating + persistent register/lookup with a fake runtime storeextensions/discord/src/components.test.ts— same shape, covering both the component and modal storesextensions/msteams/src/sent-message-cache.test.ts— same shapeextensions/matrix/src/approval-reactions.test.ts— same shape, including the resolved decision payloadValidation
Local from this worktree:
pnpm exec oxfmt --checkon touched TS files — cleanpnpm testfor the four new cache tests + relevant existing call-site tests (slack/action-runtime,slack/monitor/.../prepare,discord/monitor/monitor,matrix/.../reaction-events,matrix/approval-handler.runtime,msteams/monitor-handler/message-handler.authz) — 4 vitest projects, 126 tests passingpnpm check:changed— extensions prod typecheck, extension tests typecheck, oxlint, runtime sidecar loaders, import cycles, dup coverage, conflict markers, changelog attributions, plugin-sdk wildcard re-exports — all cleanHave not yet run a broad
pnpm check/pnpm testsweep — happy to run the broader gate (or Testbox) before flipping out of draft.Closes / refs