From 5bdb98c1a71a6580362ccbfcf202c0e4c0230f18 Mon Sep 17 00:00:00 2001 From: Felix Martin Date: Fri, 5 Jun 2026 19:43:16 -0400 Subject: [PATCH] feat(keeld): construct AW memory store, nop fallback when AW is down --- cmd/keeld/main.go | 19 +++++++++++++++++++ internal/harness/services.go | 5 ++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/cmd/keeld/main.go b/cmd/keeld/main.go index 782ecaf..62e0be5 100644 --- a/cmd/keeld/main.go +++ b/cmd/keeld/main.go @@ -14,10 +14,12 @@ import ( "time" "keel/internal/ai" + "keel/internal/aw" "keel/internal/enforce" "keel/internal/evidence" "keel/internal/harness" "keel/internal/knowledge" + "keel/internal/memory" "keel/internal/mode" "keel/internal/mode/focus" "keel/internal/mode/offscreen" @@ -63,11 +65,28 @@ func main() { return harness.Services{}, fmt.Errorf("%w: %v", settings.ErrInvalidBackend, err) } svc := ai.NewService(backend) + + // Defend against a hand-blanked aw_url: SeedFromEnv defaults this only on + // first run, but Load returns whatever the JSON file holds on later starts. + awURL := s.AWURL + if awURL == "" { + awURL = "http://localhost:5600" + } + var mem memory.Store = memory.NewNop() + awc := aw.New(awURL) + if err := awc.EnsureBucket(context.Background(), "keel.events", "keel.event", "keel"); err != nil { + log.Printf("memory: AW unavailable at %s (%v); running without memory", awURL, err) + } else { + mem = memory.NewAWStore(awc, "keel.events") + log.Printf("memory: keel.events ready at %s", awURL) + } + return harness.Services{ AI: svc, Tasks: tasks.NewMarvin(s.MarvinCmd), Knowledge: knowledge.NewFileSource(s.KnowledgePath), Enforce: enforce.NewGuard(), + Memory: mem, Clock: time.Now, }, nil } diff --git a/internal/harness/services.go b/internal/harness/services.go index e08d4b7..4540e6e 100644 --- a/internal/harness/services.go +++ b/internal/harness/services.go @@ -6,18 +6,21 @@ import ( "keel/internal/ai" "keel/internal/enforce" "keel/internal/knowledge" + "keel/internal/memory" "keel/internal/tasks" ) // Services is the shared infrastructure every mode receives from the harness. // Dir is the mode's namespaced persistence directory (~/.keel/modes/); // modes build their own file paths under it. Notify fires the harness change -// listeners (web SSE + status bar). +// listeners (web SSE + status bar). Memory is Keel's durable cross-run event +// store (AW-backed, or a nop when AW is down). type Services struct { AI *ai.Service Tasks tasks.Provider Knowledge knowledge.Source Enforce enforce.Guard + Memory memory.Store Clock func() time.Time Dir string Notify func()