mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
fix(config): migrate legacy bindings and optimize session resolve
This commit is contained in:
+31
-19
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user