fix(heartbeat): resolve default agent from DB in managed mode (#31)

In managed mode, agents live in the database, not in config files.
ResolveDefaultAgentID() always returns "default" which doesn't exist
as a real agent, causing heartbeat to silently fail.

Add resolveDefaultAgentManaged() helper that checks the managed DB
for the agent marked is_default=true, falling back to the first
available agent. Apply it in setupHeartbeat().
This commit is contained in:
Michael authored and GitHub committed 2026-03-01 21:31:21 +07:00
1 parent ad82c09720
commit e8607f9686
2 files changed
+30 -3

No files matched your search

+1 -1
View File
@@ -815,7 +815,7 @@ func runGateway() {
}
// Start heartbeat service (matching TS heartbeat-runner.ts).
heartbeatSvc := setupHeartbeat(cfg, agentRouter, sessStore, msgBus, workspace)
heartbeatSvc := setupHeartbeat(cfg, agentRouter, sessStore, msgBus, workspace, managedStores)
if heartbeatSvc != nil {
heartbeatSvc.Start()
}
+29 -2
View File
@@ -369,7 +369,7 @@ func setupTTS(cfg *config.Config) *tts.Manager {
// setupHeartbeat creates and configures the heartbeat service from config.
// Returns nil if heartbeats are disabled (every="0m" or no config).
// Matching TS startHeartbeatRunner().
func setupHeartbeat(cfg *config.Config, router *agent.Router, sess store.SessionStore, msgBus *bus.MessageBus, workspace string) *heartbeat.Service {
func setupHeartbeat(cfg *config.Config, router *agent.Router, sess store.SessionStore, msgBus *bus.MessageBus, workspace string, managedStores *store.Stores) *heartbeat.Service {
hbCfg := cfg.Agents.Defaults.Heartbeat
// Determine interval
@@ -389,7 +389,7 @@ func setupHeartbeat(cfg *config.Config, router *agent.Router, sess store.Session
return nil
}
agentID := cfg.ResolveDefaultAgentID()
agentID := resolveDefaultAgentManaged(cfg, managedStores)
svcCfg := heartbeat.Config{
AgentID: agentID,
@@ -440,3 +440,30 @@ func setupHeartbeat(cfg *config.Config, router *agent.Router, sess store.Session
func parseDuration(s string) (time.Duration, error) {
return time.ParseDuration(s)
}
// resolveDefaultAgentManaged resolves the default agent ID, falling back to the
// managed-mode DB when config returns the generic "default" (which doesn't exist
// as a real agent in managed mode — agents live in the DB, not config).
func resolveDefaultAgentManaged(cfg *config.Config, managedStores *store.Stores) string {
agentID := cfg.ResolveDefaultAgentID()
if managedStores == nil || managedStores.Agents == nil || agentID != config.DefaultAgentID {
return agentID
}
agents, err := managedStores.Agents.List(context.Background(), "")
if err != nil {
slog.Warn("resolveDefaultAgentManaged: failed to list agents", "error", err)
return agentID
}
for _, a := range agents {
if a.IsDefault {
return a.AgentKey
}
}
// No agent marked as default — use first available
if len(agents) > 0 {
return agents[0].AgentKey
}
return agentID
}