Skip to content

Extract internal/convhistory — consolidate 9 cloned conversation-history modules #25

@rafeegnash

Description

@rafeegnash

Problem

Nine providers re-implement conversation persistence:

Each provider has its own Entries cap, safeSlug/sanitizeFilename, historyPath, Load/Save. The five fields that actually differ (entry struct, status struct, slug key) are easy to parameterise. Three providers got the tight model; six did not.

Where

All nine files above.

Fix

Create internal/convhistory:

type History[E ConvEntry] struct {
    file       string  // e.g. "sentry-<slug>.json"
    maxEntries int
    entries    []E
    mu         sync.RWMutex
}

func New[E ConvEntry](provider, slug string, maxEntries int) *History[E]
func (h *History[E]) Add(e E)
func (h *History[E]) Recent(n int) []E
func (h *History[E]) Load() error
func (h *History[E]) Save() error  // 0o600

func SafeSlug(s string) string  // whitelist [A-Za-z0-9_-]

Each provider becomes ~30 lines: register the entry type, expose Add/Save/Recent.

Closes #22 and #23 by construction.

Acceptance criteria

  • All nine providers use the shared package
  • All saved files are 0o600
  • All slugs go through SafeSlug — adversarial inputs (../../etc/passwd) return default or sanitized
  • Generic tests cover the path-traversal cases once, not nine times

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions