refactor(media): add MediaStore for unified media file lifecycle management

Channels previously deleted downloaded media files via defer os.Remove,
racing with the async Agent consumer. Introduce MediaStore to decouple
file ownership: channels register files on download, Agent releases them
after processing via ReleaseAll(scope).

- New pkg/media with MediaStore interface + FileMediaStore implementation
- InboundMessage gains MediaScope field for lifecycle tracking
- BaseChannel gains SetMediaStore/GetMediaStore + BuildMediaScope helper
- Manager injects MediaStore into channels; AgentLoop releases on completion
- Telegram, Discord, Slack, OneBot, LINE channels migrated from defer
  os.Remove to store.Store() with media:// refs
This commit is contained in:
Hoshina
2026-02-22 23:27:55 +08:00
parent a91de8546c
commit 038fdf5000
12 changed files with 484 additions and 127 deletions
+7 -2
View File
@@ -32,6 +32,7 @@ import (
"github.com/sipeed/picoclaw/pkg/health"
"github.com/sipeed/picoclaw/pkg/heartbeat"
"github.com/sipeed/picoclaw/pkg/logger"
"github.com/sipeed/picoclaw/pkg/media"
"github.com/sipeed/picoclaw/pkg/providers"
"github.com/sipeed/picoclaw/pkg/state"
"github.com/sipeed/picoclaw/pkg/tools"
@@ -116,13 +117,17 @@ func gatewayCmd(debug bool) error {
return tools.SilentResult(response)
})
channelManager, err := channels.NewManager(cfg, msgBus)
// Create media store for file lifecycle management
mediaStore := media.NewFileMediaStore()
channelManager, err := channels.NewManager(cfg, msgBus, mediaStore)
if err != nil {
return fmt.Errorf("error creating channel manager: %w", err)
}
// Inject channel manager into agent loop for command handling
// Inject channel manager and media store into agent loop
agentLoop.SetChannelManager(channelManager)
agentLoop.SetMediaStore(mediaStore)
var transcriber *voice.GroqTranscriber
groqAPIKey := cfg.Providers.Groq.APIKey