Files
clawgo/pkg/tools/session_search.go
2026-04-13 13:41:01 +08:00

104 lines
3.2 KiB
Go

package tools
import (
"context"
"fmt"
"strings"
"github.com/YspCoder/clawgo/pkg/session"
)
type SessionSearchTool struct {
manager *session.SessionManager
}
func NewSessionSearchTool(manager *session.SessionManager) *SessionSearchTool {
return &SessionSearchTool{manager: manager}
}
func (t *SessionSearchTool) Name() string { return "session_search" }
func (t *SessionSearchTool) Description() string {
return "Search past session history across JSONL session logs. Use when the user refers to previous conversations, past decisions, or earlier project work."
}
func (t *SessionSearchTool) Parameters() map[string]interface{} {
return map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"query": map[string]interface{}{
"type": "string",
"description": "Keywords or short phrase to search across past sessions.",
},
"limit": map[string]interface{}{
"type": "integer",
"description": "Maximum number of matching sessions to return.",
"default": 5,
},
"kinds": map[string]interface{}{
"type": "array",
"description": "Optional session kinds filter, e.g. main, cron, subagent, hook.",
"items": map[string]interface{}{"type": "string"},
},
"exclude_current": map[string]interface{}{
"type": "boolean",
"description": "Exclude the current session from results when session_key is provided.",
"default": true,
},
"session_key": map[string]interface{}{
"type": "string",
"description": "Optional current session key for exclude_current behavior.",
},
},
"required": []string{"query"},
}
}
func (t *SessionSearchTool) Execute(ctx context.Context, args map[string]interface{}) (string, error) {
_ = ctx
if t == nil || t.manager == nil {
return "", fmt.Errorf("session manager not configured")
}
query := MapStringArg(args, "query")
if query == "" {
return "", fmt.Errorf("query is required")
}
limit := MapIntArg(args, "limit", 5)
kinds := MapStringListArg(args, "kinds")
excludeCurrent, excludeSet := MapBoolArg(args, "exclude_current")
excludeKey := ""
if excludeSet && excludeCurrent {
excludeKey = MapStringArg(args, "session_key")
}
results := t.manager.Search(query, kinds, excludeKey, limit)
if len(results) == 0 {
return fmt.Sprintf("No past sessions matched %q.", query), nil
}
var sb strings.Builder
sb.WriteString(fmt.Sprintf("Session search results for %q:\n\n", query))
for _, item := range results {
sb.WriteString(fmt.Sprintf("- %s kind=%s updated=%s score=%d\n", item.Key, item.Kind, item.UpdatedAt.Format("2006-01-02 15:04:05"), item.Score))
if summary := strings.TrimSpace(item.Summary); summary != "" {
sb.WriteString(" summary: " + singleLine(summary, 220) + "\n")
}
for _, snippet := range item.Snippets {
line := singleLine(snippet.Content, 220)
sb.WriteString(fmt.Sprintf(" [#%d][%s] %s\n", snippet.Seq, snippet.Role, line))
}
sb.WriteString("\n")
}
return strings.TrimSpace(sb.String()), nil
}
func (t *SessionSearchTool) ParallelSafe() bool { return true }
func singleLine(s string, max int) string {
s = strings.TrimSpace(strings.ReplaceAll(s, "\n", " "))
if max > 0 && len(s) > max {
return s[:max] + "..."
}
return s
}