package server import ( "regexp" "sort" "strings" "github.com/daniel/cursor-adapter/internal/types" ) // Cowork-style mount path: /sessions/--/mnt/ // (and any deeper subpath; we capture only the mount root). var mountPathRe = regexp.MustCompile(`/sessions/[a-z][a-z0-9]*(?:-[a-z][a-z0-9]*)+/mnt/[^\s/'"]+`) // extractMountHints walks all prior tool_result blocks in the conversation // and returns any Cowork-style /sessions//mnt/ mount roots // they reveal, deduped & sorted. // // This is purely stateless — we re-derive the set from the request body // every turn. No server-side cache to invalidate, and it survives proxy // restarts because the caller (Claude Desktop) replays the full history // on each request anyway. func extractMountHints(req types.AnthropicMessagesRequest) []string { seen := map[string]struct{}{} for _, m := range req.Messages { for _, b := range m.Content { if b.Type != "tool_result" { continue } for _, p := range mountPathRe.FindAllString(renderToolResultContent(b.Content), -1) { seen[p] = struct{}{} } } } if len(seen) == 0 { return nil } out := make([]string, 0, len(seen)) for p := range seen { out = append(out, p) } sort.Strings(out) return out } // renderMountHints turns a list of mount roots into a prompt section the // brain can refer to. Returns "" when there are no hints. func renderMountHints(hints []string) string { if len(hints) == 0 { return "" } var b strings.Builder b.WriteString("Known host-mount paths (discovered earlier in this conversation, prefer these for any host file work):\n") for _, h := range hints { b.WriteString("- ") b.WriteString(h) b.WriteByte('\n') } return b.String() }