From 40855135304c41f14a04b8155b19c4143b1a425f Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Fri, 22 May 2026 06:01:03 -0400 Subject: [PATCH] feat(platform): digest relay, inbox channel post, worker semver gate Record digest relay handoffs, post digest summaries to configured inbox channels, validate worker versions on announce, sync digestRelayHandoff on GUILD_SYNC, and expose getLastDigestRelayHandoff (Phase 109 v0.8.73). Co-authored-by: Cursor --- index.js | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 98220a8..3c1f953 100644 --- a/index.js +++ b/index.js @@ -49,6 +49,7 @@ const { mergeHookFailureDigests, buildAutomationScheduleDashboard, buildAutomationHealthDashboard, + buildDigestRelayHandoff, computeArchivePeerHealthScore, computeArchiveFetchBackoffMs, HookFailureAlertStore @@ -58,7 +59,8 @@ const { buildWorkerLaunchScript, buildWorkerInstallPlan, getWorkerTemplate, - WorkerReleaseHintStore + WorkerReleaseHintStore, + validateWorkerReleaseVersion } = require('pearcord-integration-worker') const { PearcordIntegrationWorker } = require('./integration-worker') const { ACTION_TYPE_LIST, CATEGORY_OPTIONS } = require('pearcord-automation-export') @@ -1170,6 +1172,7 @@ class PearcordPlatform extends EventEmitter { automationDigestNotifyPrefs = await this.deliveryReceipts.exportAutomationDigestNotifyPrefsSlice() } + let digestRelayHandoff = null const auditRecent = await this._exportAuditSyncSlice(guild.id) let auditExportSchedule = null let digestExportSchedule = null @@ -1188,6 +1191,7 @@ class PearcordPlatform extends EventEmitter { await this.deliveryReceipts.exportAutomationDigestExportSnapshotsSlice(2) auditExportArchives = await this.deliveryReceipts.exportAuditExportArchivesSlice(3) archivePeerSeen = await this.deliveryReceipts.exportArchivePeerSeenSlice(16) + digestRelayHandoff = await this.deliveryReceipts.exportDigestRelayHandoffSlice() const digestSnap = await this.deliveryReceipts.getHookFailureDigestSnapshot() if (digestSnap) { hookFailureDigest = { @@ -1244,6 +1248,7 @@ class PearcordPlatform extends EventEmitter { receiptRetention, hookFailurePrefs, automationDigestNotifyPrefs, + digestRelayHandoff, auditRecent, auditExportSchedule, digestExportSchedule, @@ -1353,6 +1358,13 @@ class PearcordPlatform extends EventEmitter { payload.automationDigestNotifyPrefs ) } + if (payload.digestRelayHandoff) { + await this._initDeliveryReceipts(payload.guildId) + await this.deliveryReceipts.ingestDigestRelayHandoffSlice( + payload.guildId, + payload.digestRelayHandoff + ) + } if (payload.auditExportSchedule) { await this._initDeliveryReceipts(payload.guildId) await this.deliveryReceipts.ingestAuditExportScheduleSlice( @@ -2610,6 +2622,7 @@ class PearcordPlatform extends EventEmitter { deliveryMetrics = null } const automationDigestNotifyPrefs = await this.getAutomationDigestNotifyPrefs() + const digestRelayHandoff = await this.getLastDigestRelayHandoff() return buildAutomationHealthDashboard({ auditExportSchedule, digestExportSchedule, @@ -2618,13 +2631,27 @@ class PearcordPlatform extends EventEmitter { archiveFetchPeers, workerReleaseHint, deliveryMetrics, - automationDigestNotifyPrefs + automationDigestNotifyPrefs, + digestRelayHandoff }) } + async getLastDigestRelayHandoff () { + if (!this.guild?.guild) return null + await this._initDeliveryReceipts(this.guild.guild.id) + return this.deliveryReceipts.getLastDigestRelayHandoff() + } + async getAutomationDigestNotifyPrefs () { if (!this.guild?.guild) { - return { notifyOnExport: true, userMute: false, guildMute: false, muteNotifications: false } + return { + notifyOnExport: true, + userMute: false, + guildMute: false, + muteNotifications: false, + digestInboxChannelId: null, + digestRelayEnabled: false + } } await this._initDeliveryReceipts(this.guild.guild.id) const userId = this.identity.user?.id @@ -2636,7 +2663,9 @@ class PearcordPlatform extends EventEmitter { notifyOnExport: guild.notifyOnExport !== false, guildMute: !!guild.muteNotifications, userMute, - muteNotifications: !!guild.muteNotifications || userMute + muteNotifications: !!guild.muteNotifications || userMute, + digestInboxChannelId: guild.digestInboxChannelId || null, + digestRelayEnabled: !!guild.digestRelayEnabled } } @@ -2656,6 +2685,12 @@ class PearcordPlatform extends EventEmitter { if (partial.muteNotifications != null) { patch.muteNotifications = !!partial.muteNotifications } + if (partial.digestInboxChannelId !== undefined) { + patch.digestInboxChannelId = partial.digestInboxChannelId || null + } + if (partial.digestRelayEnabled != null) { + patch.digestRelayEnabled = !!partial.digestRelayEnabled + } await this.deliveryReceipts.setAutomationDigestNotifyPrefs(patch) setTimeout(() => this._pushGuildSyncToMesh().catch(() => {}), 400) } else { @@ -2733,10 +2768,59 @@ class PearcordPlatform extends EventEmitter { } await this._notifyAutomationDigestExport({ exported, snap }).catch(() => {}) await this._fanoutAutomationDigestExportEvent({ exported, snap }).catch(() => {}) + await this._recordDigestRelayHandoff({ exported, snap }).catch(() => {}) + await this._postDigestToInboxChannel({ exported, snap }).catch(() => {}) setTimeout(() => this._pushGuildSyncToMesh().catch(() => {}), 400) return { ...exported, snapshotId: snap?.id || null } } + async _recordDigestRelayHandoff ({ exported, snap } = {}) { + if (!this.guild?.guild) return null + await this._initDeliveryReceipts(this.guild.guild.id) + const prefs = await this.deliveryReceipts.getAutomationDigestNotifyPrefs() + if (!prefs.digestRelayEnabled) return null + const handoff = buildDigestRelayHandoff({ + guildId: this.guild.guild.id, + guildName: this.guild.guild.name, + exported, + snapshot: snap + }) + await this.deliveryReceipts.appendDigestRelayHandoff(handoff) + this.emit('digest-relay-handoff', handoff) + return handoff + } + + async _postDigestToInboxChannel ({ exported, snap } = {}) { + if (!this.guild?.guild || !this.messages) return null + const roles = await this._memberRoles() + if (!roleHasPermission(roles, PERMISSION.MANAGE_GUILD)) return null + await this._initDeliveryReceipts(this.guild.guild.id) + const prefs = await this.deliveryReceipts.getAutomationDigestNotifyPrefs() + const channelId = prefs.digestInboxChannelId + if (!channelId) return null + const ch = await this.db.get(COLLECTIONS.CHANNELS, { + id: channelId, + guildId: this.guild.guild.id + }) + if (!ch || (ch.type !== 'text' && ch.type !== 'announcement')) return null + const preview = String(exported.body || '').slice(0, 1600) + const lines = [ + '**๐Ÿ“‹ Automation schedule digest export**', + `Format: \`${exported.format || 'text'}\` ยท ${snap?.summaryLines || 0} lines`, + '', + preview, + preview.length < (exported.body?.length || 0) ? '\n_(truncated โ€” fetch full snapshot from mesh)_' : '' + ] + const prev = this.activeChannelId + await this.selectChannel(channelId) + try { + const msg = await this.sendMessage(lines.join('\n')) + return msg + } finally { + if (prev) await this.selectChannel(prev) + } + } + async _notifyAutomationDigestExport ({ exported, snap } = {}) { if (!this.notifications || !this.identity.user || !this.guild?.guild) return null const roles = await this._memberRoles() @@ -5549,13 +5633,20 @@ class PearcordPlatform extends EventEmitter { return this.workerReleaseHints.buildViewHint(this.guild.guild.id, this.appVersion) } - async announceWorkerRelease ({ workerVersion, pearUri, releaseNotes } = {}) { + async announceWorkerRelease ({ workerVersion, pearUri, releaseNotes, allowDowngrade } = {}) { if (!this.guild?.guild) throw new Error('no guild') const roles = await this._memberRoles() if (!roleHasPermission(roles, PERMISSION.MANAGE_GUILD)) { throw new Error('no permission to announce worker release') } const version = workerVersion || this.appVersion + const check = validateWorkerReleaseVersion(this.appVersion, version, { + allowDowngrade: !!allowDowngrade, + allowSame: false + }) + if (!check.ok) { + throw new Error(check.message || 'invalid worker release version') + } await this._initWorkerReleaseHints() const row = await this.workerReleaseHints.setHint({ guildId: this.guild.guild.id, @@ -7082,6 +7173,7 @@ class PearcordPlatform extends EventEmitter { let automationScheduleDashboard = null let automationHealthDashboard = null let automationDigestNotifyPrefs = null + let digestRelayHandoff = null let auditArchiveFetchPeers = null let workerReleaseHint = null let workerInstallPlan = null @@ -7095,6 +7187,7 @@ class PearcordPlatform extends EventEmitter { automationHealthDashboard = await this.getAutomationHealthDashboard() automationScheduleDashboard = automationHealthDashboard automationDigestNotifyPrefs = await this.getAutomationDigestNotifyPrefs() + digestRelayHandoff = await this.getLastDigestRelayHandoff() auditArchiveFetchPeers = await this.listAuditArchiveFetchPeers() workerReleaseHint = await this.getWorkerReleaseHint() this._lastWorkerReleaseHintView = workerReleaseHint @@ -7273,6 +7366,7 @@ class PearcordPlatform extends EventEmitter { automationScheduleDashboard, automationHealthDashboard, automationDigestNotifyPrefs, + digestRelayHandoff, auditArchiveFetchPeers, workerReleaseHint, workerInstallPlan,