Skip to content
Open
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
ca7451d
fix(plugin-discord): merge multi-bot support with printBanner from up…
odilitime Dec 23, 2025
9e4eb68
fix(plugin-discord): consolidate IDiscordService interface and add co…
odilitime Dec 23, 2025
91168f9
fix(plugin-discord): merge multi-client registry, permission audit ev…
odilitime Dec 23, 2025
d002915
fix(plugin-discord): merge audio channel system, use runtime.logger c…
odilitime Dec 23, 2025
5ad4833
fix(plugin-discord): add attachment handling to progressive update path
odilitime Dec 23, 2025
ef2c11a
fix(plugin-discord): add component type handling and fix null filtering
odilitime Dec 23, 2025
912bb86
chore(plugin-discord): update attachments module
odilitime Dec 23, 2025
5cd2a1b
chore(plugin-discord): update environment module
odilitime Dec 23, 2025
5a4e3ba
chore(plugin-discord): remove unused UUID import
odilitime Dec 23, 2025
10c39a8
chore(plugin-discord): update tests module
odilitime Dec 23, 2025
676e11a
test(plugin-discord): update MessageManager test mocks
odilitime Dec 23, 2025
f99221a
feat(plugin-discord): add ClientRegistry for multi-bot support
odilitime Dec 23, 2025
72032ba
feat(plugin-discord): add IAudioSink and IAudioBroadcast contracts
odilitime Dec 23, 2025
9a18be9
feat(plugin-discord): add DiscordAudioSink implementation
odilitime Dec 23, 2025
1575dab
feat(plugin-discord): add audio channel priority system
odilitime Dec 23, 2025
b0bdeb8
feat(plugin-discord): add progressive message updates
odilitime Dec 23, 2025
1fa716a
feat(plugin-discord): add voice connection manager
odilitime Dec 23, 2025
63bbe6a
feat(plugin-discord): add audioState provider
odilitime Dec 23, 2025
bc38752
feat(plugin-discord): add setListeningActivity and setVoiceChannelSta…
odilitime Dec 23, 2025
27bc17c
test(plugin-discord): add VoiceManager tests
odilitime Dec 23, 2025
4c22966
test(plugin-discord): add multi-bot audio tests
odilitime Dec 23, 2025
79b5827
test(plugin-discord): add progressive message tests
odilitime Dec 23, 2025
275907c
test(plugin-discord): add token validation tests
odilitime Dec 23, 2025
d18416a
docs(plugin-discord): update README with multi-bot and audio features
odilitime Dec 23, 2025
693f58a
docs(plugin-discord): add architecture documentation
odilitime Dec 23, 2025
86c2d1d
token can't be null
odilitime Dec 23, 2025
8fefa21
Unused import logger.
odilitime Dec 23, 2025
3b18151
Unused variable playPromise.
odilitime Dec 23, 2025
96776a3
Use setPresence({ activities: [] }) to clear bot activity instead of …
odilitime Dec 24, 2025
f266fab
Incomplete cleanup: missing timer/bridge cleanup.
odilitime Dec 24, 2025
a1de596
Bug: Event emission accesses deleted map entry.
odilitime Dec 24, 2025
d6f836f
VoiceTarget play and stop methods should handle errors gracefully.
odilitime Dec 24, 2025
5edb78d
Merge branch '1.x' of https://github.com/elizaos-plugins/plugin-disco…
odilitime Dec 24, 2025
2967be9
fix(plugin-discord): resolve merge conflicts and improve logging
odilitime Dec 24, 2025
c081fa6
fix(plugin-discord): fix audio sink reconnection and voice settings p…
odilitime Dec 24, 2025
f8eab34
fix(plugin-discord): multiple bug fixes and improvements
odilitime Dec 24, 2025
2d2983b
fix(plugin-discord): pre-populate activeConnections with existing voi…
odilitime Dec 24, 2025
cb8fab8
fix(plugin-discord): fix test imports and remove external plugin depe…
odilitime Dec 24, 2025
308fa93
fix(plugin-discord): add LLM response validation in setListeningActivity
odilitime Dec 24, 2025
743af18
improve backwards support
odilitime Dec 24, 2025
feb536b
fix: resolve merge conflicts and add new providers
odilitime Dec 24, 2025
8ddc016
fix: resolve merge conflicts and add new providers
odilitime Dec 24, 2025
e8e1374
fix: resolve merge conflict in service.ts - add generateInviteUrl import
odilitime Dec 24, 2025
058453c
fix: forbidden emoji fallback and multi-token config check
odilitime Dec 24, 2025
87e358f
fix: improve emoji fallback and standardize tests on bun:test
odilitime Dec 24, 2025
0c1ef0f
fix: remove unused generateInviteUrl import from service.ts
odilitime Dec 24, 2025
3396087
fix: filter base64 images from memories to prevent context bloat
odilitime Dec 24, 2025
a4c2ed4
fix: add voiceManager null guards in auto-join logic
odilitime Dec 24, 2025
9c5f613
fix(plugin-discord): reduce log spam and add DISCORD_SERVICE_NAME con…
odilitime Dec 29, 2025
d1c15fb
fix(plugin-discord): track auto-join timeout and use correct voice ma…
odilitime Dec 29, 2025
7a7ba8c
feat(plugin-discord): use character name in logs instead of agentId
odilitime Dec 29, 2025
ae4d8e1
fix(plugin-discord): use UUID for Memory.agentId and fix member fetch…
odilitime Dec 29, 2025
81a6da2
fix(plugin-discord): prevent race condition in progressive message up…
odilitime Dec 29, 2025
d163416
fix(plugin-discord): use UUID for Memory.agentId in voice.ts
odilitime Dec 29, 2025
93fb31f
feat(discord): reply context, agent role provider, backwards compat, …
odilitime Dec 30, 2025
4f96e02
fix(discord): setVoiceChannelStatus backwards compat, isDataUrl dedup…
odilitime Dec 30, 2025
760f5ee
fix(discord): add Partials for DM message support
odilitime Jan 15, 2026
a0773c9
fix(discord): emoji fallback and client cleanup on failed registration
odilitime Jan 15, 2026
be12cf2
fix(discord): await pending logins before destroyAll cleanup
odilitime Jan 15, 2026
efb42f9
fix(discord): prevent listener leaks and race conditions in loginBot
odilitime Jan 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 233 additions & 0 deletions COORDINATION_PATTERNS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
# Coordination Patterns for Music Playback

