fix(mail): handle crew/polecat ambiguity in notification session lookup (#914)
When sending mail notifications, the canonical address format (rig/name) doesn't distinguish between crew workers (session: gt-rig-crew-name) and polecats (session: gt-rig-name). This caused notifications to fail for crew workers in other rigs. Solution: Try both possible session IDs when the address is ambiguous, using the first one that has an active session. Supersedes PR #896 which only handled slash-to-dash conversion. Fixes: gt-h5btjg
This commit is contained in:
+58
-20
@@ -991,47 +991,85 @@ func (r *Router) GetMailbox(address string) (*Mailbox, error) {
|
||||
|
||||
// notifyRecipient sends a notification to a recipient's tmux session.
|
||||
// Uses NudgeSession to add the notification to the agent's conversation history.
|
||||
// Supports mayor/, rig/polecat, and rig/refinery addresses.
|
||||
// Supports mayor/, deacon/, rig/crew/name, rig/polecats/name, and rig/name addresses.
|
||||
func (r *Router) notifyRecipient(msg *Message) error {
|
||||
sessionID := addressToSessionID(msg.To)
|
||||
if sessionID == "" {
|
||||
sessionIDs := addressToSessionIDs(msg.To)
|
||||
if len(sessionIDs) == 0 {
|
||||
return nil // Unable to determine session ID
|
||||
}
|
||||
|
||||
// Check if session exists
|
||||
hasSession, err := r.tmux.HasSession(sessionID)
|
||||
if err != nil || !hasSession {
|
||||
return nil // No active session, skip notification
|
||||
// Try each possible session ID until we find one that exists.
|
||||
// This handles the ambiguity where canonical addresses (rig/name) don't
|
||||
// distinguish between crew workers (gt-rig-crew-name) and polecats (gt-rig-name).
|
||||
for _, sessionID := range sessionIDs {
|
||||
hasSession, err := r.tmux.HasSession(sessionID)
|
||||
if err != nil || !hasSession {
|
||||
continue
|
||||
}
|
||||
|
||||
// Send notification to the agent's conversation history
|
||||
notification := fmt.Sprintf("📬 You have new mail from %s. Subject: %s. Run 'gt mail inbox' to read.", msg.From, msg.Subject)
|
||||
return r.tmux.NudgeSession(sessionID, notification)
|
||||
}
|
||||
|
||||
// Send notification to the agent's conversation history
|
||||
notification := fmt.Sprintf("📬 You have new mail from %s. Subject: %s. Run 'gt mail inbox' to read.", msg.From, msg.Subject)
|
||||
return r.tmux.NudgeSession(sessionID, notification)
|
||||
return nil // No active session found
|
||||
}
|
||||
|
||||
// addressToSessionID converts a mail address to a tmux session ID.
|
||||
// Returns empty string if address format is not recognized.
|
||||
func addressToSessionID(address string) string {
|
||||
// addressToSessionIDs converts a mail address to possible tmux session IDs.
|
||||
// Returns multiple candidates since the canonical address format (rig/name)
|
||||
// doesn't distinguish between crew workers (gt-rig-crew-name) and polecats
|
||||
// (gt-rig-name). The caller should try each and use the one that exists.
|
||||
//
|
||||
// This supersedes the approach in PR #896 which only handled slash-to-dash
|
||||
// conversion but didn't address the crew/polecat ambiguity.
|
||||
func addressToSessionIDs(address string) []string {
|
||||
// Mayor address: "mayor/" or "mayor"
|
||||
if strings.HasPrefix(address, "mayor") {
|
||||
return session.MayorSessionName()
|
||||
return []string{session.MayorSessionName()}
|
||||
}
|
||||
|
||||
// Deacon address: "deacon/" or "deacon"
|
||||
if strings.HasPrefix(address, "deacon") {
|
||||
return session.DeaconSessionName()
|
||||
return []string{session.DeaconSessionName()}
|
||||
}
|
||||
|
||||
// Rig-based address: "rig/target"
|
||||
// Rig-based address: "rig/target" or "rig/crew/name" or "rig/polecats/name"
|
||||
parts := strings.SplitN(address, "/", 2)
|
||||
if len(parts) != 2 || parts[1] == "" {
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
rig := parts[0]
|
||||
target := parts[1]
|
||||
|
||||
// Polecat: gt-rig-polecat
|
||||
// Refinery: gt-rig-refinery (if refinery has its own session)
|
||||
return fmt.Sprintf("gt-%s-%s", rig, target)
|
||||
// If target already has crew/ or polecats/ prefix, use it directly
|
||||
// e.g., "gastown/crew/holden" → "gt-gastown-crew-holden"
|
||||
if strings.HasPrefix(target, "crew/") || strings.HasPrefix(target, "polecats/") {
|
||||
return []string{fmt.Sprintf("gt-%s-%s", rig, strings.ReplaceAll(target, "/", "-"))}
|
||||
}
|
||||
|
||||
// Special cases that don't need crew variant
|
||||
if target == "witness" || target == "refinery" {
|
||||
return []string{fmt.Sprintf("gt-%s-%s", rig, target)}
|
||||
}
|
||||
|
||||
// For normalized addresses like "gastown/holden", try both:
|
||||
// 1. Crew format: gt-gastown-crew-holden
|
||||
// 2. Polecat format: gt-gastown-holden
|
||||
// Return crew first since crew workers are more commonly missed.
|
||||
return []string{
|
||||
session.CrewSessionName(rig, target), // gt-rig-crew-name
|
||||
session.PolecatSessionName(rig, target), // gt-rig-name
|
||||
}
|
||||
}
|
||||
|
||||
// addressToSessionID converts a mail address to a tmux session ID.
|
||||
// Returns empty string if address format is not recognized.
|
||||
// Deprecated: Use addressToSessionIDs for proper crew/polecat handling.
|
||||
func addressToSessionID(address string) string {
|
||||
ids := addressToSessionIDs(address)
|
||||
if len(ids) == 0 {
|
||||
return ""
|
||||
}
|
||||
return ids[0]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user