fix(config): migrate legacy bindings and optimize session resolve

This commit is contained in:
Hoshina
2026-04-07 22:57:10 +08:00
parent 3d60385958
commit 27db03e5ca
5 changed files with 436 additions and 19 deletions
+31 -19
View File
@@ -224,33 +224,50 @@ func (s *JSONLStore) UpsertSessionMeta(
}
// ResolveSessionKey returns the canonical session key for a candidate key.
// It first checks direct key existence, then scans metadata aliases on miss.
// It short-circuits direct canonical keys when possible, then scans metadata
// once to resolve aliases or canonical metadata keys.
func (s *JSONLStore) ResolveSessionKey(_ context.Context, sessionKey string) (string, bool, error) {
sessionKey = strings.TrimSpace(sessionKey)
if sessionKey == "" {
return "", false, nil
}
hasDirectSession := s.sessionExists(sessionKey)
if hasDirectSession && shouldShortCircuitSessionResolve(sessionKey) {
return sessionKey, true, nil
}
entries, err := os.ReadDir(s.dir)
if err != nil {
return "", false, fmt.Errorf("memory: read sessions dir: %w", err)
}
var directMetaMatch string
for _, entry := range entries {
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".meta.json") {
continue
}
data, readErr := os.ReadFile(filepath.Join(s.dir, entry.Name()))
if readErr != nil {
return "", false, fmt.Errorf("memory: read meta: %w", readErr)
log.Printf("memory: skipping unreadable meta %s: %v", entry.Name(), readErr)
continue
}
var meta SessionMeta
if err := json.Unmarshal(data, &meta); err != nil {
return "", false, fmt.Errorf("memory: decode meta: %w", err)
log.Printf("memory: skipping corrupt meta %s: %v", entry.Name(), err)
continue
}
if meta.Key == "" {
continue
}
if meta.Key == sessionKey {
directMetaMatch = meta.Key
}
for _, alias := range meta.Aliases {
if alias == sessionKey && meta.Key != sessionKey {
return meta.Key, true, nil
@@ -258,30 +275,25 @@ func (s *JSONLStore) ResolveSessionKey(_ context.Context, sessionKey string) (st
}
}
for _, entry := range entries {
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".meta.json") {
continue
}
data, readErr := os.ReadFile(filepath.Join(s.dir, entry.Name()))
if readErr != nil {
return "", false, fmt.Errorf("memory: read meta: %w", readErr)
}
var meta SessionMeta
if err := json.Unmarshal(data, &meta); err != nil {
return "", false, fmt.Errorf("memory: decode meta: %w", err)
}
if meta.Key == sessionKey {
return meta.Key, true, nil
}
if directMetaMatch != "" {
return directMetaMatch, true, nil
}
if s.sessionExists(sessionKey) {
if hasDirectSession {
return sessionKey, true, nil
}
return "", false, nil
}
func shouldShortCircuitSessionResolve(sessionKey string) bool {
sessionKey = strings.TrimSpace(strings.ToLower(sessionKey))
if sessionKey == "" {
return false
}
return !strings.ContainsAny(sessionKey, ":/\\")
}
// readMessages reads valid JSON lines from a .jsonl file, skipping
// the first `skip` lines without unmarshaling them. This avoids the
// cost of json.Unmarshal on logically truncated messages.
+57
View File
@@ -322,6 +322,63 @@ func TestResolveSessionKeyByAlias_PrefersMetadataOverLegacyFile(t *testing.T) {
}
}
func TestResolveSessionKey_DirectHitSkipsCorruptMetadata(t *testing.T) {
store := newTestStore(t)
ctx := context.Background()
if err := store.AddMessage(ctx, "canonical", "user", "hello"); err != nil {
t.Fatalf("AddMessage() error = %v", err)
}
if err := os.WriteFile(
filepath.Join(store.dir, "broken.meta.json"),
[]byte("{not-json"),
0o644,
); err != nil {
t.Fatalf("WriteFile(broken.meta.json) error = %v", err)
}
resolved, found, err := store.ResolveSessionKey(ctx, "canonical")
if err != nil {
t.Fatalf("ResolveSessionKey() error = %v", err)
}
if !found {
t.Fatal("ResolveSessionKey() did not find direct session")
}
if resolved != "canonical" {
t.Fatalf("resolved = %q, want %q", resolved, "canonical")
}
}
func TestResolveSessionKey_SkipsCorruptMetadataDuringAliasScan(t *testing.T) {
store := newTestStore(t)
ctx := context.Background()
if err := store.AddMessage(ctx, "canonical", "user", "hello"); err != nil {
t.Fatalf("AddMessage() error = %v", err)
}
if err := store.UpsertSessionMeta(ctx, "canonical", nil, []string{"legacy:key"}); err != nil {
t.Fatalf("UpsertSessionMeta() error = %v", err)
}
if err := os.WriteFile(
filepath.Join(store.dir, "broken.meta.json"),
[]byte("{not-json"),
0o644,
); err != nil {
t.Fatalf("WriteFile(broken.meta.json) error = %v", err)
}
resolved, found, err := store.ResolveSessionKey(ctx, "legacy:key")
if err != nil {
t.Fatalf("ResolveSessionKey() error = %v", err)
}
if !found {
t.Fatal("ResolveSessionKey() did not find alias")
}
if resolved != "canonical" {
t.Fatalf("resolved = %q, want %q", resolved, "canonical")
}
}
func TestTruncateHistory_KeepLast(t *testing.T) {
store := newTestStore(t)
ctx := context.Background()