> **Status**: Implemented (Broadcast Architecture)
> **Last Updated**: December 2025

## Overview

This document describes the coordination patterns used in the music playback system, based on the broadcast architecture with `IAudioBroadcast` and `IAudioSink` contracts.

## Architecture Summary

```
┌─────────────────────────────────────────────────────────┐
│ plugin-music-player │
│ MusicQueue → Broadcast (IAudioBroadcast) → Multiplex │
└─────────────────────────────────────────────────────────┘
Events: 'track:started', 'track:finished'
Method: subscribe() / unsubscribe()
┌─────────────────────────▼───────────────────────────────┐
│ plugin-discord │
│ DiscordAudioSink (IAudioSink) ← feed(stream) │
│ Events: 'statusChange' │
└─────────────────────────────────────────────────────────┘
```

## Pattern 1: Contract-Based Decoupling

### Problem
Plugins need to communicate without tight coupling.

### Solution
Define contracts (interfaces) that each plugin owns:

```typescript
// plugin-music-player owns IAudioBroadcast
interface IAudioBroadcast {
subscribe(consumerId: string): AudioSubscription;
unsubscribe(consumerId: string): void;
feedAudio(stream: Readable, metadata?: AudioBroadcastMetadata): Promise<void>;
// ...
}

// plugin-discord owns IAudioSink
interface IAudioSink {
feed(stream: Readable): Promise<void>;
connect(channelId: string): Promise<void>;
disconnect(): Promise<void>;
// ...
}
```

### Benefits
- Plugins only depend on contracts, not implementations
- Either plugin can be replaced or upgraded independently
- Clear ownership boundaries

## Pattern 2: Event-Based Coordination

### Problem
External plugins (radio, DJ) need to know when tracks start/finish.

### Solution
Use EventEmitter for state change notifications:

```typescript
// IAudioBroadcast emits events
broadcast.on('track:started', (metadata) => {
console.log(`Now playing: ${metadata.title}`);
});

broadcast.on('track:finished', (metadata) => {
// Trigger next action
});

broadcast.on('silence:started', () => {
// Queue is empty
});

// IAudioSink emits status changes
sink.on('statusChange', (status) => {
// 'connected' | 'disconnected' | 'connecting' | 'error'
});
```

