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 70019836b5
commit 8116bcb6bc
12 changed files with 484 additions and 127 deletions
+7 -2
View File
@@ -33,6 +33,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"
@@ -123,14 +124,18 @@ func gatewayCmd() {
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 {
fmt.Printf("Error creating channel manager: %v\n", err)
os.Exit(1)
}
// 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