Merge pull request #2992 from chengzhichao-xydt/codex/skip-main-session-alias-promotion

fix(session): skip main-session alias during history promotion
This commit is contained in:
Mauro
2026-06-04 09:01:26 +02:00
committed by GitHub
+38
View File
@@ -4,6 +4,8 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"context" "context"
"crypto/sha256"
"encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"hash/fnv" "hash/fnv"
@@ -227,6 +229,11 @@ func (s *JSONLStore) UpsertSessionMeta(
// PromoteAliasHistory atomically promotes the first non-empty alias session // PromoteAliasHistory atomically promotes the first non-empty alias session
// into the canonical session when the canonical session is still empty. // into the canonical session when the canonical session is still empty.
//
// Main-session aliases (e.g. "agent:main:main" or its opaque form) are
// skipped during promotion. The main session is a shared global fallback
// and promoting its history into individual sessions would attach stale
// messages to every new Web UI session (issue #2972).
func (s *JSONLStore) PromoteAliasHistory( func (s *JSONLStore) PromoteAliasHistory(
_ context.Context, _ context.Context,
sessionKey string, sessionKey string,
@@ -240,6 +247,9 @@ func (s *JSONLStore) PromoteAliasHistory(
aliases = normalizeAliases(sessionKey, aliases) aliases = normalizeAliases(sessionKey, aliases)
for _, alias := range aliases { for _, alias := range aliases {
if isMainSessionAlias(alias) {
continue
}
unlock := s.lockSessionPair(sessionKey, alias) unlock := s.lockSessionPair(sessionKey, alias)
promoted, err := s.promoteAliasHistoryLocked(sessionKey, alias, scope, aliases) promoted, err := s.promoteAliasHistoryLocked(sessionKey, alias, scope, aliases)
unlock() unlock()
@@ -251,6 +261,34 @@ func (s *JSONLStore) PromoteAliasHistory(
return false, nil return false, nil
} }
// isMainSessionAlias reports whether alias is the legacy or opaque main-session
// key. The main session ("agent:<agent>:main") is a shared fallback and should
// not have its history promoted into individual per-channel sessions.
func isMainSessionAlias(alias string) bool {
if alias == "" {
return false
}
// Legacy form: "agent:main:main" (exactly 3 colon-separated parts)
// Must not match "agent:sales:direct:main" etc.
if strings.HasPrefix(alias, "agent:") && strings.HasSuffix(alias, ":main") {
parts := strings.SplitN(alias, ":", 4)
if len(parts) == 3 {
return true
}
}
// Opaque form: "sk_v1_" + SHA256("agent:main:main")
if strings.HasPrefix(alias, "sk_v1_") {
for _, agentID := range []string{"main", "Main", "MAIN"} {
legacy := "agent:" + agentID + ":main"
hash := sha256.Sum256([]byte(legacy))
if "sk_v1_"+hex.EncodeToString(hash[:]) == alias {
return true
}
}
}
return false
}
// ResolveSessionKey returns the canonical session key for a candidate key. // ResolveSessionKey returns the canonical session key for a candidate key.
// It short-circuits direct canonical keys when possible, then scans metadata // It short-circuits direct canonical keys when possible, then scans metadata
// once to resolve aliases or canonical metadata keys. // once to resolve aliases or canonical metadata keys.