### Use Cases
- Radio plugin listening for tracks to announce
- DJ plugin waiting for track finish to add commentary
- Web UI updating now-playing display

## Pattern 3: Auto-Wiring via Service Discovery

### Problem
Plugins need to connect to each other at runtime.

### Solution
Use `runtime.getService()` for discovery and wire automatically:

```typescript
// In MusicService (plugin-music-player)
async autoSubscribeDiscord(guildId: string, broadcast: IAudioBroadcast) {
const discordService = this.runtime.getService('discord');
if (!discordService) return; // Graceful degradation

const sink = discordService.getAudioSink(guildId);
if (!sink) return;

// Auto-wire on connection
sink.on('statusChange', async (status) => {
if (status === 'connected') {
const subscription = broadcast.subscribe(`discord-${guildId}`);
await sink.feed(subscription.stream);
}
});
}
```

### Benefits
- Zero manual configuration
- Works when both plugins loaded
- Gracefully degrades when one is missing

## Pattern 4: Reconnection Handling

### Problem
Network hiccups cause Discord disconnections; playback should resume.

### Solution
Use `statusChange` events for automatic recovery:

```typescript
sink.on('statusChange', async (status) => {
if (status === 'connected') {
// Re-subscribe to get fresh stream from live point
const subscription = broadcast.subscribe(`discord-${guildId}`);
await sink.feed(subscription.stream);
logger.info('Discord reconnected, re-subscribed to broadcast');
}
});
```

### Key Insight
Re-subscribing gets the current position in the broadcast, not the beginning. This is because the broadcast is always "live" - like tuning into a radio station.

## Pattern 5: Non-Blocking Multiplexing

### Problem
Slow consumers (laggy web clients) could block the main stream.

### Solution
Each consumer gets an independent `PassThrough` stream with backpressure handling:

```typescript
// In StreamMultiplexer
source.on('data', (chunk) => {
for (const [id, consumer] of consumers) {
if (!consumer.write(chunk)) {
// Consumer buffer full - drop frame for this consumer
logger.debug(`Backpressure on ${id}, dropping chunk`);
}
}
});
```

### Result
- Discord playback unaffected by web client performance
- Each consumer independent
- No blocking the source stream

## Pattern 6: Silence Injection

### Problem
Empty queue causes Discord voice connection timeout.

### Solution
`StreamCore` injects silence frames when no audio is being fed:

```typescript
// In StreamCore
startSilence() {
this.silenceInterval = setInterval(() => {
this.output.write(OPUS_SILENCE_FRAME); // 10ms of silence
}, 10);
}

feed(stream: Readable) {
this.stopSilence(); // Real audio coming
stream.pipe(this.output, { end: false });
stream.on('end', () => this.startSilence());
}
```

### Benefits
- Voice connection stays alive indefinitely
- Seamless transition when new tracks added
- No manual connection management needed

## Pattern Summary

| Pattern | Use Case | Key Mechanism |
|---------|----------|---------------|
| **Contracts** | Plugin decoupling | IAudioBroadcast / IAudioSink |
| **Events** | State notifications | EventEmitter |
| **Auto-Wiring** | Runtime connection | runtime.getService() |
| **Reconnection** | Resilience | statusChange event |
| **Multiplexing** | Multiple consumers | PassThrough + backpressure |
| **Silence** | Connection keep-alive | Interval-based frame injection |

## Implementation Locations

### This Package (plugin-discord)

| Pattern | File |
|---------|------|
| IAudioSink | `src/contracts.ts` |
| Discord sink | `src/sinks/discordAudioSink.ts` |

### External Package (plugin-music-player)

> **Note**: The following files are located in `packages/plugin-music-player/` within the monorepo.

| Pattern | File |
|---------|------|
| IAudioBroadcast | `packages/plugin-music-player/src/contracts.ts` |
| Auto-wiring | `packages/plugin-music-player/src/service.ts` |
| Multiplexing | `packages/plugin-music-player/src/core/streamMultiplexer.ts` |
| Silence injection | `packages/plugin-music-player/src/core/streamCore.ts` |

## Related Documentation

- [MUSIC_ARCHITECTURE.md](./MUSIC_ARCHITECTURE.md) - Full architecture overview
- [plugin-music-player README](../plugin-music-player/README.md) - Usage guide
Loading