Skip to content

Commit 8ddc268

Browse files
authored
Merge pull request #267 from AppSprout-dev/feat/agent-ux-improvements
Agent UX: type filter, recall_project noise reduction, current session
2 parents 8434586 + 89328ec commit 8ddc268

3 files changed

Lines changed: 41 additions & 10 deletions

File tree

internal/agent/retrieval/agent.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ type QueryRequest struct {
136136
TimeTo time.Time // if set, filter memories created before this time
137137
Source string // if set, filter by memory source (mcp, filesystem, terminal, clipboard)
138138
State string // if set, filter by memory state (active, fading, archived)
139+
Type string // if set, filter by memory type (decision, error, insight, learning, general)
139140
MinSalience float32 // if > 0, filter out memories below this salience
140141
IncludeSuppressed bool // if true, include recall-suppressed memories
141142
}
@@ -265,7 +266,7 @@ func (ra *RetrievalAgent) Query(ctx context.Context, req QueryRequest) (QueryRes
265266
ranked := ra.rankResults(ctx, activated, req.IncludeReasoning)
266267

267268
// Step 7: Apply filters (project, time, source, state, salience)
268-
if req.Project != "" || !req.TimeFrom.IsZero() || !req.TimeTo.IsZero() || req.Source != "" || req.State != "" || req.MinSalience > 0 {
269+
if req.Project != "" || !req.TimeFrom.IsZero() || !req.TimeTo.IsZero() || req.Source != "" || req.State != "" || req.Type != "" || req.MinSalience > 0 {
269270
ranked = ra.applyFilters(ranked, req)
270271
}
271272

@@ -1061,6 +1062,9 @@ func (ra *RetrievalAgent) applyFilters(results []store.RetrievalResult, req Quer
10611062
if req.State != "" && r.Memory.State != req.State {
10621063
continue
10631064
}
1065+
if req.Type != "" && r.Memory.Type != req.Type {
1066+
continue
1067+
}
10641068
if req.MinSalience > 0 && r.Memory.Salience < req.MinSalience {
10651069
continue
10661070
}

internal/mcp/server.go

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,11 @@ func (srv *MCPServer) handleRecall(ctx context.Context, args map[string]interfac
416416
state = s
417417
}
418418

419+
memType := ""
420+
if t, ok := args["type"].(string); ok {
421+
memType = t
422+
}
423+
419424
var minSalience float32
420425
if ms, ok := args["min_salience"].(float64); ok {
421426
minSalience = float32(ms)
@@ -457,7 +462,7 @@ func (srv *MCPServer) handleRecall(ctx context.Context, args map[string]interfac
457462
srv.log.Error("concept recall failed", "concepts", concepts, "error", err)
458463
return nil, fmt.Errorf("concept recall failed: %w", err)
459464
}
460-
filtered := filterMemories(memories, source, state, minSalience)
465+
filtered := filterMemories(memories, source, state, memType, minSalience)
461466
text := fmt.Sprintf("Found %d memories matching concepts %v:\n\n", len(filtered), concepts)
462467
for i, mem := range filtered {
463468
text += fmt.Sprintf("%d. %s\n Summary: %s\n Concepts: %v\n\n",
@@ -477,6 +482,7 @@ func (srv *MCPServer) handleRecall(ctx context.Context, args map[string]interfac
477482
Project: project,
478483
Source: source,
479484
State: state,
485+
Type: memType,
480486
MinSalience: minSalience,
481487
}
482488

@@ -779,8 +785,12 @@ func (srv *MCPServer) handleRecallProject(ctx context.Context, args map[string]i
779785
limit = int(l)
780786
}
781787

782-
// Parse optional filters
783-
source, state, minSalience := parseRecallFilters(args)
788+
// Parse optional filters — default min_salience to 0.7 for project recall
789+
// to filter out watcher noise that agents don't need.
790+
source, state, memType, minSalience := parseRecallFilters(args)
791+
if _, explicit := args["min_salience"]; !explicit && minSalience == 0 {
792+
minSalience = 0.7
793+
}
784794

785795
// Get project summary
786796
summary, err := srv.store.GetProjectSummary(ctx, project)
@@ -824,6 +834,7 @@ func (srv *MCPServer) handleRecallProject(ctx context.Context, args map[string]i
824834
Project: project,
825835
Source: source,
826836
State: state,
837+
Type: memType,
827838
MinSalience: minSalience,
828839
}
829840

@@ -849,7 +860,7 @@ func (srv *MCPServer) handleRecallProject(ctx context.Context, args map[string]i
849860
srv.log.Error("project recall failed", "project", project, "error", err)
850861
return nil, fmt.Errorf("project recall failed: %w", err)
851862
}
852-
filtered := filterMemories(memories, source, state, minSalience)
863+
filtered := filterMemories(memories, source, state, memType, minSalience)
853864

854865
text += fmt.Sprintf("\nMemories (%d):\n\n", len(filtered))
855866
for i, mem := range filtered {
@@ -875,7 +886,7 @@ func (srv *MCPServer) handleRecallTimeline(ctx context.Context, args map[string]
875886
limit = int(l)
876887
}
877888

878-
source, state, minSalience := parseRecallFilters(args)
889+
source, state, memType, minSalience := parseRecallFilters(args)
879890

880891
from := time.Now().Add(-time.Duration(hoursBack) * time.Hour)
881892
to := time.Now()
@@ -886,7 +897,7 @@ func (srv *MCPServer) handleRecallTimeline(ctx context.Context, args map[string]
886897
return nil, fmt.Errorf("timeline recall failed: %w", err)
887898
}
888899

889-
filtered := filterMemories(memories, source, state, minSalience)
900+
filtered := filterMemories(memories, source, state, memType, minSalience)
890901

891902
text := fmt.Sprintf("Timeline (last %dh, %d memories):\n\n", hoursBack, len(filtered))
892903
for i, mem := range filtered {
@@ -1511,21 +1522,24 @@ func toolError(text string) map[string]interface{} {
15111522
}
15121523

15131524
// parseRecallFilters extracts optional source/state/min_salience from MCP args.
1514-
func parseRecallFilters(args map[string]interface{}) (source, state string, minSalience float32) {
1525+
func parseRecallFilters(args map[string]interface{}) (source, state, memType string, minSalience float32) {
15151526
if s, ok := args["source"].(string); ok {
15161527
source = s
15171528
}
15181529
if s, ok := args["state"].(string); ok {
15191530
state = s
15201531
}
1532+
if t, ok := args["type"].(string); ok {
1533+
memType = t
1534+
}
15211535
if ms, ok := args["min_salience"].(float64); ok {
15221536
minSalience = float32(ms)
15231537
}
15241538
return
15251539
}
15261540

15271541
// filterMemories filters a slice of memories by source, state, and minimum salience.
1528-
func filterMemories(memories []store.Memory, source, state string, minSalience float32) []store.Memory {
1542+
func filterMemories(memories []store.Memory, source, state, memType string, minSalience float32) []store.Memory {
15291543
var filtered []store.Memory
15301544
for _, m := range memories {
15311545
if source != "" && m.Source != source {
@@ -1534,6 +1548,9 @@ func filterMemories(memories []store.Memory, source, state string, minSalience f
15341548
if state != "" && m.State != state {
15351549
continue
15361550
}
1551+
if memType != "" && m.Type != memType {
1552+
continue
1553+
}
15371554
if minSalience > 0 && m.Salience < minSalience {
15381555
continue
15391556
}
@@ -1627,6 +1644,11 @@ func (srv *MCPServer) handleRecallSession(ctx context.Context, args map[string]i
16271644
return nil, fmt.Errorf("session_id parameter is required")
16281645
}
16291646

1647+
// Allow "current" to resolve to the active MCP session.
1648+
if sessionID == "current" {
1649+
sessionID = srv.sessionID
1650+
}
1651+
16301652
limit := 20
16311653
if l, ok := args["limit"].(float64); ok && int(l) > 0 {
16321654
limit = int(l)

internal/mcp/tools.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ func recallToolDef() ToolDefinition {
6161
"type": "string",
6262
"description": "Filter by memory source: mcp, filesystem, terminal, clipboard",
6363
},
64+
"type": map[string]interface{}{
65+
"type": "string",
66+
"description": "Filter by memory type: decision, error, insight, learning, general",
67+
"enum": []string{"decision", "error", "insight", "learning", "general"},
68+
},
6469
"min_salience": map[string]interface{}{
6570
"type": "number",
6671
"description": "Minimum salience threshold (0.0-1.0). Filters out low-quality memories.",
@@ -370,7 +375,7 @@ func listSessionsToolDef() ToolDefinition {
370375
func recallSessionToolDef() ToolDefinition {
371376
return ToolDefinition{
372377
Name: "recall_session",
373-
Description: "Retrieve all memories from a specific MCP session, ordered by creation time. Use list_sessions to find session IDs.",
378+
Description: "Retrieve all memories from a specific MCP session, ordered by creation time. Use \"current\" for the active session, or list_sessions to find past session IDs.",
374379
InputSchema: map[string]interface{}{
375380
"type": "object",
376381
"properties": map[string]interface{}{

0 commit comments

Comments
 (0)