feat: wire session JSON mesh push, gossip, and view state (v0.8.690)

Implement pushSessionActivityJsonToMesh, pushAvatarDecorationExportToMesh,
gossip handlers, partition heal hooks, and sessionJsonMixin assignment.
Populate sessionActivityJsonMeta/rows and avatar decoration export in guild view.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-06-03 06:54:46 -04:00
co-authored by Cursor
parent e898912942
commit ca345dbc90
2 changed files with 232 additions and 1 deletions
+2
View File
@@ -2,6 +2,8 @@
Application facade: one `PearcordPlatform` class that wires identity, database, guild mesh, DMs, invites, voice, discovery, bots, and dozens of feature modules for the Pearcord desktop sidecar and companion.
**Phase 714 (v0.8.690):** Session activity JSON & avatar decoration export — `session-json-mixin.js`, `session.json` spans (`guildSessionJsonCount`), `pushSessionActivityJsonToMesh`, `pushAvatarDecorationExportToMesh`, deep link `openSessionJson`. Bundle: `npm run test:ci-phase714`.
**Phase 711 (v0.8.686):** Activity invite JSON & profile banner export — `activity-json-mixin.js`, `activity.json` spans (`guildActivityJsonCount`), `pushActivityInviteJsonToMesh`, `pushProfileBannerExportToMesh`, deep link `openActivityJson`. Bundle: `npm run test:ci-phase711`.
**Phase 710 (v0.8.685):** Custom status JSON & guild widget export — `status-json-mixin.js`, `status.json` spans (`guildStatusJsonCount`), `pushCustomStatusJsonToMesh`, `pushGuildWidgetExportToMesh`, deep link `openStatusJson`. Bundle: `npm run test:ci-phase710`.
+230 -1
View File
@@ -194,6 +194,12 @@ const {
buildProfileBannerExportJsonBody,
signProfileBannerExportJson,
verifyProfileBannerExportJson,
buildSessionActivityJsonBody,
signSessionActivityJson,
verifySessionActivityJson,
buildAvatarDecorationExportJsonBody,
signAvatarDecorationExportJson,
verifyAvatarDecorationExportJson,
formatAutomationScheduleDigestExport,
mergeHookFailureDigests,
buildAutomationScheduleDashboard,
@@ -1797,6 +1803,12 @@ class PearcordPlatform extends EventEmitter {
const vanityUrlJsonHeal = await this._healVanityUrlExportCursorOnPartition(gid).catch(() => ({
relisted: 0
}))
const sessionActivityJsonHeal = await this._healSessionActivityExportCursorOnPartition(gid).catch(() => ({
relisted: 0
}))
const avatarDecorationExportJsonHeal = await this._healAvatarDecorationExportCursorOnPartition(gid).catch(() => ({
relisted: 0
}))
return {
voiceApplied,
emojiSlots,
@@ -1857,7 +1869,9 @@ class PearcordPlatform extends EventEmitter {
oauthGrantJsonHeal,
oauthInstallTokenJsonHeal,
inviteLinkJsonHeal,
vanityUrlJsonHeal
vanityUrlJsonHeal,
sessionActivityJsonHeal,
avatarDecorationExportJsonHeal
}
}
@@ -11190,6 +11204,12 @@ class PearcordPlatform extends EventEmitter {
guildInstance.on('profile-banner-export-json-sync', (payload) => {
this._onProfileBannerExportJsonGossip(payload).catch(() => {})
})
guildInstance.on('session-activity-json-sync', (payload) => {
this._onSessionActivityJsonGossip(payload).catch(() => {})
})
guildInstance.on('avatar-decoration-export-json-sync', (payload) => {
this._onAvatarDecorationExportJsonGossip(payload).catch(() => {})
})
guildInstance.on('message-search-request', (payload) => {
this._onMessageSearchRequestGossip(payload).catch(() => {})
})
@@ -26676,6 +26696,172 @@ class PearcordPlatform extends EventEmitter {
return row
}
async _sessionJsonRegistryEntries () {
if (!this.guild?.guild) return []
const selfId = this.identity?.user?.id || ''
const channels = (await this.guild.listChannels().catch(() => [])) || []
const entries = []
const seen = new Set()
const pushSession = (uid, sessionName, sessionActivity, parentId, durationSec = 0, exportedAt = Date.now()) => {
const key = `${uid}:${parentId || sessionName}`
if (seen.has(key)) return
entries.push({
userId: uid,
sessionName: sessionName || 'session',
sessionActivity: sessionActivity || 'desktop',
parentId: parentId || null,
durationSec,
exportedAt
})
seen.add(key)
}
for (const ch of channels.filter((c) => c.type === 'voice' || c.type === 'stage').slice(0, 32)) {
pushSession(selfId, ch.name, ch.type, ch.id, 0)
}
if (selfId && this.presence?.activity) {
pushSession(selfId, this.presence.activity.name || 'Pearcord', this.presence.activity.type || 'playing', null, this.presence.activity.durationSec || 0)
}
if (!entries.length) {
pushSession(selfId, 'Pearcord', 'desktop', null, 0)
}
return entries
}
async getSessionActivityJsonExport () {
const gid = this.guild?.guild?.id || null
const span = this.log.time('session.json', { spanKind: 'session.json', guildId: gid, context: 'read' })
try {
if (!this.guild?.guild) { span.end({ guildSessionJsonCount: 0, skipped: true }); return null }
await this._initDeliveryReceipts(this.guild.guild.id)
const row = await this.deliveryReceipts.getSessionActivityJsonExport()
if (!row?.signature) {
span.end({ guildSessionJsonCount: row ? 1 : 0, hasSignature: false, bridgeKind: 'session.json' })
return row
}
const verified = verifySessionActivityJson(exportBodyFromSlice(row) || row.jsonBody, row.signature, { guildId: this.guild.guild.id, relaySecret: this.guild.guild.id })
const out = { ...row, signatureValid: verified.ok, sessionCount: row.sessionCount || 0 }
span.end({ guildSessionJsonCount: 1, hasSignature: true, signatureValid: verified.ok, bridgeKind: 'session.json' })
return out
} catch (err) {
this.log.error('session.json error', { guildId: gid, context: 'read', error: err?.message || String(err) })
span.fail(err); throw err
}
}
async exportSessionActivityJson (opts = {}) {
if (!this.guild?.guild) throw new Error('no guild')
await this._initDeliveryReceipts(this.guild.guild.id)
const entries = await this._sessionJsonRegistryEntries()
const jsonBody = buildSessionActivityJsonBody(entries)
const signed = signSessionActivityJson(jsonBody, { guildId: this.guild.guild.id, relaySecret: this.guild.guild.id })
const exportedAt = Date.now()
if (opts.recordMesh !== false) {
await this.deliveryReceipts.recordSessionActivityJsonExport({ jsonBody: signed.jsonBody || signed.json, signature: signed.signature, signatureAlg: signed.signatureAlg, exportedAt, sessionCount: entries.length })
}
return { format: 'json', body: signed.json, signature: signed.signature, signatureAlg: signed.signatureAlg, exportedAt, sessionCount: entries.length }
}
async pushSessionActivityJsonToMesh () {
const gid = this.guild?.guild?.id || null
const span = this.log.time('session.json', { spanKind: 'session.json', guildId: gid, context: 'mesh.push' })
try {
if (!this.guild?.guild) throw new Error('no guild')
if (!(await this._hasPerm(PERMISSION.MANAGE_GUILD))) throw new Error('no permission to sync session activity JSON')
const exported = await this.exportSessionActivityJson({ recordMesh: false })
if (!exported.signature) throw new Error('session activity json export not signed')
const payload = { guildId: this.guild.guild.id, exportedAt: exported.exportedAt || Date.now(), exportedBy: this.identity.user?.id || null, jsonBody: exported.body, signature: exported.signature, signatureAlg: exported.signatureAlg, sessionCount: exported.sessionCount || 0 }
if (this.guild.gossipSessionActivityJsonSync) this.guild.gossipSessionActivityJsonSync(payload)
this.emit('session-activity-json-sync', payload)
await this.deliveryReceipts.recordSessionActivityJsonExport({ jsonBody: exported.body, signature: exported.signature, signatureAlg: exported.signatureAlg, exportedAt: exported.exportedAt, sessionCount: exported.sessionCount })
span.end({ guildSessionJsonCount: exported.sessionCount || 1, meshPushed: true, bridgeKind: 'session.json' })
return exported
} catch (err) {
this.log.error('session.json error', { guildId: gid, context: 'mesh.push', error: err?.message || String(err) })
span.fail(err); throw err
}
}
async clearSessionActivityJsonExports () {
if (!this.guild?.guild) throw new Error('no guild')
if (!(await this._hasPerm(PERMISSION.MANAGE_GUILD))) throw new Error('no permission to clear session activity JSON exports')
await this._initDeliveryReceipts(this.guild.guild.id)
const removed = await this.deliveryReceipts.clearSessionActivityJsonExports()
this.emit('session-activity-json-cleared', { removed })
return { removed }
}
async getLastAvatarDecorationExport () {
if (!this.guild?.guild) return null
const snap = this._lastAvatarDecorationExport
if (snap?.guildId === this.guild.guild.id) return snap
const user = this.identity?.user || {}
return { guildId: this.guild.guild.id, userId: user.id || null, exportedAt: Date.now(), decorationCount: 0, signed: false, meshPushed: false }
}
async exportAvatarDecorationMesh (opts = {}) {
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 export avatar decoration mesh')
const user = this.identity?.user || {}
const entries = [{
userId: user.id || '',
displayName: user.displayName || user.username || '',
avatarDecoration: user.avatarDecoration || user.avatarDecorationHash || '',
accentColor: user.accentColor || '',
exportedAt: Date.now()
}]
const jsonBody = buildAvatarDecorationExportJsonBody(entries)
const signed = signAvatarDecorationExportJson(jsonBody, { guildId: this.guild.guild.id, relaySecret: this.guild.guild.id })
const exportedAt = Date.now()
const meta = { guildId: this.guild.guild.id, exportedAt, exportedBy: user.id || null, decorationCount: entries.length, signed: !!signed.signature, meshPushed: false }
this._lastAvatarDecorationExport = meta
if (opts.recordMesh !== false) {
await this.deliveryReceipts.recordAvatarDecorationExportJson({ jsonBody: signed.jsonBody || signed.json, signature: signed.signature, signatureAlg: signed.signatureAlg, exportedAt, decorationCount: entries.length })
}
return { format: 'json', body: signed.json, signature: signed.signature, signatureAlg: signed.signatureAlg, exportedAt, decorationCount: entries.length, meta }
}
async pushAvatarDecorationExportToMesh () {
const gid = this.guild?.guild?.id || null
const span = this.log.time('session.json', { spanKind: 'session.json', guildId: gid, context: 'mesh.avatar-decoration' })
try {
const exported = await this.exportAvatarDecorationMesh({ recordMesh: false })
if (!exported.signature) throw new Error('avatar decoration export json not signed')
const payload = { guildId: this.guild.guild.id, exportedAt: exported.exportedAt || Date.now(), exportedBy: this.identity.user?.id || null, jsonBody: exported.body, signature: exported.signature, signatureAlg: exported.signatureAlg, decorationCount: exported.decorationCount || 0 }
if (this.guild.gossipAvatarDecorationExportJsonSync) this.guild.gossipAvatarDecorationExportJsonSync(payload)
this.emit('avatar-decoration-export-json-sync', payload)
await this.deliveryReceipts.recordAvatarDecorationExportJson({ jsonBody: exported.body, signature: exported.signature, signatureAlg: exported.signatureAlg, exportedAt: exported.exportedAt, decorationCount: exported.decorationCount })
this._lastAvatarDecorationExport = { ...exported.meta, meshPushed: true }
span.end({ guildSessionJsonCount: exported.decorationCount || 0, meshPushed: true, bridgeKind: 'session.json' })
return exported
} catch (err) {
this.log.error('session.json error', { guildId: gid, context: 'mesh.avatar-decoration', error: err?.message || String(err) })
span.fail(err); throw err
}
}
async _onSessionActivityJsonGossip (payload) {
if (!payload?.guildId || !payload?.jsonBody || !payload?.signature) return null
if (this.guild?.guild?.id !== payload.guildId) return null
await this._initDeliveryReceipts(payload.guildId)
this._shouldGossipSessionActivityRegistryJson(payload)
const row = await this.deliveryReceipts.ingestSessionActivityJsonSlice(payload.guildId, payload)
if (row?.duplicate) this._sessionActivityJsonDuplicateAt = row.exportedAt || payload.exportedAt || null
if (row) this.emit('session-activity-json-sync', payload)
return row
}
async _onAvatarDecorationExportJsonGossip (payload) {
if (!payload?.guildId || !payload?.jsonBody || !payload?.signature) return null
if (this.guild?.guild?.id !== payload.guildId) return null
await this._initDeliveryReceipts(payload.guildId)
this._shouldGossipAvatarDecorationExportJson(payload)
const row = await this.deliveryReceipts.ingestAvatarDecorationExportJsonSlice(payload.guildId, payload)
if (row?.duplicate) this._avatarDecorationExportJsonDuplicateAt = row.exportedAt || payload.exportedAt || null
if (row) this.emit('avatar-decoration-export-json-sync', payload)
return row
}
async getAuditExportSchedule () {
if (!this.guild?.guild) {
@@ -31612,6 +31798,11 @@ class PearcordPlatform extends EventEmitter {
let profileBannerExportJsonRows = []
let lastProfileBannerExport = null
let guildActivityInvites = []
let sessionActivityJsonMeta = null
let sessionActivityJsonRows = []
let avatarDecorationExportJsonRows = []
let lastAvatarDecorationExport = null
let guildSessionActivities = []
let lastComplianceSnapshot = null
let lastArchivePeerExport = null
let automationScheduleDashboard = null
@@ -31859,6 +32050,29 @@ class PearcordPlatform extends EventEmitter {
localVersion: this.appVersion
})
}
sessionActivityJsonMeta = await this.getSessionActivityJsonExport().catch(() => null)
sessionActivityJsonRows = (await this.deliveryReceipts.listSessionActivityJsonExports(32)).map((row) => {
if (!row?.signature || !guild?.id) return row
const verified = verifySessionActivityJson(exportBodyFromSlice(row) || row.jsonBody, row.signature, { guildId: guild.id, relaySecret: guild.id })
return { ...row, signatureValid: verified.ok }
})
avatarDecorationExportJsonRows = (await this.deliveryReceipts.listAvatarDecorationExportJsonExports(32)).map((row) => {
if (!row?.signature || !guild?.id) return row
const verified = verifyAvatarDecorationExportJson(exportBodyFromSlice(row) || row.jsonBody, row.signature, { guildId: guild.id, relaySecret: guild.id })
return { ...row, signatureValid: verified.ok }
})
lastAvatarDecorationExport = await this.getLastAvatarDecorationExport()
guildSessionActivities = channels
.filter((c) => c.type === 'voice' || c.type === 'stage')
.slice(0, 16)
.map((c) => ({
id: c.id,
name: c.name,
role: c.type,
parentId: c.parentId || null,
createdAt: c.createdAt || null,
updatedAt: c.updatedAt || Date.now()
}))
}
const auditLogFilterOptions = [
{ id: 'all', label: 'All' },
@@ -32204,6 +32418,10 @@ class PearcordPlatform extends EventEmitter {
this._activityInviteJsonDuplicateAt = null
const profileBannerExportJsonDuplicateAt = this._profileBannerExportJsonDuplicateAt || null
this._profileBannerExportJsonDuplicateAt = null
const sessionActivityJsonDuplicateAt = this._sessionActivityJsonDuplicateAt || null
this._sessionActivityJsonDuplicateAt = null
const avatarDecorationExportJsonDuplicateAt = this._avatarDecorationExportJsonDuplicateAt || null
this._avatarDecorationExportJsonDuplicateAt = null
return {
onboarded: this.onboarded,
sessionReady: this._sessionReady,
@@ -32377,6 +32595,8 @@ class PearcordPlatform extends EventEmitter {
guildWidgetExportJsonDuplicateAt,
activityInviteJsonDuplicateAt,
profileBannerExportJsonDuplicateAt,
sessionActivityJsonDuplicateAt,
avatarDecorationExportJsonDuplicateAt,
digestRelayHandoffJsonMeta,
digestRelayHandoffJsonRows,
archivePeerExportJsonRows,
@@ -32500,6 +32720,13 @@ class PearcordPlatform extends EventEmitter {
(customStatusJsonRows || []).length + (guildCustomStatuses || []).length,
guildActivityJsonCount:
(activityInviteJsonRows || []).length + (guildActivityInvites || []).length,
sessionActivityJsonMeta,
sessionActivityJsonRows,
avatarDecorationExportJsonRows,
lastAvatarDecorationExport,
guildSessionActivities,
guildSessionJsonCount:
(sessionActivityJsonRows || []).length + (guildSessionActivities || []).length,
automationScheduleDashboard,
automationHealthDashboard,
automationDigestNotifyPrefs,
@@ -33122,6 +33349,7 @@ const { presenceJsonMixin } = require('./presence-json-mixin')
const { notificationJsonMixin } = require('./notification-json-mixin')
const { statusJsonMixin } = require('./status-json-mixin')
const { activityJsonMixin } = require('./activity-json-mixin')
const { sessionJsonMixin } = require('./session-json-mixin')
Object.assign(PearcordPlatform.prototype, pollSchedulingMixin)
Object.assign(PearcordPlatform.prototype, notificationsActivityMixin)
Object.assign(PearcordPlatform.prototype, userSettingsMixin)
@@ -33160,3 +33388,4 @@ Object.assign(PearcordPlatform.prototype, presenceJsonMixin)
Object.assign(PearcordPlatform.prototype, notificationJsonMixin)
Object.assign(PearcordPlatform.prototype, statusJsonMixin)
Object.assign(PearcordPlatform.prototype, activityJsonMixin)
Object.assign(PearcordPlatform.prototype, sessionJsonMixin)